perfdhcp.cc 87 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566
  1. /*
  2. * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  3. *
  4. * Permission to use, copy, modify, and/or distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  9. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  10. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  12. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  13. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  14. * PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include <config.h>
  17. #ifndef HAVE_GETIFADDRS
  18. /*
  19. * Solaris 10 does not have the getifaddrs() function available (although it
  20. * is present on Solaris 11 and later). For the present we will not implement
  21. * a replacement (as we do with clock_gettime) as the implementation is
  22. * relatively complex. Just output a message saying that the utility is not
  23. * supported on this operating system.
  24. */
  25. #include <stdio.h>
  26. int
  27. main(const int argc, char* const argv[])
  28. {
  29. fprintf(stderr, "perfdhcp is not supported on this version of the operating system\n");
  30. return (1);
  31. }
  32. #else
  33. /* getifaddrs() present, so the code should compile */
  34. #define __STDC_LIMIT_MACROS
  35. #ifdef __linux__
  36. #ifndef _GNU_SOURCE
  37. #define _GNU_SOURCE
  38. #endif
  39. #endif
  40. #include <sys/types.h>
  41. #include <sys/select.h>
  42. #include <sys/socket.h>
  43. #include <sys/wait.h>
  44. #include <net/if.h>
  45. #include <netinet/in.h>
  46. #include <arpa/inet.h>
  47. #include <ctype.h>
  48. #include <errno.h>
  49. #include <fcntl.h>
  50. #include <ifaddrs.h>
  51. #include <math.h>
  52. #include <netdb.h>
  53. #include <signal.h>
  54. #include <stdint.h>
  55. #include <stdio.h>
  56. #include <stdlib.h>
  57. #include <string.h>
  58. #include <time.h>
  59. #include <unistd.h>
  60. #ifndef HAVE_PSELECT
  61. #include <assert.h>
  62. /* Platforms such as OpenBSD don't provide a pselect(), so we use our
  63. own implementation for this testcase, which wraps around select() and
  64. hence doesn't implement the high precision timer. This implementation
  65. is fine for our purpose. */
  66. static int
  67. pselect (int nfds, fd_set *readfds, fd_set *writefds,
  68. fd_set *exceptfds, const struct timespec *timeout,
  69. const sigset_t *sigmask)
  70. {
  71. struct timeval my_timeout;
  72. /* Our particular usage of pselect() doesn't use these fields. */
  73. assert(writefds == NULL);
  74. assert(exceptfds == NULL);
  75. assert(sigmask == NULL);
  76. my_timeout.tv_sec = timeout->tv_sec;
  77. my_timeout.tv_usec = timeout->tv_nsec / 1000;
  78. return (select(nfds, readfds, writefds, exceptfds, &my_timeout));
  79. }
  80. #endif /* !HAVE_PSELECT */
  81. /* DHCPv4 defines (to be moved/shared) */
  82. #define DHCP_OFF_OPCODE 0
  83. #define DHCP_OFF_HTYPE 1
  84. #define DHCP_OFF_HLEN 2
  85. #define DHCP_OFF_HOPS 3
  86. #define DHCP_OFF_XID 4
  87. #define DHCP_OFF_SECS 8
  88. #define DHCP_OFF_FLAGS 10
  89. #define DHCP_OFF_CIADDR 12
  90. #define DHCP_OFF_YIADDR 16
  91. #define DHCP_OFF_SADDR 20
  92. #define DHCP_OFF_GIADDR 24
  93. #define DHCP_OFF_CHADDR 28
  94. #define DHCP_OFF_SNAME 44
  95. #define DHCP_OFF_FILE 108
  96. #define DHCP_OFF_COOKIE 236
  97. #define DHCP_OFF_OPTIONS 240
  98. #define BOOTP_OP_REQUEST 1
  99. #define BOOTP_OP_REPLY 2
  100. #define BOOTP_MIN_LEN 300
  101. #define DHCP_OP_DISCOVER 1
  102. #define DHCP_OP_OFFER 2
  103. #define DHCP_OP_REQUEST 3
  104. #define DHCP_OP_DECLINE 4
  105. #define DHCP_OP_ACK 5
  106. #define DHCP_OP_NAK 6
  107. #define DHCP_OP_RELEASE 7
  108. #define DHCP_OP_INFORM 8
  109. #define DHCP_HTYPE_ETHER 1
  110. #define DHCP_OPT_PAD 0
  111. #define DHCP_OPT_SUBNET_MASK 1
  112. #define DHCP_OPT_TIME_OFFSET 2
  113. #define DHCP_OPT_ROUTERS 3
  114. #define DHCP_OPT_DNS_SERVERS 6
  115. #define DHCP_OPT_HOST_NAME 12
  116. #define DHCP_OPT_DOMAIN_NAME 15
  117. #define DHCP_OPT_BROADCAST 28
  118. #define DHCP_OPT_DHCP_ADDRESS 50
  119. #define DHCP_OPT_DHCP_LEASE 51
  120. #define DHCP_OPT_DHCP_MSGTYPE 53
  121. #define DHCP_OPT_DHCP_SRVID 54
  122. #define DHCP_OPT_DHCP_PRL 55
  123. #define DHCP_OPT_END 255
  124. #define DHCP_OPTLEN_SRVID 6
  125. /* DHCPv6 defines (to be moved/shared) */
  126. #define DHCP6_OFF_MSGTYP 0
  127. #define DHCP6_OFF_XID 1
  128. #define DHCP6_OFF_OPTIONS 4
  129. #define DHCP6_OP_SOLICIT 1
  130. #define DHCP6_OP_ADVERTISE 2
  131. #define DHCP6_OP_REQUEST 3
  132. #define DHCP6_OP_REPLY 7
  133. #define DHCP6_OPT_CLIENTID 1
  134. #define DHCP6_OPT_SERVERID 2
  135. #define DHCP6_OPT_IA_NA 3
  136. #define DHCP6_OPT_ORO 6
  137. #define DHCP6_OPT_ELAPSED_TIME 8
  138. #define DHCP6_OPT_STATUS_CODE 13
  139. #define DHCP6_OPT_RAPID_COMMIT 14
  140. #define DHCP6_OPT_NAME_SERVERS 23
  141. #define DHCP6_OPT_DOMAIN_SEARCH 24
  142. #define DHCP6_ST_SUCCESS 0
  143. #define DHCP6_ST_NOADDRSAVAIL 2
  144. #define DHCP6_DUID_LLT 1
  145. #define DHCP6_DUID_EPOCH 946684800
  146. /* tail queue macros (from FreeBSD 8.2 /sys/sys/queue.h, to be moved/shared) */
  147. #define ISC_TAILQ_HEAD(name, type) \
  148. struct name { \
  149. struct type *tqh_first; \
  150. struct type **tqh_last; \
  151. }
  152. #define ISC_TAILQ_ENTRY(type) \
  153. struct { \
  154. struct type *tqe_next; \
  155. struct type **tqe_prev; \
  156. }
  157. #define ISC_TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
  158. #define ISC_TAILQ_FIRST(head) ((head)->tqh_first)
  159. #define ISC_TAILQ_LAST(head, headname) \
  160. (*(((struct headname *)((head)->tqh_last))->tqh_last))
  161. #define ISC_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
  162. #define ISC_TAILQ_PREV(elm, headname, field) \
  163. (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
  164. #define ISC_TAILQ_INIT(head) do { \
  165. ISC_TAILQ_FIRST((head)) = NULL; \
  166. (head)->tqh_last = &ISC_TAILQ_FIRST((head)); \
  167. } while (0)
  168. #define ISC_TAILQ_INSERT_HEAD(head, elm, field) do { \
  169. ISC_TAILQ_NEXT((elm), field) = ISC_TAILQ_FIRST((head)); \
  170. if (!ISC_TAILQ_EMPTY((head))) \
  171. ISC_TAILQ_FIRST((head))->field.tqe_prev = \
  172. &ISC_TAILQ_NEXT((elm), field); \
  173. else \
  174. (head)->tqh_last = &ISC_TAILQ_NEXT((elm), field); \
  175. ISC_TAILQ_FIRST((head)) = (elm); \
  176. (elm)->field.tqe_prev = &ISC_TAILQ_FIRST((head)); \
  177. } while (0)
  178. #define ISC_TAILQ_INSERT_TAIL(head, elm, field) do { \
  179. ISC_TAILQ_NEXT((elm), field) = NULL; \
  180. (elm)->field.tqe_prev = (head)->tqh_last; \
  181. *(head)->tqh_last = (elm); \
  182. (head)->tqh_last = &ISC_TAILQ_NEXT((elm), field); \
  183. } while (0)
  184. #define ISC_TAILQ_REMOVE(head, elm, field) do { \
  185. if ((ISC_TAILQ_NEXT((elm), field)) != NULL) \
  186. ISC_TAILQ_NEXT((elm), field)->field.tqe_prev = \
  187. (elm)->field.tqe_prev; \
  188. else \
  189. (head)->tqh_last = (elm)->field.tqe_prev; \
  190. *(elm)->field.tqe_prev = ISC_TAILQ_NEXT((elm), field); \
  191. } while (0)
  192. #define ISC_TAILQ_FOREACH(var, head, field) \
  193. for ((var) = ISC_TAILQ_FIRST((head)); \
  194. (var); \
  195. (var) = ISC_TAILQ_NEXT((var), field))
  196. #define ISC_TAILQ_FOREACH_SAFE(var, head, field, tvar) \
  197. for ((var) = ISC_TAILQ_FIRST((head)); \
  198. (var) && ((tvar) = ISC_TAILQ_NEXT((var), field), 1); \
  199. (var) = (tvar))
  200. /*
  201. * Data structures
  202. */
  203. /*
  204. * exchange:
  205. * - per exchange values:
  206. * * order (for debugging)
  207. * * xid (even/odd for 4 packet exchanges)
  208. * * random (for debugging)
  209. * * time-stamps
  210. * * server ID (for 3rd packet)
  211. * * IA_NA (for IPv6 3rd packet)
  212. *
  213. * sent/rcvd global chains, "next to be received" on entry cache,
  214. * and hash table for xid -> data structure fast matching
  215. * (using the assumption collisions are unlikely, cf birthday problem)
  216. */
  217. struct exchange { /* per exchange structure */
  218. ISC_TAILQ_ENTRY(exchange) gchain; /* global chaining */
  219. ISC_TAILQ_ENTRY(exchange) hchain; /* hash table chaining */
  220. uint64_t order0, order2; /* number of this exchange */
  221. uint32_t xid; /* transaction ID */
  222. uint32_t rnd; /* random part */
  223. struct timespec ts0, ts1, ts2, ts3; /* timespecs */
  224. uint8_t *sid; /* server ID */
  225. size_t sidlen; /* server ID length */
  226. uint8_t *iana; /* (IPv6) IA_NA */
  227. size_t ianalen; /* (IPv6) IA_NA length */
  228. };
  229. struct exchange *xnext0, *xnext2; /* next to be received */
  230. ISC_TAILQ_HEAD(xlist, exchange); /* exchange list */
  231. struct xlist xsent0, xsent2, xrcvd0, xrcvd2; /* sent and received lists */
  232. uint64_t xscount0, xscount2; /* sent counters */
  233. uint64_t xrcount0, xrcount2; /* received counters */
  234. caddr_t exchanges0, exchanges2; /* hash tables */
  235. uint32_t hashsize0, hashsize2; /* hash table sizes */
  236. /*
  237. * statictics counters and accumulators
  238. */
  239. uint64_t tooshort, orphans, locallimit; /* error counters */
  240. uint64_t latesent, compsend, latercvd; /* rate stats */
  241. uint64_t multrcvd, shortwait, collected[2]; /* rate stats (cont) */
  242. double dmin0 = 999999999., dmin2 = 999999999.; /* minimum delays */
  243. double dmax0 = 0., dmax2 = 0.; /* maximum delays */
  244. double dsum0 = 0., dsum2 = 0.; /* delay sums */
  245. double dsumsq0 = 0., dsumsq2 = 0.; /* square delay sums */
  246. /*
  247. * command line parameters
  248. */
  249. int ipversion = 0; /* IP version */
  250. int simple; /* DO/SA in place of DORR/SARR */
  251. int rate; /* rate in exchange per second */
  252. int report; /* delay between two reports */
  253. uint32_t range; /* randomization range */
  254. uint32_t maxrandom; /* maximum random value */
  255. int basecnt; /* base count */
  256. char *base[4]; /* bases */
  257. int gotnumreq; /* numreq[0] was set */
  258. int numreq[2]; /* number of exchange */
  259. int period; /* test period */
  260. int gotlosttime; /* losttime[0] was set */
  261. double losttime[2] = {1., 1.}; /* time after a request is lost */
  262. int gotmaxdrop; /* max{p}drop[0] was set */
  263. int maxdrop[2]; /* maximum number of lost requests */
  264. double maxpdrop[2] = { 0., 0.}; /* maximum percentage */
  265. char *localname; /* local address or interface */
  266. int isinterface; /* interface vs local address */
  267. int preload; /* preload exchanges */
  268. int aggressivity = 1; /* back to back exchanges */
  269. int localport; /* local port number (host endian) */
  270. int seeded; /* is a seed provided */
  271. unsigned int seed; /* randomization seed */
  272. int isbroadcast; /* use broadcast */
  273. int rapidcommit; /* add rapid commit option */
  274. int usefirst; /* where to take the server-ID */
  275. char *templatefile[2]; /* template file name */
  276. int xidoffset[2] = {-1, -1}; /* template offsets (xid)*/
  277. int rndoffset[2] = {-1, -1}; /* template offsets (random) */
  278. int elpoffset = -1; /* template offset (elapsed time) */
  279. int sidoffset = -1; /* template offset (server ID) */
  280. int ripoffset = -1; /* template offset (requested IP) */
  281. char *diags; /* diagnostic selectors */
  282. char *wrapped; /* wrapped command */
  283. char *servername; /* server */
  284. /*
  285. * global variables
  286. */
  287. struct sockaddr_storage localaddr; /* local socket address */
  288. struct sockaddr_storage serveraddr; /* server socket address */
  289. int sock; /* socket descriptor */
  290. int interrupted, fatal; /* to finish flags */
  291. uint8_t obuf[4096], ibuf[4096]; /* I/O buffers */
  292. char tbuf[8200]; /* template buffer */
  293. struct timespec boot; /* the date of boot */
  294. struct timespec last; /* the date of last send */
  295. struct timespec due; /* the date of next send */
  296. struct timespec dreport; /* the date of next reporting */
  297. struct timespec finished; /* the date of finish */
  298. uint8_t *gsrvid; /* global server id */
  299. size_t gsrvidlen; /* and its length */
  300. uint8_t gsrvidbuf[64]; /* and its storage */
  301. /* MAC address */
  302. uint8_t mac_prefix[6] = { 0x00, 0x0c, 0x01, 0x02, 0x03, 0x04 };
  303. /* DUID prefix */
  304. uint8_t *duid_prefix;
  305. int duid_length;
  306. /* magic cookie for BOOTP/DHCPv4 */
  307. uint8_t dhcp_cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
  308. /*
  309. * templates
  310. *
  311. * note: the only hard point is what are the offsets:
  312. * - xid_discover4 and xid_request4: first of the 4 octet long
  313. * transaction ID (default DHCP_OFF_XID = 4)
  314. * - random_discover4 and random_request4: last of the 6 octet long
  315. * MAC address (default DHCP_OFF_CHADDR + 6 = 28 + 6)
  316. * - elapsed_request4: first of the 2 octet long secs field
  317. * (default DHCP_OFF_SECS = 8, 0 means disabled)
  318. * - serverid_request4: first of the 6 octet long server ID option
  319. * (no default, required)
  320. * - reqaddr_request4: first of the 4 octet long requested IP address
  321. * option content (i.e., the address itself, btw OFFER yiaddr)
  322. * (no default, required)
  323. * - xid_solicit6 and xid_request6: first of the 3 octet long
  324. * transaction ID (default DHCP6_OFF_XID = 1)
  325. * - random_solicit6 and random_request6: last of the DUID in the
  326. * client ID option (no default, required when rate is set)
  327. * - elapsed_request6: first of the 2 octet long content of
  328. * the option elapsed time option (no default, 0 means disabled)
  329. * - serverid_request6: position where the variable length server ID
  330. * option is inserted (no default, required, set to length means append)
  331. * - reqaddr_request6: position where of the variable length requested
  332. * IP address option is inserted (no default, required, set to
  333. * length means append)
  334. */
  335. size_t length_discover4;
  336. uint8_t template_discover4[4096];
  337. size_t xid_discover4;
  338. size_t random_discover4;
  339. size_t length_request4;
  340. uint8_t template_request4[4096];
  341. size_t xid_request4;
  342. size_t elapsed_request4;
  343. size_t random_request4;
  344. size_t serverid_request4;
  345. size_t reqaddr_request4;
  346. size_t length_solicit6;
  347. uint8_t template_solicit6[4096];
  348. size_t xid_solicit6;
  349. size_t random_solicit6;
  350. size_t length_request6;
  351. uint8_t template_request6[4096];
  352. size_t xid_request6;
  353. size_t elapsed_request6;
  354. size_t random_request6;
  355. size_t serverid_request6;
  356. size_t reqaddr_request6;
  357. // use definition of CLOCK_REALTIME (or lack of thereof) as an indicator
  358. // if the code is being compiled or Linux (or somewhere else)
  359. // Perhaps this should be based on OS_LINUX define?
  360. #if !defined (CLOCK_REALTIME)
  361. #define CLOCK_REALTIME 0
  362. /// @brief clock_gettime implementation for non-Linux systems
  363. ///
  364. /// This implementation lacks nanosecond resolution. It is intended
  365. /// to be used on non-Linux systems that does not provide clock_gettime
  366. /// implementation.
  367. ///
  368. /// @param clockid ignored (kept for Linux prototype compatibility)
  369. /// @param tp timespec structure
  370. ///
  371. /// @return always zero (kept for compatibility reasons)
  372. int clock_gettime(int clockid, struct timespec *tp) {
  373. struct timeval tv;
  374. gettimeofday(&tv, NULL);
  375. tp->tv_sec = tv.tv_sec;
  376. tp->tv_nsec = tv.tv_usec*1000;
  377. return (0);
  378. }
  379. #endif
  380. /*
  381. * initialize data structures handling exchanges
  382. */
  383. void
  384. inits(void)
  385. {
  386. struct xlist *bucket;
  387. caddr_t p;
  388. size_t len, i;
  389. ISC_TAILQ_INIT(&xsent0);
  390. ISC_TAILQ_INIT(&xsent2);
  391. ISC_TAILQ_INIT(&xrcvd0);
  392. ISC_TAILQ_INIT(&xrcvd2);
  393. /// compute hashsizes
  394. hashsize0 = 1024;
  395. len = sizeof(*bucket) * hashsize0;
  396. exchanges0 = (caddr_t)malloc(len);
  397. if (exchanges0 == NULL) {
  398. perror("malloc(exchanges0)");
  399. exit(1);
  400. }
  401. for (i = 0, p = exchanges0; i < hashsize0; i++, p += sizeof(*bucket)) {
  402. bucket = (struct xlist *) p;
  403. ISC_TAILQ_INIT(bucket);
  404. }
  405. if (simple != 0)
  406. return;
  407. hashsize2 = 1024;
  408. len = sizeof(*bucket) * hashsize2;
  409. exchanges2 = (caddr_t)malloc(len);
  410. if (exchanges2 == NULL) {
  411. perror("malloc(exchanges2)");
  412. exit(1);
  413. }
  414. for (i = 0, p = exchanges2; i < hashsize2; i++, p += sizeof(*bucket)) {
  415. bucket = (struct xlist *) p;
  416. ISC_TAILQ_INIT(bucket);
  417. }
  418. }
  419. /*
  420. * randomize the value of the given field:
  421. * - offset of the field
  422. * - random seed (used as it when suitable)
  423. * - returns the random value which was used
  424. */
  425. uint32_t
  426. randomize(size_t offset, uint32_t r)
  427. {
  428. uint32_t v;
  429. if (range == 0)
  430. return 0;
  431. if (range == UINT32_MAX)
  432. return r;
  433. if (maxrandom != 0)
  434. while (r >= maxrandom)
  435. r = (uint32_t) random();
  436. r %= range + 1;
  437. v = r;
  438. v += obuf[offset];
  439. obuf[offset] = v;
  440. if (v < 256)
  441. return r;
  442. v >>= 8;
  443. v += obuf[offset - 1];
  444. obuf[offset - 1] = v;
  445. if (v < 256)
  446. return r;
  447. v >>= 8;
  448. v += obuf[offset - 2];
  449. obuf[offset - 2] = v;
  450. if (v < 256)
  451. return r;
  452. v >>= 8;
  453. v += obuf[offset - 3];
  454. obuf[offset - 3] = v;
  455. return r;
  456. }
  457. /*
  458. * receive a reply (4th packet), shared between IPv4 and IPv6:
  459. * - transaction ID xid
  460. * - receiving time-stamp now
  461. * called from receive[46]() when the xid is odd
  462. */
  463. void
  464. receive_reply(uint32_t xid, struct timespec *now)
  465. {
  466. struct exchange *x, *t;
  467. struct xlist *bucket;
  468. uint32_t hash;
  469. int checklost;
  470. double delta;
  471. /* bucket is needed even when the next cache matches */
  472. hash = (xid >> 1) & (hashsize2 - 1);
  473. bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
  474. /* try the 'next to be received' cache */
  475. if ((xnext2 != NULL) && (xnext2->xid == xid)) {
  476. x = xnext2;
  477. goto found;
  478. }
  479. /* usually the lost probability is low for request/reply */
  480. checklost = 1;
  481. /* look for the exchange */
  482. ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
  483. double waited;
  484. if (x->xid == xid)
  485. goto found;
  486. if (checklost <= 0)
  487. continue;
  488. checklost = 0;
  489. /* check for a timed-out exchange */
  490. waited = now->tv_sec - x->ts2.tv_sec;
  491. waited += (now->tv_nsec - x->ts2.tv_nsec) / 1e9;
  492. if (waited < losttime[1])
  493. continue;
  494. /* garbage collect timed-out exchange */
  495. ISC_TAILQ_REMOVE(bucket, x, hchain);
  496. ISC_TAILQ_REMOVE(&xsent2, x, gchain);
  497. free(x);
  498. collected[1] += 1;
  499. }
  500. /* no match? very late or not for us */
  501. orphans++;
  502. return;
  503. /* got it: update stats and move to the received queue */
  504. found:
  505. xrcount2++;
  506. x->ts3 = *now;
  507. delta = x->ts3.tv_sec - x->ts2.tv_sec;
  508. delta += (x->ts3.tv_nsec - x->ts2.tv_nsec) / 1e9;
  509. if (delta < dmin2)
  510. dmin2 = delta;
  511. if (delta > dmax2)
  512. dmax2 = delta;
  513. dsum2 += delta;
  514. dsumsq2 += delta * delta;
  515. xnext2 = ISC_TAILQ_NEXT(x, gchain);
  516. ISC_TAILQ_REMOVE(bucket, x, hchain);
  517. ISC_TAILQ_REMOVE(&xsent2, x, gchain);
  518. ISC_TAILQ_INSERT_TAIL(&xrcvd2, x, gchain);
  519. }
  520. /*
  521. * get the DHCPv4 socket descriptor
  522. * (the only complexity is broadcast enabling: there is no easy way to
  523. * recognize broadcast addresses, so the command line -B flag)
  524. */
  525. void
  526. getsock4(void)
  527. {
  528. int ret;
  529. /* update local port */
  530. if (localport != 0) {
  531. uint16_t lp = htons((uint16_t) localport);
  532. ((struct sockaddr_in *) &localaddr)->sin_port = lp;
  533. }
  534. sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  535. if (sock < 0) {
  536. perror("socket");
  537. exit(1);
  538. }
  539. ret = bind(sock,
  540. (struct sockaddr *) &localaddr,
  541. sizeof(struct sockaddr_in));
  542. if (ret < 0) {
  543. perror("bind");
  544. exit(1);
  545. }
  546. /* enable broadcast if needed or required */
  547. if (isbroadcast != 0) {
  548. int on = 1;
  549. ret = setsockopt(sock,
  550. SOL_SOCKET, SO_BROADCAST,
  551. &on, sizeof(on));
  552. if (ret < 0) {
  553. perror("setsockopt(SO_BROADCAST)");
  554. exit(1);
  555. }
  556. }
  557. }
  558. /*
  559. * build a DHCPv4 DISCOVER from a relay template
  560. * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
  561. * (assume the link is Ethernet)
  562. */
  563. void
  564. build_template_discover4(void)
  565. {
  566. uint8_t *p = template_discover4;
  567. length_discover4 = BOOTP_MIN_LEN;
  568. xid_discover4 = DHCP_OFF_XID;
  569. random_discover4 = DHCP_OFF_CHADDR + 6;
  570. /* opcode */
  571. p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
  572. /* hardware address type */
  573. p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
  574. /* hardware address length */
  575. p[DHCP_OFF_HLEN] = 6;
  576. /* hops */
  577. p[DHCP_OFF_HOPS] = 1;
  578. /* gateway address */
  579. memcpy(p + DHCP_OFF_GIADDR,
  580. &((struct sockaddr_in *) &localaddr)->sin_addr,
  581. 4);
  582. /* hardware address */
  583. memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
  584. /* cookie */
  585. memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
  586. /* options */
  587. p += DHCP_OFF_OPTIONS;
  588. /* inline DHCP message type */
  589. *p++ = DHCP_OPT_DHCP_MSGTYPE;
  590. *p++ = 1;
  591. *p++ = DHCP_OP_DISCOVER;
  592. /* inline DHCP parameter request list (default) */
  593. *p++ = DHCP_OPT_DHCP_PRL;
  594. *p++ = 7;
  595. *p++ = DHCP_OPT_SUBNET_MASK;
  596. *p++ = DHCP_OPT_BROADCAST;
  597. *p++ = DHCP_OPT_TIME_OFFSET;
  598. *p++ = DHCP_OPT_ROUTERS;
  599. *p++ = DHCP_OPT_DOMAIN_NAME;
  600. *p++ = DHCP_OPT_DNS_SERVERS;
  601. *p++ = DHCP_OPT_HOST_NAME;
  602. /* end */
  603. *p = DHCP_OPT_END;
  604. }
  605. /*
  606. * get a DHCPv4 client/relay first packet (usually a DISCOVER) template
  607. * from the file given in the command line (-T<template-file>)
  608. * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
  609. */
  610. void
  611. get_template_discover4(void)
  612. {
  613. uint8_t *p = template_discover4;
  614. int fd, cc, i, j;
  615. fd = open(templatefile[0], O_RDONLY);
  616. if (fd < 0) {
  617. fprintf(stderr, "open(%s): %s\n",
  618. templatefile[0], strerror(errno));
  619. exit(2);
  620. }
  621. cc = read(fd, tbuf, sizeof(tbuf));
  622. (void) close(fd);
  623. if (cc < 0) {
  624. fprintf(stderr, "read(%s): %s\n",
  625. templatefile[0], strerror(errno));
  626. exit(1);
  627. }
  628. if (cc < 100) {
  629. fprintf(stderr, "file '%s' too short\n", templatefile[0]);
  630. exit(2);
  631. }
  632. if (cc > 8193) {
  633. fprintf(stderr,"file '%s' too large\n", templatefile[0]);
  634. exit(2);
  635. }
  636. j = 0;
  637. for (i = 0; i < cc; i++) {
  638. if (isspace((int) tbuf[i]))
  639. continue;
  640. if (!isxdigit((int) tbuf[i])) {
  641. fprintf(stderr,
  642. "illegal char[%d]='%c' in file '%s'\n",
  643. i, (int) tbuf[i], templatefile[0]);
  644. exit(2);
  645. }
  646. tbuf[j] = tbuf[i];
  647. j++;
  648. }
  649. cc = j;
  650. if ((cc & 1) != 0) {
  651. fprintf(stderr,
  652. "odd number of hexadecimal digits in file '%s'\n",
  653. templatefile[0]);
  654. exit(2);
  655. }
  656. length_discover4 = cc >> 1;
  657. for (i = 0; i < cc; i += 2)
  658. (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
  659. if (xidoffset[0] >= 0)
  660. xid_discover4 = (size_t) xidoffset[0];
  661. else
  662. xid_discover4 = DHCP_OFF_XID;
  663. if (xid_discover4 + 4 > length_discover4) {
  664. fprintf(stderr,
  665. "xid (at %zu) outside the template (length %zu)?\n",
  666. xid_discover4, length_discover4);
  667. exit(2);
  668. }
  669. if (rndoffset[0] >= 0)
  670. random_discover4 = (size_t) rndoffset[0];
  671. else
  672. random_discover4 = DHCP_OFF_CHADDR + 6;
  673. if (random_discover4 > length_discover4) {
  674. fprintf(stderr,
  675. "random (at %zu) outside the template (length %zu)?\n",
  676. random_discover4, length_discover4);
  677. exit(2);
  678. }
  679. }
  680. /*
  681. * build a DHCPv4 REQUEST from a relay template
  682. * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
  683. * (assume the link is Ethernet)
  684. */
  685. void
  686. build_template_request4(void)
  687. {
  688. uint8_t *p = template_request4;
  689. length_request4 = BOOTP_MIN_LEN;
  690. xid_request4 = DHCP_OFF_XID;
  691. elapsed_request4 = DHCP_OFF_SECS;
  692. random_request4 = DHCP_OFF_CHADDR + 6;
  693. /* opcode */
  694. p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
  695. /* hardware address type */
  696. p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
  697. /* hardware address length */
  698. p[DHCP_OFF_HLEN] = 6;
  699. /* hops */
  700. p[DHCP_OFF_HOPS] = 1;
  701. /* gateway address */
  702. memcpy(p + DHCP_OFF_GIADDR,
  703. &((struct sockaddr_in *) &localaddr)->sin_addr,
  704. 4);
  705. /* hardware address */
  706. memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
  707. /* cookie */
  708. memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
  709. /* options */
  710. p += DHCP_OFF_OPTIONS;
  711. /* inline DHCP message type */
  712. *p++ = DHCP_OPT_DHCP_MSGTYPE;
  713. *p++ = 1;
  714. *p++ = DHCP_OP_REQUEST;
  715. /* place for DHCP server id (option) */
  716. serverid_request4 = p - template_request4;
  717. p += DHCP_OPTLEN_SRVID;
  718. /* place for DHCP requested IP address (address) */
  719. *p++ = DHCP_OPT_DHCP_ADDRESS;
  720. *p++ = 4;
  721. reqaddr_request4 = p - template_request4;
  722. p += 4;
  723. /* inline DHCP parameter request list (default) */
  724. *p++ = DHCP_OPT_DHCP_PRL;
  725. *p++ = 7;
  726. *p++ = DHCP_OPT_SUBNET_MASK;
  727. *p++ = DHCP_OPT_BROADCAST;
  728. *p++ = DHCP_OPT_TIME_OFFSET;
  729. *p++ = DHCP_OPT_ROUTERS;
  730. *p++ = DHCP_OPT_DOMAIN_NAME;
  731. *p++ = DHCP_OPT_DNS_SERVERS;
  732. *p++ = DHCP_OPT_HOST_NAME;
  733. /* end */
  734. *p = DHCP_OPT_END;
  735. }
  736. /*
  737. * get a DHCPv4 client/relay third packet (usually a REQUEST) template
  738. * from the file given in the command line (-T<template-file>)
  739. * and offsets (-X,-O,-E,-S,-I).
  740. */
  741. void
  742. get_template_request4(void)
  743. {
  744. uint8_t *p = template_request4;
  745. int fd, cc, i, j;
  746. fd = open(templatefile[1], O_RDONLY);
  747. if (fd < 0) {
  748. fprintf(stderr, "open(%s): %s\n",
  749. templatefile[1], strerror(errno));
  750. exit(2);
  751. }
  752. cc = read(fd, tbuf, sizeof(tbuf));
  753. (void) close(fd);
  754. if (cc < 0) {
  755. fprintf(stderr, "read(%s): %s\n",
  756. templatefile[1], strerror(errno));
  757. exit(1);
  758. }
  759. if (cc < 100) {
  760. fprintf(stderr, "file '%s' too short\n", templatefile[1]);
  761. exit(2);
  762. }
  763. if (cc > 8193) {
  764. fprintf(stderr,"file '%s' too large\n", templatefile[1]);
  765. exit(2);
  766. }
  767. j = 0;
  768. for (i = 0; i < cc; i++) {
  769. if (isspace((int) tbuf[i]))
  770. continue;
  771. if (!isxdigit((int) tbuf[i])) {
  772. fprintf(stderr,
  773. "illegal char[%d]='%c' in file '%s'\n",
  774. i, (int) tbuf[i], templatefile[1]);
  775. exit(2);
  776. }
  777. tbuf[j] = tbuf[i];
  778. j++;
  779. }
  780. cc = j;
  781. if ((cc & 1) != 0) {
  782. fprintf(stderr,
  783. "odd number of hexadecimal digits in file '%s'\n",
  784. templatefile[1]);
  785. exit(2);
  786. }
  787. length_request4 = cc >> 1;
  788. for (i = 0; i < cc; i += 2)
  789. (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
  790. if (xidoffset[1] >= 0)
  791. xid_request4 = (size_t) xidoffset[1];
  792. else
  793. xid_request4 = DHCP_OFF_XID;
  794. if (xid_request4 + 4 > length_request4) {
  795. fprintf(stderr,
  796. "xid (at %zu) outside the template (length %zu)?\n",
  797. xid_request4, length_request4);
  798. exit(2);
  799. }
  800. if (rndoffset[1] >= 0)
  801. random_request4 = (size_t) rndoffset[1];
  802. else
  803. random_request4 = DHCP_OFF_CHADDR + 6;
  804. if (random_request4 > length_request4) {
  805. fprintf(stderr,
  806. "random (at %zu) outside the template (length %zu)?\n",
  807. random_request4, length_request4);
  808. exit(2);
  809. }
  810. if (elpoffset >= 0)
  811. elapsed_request4 = (size_t) elpoffset;
  812. else
  813. elapsed_request4 = DHCP_OFF_SECS;
  814. if (elapsed_request4 + 2 > length_request4) {
  815. fprintf(stderr,
  816. "secs (at %zu) outside the template (length %zu)?\n",
  817. elapsed_request4, length_request4);
  818. exit(2);
  819. }
  820. serverid_request4 = (size_t) sidoffset;
  821. if (serverid_request4 + 6 > length_request4) {
  822. fprintf(stderr,
  823. "server-id option (at %zu) outside the template "
  824. "(length %zu)?\n",
  825. serverid_request4, length_request4);
  826. exit(2);
  827. }
  828. reqaddr_request4 = (size_t) ripoffset;
  829. if (reqaddr_request4 + 4 > length_request4) {
  830. fprintf(stderr,
  831. "requested-ip-address option (at %zu) outside "
  832. "the template (length %zu)?\n",
  833. reqaddr_request4, length_request4);
  834. exit(2);
  835. }
  836. }
  837. /*
  838. * send the DHCPv4 REQUEST third packet
  839. * (the transaction ID is odd)
  840. * (TODO: check for errors in the OFFER)
  841. */
  842. void
  843. send_request4(struct exchange *x0)
  844. {
  845. struct exchange *x;
  846. struct xlist *bucket;
  847. uint32_t hash;
  848. ssize_t ret;
  849. x = (struct exchange *) malloc(sizeof(*x));
  850. if (x == NULL) {
  851. locallimit++;
  852. perror("send2");
  853. return;
  854. }
  855. memcpy(x, x0, sizeof(*x));
  856. x->order2 = xscount2++;
  857. x->xid |= 1;
  858. hash = x->xid >> 1;
  859. ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
  860. hash &= hashsize2 - 1;
  861. bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
  862. ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
  863. memcpy(obuf, template_request4, length_request4);
  864. /* xid */
  865. memcpy(obuf + xid_request4, &x->xid, 4);
  866. /* random */
  867. randomize(random_request4, x->rnd);
  868. /* secs */
  869. if (elapsed_request4 > 0) {
  870. int secs;
  871. secs = x->ts1.tv_sec - x->ts0.tv_sec;
  872. if (x->ts1.tv_nsec < x->ts0.tv_nsec)
  873. secs += 1;
  874. if (secs > 0) {
  875. obuf[elapsed_request4] = secs >> 8;
  876. obuf[elapsed_request4 + 1] = secs & 0xff;
  877. }
  878. }
  879. /* server ID */
  880. memcpy(obuf + serverid_request4, x->sid, x->sidlen);
  881. /* requested IP address */
  882. memcpy(obuf + reqaddr_request4, ibuf + DHCP_OFF_YIADDR, 4);
  883. /* timestamp */
  884. ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
  885. if (ret < 0) {
  886. perror("clock_gettime(send2)");
  887. fatal = 1;
  888. return;
  889. }
  890. ret = sendto(sock, obuf, length_request4, 0,
  891. (struct sockaddr *) &serveraddr,
  892. sizeof(struct sockaddr_in));
  893. if (ret >= 0)
  894. return;
  895. if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
  896. (errno == ENOBUFS) || (errno == ENOMEM))
  897. locallimit++;
  898. perror("send2");
  899. }
  900. /*
  901. * send the DHCPv4 DISCOVER first packet
  902. * (for 4-exchange, the transaction ID xid is even)
  903. */
  904. int
  905. send4(void)
  906. {
  907. struct exchange *x;
  908. struct xlist *bucket;
  909. uint32_t hash;
  910. ssize_t ret;
  911. x = (struct exchange *) malloc(sizeof(*x));
  912. if (x == NULL)
  913. return -ENOMEM;
  914. memset(x, 0, sizeof(*x));
  915. x->order0 = xscount0++;
  916. hash = x->rnd = (uint32_t) random();
  917. if (simple == 0)
  918. x->xid = hash << 1;
  919. else
  920. x->xid = hash;
  921. ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
  922. hash &= hashsize0 - 1;
  923. bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
  924. ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
  925. memcpy(obuf, template_discover4, length_discover4);
  926. /* xid */
  927. memcpy(obuf + xid_discover4, &x->xid, 4);
  928. /* random */
  929. x->rnd = randomize(random_discover4, x->rnd);
  930. /* timestamp */
  931. ret = clock_gettime(CLOCK_REALTIME, &last);
  932. if (ret < 0) {
  933. perror("clock_gettime(send)");
  934. fatal = 1;
  935. return -errno;
  936. }
  937. x->ts0 = last;
  938. errno = 0;
  939. ret = sendto(sock, obuf, length_discover4, 0,
  940. (struct sockaddr *) &serveraddr,
  941. sizeof(struct sockaddr_in));
  942. if (ret == (ssize_t) length_discover4)
  943. return 0;
  944. return -errno;
  945. }
  946. /*
  947. * scan a DHCPv4 OFFER to get its server-id option
  948. */
  949. int
  950. scan_for_srvid4(struct exchange *x, size_t cc)
  951. {
  952. size_t off = DHCP_OFF_OPTIONS;
  953. for (;;) {
  954. if (off + DHCP_OPTLEN_SRVID > cc) {
  955. fprintf(stderr, "truncated\n");
  956. return -1;
  957. }
  958. if (ibuf[off] == DHCP_OPT_DHCP_SRVID)
  959. break;
  960. if (ibuf[off] == DHCP_OPT_END) {
  961. fprintf(stderr, "server-id not found\n");
  962. return -1;
  963. }
  964. if (ibuf[off] == DHCP_OPT_PAD) {
  965. off++;
  966. continue;
  967. }
  968. off += 2 + ibuf[off + 1];
  969. }
  970. /* check length */
  971. if (ibuf[off + 1] != DHCP_OPTLEN_SRVID - 2) {
  972. fprintf(stderr,
  973. "bad server-id length (%hhu)\n",
  974. ibuf[off + 1]);
  975. return -1;
  976. }
  977. /* cache it in the global variables when required and not yet done */
  978. if ((usefirst != 0) && (gsrvid == NULL)) {
  979. memcpy(gsrvidbuf, ibuf + off, DHCP_OPTLEN_SRVID);
  980. gsrvid = gsrvidbuf;
  981. gsrvidlen = DHCP_OPTLEN_SRVID;
  982. }
  983. x->sid = ibuf + off;
  984. x->sidlen = DHCP_OPTLEN_SRVID;
  985. return 0;
  986. }
  987. /*
  988. * receive a DHCPv4 packet
  989. */
  990. void
  991. receive4(void)
  992. {
  993. struct exchange *x, *t;
  994. struct xlist *bucket;
  995. struct timespec now;
  996. ssize_t cc;
  997. uint32_t xid, hash;
  998. int checklost = 0;
  999. double delta;
  1000. cc = recv(sock, ibuf, sizeof(ibuf), 0);
  1001. if (cc < 0) {
  1002. if ((errno == EAGAIN) ||
  1003. (errno == EWOULDBLOCK) ||
  1004. (errno == EINTR))
  1005. return;
  1006. perror("recv");
  1007. fatal = 1;
  1008. return;
  1009. }
  1010. /* enforce a reasonable length */
  1011. if (cc < BOOTP_MIN_LEN) {
  1012. tooshort++;
  1013. return;
  1014. }
  1015. /* must be a BOOTP REPLY */
  1016. if (ibuf[DHCP_OFF_OPCODE] != BOOTP_OP_REPLY)
  1017. return;
  1018. if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
  1019. perror("clock_gettime(receive)");
  1020. fatal = 1;
  1021. return;
  1022. }
  1023. memcpy(&xid, ibuf + xid_discover4, 4);
  1024. /* 4-packet exchange even/odd xid */
  1025. if (simple == 0) {
  1026. if ((xid & 1) != 0) {
  1027. receive_reply(xid, &now);
  1028. return;
  1029. }
  1030. hash = (xid >> 1) & (hashsize0 - 1);
  1031. } else
  1032. hash = xid & (hashsize0 - 1);
  1033. /* now it is the second packet, get the bucket which is needed */
  1034. bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
  1035. /* try the 'next to be received' cache */
  1036. if ((xnext0 != NULL) && (xnext0->xid == xid)) {
  1037. x = xnext0;
  1038. goto found;
  1039. }
  1040. /* if the rate is not illimited, garbage collect up to 3
  1041. timed-out exchanges */
  1042. if (rate != 0)
  1043. checklost = 3;
  1044. /* look for the exchange */
  1045. ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
  1046. double waited;
  1047. if (x->xid == xid)
  1048. goto found;
  1049. if (checklost <= 0)
  1050. continue;
  1051. /* check for a timed-out exchange */
  1052. waited = now.tv_sec - x->ts0.tv_sec;
  1053. waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
  1054. if (waited < losttime[0]) {
  1055. checklost = 0;
  1056. continue;
  1057. }
  1058. /* garbage collect timed-out exchange */
  1059. checklost--;
  1060. ISC_TAILQ_REMOVE(bucket, x, hchain);
  1061. ISC_TAILQ_REMOVE(&xsent0, x, gchain);
  1062. free(x);
  1063. collected[0] += 1;
  1064. }
  1065. /* no match? very late or not for us */
  1066. orphans++;
  1067. return;
  1068. /* got it: update stats and move to the received queue */
  1069. found:
  1070. xrcount0++;
  1071. x->ts1 = now;
  1072. delta = x->ts1.tv_sec - x->ts0.tv_sec;
  1073. delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
  1074. if (delta < dmin0)
  1075. dmin0 = delta;
  1076. if (delta > dmax0)
  1077. dmax0 = delta;
  1078. dsum0 += delta;
  1079. dsumsq0 += delta * delta;
  1080. xnext0 = ISC_TAILQ_NEXT(x, gchain);
  1081. ISC_TAILQ_REMOVE(bucket, x, hchain);
  1082. ISC_TAILQ_REMOVE(&xsent0, x, gchain);
  1083. ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
  1084. /* if the exchange is not finished, go to the second part */
  1085. if (simple == 0) {
  1086. int ret = 0;
  1087. /* the server-ID option is needed */
  1088. if ((usefirst != 0) && (gsrvid != NULL)) {
  1089. x->sid = gsrvid;
  1090. x->sidlen = gsrvidlen;
  1091. } else
  1092. ret = scan_for_srvid4(x, cc);
  1093. if (ret >= 0)
  1094. send_request4(x);
  1095. }
  1096. }
  1097. /*
  1098. * get the DHCPv6 socket descriptor
  1099. */
  1100. void
  1101. getsock6(void)
  1102. {
  1103. struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &serveraddr;
  1104. int ret;
  1105. /* update local port */
  1106. if (localport != 0) {
  1107. uint16_t lp = htons((uint16_t) localport);
  1108. ((struct sockaddr_in6 *) &localaddr)->sin6_port = lp;
  1109. }
  1110. sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
  1111. if (sock < 0) {
  1112. perror("socket");
  1113. exit(1);
  1114. }
  1115. ret = bind(sock,
  1116. (struct sockaddr *) &localaddr,
  1117. sizeof(struct sockaddr_in6));
  1118. if (ret < 0) {
  1119. perror("Failed to bind v6 socket to local-link address");
  1120. exit(1);
  1121. }
  1122. /* perform the multicast stuff when the destination is multicast */
  1123. if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
  1124. int hops = 1;
  1125. ret = setsockopt(sock,
  1126. IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
  1127. &hops, sizeof(hops));
  1128. if (ret < 0) {
  1129. perror("setsockopt(IPV6_MULTICAST_HOPS)");
  1130. exit(1);
  1131. }
  1132. }
  1133. if (isinterface && IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
  1134. unsigned idx = if_nametoindex(localname);
  1135. if (idx == 0) {
  1136. fprintf(stderr,
  1137. "if_nametoindex(%s) failed\n",
  1138. localname);
  1139. exit(1);
  1140. }
  1141. ret = setsockopt(sock,
  1142. IPPROTO_IPV6, IPV6_MULTICAST_IF,
  1143. &idx, sizeof(idx));
  1144. if (ret < 0) {
  1145. perror("setsockopt(IPV6_MULTICAST_IF)");
  1146. exit(1);
  1147. }
  1148. }
  1149. }
  1150. /*
  1151. * build a DHCPv6 SOLICIT template
  1152. * (implicit parameter is the DUID, don't assume an Ethernet link)
  1153. */
  1154. void
  1155. build_template_solicit6(void)
  1156. {
  1157. uint8_t *p = template_solicit6;
  1158. xid_solicit6 = DHCP6_OFF_XID;
  1159. /* message type */
  1160. p[DHCP6_OFF_MSGTYP] = DHCP6_OP_SOLICIT;
  1161. /* options */
  1162. p += DHCP6_OFF_OPTIONS;
  1163. /* elapsed time */
  1164. p[1] = DHCP6_OPT_ELAPSED_TIME;
  1165. p[3] = 2;
  1166. p += 6;
  1167. /* rapid commit */
  1168. if (rapidcommit != 0) {
  1169. p[1] = DHCP6_OPT_RAPID_COMMIT;
  1170. p += 4;
  1171. }
  1172. /* client ID */
  1173. p[1] = DHCP6_OPT_CLIENTID;
  1174. p[3] = duid_length;
  1175. memcpy(p + 4, duid_prefix, duid_length);
  1176. p += 4 + duid_length;
  1177. random_solicit6 = p - template_solicit6;
  1178. /* option request option */
  1179. p[1] = DHCP6_OPT_ORO;
  1180. p[3] = 4;
  1181. p[5] = DHCP6_OPT_NAME_SERVERS;
  1182. p[7] = DHCP6_OPT_DOMAIN_SEARCH;
  1183. p += 8;
  1184. /* IA_NA (IAID = 1, T1 = 3600, T2 = 5400) */
  1185. p[1] = DHCP6_OPT_IA_NA;
  1186. p[3] = 12;
  1187. p[7] = 1;
  1188. p[10] = 3600 >> 8;
  1189. p[11] = 3600 & 0xff;
  1190. p[14] = 5400 >> 8;
  1191. p[15] = 5400 & 0xff;
  1192. p += 16;
  1193. /* set length */
  1194. length_solicit6 = p - template_solicit6;
  1195. }
  1196. /*
  1197. * get a DHCPv6 first packet (usually a SOLICIT) template
  1198. * from the file given in the command line (-T<template-file>)
  1199. * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
  1200. */
  1201. void
  1202. get_template_solicit6(void)
  1203. {
  1204. uint8_t *p = template_solicit6;
  1205. int fd, cc, i, j;
  1206. fd = open(templatefile[0], O_RDONLY);
  1207. if (fd < 0) {
  1208. fprintf(stderr, "open(%s): %s\n",
  1209. templatefile[0], strerror(errno));
  1210. exit(2);
  1211. }
  1212. cc = read(fd, tbuf, sizeof(tbuf));
  1213. (void) close(fd);
  1214. if (cc < 0) {
  1215. fprintf(stderr, "read(%s): %s\n",
  1216. templatefile[0], strerror(errno));
  1217. exit(1);
  1218. }
  1219. if (cc < 10) {
  1220. fprintf(stderr, "file '%s' too short\n", templatefile[0]);
  1221. exit(2);
  1222. }
  1223. if (cc > 8193) {
  1224. fprintf(stderr,"file '%s' too large\n", templatefile[0]);
  1225. exit(2);
  1226. }
  1227. j = 0;
  1228. for (i = 0; i < cc; i++) {
  1229. if (isspace((int) tbuf[i]))
  1230. continue;
  1231. if (!isxdigit((int) tbuf[i])) {
  1232. fprintf(stderr,
  1233. "illegal char[%d]='%c' in file '%s'\n",
  1234. i, (int) tbuf[i], templatefile[0]);
  1235. exit(2);
  1236. }
  1237. tbuf[j] = tbuf[i];
  1238. j++;
  1239. }
  1240. cc = j;
  1241. if ((cc & 1) != 0) {
  1242. fprintf(stderr,
  1243. "odd number of hexadecimal digits in file '%s'\n",
  1244. templatefile[0]);
  1245. exit(2);
  1246. }
  1247. length_solicit6 = cc >> 1;
  1248. for (i = 0; i < cc; i += 2)
  1249. (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
  1250. if (xidoffset[0] >= 0)
  1251. xid_solicit6 = (size_t) xidoffset[0];
  1252. else
  1253. xid_solicit6 = DHCP6_OFF_XID;
  1254. if (xid_solicit6 + 3 > length_solicit6) {
  1255. fprintf(stderr,
  1256. "xid (at %zu) is outside the template (length %zu)?\n",
  1257. xid_solicit6, length_solicit6);
  1258. exit(2);
  1259. }
  1260. if (rndoffset[0] >= 0)
  1261. random_solicit6 = (size_t) rndoffset[0];
  1262. else
  1263. random_solicit6 = 0;
  1264. if (random_solicit6 > length_solicit6) {
  1265. fprintf(stderr,
  1266. "random (at %zu) outside the template (length %zu)?\n",
  1267. random_solicit6, length_solicit6);
  1268. exit(2);
  1269. }
  1270. }
  1271. /*
  1272. * build a DHCPv6 REQUEST template
  1273. * (implicit parameter is the DUID, don't assume an Ethernet link)
  1274. */
  1275. void
  1276. build_template_request6(void)
  1277. {
  1278. uint8_t *p = template_request6;
  1279. xid_request6 = DHCP6_OFF_XID;
  1280. /* message type */
  1281. p[DHCP6_OFF_MSGTYP] = DHCP6_OP_REQUEST;
  1282. /* options */
  1283. p += DHCP6_OFF_OPTIONS;
  1284. /* elapsed time */
  1285. p[1] = DHCP6_OPT_ELAPSED_TIME;
  1286. p[3] = 2;
  1287. p += 4;
  1288. elapsed_request6 = p - template_request6;
  1289. p += 2;
  1290. /* client ID */
  1291. p[1] = DHCP6_OPT_CLIENTID;
  1292. p[3] = duid_length;
  1293. memcpy(p + 4, duid_prefix, duid_length);
  1294. p += 4 + duid_length;
  1295. random_request6 = p - template_request6;
  1296. /* option request option */
  1297. p[1] = DHCP6_OPT_ORO;
  1298. p[3] = 4;
  1299. p[5] = DHCP6_OPT_NAME_SERVERS;
  1300. p[7] = DHCP6_OPT_DOMAIN_SEARCH;
  1301. p += 8;
  1302. /* server ID and IA_NA */
  1303. serverid_request6 = p - template_request6;
  1304. reqaddr_request6 = p - template_request6;
  1305. /* set length */
  1306. length_request6 = p - template_request6;
  1307. }
  1308. /*
  1309. * get a DHCPv6 third packet (usually a REQUEST) template
  1310. * from the file given in the command line (-T<template-file>)
  1311. * and offsets (-X,-O,-E,-S,-I).
  1312. */
  1313. void
  1314. get_template_request6(void)
  1315. {
  1316. uint8_t *p = template_request6;
  1317. int fd, cc, i, j;
  1318. fd = open(templatefile[1], O_RDONLY);
  1319. if (fd < 0) {
  1320. fprintf(stderr, "open(%s): %s\n",
  1321. templatefile[1], strerror(errno));
  1322. exit(2);
  1323. }
  1324. cc = read(fd, tbuf, sizeof(tbuf));
  1325. (void) close(fd);
  1326. if (cc < 0) {
  1327. fprintf(stderr, "read(%s): %s\n",
  1328. templatefile[1], strerror(errno));
  1329. exit(1);
  1330. }
  1331. if (cc < 10) {
  1332. fprintf(stderr, "file '%s' too short\n", templatefile[1]);
  1333. exit(2);
  1334. }
  1335. if (cc > 8193) {
  1336. fprintf(stderr,"file '%s' too large\n", templatefile[1]);
  1337. exit(2);
  1338. }
  1339. j = 0;
  1340. for (i = 0; i < cc; i++) {
  1341. if (isspace((int) tbuf[i]))
  1342. continue;
  1343. if (!isxdigit((int) tbuf[i])) {
  1344. fprintf(stderr,
  1345. "illegal char[%d]='%c' in file '%s'\n",
  1346. i, (int) tbuf[i], templatefile[1]);
  1347. exit(2);
  1348. }
  1349. tbuf[j] = tbuf[i];
  1350. j++;
  1351. }
  1352. cc = j;
  1353. if ((cc & 1) != 0) {
  1354. fprintf(stderr,
  1355. "odd number of hexadecimal digits in file '%s'\n",
  1356. templatefile[1]);
  1357. exit(2);
  1358. }
  1359. length_request6 = cc >> 1;
  1360. for (i = 0; i < cc; i += 2)
  1361. (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
  1362. if (xidoffset[1] >= 0)
  1363. xid_request6 = (size_t) xidoffset[1];
  1364. else
  1365. xid_request6 = DHCP6_OFF_XID;
  1366. if (xid_request6 + 3 > length_request6) {
  1367. fprintf(stderr,
  1368. "xid (at %zu) is outside the template (length %zu)?\n",
  1369. xid_request6, length_request6);
  1370. exit(2);
  1371. }
  1372. if (rndoffset[1] >= 0)
  1373. random_request6 = (size_t) rndoffset[1];
  1374. else
  1375. random_request6 = 0;
  1376. if (random_request6 > length_request6) {
  1377. fprintf(stderr,
  1378. "random (at %zu) outside the template (length %zu)?\n",
  1379. random_request6, length_request6);
  1380. exit(2);
  1381. }
  1382. if (elpoffset >= 0)
  1383. elapsed_request6 = (size_t) elpoffset;
  1384. if (elapsed_request6 + 2 > length_request6) {
  1385. fprintf(stderr,
  1386. "secs (at %zu) outside the template (length %zu)?\n",
  1387. elapsed_request6, length_request6);
  1388. exit(2);
  1389. }
  1390. serverid_request6 = (size_t) sidoffset;
  1391. if (serverid_request6 > length_request6) {
  1392. fprintf(stderr,
  1393. "server-id option (at %zu) outside the template "
  1394. "(length %zu)?\n",
  1395. serverid_request6, length_request6);
  1396. exit(2);
  1397. }
  1398. reqaddr_request6 = (size_t) ripoffset;
  1399. if (reqaddr_request6 > length_request6) {
  1400. fprintf(stderr,
  1401. "requested-ip-address option (at %zu) outside "
  1402. "the template (length %zu)?\n",
  1403. reqaddr_request6, length_request6);
  1404. exit(2);
  1405. }
  1406. }
  1407. /*
  1408. * send the DHCPv6 REQUEST third packet
  1409. * (the transaction ID is odd)
  1410. * (TODO: check for errors in the ADVERTISE)
  1411. */
  1412. void
  1413. send_request6(struct exchange *x0)
  1414. {
  1415. struct exchange *x;
  1416. struct xlist *bucket;
  1417. size_t len;
  1418. uint32_t hash;
  1419. ssize_t ret;
  1420. x = (struct exchange *) malloc(sizeof(*x));
  1421. if (x == NULL) {
  1422. locallimit++;
  1423. perror("send2");
  1424. return;
  1425. }
  1426. memcpy(x, x0, sizeof(*x));
  1427. x->order2 = xscount2++;
  1428. x->xid |= 1;
  1429. hash = x->xid >> 1;
  1430. ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
  1431. hash &= hashsize2 - 1;
  1432. bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
  1433. ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
  1434. len = length_request6;
  1435. memcpy(obuf, template_request6, len);
  1436. /* xid */
  1437. obuf[xid_request6] = x->xid >> 16;
  1438. obuf[xid_request6 + 1] = x->xid >> 8;
  1439. obuf[xid_request6 + 2] = x->xid;
  1440. /* random */
  1441. randomize(random_request6, x->rnd);
  1442. /* elapsed time */
  1443. if (elapsed_request6 > 0) {
  1444. int et;
  1445. et = (x->ts1.tv_sec - x->ts0.tv_sec) * 100;
  1446. et += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 10000000;
  1447. if (et > 65535) {
  1448. obuf[elapsed_request6] = 0xff;
  1449. obuf[elapsed_request6 + 1] = 0xff;
  1450. } else if (et > 0) {
  1451. obuf[elapsed_request6] = et >> 8;
  1452. obuf[elapsed_request6 + 1] = et & 0xff;
  1453. }
  1454. }
  1455. /* server ID */
  1456. if (serverid_request6 < length_request6)
  1457. memmove(obuf + serverid_request6 + x->sidlen,
  1458. obuf + serverid_request6,
  1459. x->sidlen);
  1460. memcpy(obuf + serverid_request6, x->sid, x->sidlen);
  1461. len += x->sidlen;
  1462. /* IA_NA */
  1463. if (reqaddr_request6 < serverid_request6) {
  1464. memmove(obuf + reqaddr_request6 + x->ianalen,
  1465. obuf + reqaddr_request6,
  1466. x->ianalen);
  1467. memcpy(obuf + reqaddr_request6, x->iana, x->ianalen);
  1468. } else if (reqaddr_request6 < length_request6) {
  1469. memmove(obuf + reqaddr_request6 + x->sidlen + x->ianalen,
  1470. obuf + reqaddr_request6 + x->sidlen,
  1471. x->ianalen);
  1472. memcpy(obuf + reqaddr_request6+ x->sidlen,
  1473. x->iana,
  1474. x->ianalen);
  1475. } else
  1476. memcpy(obuf + len, x->iana, x->ianalen);
  1477. len += x->ianalen;
  1478. /* timestamp */
  1479. ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
  1480. if (ret < 0) {
  1481. perror("clock_gettime(send2)");
  1482. fatal = 1;
  1483. return;
  1484. }
  1485. ret = sendto(sock, obuf, len, 0,
  1486. (struct sockaddr *) &serveraddr,
  1487. sizeof(struct sockaddr_in6));
  1488. if (ret >= 0)
  1489. return;
  1490. if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
  1491. (errno == ENOBUFS) || (errno == ENOMEM))
  1492. locallimit++;
  1493. perror("send2");
  1494. }
  1495. /*
  1496. * send the DHCPv6 SOLICIT first packet
  1497. * (for 4-exchange, the transaction ID xid is even)
  1498. */
  1499. int
  1500. send6(void)
  1501. {
  1502. struct exchange *x;
  1503. struct xlist *bucket;
  1504. uint32_t hash;
  1505. ssize_t ret;
  1506. x = (struct exchange *) malloc(sizeof(*x));
  1507. if (x == NULL)
  1508. return -ENOMEM;
  1509. memset(x, 0, sizeof(*x));
  1510. x->order0 = xscount0++;
  1511. hash = x->rnd = (uint32_t) random();
  1512. if (simple == 0)
  1513. x->xid = (hash << 1) & 0x00ffffff;
  1514. else
  1515. x->xid = hash & 0x00ffffff;
  1516. ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
  1517. hash &= hashsize0 - 1;
  1518. bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
  1519. ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
  1520. memcpy(obuf, template_solicit6, length_solicit6);
  1521. /* xid */
  1522. obuf[xid_solicit6] = x->xid >> 16;
  1523. obuf[xid_solicit6 + 1] = x->xid >> 8;
  1524. obuf[xid_solicit6 + 2] = x->xid;
  1525. /* random */
  1526. x->rnd = randomize(random_solicit6, x->rnd);
  1527. /* timestamp */
  1528. ret = clock_gettime(CLOCK_REALTIME, &last);
  1529. if (ret < 0) {
  1530. perror("clock_gettime(send)");
  1531. fatal = 1;
  1532. return -errno;
  1533. }
  1534. x->ts0 = last;
  1535. errno = 0;
  1536. ret = sendto(sock, obuf, length_solicit6, 0,
  1537. (struct sockaddr *) &serveraddr,
  1538. sizeof(struct sockaddr_in6));
  1539. if (ret == (ssize_t) length_solicit6)
  1540. return 0;
  1541. return -errno;
  1542. }
  1543. /*
  1544. * scan a DHCPv6 ADVERTISE to get its server-id option
  1545. */
  1546. int
  1547. scan_for_srvid6(struct exchange *x, size_t cc)
  1548. {
  1549. size_t off = DHCP6_OFF_OPTIONS, len;
  1550. for (;;) {
  1551. if (off + 4 > cc) {
  1552. fprintf(stderr, "server-id not found\n");
  1553. return -1;
  1554. }
  1555. if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_SERVERID))
  1556. break;
  1557. off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
  1558. }
  1559. len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
  1560. /* cache it in the global variables when required and not yet done */
  1561. if ((usefirst != 0) && (gsrvid == NULL)) {
  1562. if (len <= sizeof(gsrvidbuf)) {
  1563. memcpy(gsrvidbuf, ibuf + off, len);
  1564. gsrvid = gsrvidbuf;
  1565. gsrvidlen = len;
  1566. } else {
  1567. gsrvid = (uint8_t *) malloc(len);
  1568. if (gsrvid == NULL) {
  1569. perror("malloc(gsrvid");
  1570. return -1;
  1571. }
  1572. memcpy(gsrvid, ibuf + off, len);
  1573. gsrvidlen = len;
  1574. }
  1575. }
  1576. x->sid = ibuf + off;
  1577. x->sidlen = len;
  1578. return 0;
  1579. }
  1580. /*
  1581. * scan a DHCPv6 ADVERTISE to get its IA_NA option
  1582. * (TODO: check for errors)
  1583. */
  1584. int
  1585. scan_for_ia_na(struct exchange *x, size_t cc)
  1586. {
  1587. size_t off = DHCP6_OFF_OPTIONS, len;
  1588. for (;;) {
  1589. if (off + 4 > cc) {
  1590. fprintf(stderr, "ia-na not found\n");
  1591. return -1;
  1592. }
  1593. if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_IA_NA))
  1594. break;
  1595. off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
  1596. }
  1597. len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
  1598. x->iana = ibuf + off;
  1599. x->ianalen = len;
  1600. return 0;
  1601. }
  1602. /*
  1603. * receive a DHCPv6 packet
  1604. */
  1605. void
  1606. receive6(void)
  1607. {
  1608. struct exchange *x, *t;
  1609. struct xlist *bucket;
  1610. struct timespec now;
  1611. ssize_t cc;
  1612. uint32_t xid, hash;
  1613. int checklost = 0;
  1614. double delta;
  1615. cc = recv(sock, ibuf, sizeof(ibuf), 0);
  1616. if (cc < 0) {
  1617. if ((errno == EAGAIN) ||
  1618. (errno == EWOULDBLOCK) ||
  1619. (errno == EINTR))
  1620. return;
  1621. perror("recv");
  1622. fatal = 1;
  1623. return;
  1624. }
  1625. /* enforce a reasonable length */
  1626. if (cc < 22) {
  1627. tooshort++;
  1628. return;
  1629. }
  1630. if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
  1631. perror("clock_gettime(receive)");
  1632. fatal = 1;
  1633. return;
  1634. }
  1635. xid = ibuf[xid_solicit6] << 16;
  1636. xid |= ibuf[xid_solicit6 + 1] << 8;
  1637. xid |= ibuf[xid_solicit6 + 2];
  1638. /* 4-packet exchange even/odd xid */
  1639. if (simple == 0) {
  1640. if ((xid & 1) != 0) {
  1641. receive_reply(xid, &now);
  1642. return;
  1643. }
  1644. hash = (xid >> 1) & (hashsize0 - 1);
  1645. } else
  1646. hash = xid & (hashsize0 - 1);
  1647. /* now it is the second packet, get the bucket which is needed */
  1648. bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
  1649. /* try the 'next to be received' cache */
  1650. if ((xnext0 != NULL) && (xnext0->xid == xid)) {
  1651. x = xnext0;
  1652. goto found;
  1653. }
  1654. /* if the rate is not illimited, garbage collect up to 3
  1655. timed-out exchanges */
  1656. if (rate != 0)
  1657. checklost = 3;
  1658. /* look for the exchange */
  1659. ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
  1660. double waited;
  1661. if (x->xid == xid)
  1662. goto found;
  1663. if (checklost <= 0)
  1664. continue;
  1665. /* check for a timed-out exchange */
  1666. waited = now.tv_sec - x->ts0.tv_sec;
  1667. waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
  1668. if (waited < losttime[0]) {
  1669. checklost = 0;
  1670. continue;
  1671. }
  1672. /* garbage collect timed-out exchange */
  1673. checklost--;
  1674. ISC_TAILQ_REMOVE(bucket, x, hchain);
  1675. ISC_TAILQ_REMOVE(&xsent0, x, gchain);
  1676. free(x);
  1677. collected[0] += 1;
  1678. }
  1679. /* no match? very late or not for us */
  1680. orphans++;
  1681. return;
  1682. /* got it: update stats and move to the received queue */
  1683. found:
  1684. xrcount0++;
  1685. x->ts1 = now;
  1686. delta = x->ts1.tv_sec - x->ts0.tv_sec;
  1687. delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
  1688. if (delta < dmin0)
  1689. dmin0 = delta;
  1690. if (delta > dmax0)
  1691. dmax0 = delta;
  1692. dsum0 += delta;
  1693. dsumsq0 += delta * delta;
  1694. xnext0 = ISC_TAILQ_NEXT(x, gchain);
  1695. ISC_TAILQ_REMOVE(bucket, x, hchain);
  1696. ISC_TAILQ_REMOVE(&xsent0, x, gchain);
  1697. ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
  1698. /* if the exchange is not finished, go to the second part */
  1699. if (simple == 0) {
  1700. int ret = 0;
  1701. /* the server-ID option is needed */
  1702. if ((usefirst != 0) && (gsrvid != NULL)) {
  1703. x->sid = gsrvid;
  1704. x->sidlen = gsrvidlen;
  1705. } else
  1706. ret = scan_for_srvid6(x, cc);
  1707. /* and the IA_NA option too */
  1708. if (ret >= 0)
  1709. ret = scan_for_ia_na(x, cc);
  1710. if (ret >= 0)
  1711. send_request6(x);
  1712. }
  1713. }
  1714. /*
  1715. * decode a base command line parameter
  1716. * (currently only MAC address and DUID are supported)
  1717. */
  1718. void
  1719. decodebase(void)
  1720. {
  1721. char *b0 = base[basecnt];
  1722. /* MAC address (alias Ethernet address) */
  1723. if ((strncasecmp(b0, "mac=", 4) == 0) ||
  1724. (strncasecmp(b0, "ether=", 6) == 0)) {
  1725. char *b;
  1726. b = strchr(b0, '=') + 1;
  1727. if (sscanf(b, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
  1728. &mac_prefix[0], &mac_prefix[1],
  1729. &mac_prefix[2], &mac_prefix[3],
  1730. &mac_prefix[4], &mac_prefix[5]) == 6) {
  1731. if ((diags != NULL) && (strchr(diags, 'a') != NULL))
  1732. printf("set MAC to %s\n", b);
  1733. return;
  1734. }
  1735. fprintf(stderr,
  1736. "expected base in '%*s=xx:xx:xx:xx:xx:xx' format\n",
  1737. (int) (b - b0 - 1), b0);
  1738. exit(2);
  1739. }
  1740. /* DUID */
  1741. if (strncasecmp(b0, "duid=", 5) == 0) {
  1742. char *b;
  1743. size_t i, l;
  1744. if (duid_prefix != NULL) {
  1745. fprintf(stderr, "duid was already set?\n");
  1746. exit(2);
  1747. }
  1748. b = strchr(b0, '=') + 1;
  1749. l = 0;
  1750. while (*b != '\0') {
  1751. if (!isxdigit((int) *b)) {
  1752. fprintf(stderr,
  1753. "illegal char '%c' in duid\n",
  1754. (int) *b);
  1755. exit(2);
  1756. }
  1757. b++;
  1758. l++;
  1759. }
  1760. if ((l & 1) != 0) {
  1761. fprintf(stderr,
  1762. "odd number of hexadecimal digits in duid\n");
  1763. exit(2);
  1764. }
  1765. l /= 2;
  1766. if (l > 64) {
  1767. fprintf(stderr, "duid too large\n");
  1768. exit(2);
  1769. }
  1770. duid_prefix = (uint8_t *) malloc(l);
  1771. if (duid_prefix == NULL) {
  1772. perror("malloc(duid)");
  1773. exit(1);
  1774. }
  1775. b = strchr(b0, '=') + 1;
  1776. for (i = 0; i < l; i++, b += 2)
  1777. (void) sscanf(b, "%02hhx", &duid_prefix[i]);
  1778. duid_length = l;
  1779. if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
  1780. b = strchr(b0, '=') + 1;
  1781. printf("set DUID[%d] to %s\n",
  1782. duid_length, b);
  1783. }
  1784. return;
  1785. }
  1786. /* other */
  1787. fprintf(stderr, "not yet supported base '%s'\n", b0);
  1788. exit(2);
  1789. }
  1790. /*
  1791. * get the server socket address from the command line:
  1792. * - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
  1793. */
  1794. void
  1795. getserveraddr(const int flags)
  1796. {
  1797. struct addrinfo hints, *res;
  1798. char *service;
  1799. int ret;
  1800. memset(&hints, 0, sizeof(hints));
  1801. if (ipversion == 4) {
  1802. hints.ai_family = AF_INET;
  1803. service = "67";
  1804. } else {
  1805. hints.ai_family = AF_INET6;
  1806. service = "547";
  1807. }
  1808. hints.ai_socktype = SOCK_DGRAM;
  1809. hints.ai_flags = AI_NUMERICSERV | flags;
  1810. #if defined(AI_ADDRCONFIG)
  1811. hints.ai_flags |= AI_ADDRCONFIG;
  1812. #endif
  1813. hints.ai_protocol = IPPROTO_UDP;
  1814. ret = getaddrinfo(servername, service, &hints, &res);
  1815. if (ret != 0) {
  1816. fprintf(stderr, "bad server=%s: %s\n",
  1817. servername, gai_strerror(ret));
  1818. exit(2);
  1819. }
  1820. if (res->ai_next != NULL) {
  1821. fprintf(stderr, "ambiguous server=%s\n", servername);
  1822. exit(2);
  1823. }
  1824. memcpy(&serveraddr, res->ai_addr, res->ai_addrlen);
  1825. freeaddrinfo(res);
  1826. }
  1827. /*
  1828. * get the interface from the command line:
  1829. * - name of the interface
  1830. * - socket address to fill
  1831. * (in IPv6, get the first link-local address)
  1832. */
  1833. void
  1834. getinterface(const char *name, struct sockaddr_storage *ss)
  1835. {
  1836. struct ifaddrs *ifaddr, *ifa;
  1837. int ret;
  1838. size_t len;
  1839. ret = getifaddrs(&ifaddr);
  1840. if (ret < 0) {
  1841. perror("getifaddrs");
  1842. exit(1);
  1843. }
  1844. for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
  1845. if (ifa->ifa_name == NULL)
  1846. continue;
  1847. if (strcmp(ifa->ifa_name, name) != 0)
  1848. continue;
  1849. if ((ifa->ifa_flags & IFF_UP) == 0)
  1850. continue;
  1851. if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
  1852. continue;
  1853. if (ifa->ifa_addr == NULL)
  1854. continue;
  1855. if ((ipversion == 4) &&
  1856. (ifa->ifa_addr->sa_family != AF_INET))
  1857. continue;
  1858. if (ipversion == 6) {
  1859. struct sockaddr_in6 *a6;
  1860. a6 = (struct sockaddr_in6 *) ifa->ifa_addr;
  1861. if (a6->sin6_family != AF_INET6)
  1862. continue;
  1863. if (!IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr))
  1864. continue;
  1865. }
  1866. if (ipversion == 4)
  1867. len = sizeof(struct sockaddr_in);
  1868. else
  1869. len = sizeof(struct sockaddr_in6);
  1870. memcpy(ss, ifa->ifa_addr, len);
  1871. /* fill the server port */
  1872. if (ipversion == 4)
  1873. ((struct sockaddr_in *) ss)->sin_port = htons(67);
  1874. else
  1875. ((struct sockaddr_in6 *) ss)->sin6_port = htons(546);
  1876. return;
  1877. }
  1878. fprintf(stderr, "can't find interface %s\n", name);
  1879. exit(1);
  1880. }
  1881. /*
  1882. * get the local socket address from the command line
  1883. * (if it doesn't work, try an interface name)
  1884. */
  1885. void
  1886. getlocaladdr(void)
  1887. {
  1888. struct addrinfo hints, *res;
  1889. char *service;
  1890. int ret;
  1891. memset(&hints, 0, sizeof(hints));
  1892. if (ipversion == 4) {
  1893. hints.ai_family = AF_INET;
  1894. service = "67";
  1895. } else {
  1896. hints.ai_family = AF_INET6;
  1897. service = "546";
  1898. }
  1899. hints.ai_socktype = SOCK_DGRAM;
  1900. hints.ai_flags = AI_NUMERICSERV;
  1901. #if defined(AI_ADDRCONFIG)
  1902. hints.ai_flags |= AI_ADDRCONFIG;
  1903. #endif
  1904. hints.ai_protocol = IPPROTO_UDP;
  1905. ret = getaddrinfo(localname, service, &hints, &res);
  1906. if ((ret == EAI_NONAME)
  1907. #ifdef EAI_NODATA
  1908. || (ret == EAI_NODATA)
  1909. #endif
  1910. ) {
  1911. isinterface = 1;
  1912. getinterface(localname, &localaddr);
  1913. return;
  1914. }
  1915. if (ret != 0) {
  1916. fprintf(stderr,
  1917. "bad -l<local-addr=%s>: %s\n",
  1918. localname,
  1919. gai_strerror(ret));
  1920. exit(2);
  1921. }
  1922. /* refuse multiple addresses */
  1923. if (res->ai_next != NULL) {
  1924. fprintf(stderr,
  1925. "ambiguous -l<local-addr=%s>\n",
  1926. localname);
  1927. exit(2);
  1928. }
  1929. memcpy(&localaddr, res->ai_addr, res->ai_addrlen);
  1930. freeaddrinfo(res);
  1931. return;
  1932. }
  1933. /*
  1934. * get the local socket address from the server one
  1935. */
  1936. void
  1937. getlocal(void)
  1938. {
  1939. int ret, s;
  1940. socklen_t l;
  1941. if (ipversion == 4) {
  1942. l = sizeof(struct sockaddr_in);
  1943. s = socket(PF_INET, SOCK_DGRAM, 0);
  1944. } else {
  1945. l = sizeof(struct sockaddr_in6);
  1946. s = socket(PF_INET6, SOCK_DGRAM, 0);
  1947. }
  1948. if (s < 0) {
  1949. perror("socket");
  1950. exit(1);
  1951. }
  1952. if ((ipversion == 4) && (isbroadcast != 0)) {
  1953. int on = 1;
  1954. ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
  1955. if (ret < 0) {
  1956. perror("setsockopt(SO_BROADCAST)");
  1957. exit(1);
  1958. }
  1959. }
  1960. ret = connect(s, (struct sockaddr *) &serveraddr, l);
  1961. if (ret < 0) {
  1962. perror("connect");
  1963. exit(1);
  1964. }
  1965. l = sizeof(localaddr);
  1966. ret = getsockname(s, (struct sockaddr *) &localaddr, &l);
  1967. if (ret < 0) {
  1968. perror("getsockname");
  1969. exit(1);
  1970. }
  1971. (void) close(s);
  1972. /* fill the local port */
  1973. if (ipversion == 4)
  1974. ((struct sockaddr_in *) &localaddr)->sin_port = htons(67);
  1975. else
  1976. ((struct sockaddr_in6 *) &localaddr)->sin6_port = htons(546);
  1977. }
  1978. /*
  1979. * intermediate reporting
  1980. * (note: an in-transit packet can be reported as dropped)
  1981. */
  1982. void
  1983. reporting(void)
  1984. {
  1985. dreport.tv_sec += report;
  1986. if (xscount2 == 0) {
  1987. printf("sent: %llu, received: %llu (drops: %lld)",
  1988. (unsigned long long) xscount0,
  1989. (unsigned long long) xrcount0,
  1990. (long long) (xscount0 - xrcount0));
  1991. if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
  1992. double avg;
  1993. avg = dsum0 / xrcount0;
  1994. printf(" average: %.3f ms", avg * 1e3);
  1995. }
  1996. } else {
  1997. printf("sent: %llu/%llu received: %llu/%llu "
  1998. "(drops: %lld/%lld)",
  1999. (unsigned long long) xscount0,
  2000. (unsigned long long) xscount2,
  2001. (unsigned long long) xrcount0,
  2002. (unsigned long long) xrcount2,
  2003. (long long) (xscount0 - xrcount0),
  2004. (long long) (xscount2 - xrcount2));
  2005. if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
  2006. double avg0, avg2;
  2007. avg0 = dsum0 / xrcount0;
  2008. if (xrcount2 != 0)
  2009. avg2 = dsum2 / xrcount2;
  2010. else
  2011. avg2 = 0.;
  2012. printf(" average: %.3f/%.3f ms",
  2013. avg0 * 1e3, avg2 * 1e3);
  2014. }
  2015. }
  2016. printf("\n");
  2017. }
  2018. /*
  2019. * SIGCHLD handler
  2020. */
  2021. void
  2022. reapchild(int sig)
  2023. {
  2024. // Do something with variables to avoid unused-variable warning
  2025. int status;
  2026. status = sig;
  2027. status = 0;
  2028. while (wait3(&status, WNOHANG, NULL) > 0)
  2029. /* continue */;
  2030. }
  2031. /*
  2032. * SIGINT handler
  2033. */
  2034. void
  2035. interrupt(int sig)
  2036. {
  2037. // Do something to avoid unused-variable warning
  2038. int s = sig;
  2039. s = 1;
  2040. interrupted = s;
  2041. }
  2042. /*
  2043. * '-v' handler
  2044. */
  2045. void
  2046. version(void)
  2047. {
  2048. fprintf(stderr, "version 0.01\n");
  2049. }
  2050. /*
  2051. * usage (from the wiki)
  2052. */
  2053. void
  2054. usage(void)
  2055. {
  2056. fprintf(stderr, "%s",
  2057. "perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
  2058. " [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
  2059. " [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
  2060. " [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
  2061. " [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
  2062. " [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
  2063. " [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
  2064. "\f\n"
  2065. "The [server] argument is the name/address of the DHCP server to\n"
  2066. "contact. For DHCPv4 operation, exchanges are initiated by\n"
  2067. "transmitting a DHCP DISCOVER to this address.\n"
  2068. "\n"
  2069. "For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
  2070. "SOLICIT to this address. In the DHCPv6 case, the special name 'all'\n"
  2071. "can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
  2072. "multicast address FF02::1:2), or the special name 'servers' to refer\n"
  2073. "to All_DHCP_Servers (the multicast address FF05::1:3). The [server]\n"
  2074. "argument is optional only in the case that -l is used to specify an\n"
  2075. "interface, in which case [server] defaults to 'all'.\n"
  2076. "\n"
  2077. "The default is to perform a single 4-way exchange, effectively pinging\n"
  2078. "the server.\n"
  2079. "The -r option is used to set up a performance test, without\n"
  2080. "it exchanges are initiated as fast as possible.\n"
  2081. "\n"
  2082. "Options:\n"
  2083. "-1: Take the server-ID option from the first received message.\n"
  2084. "-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
  2085. "-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
  2086. "-a<aggressivity>: When the target sending rate is not yet reached,\n"
  2087. " control how many exchanges are initiated before the next pause.\n"
  2088. "-b<base>: The base MAC, DUID, IP, etc, used to simulate different\n"
  2089. " clients. This can be specified multiple times, each instance is\n"
  2090. " in the <type>=<value> form, for instance:\n"
  2091. " (and default) MAC=00:0c:01:02:03:04.\n"
  2092. "-d<drop-time>: Specify the time after which a request is treated as\n"
  2093. " having been lost. The value is given in seconds and may contain a\n"
  2094. " fractional component. The default is 1 second.\n"
  2095. "-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
  2096. " elapsed-time option in the (second/request) template.\n"
  2097. " The value 0 disables it.\n"
  2098. "-h: Print this help.\n"
  2099. "-i: Do only the initial part of an exchange: DO or SA, depending on\n"
  2100. " whether -6 is given.\n"
  2101. "-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
  2102. " option / (DHCPv6) IA_NA option in the (second/request) template.\n"
  2103. "-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
  2104. " hostname/address to use when communicating with the server. By\n"
  2105. " default, the interface address through which traffic would\n"
  2106. " normally be routed to the server is used.\n"
  2107. " For DHCPv6 operation, specify the name of the network interface\n"
  2108. " via which exchanges are initiated.\n"
  2109. "-L<local-port>: Specify the local port to use\n"
  2110. " (the value 0 means to use the default).\n"
  2111. "-O<random-offset>: Offset of the last octet to randomize in the template.\n"
  2112. "-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
  2113. "-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
  2114. " exchanges per second. A periodic report is generated showing the\n"
  2115. " number of exchanges which were not completed, as well as the\n"
  2116. " average response latency. The program continues until\n"
  2117. " interrupted, at which point a final report is generated.\n"
  2118. "-R<range>: Specify how many different clients are used. With 1\n"
  2119. " (the default), all requests seem to come from the same client.\n"
  2120. "-s<seed>: Specify the seed for randomization, making it repeatable.\n"
  2121. "-S<srvid-offset>: Offset of the server-ID option in the\n"
  2122. " (second/request) template.\n"
  2123. "-T<template-file>: The name of a file containing the template to use\n"
  2124. " as a stream of hexadecimal digits.\n"
  2125. "-v: Report the version number of this program.\n"
  2126. "-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
  2127. " the program.\n"
  2128. "-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
  2129. " <diagnostic-selector> is a string of single-keywords specifying\n"
  2130. " the operations for which verbose output is desired. The selector\n"
  2131. " keyletters are:\n"
  2132. " * 'a': print the decoded command line arguments\n"
  2133. " * 'e': print the exit reason\n"
  2134. " * 'i': print rate processing details\n"
  2135. " * 'r': print randomization details\n"
  2136. " * 's': print first server-id\n"
  2137. " * 't': when finished, print timers of all successful exchanges\n"
  2138. " * 'T': when finished, print templates\n"
  2139. "-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
  2140. "\n"
  2141. "DHCPv4 only options:\n"
  2142. "-B: Force broadcast handling.\n"
  2143. "\n"
  2144. "DHCPv6 only options:\n"
  2145. "-c: Add a rapid commit option (exchanges will be SA).\n"
  2146. "\n"
  2147. "The remaining options are used only in conjunction with -r:\n"
  2148. "\n"
  2149. "-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
  2150. " been dropped. Use -D0 to abort if even a single request has been\n"
  2151. " dropped. If <max-drop> includes the suffix '%', it specifies a\n"
  2152. " maximum percentage of requests that may be dropped before abort.\n"
  2153. " In this case, testing of the threshold begins after 10 requests\n"
  2154. " have been expected to be received.\n"
  2155. "-n<num-request>: Initiate <num-request> transactions. No report is\n"
  2156. " generated until all transactions have been initiated/waited-for,\n"
  2157. " after which a report is generated and the program terminates.\n"
  2158. "-p<test-period>: Send requests for the given test period, which is\n"
  2159. " specified in the same manner as -d. This can be used as an\n"
  2160. " alternative to -n, or both options can be given, in which case the\n"
  2161. " testing is completed when either limit is reached.\n"
  2162. "-t<report>: Delay in seconds between two periodic reports.\n"
  2163. "\n"
  2164. "Errors:\n"
  2165. "- tooshort: received a too short message\n"
  2166. "- orphans: received a message which doesn't match an exchange\n"
  2167. " (duplicate, late or not related)\n"
  2168. "- locallimit: reached to local system limits when sending a message.\n"
  2169. "\n"
  2170. "Exit status:\n"
  2171. "The exit status is:\n"
  2172. "0 on complete success.\n"
  2173. "1 for a general error.\n"
  2174. "2 if an error is found in the command line arguments.\n"
  2175. "3 if there are no general failures in operation, but one or more\n"
  2176. " exchanges are not successfully completed.\n");
  2177. }
  2178. /*
  2179. * main function / entry point
  2180. */
  2181. int
  2182. main(const int argc, char * const argv[])
  2183. {
  2184. int opt, flags = 0, ret, i;
  2185. long long r;
  2186. char *pc;
  2187. extern char *optarg;
  2188. extern int optind;
  2189. #define OPTIONS "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:"
  2190. /* decode options */
  2191. while ((opt = getopt(argc, argv, OPTIONS)) != -1)
  2192. switch (opt) {
  2193. case 'h':
  2194. usage();
  2195. exit(0);
  2196. case 'v':
  2197. version();
  2198. exit(0);
  2199. case '4':
  2200. if (ipversion == 6) {
  2201. fprintf(stderr, "IP version already set to 6\n");
  2202. usage();
  2203. exit(2);
  2204. }
  2205. ipversion = 4;
  2206. break;
  2207. case '6':
  2208. if (ipversion == 4) {
  2209. fprintf(stderr, "IP version already set to 4\n");
  2210. usage();
  2211. exit(2);
  2212. }
  2213. ipversion = 6;
  2214. break;
  2215. case 'r':
  2216. rate = atoi(optarg);
  2217. if (rate <= 0) {
  2218. fprintf(stderr, "rate must be a positive integer\n");
  2219. usage();
  2220. exit(2);
  2221. }
  2222. break;
  2223. case 't':
  2224. report = atoi(optarg);
  2225. if (report <= 0) {
  2226. fprintf(stderr, "report must be a positive integer\n");
  2227. usage();
  2228. exit(2);
  2229. }
  2230. break;
  2231. case 'R':
  2232. r = atoll(optarg);
  2233. if (r < 0) {
  2234. fprintf(stderr,
  2235. "range must not be a negative integer\n");
  2236. usage();
  2237. exit(2);
  2238. }
  2239. range = (uint32_t) r;
  2240. if ((range != 0) && (range != UINT32_MAX)) {
  2241. uint32_t s = range + 1;
  2242. uint64_t b = UINT32_MAX + 1, m;
  2243. m = (b / s) * s;
  2244. if (m == b)
  2245. maxrandom = 0;
  2246. else
  2247. maxrandom = (uint32_t) m;
  2248. }
  2249. break;
  2250. case 'b':
  2251. if (basecnt > 3) {
  2252. fprintf(stderr, "too many bases\n");
  2253. usage();
  2254. exit(2);
  2255. }
  2256. base[basecnt] = optarg;
  2257. decodebase();
  2258. basecnt++;
  2259. break;
  2260. case 'n':
  2261. numreq[gotnumreq] = atoi(optarg);
  2262. if (numreq[gotnumreq] <= 0) {
  2263. fprintf(stderr,
  2264. "num-request must be a positive integer\n");
  2265. usage();
  2266. exit(2);
  2267. }
  2268. gotnumreq = 1;
  2269. break;
  2270. case 'p':
  2271. period = atoi(optarg);
  2272. if (period <= 0) {
  2273. fprintf(stderr,
  2274. "test-period must be a positive integer\n");
  2275. usage();
  2276. exit(2);
  2277. }
  2278. break;
  2279. case 'd':
  2280. losttime[gotlosttime] = atof(optarg);
  2281. if (losttime[gotlosttime] <= 0.) {
  2282. fprintf(stderr,
  2283. "drop-time must be a positive number\n");
  2284. usage();
  2285. exit(2);
  2286. }
  2287. gotlosttime = 1;
  2288. break;
  2289. case 'D':
  2290. pc = strchr(optarg, '%');
  2291. if (pc != NULL) {
  2292. *pc = '\0';
  2293. maxpdrop[gotmaxdrop] = atof(optarg);
  2294. if ((maxpdrop[gotmaxdrop] <= 0) ||
  2295. (maxpdrop[gotmaxdrop] >= 100)) {
  2296. fprintf(stderr,
  2297. "invalid drop-time percentage\n");
  2298. usage();
  2299. exit(2);
  2300. }
  2301. gotmaxdrop = 1;
  2302. break;
  2303. }
  2304. maxdrop[gotmaxdrop] = atoi(optarg);
  2305. if (maxdrop[gotmaxdrop] <= 0) {
  2306. fprintf(stderr,
  2307. "max-drop must be a positive integer\n");
  2308. usage();
  2309. exit(2);
  2310. }
  2311. gotmaxdrop = 1;
  2312. break;
  2313. case 'l':
  2314. localname = optarg;
  2315. break;
  2316. case 'P':
  2317. preload = atoi(optarg);
  2318. if (preload < 0) {
  2319. fprintf(stderr,
  2320. "preload must not be a negative integer\n");
  2321. usage();
  2322. exit(2);
  2323. }
  2324. break;
  2325. case 'a':
  2326. aggressivity = atoi(optarg);
  2327. if (aggressivity <= 0) {
  2328. fprintf(stderr,
  2329. "aggressivity must be a positive integer\n");
  2330. usage();
  2331. exit(2);
  2332. }
  2333. break;
  2334. case 'L':
  2335. localport = atoi(optarg);
  2336. if (localport < 0) {
  2337. fprintf(stderr,
  2338. "local-port must not be a negative integer\n");
  2339. usage();
  2340. exit(2);
  2341. }
  2342. if (localport > (int) UINT16_MAX) {
  2343. fprintf(stderr,
  2344. "local-port must be lower than %d\n",
  2345. (int) UINT16_MAX);
  2346. usage();
  2347. exit(2);
  2348. }
  2349. break;
  2350. case 's':
  2351. seeded = 1;
  2352. seed = (unsigned int) atol(optarg);
  2353. break;
  2354. case 'i':
  2355. simple = 1;
  2356. break;
  2357. case 'B':
  2358. isbroadcast = 1;
  2359. break;
  2360. case 'c':
  2361. rapidcommit = 1;
  2362. break;
  2363. case '1':
  2364. usefirst = 1;
  2365. break;
  2366. case 'T':
  2367. if (templatefile[0] != NULL) {
  2368. if (templatefile[1] != NULL) {
  2369. fprintf(stderr,
  2370. "template-files are already set\n");
  2371. usage();
  2372. exit(2);
  2373. }
  2374. templatefile[1] = optarg;
  2375. } else
  2376. templatefile[0] = optarg;
  2377. break;
  2378. case 'X':
  2379. if (xidoffset[0] >= 0)
  2380. i = 1;
  2381. else
  2382. i = 0;
  2383. xidoffset[i] = atoi(optarg);
  2384. if (xidoffset[i] <= 0) {
  2385. fprintf(stderr,
  2386. "xid-offset must be a positive integer\n");
  2387. usage();
  2388. exit(2);
  2389. }
  2390. break;
  2391. case 'O':
  2392. if (rndoffset[0] >= 0)
  2393. i = 1;
  2394. else
  2395. i = 0;
  2396. rndoffset[i] = atoi(optarg);
  2397. if (rndoffset[i] < 3) {
  2398. fprintf(stderr,
  2399. "random-offset must be greater than 3\n");
  2400. usage();
  2401. exit(2);
  2402. }
  2403. break;
  2404. case 'E':
  2405. elpoffset = atoi(optarg);
  2406. if (elpoffset < 0) {
  2407. fprintf(stderr,
  2408. "time-offset must not be a "
  2409. "negative integer\n");
  2410. usage();
  2411. exit(2);
  2412. }
  2413. break;
  2414. case 'S':
  2415. sidoffset = atoi(optarg);
  2416. if (sidoffset <= 0) {
  2417. fprintf(stderr,
  2418. "srvid-offset must be a positive integer\n");
  2419. usage();
  2420. exit(2);
  2421. }
  2422. break;
  2423. case 'I':
  2424. ripoffset = atoi(optarg);
  2425. if (ripoffset <= 0) {
  2426. fprintf(stderr,
  2427. "ip-offset must be a positive integer\n");
  2428. usage();
  2429. exit(2);
  2430. }
  2431. break;
  2432. case 'x':
  2433. diags = optarg;
  2434. break;
  2435. case 'w':
  2436. wrapped = optarg;
  2437. break;
  2438. default:
  2439. usage();
  2440. exit(2);
  2441. }
  2442. /* adjust some global variables */
  2443. if (ipversion == 0)
  2444. ipversion = 4;
  2445. if (templatefile[1] != NULL) {
  2446. if (xidoffset[1] < 0)
  2447. xidoffset[1] = xidoffset[0];
  2448. if (rndoffset[1] < 0)
  2449. rndoffset[1] = rndoffset[0];
  2450. }
  2451. /* when required, print the internal view of the command line */
  2452. if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
  2453. printf("IPv%d", ipversion);
  2454. if (simple != 0) {
  2455. if (ipversion == 4)
  2456. printf(" DO only");
  2457. else
  2458. printf(" SA only");
  2459. }
  2460. if (rate != 0)
  2461. printf(" rate=%d", rate);
  2462. if (report != 0)
  2463. printf(" report=%d", report);
  2464. if (range != 0) {
  2465. if (strchr(diags, 'r') != NULL)
  2466. printf(" range=0..%d [0x%x]",
  2467. range,
  2468. (unsigned int) maxrandom);
  2469. else
  2470. printf(" range=0..%d", range);
  2471. }
  2472. if (basecnt != 0)
  2473. for (i = 0; i < basecnt; i++)
  2474. printf(" base[%d]='%s'", i, base[i]);
  2475. if (gotnumreq != 0)
  2476. printf(" num-request=%d,%d", numreq[0], numreq[1]);
  2477. if (period != 0)
  2478. printf(" test-period=%d", period);
  2479. printf(" drop-time=%g,%g", losttime[0], losttime[1]);
  2480. if ((maxdrop[0] != 0) || (maxdrop[1] != 0))
  2481. printf(" max-drop=%d,%d", maxdrop[0], maxdrop[1]);
  2482. if ((maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))
  2483. printf(" max-drop=%2.2f%%,%2.2f%%",
  2484. maxpdrop[0], maxpdrop[1]);
  2485. if (preload != 0)
  2486. printf(" preload=%d", preload);
  2487. printf(" aggressivity=%d", aggressivity);
  2488. if (localport != 0)
  2489. printf(" local-port=%d", localport);
  2490. if (seeded)
  2491. printf(" seed=%u", seed);
  2492. if (isbroadcast != 0)
  2493. printf(" broadcast");
  2494. if (rapidcommit != 0)
  2495. printf(" rapid-commit");
  2496. if (usefirst != 0)
  2497. printf(" use-first");
  2498. if ((templatefile[0] != NULL) && (templatefile[1] == NULL))
  2499. printf(" template-file='%s'", templatefile[0]);
  2500. else if (templatefile[1] != NULL)
  2501. printf(" template-file='%s','%s'",
  2502. templatefile[0], templatefile[1]);
  2503. if ((xidoffset[0] >= 0) && (xidoffset[1] < 0))
  2504. printf(" xid-offset=%d", xidoffset[0]);
  2505. else if (xidoffset[1] >= 0)
  2506. printf(" xid-offset=%d,%d",
  2507. xidoffset[0], xidoffset[1]);
  2508. if ((rndoffset[0] >= 0) && (rndoffset[1] < 0))
  2509. printf(" xid-offset=%d", rndoffset[0]);
  2510. else if (rndoffset[1] >= 0)
  2511. printf(" xid-offset=%d,%d",
  2512. rndoffset[0], rndoffset[1]);
  2513. if (elpoffset >= 0)
  2514. printf(" time-offset=%d", elpoffset);
  2515. if (sidoffset >= 0)
  2516. printf(" srvid-offset=%d", sidoffset);
  2517. if (ripoffset >= 0)
  2518. printf(" ip-offset=%d", ripoffset);
  2519. printf(" diagnotic-selectors='%s'", diags);
  2520. if (wrapped != NULL)
  2521. printf(" wrapped='%s'", wrapped);
  2522. printf("\n");
  2523. }
  2524. /* check DHCPv4 only options */
  2525. if ((ipversion != 4) && (isbroadcast != 0)) {
  2526. fprintf(stderr, "-b is not compatible with IPv6 (-6)\n");
  2527. usage();
  2528. exit(2);
  2529. }
  2530. /* check DHCPv6 only options */
  2531. if ((ipversion != 6) && (rapidcommit != 0)) {
  2532. fprintf(stderr, "-6 (IPv6) must be set to use -c\n");
  2533. usage();
  2534. exit(2);
  2535. }
  2536. /* check 4-packet (aka not simple) mode options */
  2537. if ((simple != 0) && (numreq[1] != 0)) {
  2538. fprintf(stderr,
  2539. "second -n<num-request> is not compatible with -i\n");
  2540. usage();
  2541. exit(2);
  2542. }
  2543. if ((simple != 0) && (losttime[1] != 1.)) {
  2544. fprintf(stderr,
  2545. "second -d<drop-time> is not compatible with -i\n");
  2546. usage();
  2547. exit(2);
  2548. }
  2549. if ((simple != 0) &&
  2550. ((maxdrop[1] != 0) || (maxpdrop[1] != 0.))) {
  2551. fprintf(stderr,
  2552. "second -D<max-drop> is not compatible with -i\n");
  2553. usage();
  2554. exit(2);
  2555. }
  2556. if ((simple != 0) && (usefirst != 0)) {
  2557. fprintf(stderr,
  2558. "-1 is not compatible with -i\n");
  2559. usage();
  2560. exit(2);
  2561. }
  2562. if ((simple != 0) && (templatefile[1] != NULL)) {
  2563. fprintf(stderr,
  2564. "second -T<template-file> is not "
  2565. "compatible with -i\n");
  2566. usage();
  2567. exit(2);
  2568. }
  2569. if ((simple != 0) && (xidoffset[1] >= 0)) {
  2570. fprintf(stderr,
  2571. "second -X<xid-offset> is not compatible with -i\n");
  2572. usage();
  2573. exit(2);
  2574. }
  2575. if ((simple != 0) && (rndoffset[1] >= 0)) {
  2576. fprintf(stderr,
  2577. "second -O<random-offset is not compatible with -i\n");
  2578. usage();
  2579. exit(2);
  2580. }
  2581. if ((simple != 0) && (elpoffset >= 0)) {
  2582. fprintf(stderr,
  2583. "-E<time-offset> is not compatible with -i\n");
  2584. usage();
  2585. exit(2);
  2586. }
  2587. if ((simple != 0) && (sidoffset >= 0)) {
  2588. fprintf(stderr,
  2589. "-S<srvid-offset> is not compatible with -i\n");
  2590. usage();
  2591. exit(2);
  2592. }
  2593. if ((simple != 0) && (ripoffset >= 0)) {
  2594. fprintf(stderr,
  2595. "-I<ip-offset> is not compatible with -i\n");
  2596. usage();
  2597. exit(2);
  2598. }
  2599. /* check simple mode options */
  2600. if ((simple == 0) && (rapidcommit != 0)) {
  2601. fprintf(stderr, "-i must be set to use -c\n");
  2602. usage();
  2603. exit(2);
  2604. }
  2605. /* check rate '-r' options */
  2606. if ((rate == 0) && (report != 0)) {
  2607. fprintf(stderr,
  2608. "-r<rate> must be set to use -t<report>\n");
  2609. usage();
  2610. exit(2);
  2611. }
  2612. if ((rate == 0) && ((numreq[0] != 0) || (numreq[1] != 0))) {
  2613. fprintf(stderr,
  2614. "-r<rate> must be set to use -n<num-request>\n");
  2615. usage();
  2616. exit(2);
  2617. }
  2618. if ((rate == 0) && (period != 0)) {
  2619. fprintf(stderr,
  2620. "-r<rate> must be set to use -p<test-period>\n");
  2621. usage();
  2622. exit(2);
  2623. }
  2624. if ((rate == 0) &&
  2625. ((maxdrop[0] != 0) || (maxdrop[1] != 0) ||
  2626. (maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))) {
  2627. fprintf(stderr,
  2628. "-r<rate> must be set to use -D<max-drop>\n");
  2629. usage();
  2630. exit(2);
  2631. }
  2632. /* check (first) template file options */
  2633. if ((templatefile[0] == NULL) && (xidoffset[0] >= 0)) {
  2634. fprintf(stderr,
  2635. "-T<template-file> must be set to "
  2636. "use -X<xid-offset>\n");
  2637. usage();
  2638. exit(2);
  2639. }
  2640. if ((templatefile[0] == NULL) && (rndoffset[0] >= 0)) {
  2641. fprintf(stderr,
  2642. "-T<template-file> must be set to "
  2643. "use -O<random-offset>\n");
  2644. usage();
  2645. exit(2);
  2646. }
  2647. /* check (second) template file options */
  2648. if ((templatefile[1] == NULL) && (elpoffset >= 0)) {
  2649. fprintf(stderr,
  2650. "second/request -T<template-file> must be set to "
  2651. "use -E<time-offset>\n");
  2652. usage();
  2653. exit(2);
  2654. }
  2655. if ((templatefile[1] == NULL) && (sidoffset >= 0)) {
  2656. fprintf(stderr,
  2657. "second/request -T<template-file> must be set to "
  2658. "use -S<srvid-offset>\n");
  2659. usage();
  2660. exit(2);
  2661. }
  2662. if ((templatefile[1] == NULL) && (ripoffset >= 0)) {
  2663. fprintf(stderr,
  2664. "second/request -T<template-file> must be set to "
  2665. "use -I<ip-offset>\n");
  2666. usage();
  2667. exit(2);
  2668. }
  2669. /* check various template file(s) and other condition(s) options */
  2670. if ((templatefile[0] != NULL) && (range > 0) && (rndoffset[0] < 0)) {
  2671. fprintf(stderr,
  2672. "-O<random-offset> must be set when "
  2673. "-T<template-file> and -R<range> are used\n");
  2674. usage();
  2675. exit(2);
  2676. }
  2677. if ((templatefile[1] != NULL) && (sidoffset < 0)) {
  2678. fprintf(stderr,
  2679. "-S<srvid-offset> must be set when second "
  2680. "-T<template-file> is used\n");
  2681. usage();
  2682. exit(2);
  2683. }
  2684. if ((templatefile[1] != NULL) && (ripoffset < 0)) {
  2685. fprintf(stderr,
  2686. "-I<ip-offset> must be set when second "
  2687. "-T<template-file> is used\n");
  2688. usage();
  2689. exit(2);
  2690. }
  2691. /* get the server argument */
  2692. if (optind < argc - 1) {
  2693. fprintf(stderr, "extra arguments?\n");
  2694. usage();
  2695. exit(2);
  2696. }
  2697. if (optind == argc - 1) {
  2698. servername = argv[optind];
  2699. /* decode special cases */
  2700. if ((ipversion == 4) &&
  2701. (strcmp(servername, "all") == 0)) {
  2702. flags = AI_NUMERICHOST;
  2703. isbroadcast = 1;
  2704. servername = "255.255.255.255";
  2705. } else if ((ipversion == 6) &&
  2706. (strcmp(servername, "all") == 0)) {
  2707. flags = AI_NUMERICHOST;
  2708. servername = "FF02::1:2";
  2709. } else if ((ipversion == 6) &&
  2710. (strcmp(servername, "servers") == 0)) {
  2711. flags = AI_NUMERICHOST;
  2712. servername = "FF05::1:3";
  2713. }
  2714. }
  2715. /* handle the local '-l' address/interface */
  2716. if (localname != NULL) {
  2717. /* given */
  2718. getlocaladdr();
  2719. if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
  2720. if (isinterface)
  2721. printf("interface='%s'\n", localname);
  2722. else
  2723. printf("local-addr='%s'\n", localname);
  2724. }
  2725. /* get the not given server from it */
  2726. if (servername == NULL) {
  2727. if (isinterface && (ipversion == 4)) {
  2728. flags = AI_NUMERICHOST;
  2729. isbroadcast = 1;
  2730. servername = "255.255.255.255";
  2731. } else if (isinterface && (ipversion == 6)) {
  2732. flags = AI_NUMERICHOST;
  2733. servername = "FF02::1:2";
  2734. } else {
  2735. fprintf(stderr,
  2736. "without an interface "
  2737. "server is required\n");
  2738. usage();
  2739. exit(2);
  2740. }
  2741. }
  2742. } else if (servername == NULL) {
  2743. fprintf(stderr, "without an interface server is required\n");
  2744. usage();
  2745. exit(2);
  2746. }
  2747. /* get the server socket address */
  2748. getserveraddr(flags);
  2749. /* finish local/server socket address stuff and print it */
  2750. if ((diags != NULL) && (strchr(diags, 'a') != NULL))
  2751. printf("server='%s'\n", servername);
  2752. if (localname == NULL)
  2753. getlocal();
  2754. if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
  2755. char addr[NI_MAXHOST];
  2756. ret = getnameinfo((struct sockaddr *) &localaddr,
  2757. sizeof(localaddr),
  2758. addr,
  2759. NI_MAXHOST,
  2760. NULL,
  2761. 0,
  2762. NI_NUMERICHOST);
  2763. if (ret != 0) {
  2764. fprintf(stderr,
  2765. "can't get the local address: %s\n",
  2766. gai_strerror(ret));
  2767. exit(1);
  2768. }
  2769. printf("local address='%s'\n", addr);
  2770. }
  2771. /* initialize exchange structures */
  2772. inits();
  2773. /* get the socket descriptor and template(s) */
  2774. if (ipversion == 4) {
  2775. getsock4();
  2776. if (templatefile[0] == NULL)
  2777. build_template_discover4();
  2778. else
  2779. get_template_discover4();
  2780. if (simple == 0) {
  2781. if (templatefile[1] == NULL)
  2782. build_template_request4();
  2783. else
  2784. get_template_request4();
  2785. }
  2786. } else {
  2787. getsock6();
  2788. if (duid_prefix != NULL) {
  2789. if (templatefile[0] == NULL)
  2790. build_template_solicit6();
  2791. else
  2792. get_template_solicit6();
  2793. if (simple == 0) {
  2794. if (templatefile[1] == NULL)
  2795. build_template_request6();
  2796. else
  2797. get_template_request6();
  2798. }
  2799. }
  2800. }
  2801. /* sanity check */
  2802. if ((unsigned) sock > FD_SETSIZE) {
  2803. fprintf(stderr, "socket descriptor (%d) too large?!\n", sock);
  2804. exit(1);
  2805. }
  2806. /* make the socket descriptor not blocking */
  2807. flags = fcntl(sock, F_GETFL, 0);
  2808. if (flags < 0) {
  2809. perror("fcntl(F_GETFL)");
  2810. exit(1);
  2811. }
  2812. if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
  2813. perror("fcntl(F_SETFL)");
  2814. exit(1);
  2815. }
  2816. /* wrapped start */
  2817. if (wrapped != NULL) {
  2818. pid_t pid;
  2819. (void) signal(SIGCHLD, reapchild);
  2820. pid = fork();
  2821. if (pid < 0) {
  2822. perror("fork");
  2823. exit(1);
  2824. } else if (pid == 0)
  2825. (void) execlp(wrapped, "start", (char *) NULL);
  2826. }
  2827. /* boot is done! */
  2828. if (clock_gettime(CLOCK_REALTIME, &boot) < 0) {
  2829. perror("clock_gettime(boot)");
  2830. exit(1);
  2831. }
  2832. /* compute the next intermediate reporting date */
  2833. if (report != 0) {
  2834. dreport.tv_sec = boot.tv_sec + report;
  2835. dreport.tv_nsec = boot.tv_nsec;
  2836. }
  2837. /* compute the DUID (the current date is needed) */
  2838. if ((ipversion == 6) && (duid_prefix == NULL)) {
  2839. uint32_t curdate;
  2840. duid_length = 14;
  2841. duid_prefix = (uint8_t *) malloc(duid_length);
  2842. if (duid_prefix == NULL) {
  2843. perror("malloc(duid)");
  2844. exit(1);
  2845. }
  2846. duid_prefix[0] = DHCP6_DUID_LLT >> 8;
  2847. duid_prefix[1] = DHCP6_DUID_LLT;
  2848. duid_prefix[2] = DHCP_HTYPE_ETHER >> 8;
  2849. duid_prefix[3] = DHCP_HTYPE_ETHER;
  2850. curdate = htonl(boot.tv_sec - DHCP6_DUID_EPOCH);
  2851. memcpy(duid_prefix + 4, &curdate, 4);
  2852. memcpy(duid_prefix + 8, mac_prefix, 6);
  2853. /* the DUID is in template(s) */
  2854. if (templatefile[0] == NULL)
  2855. build_template_solicit6();
  2856. else
  2857. get_template_solicit6();
  2858. if (simple == 0) {
  2859. if (templatefile[1] == NULL)
  2860. build_template_request6();
  2861. else
  2862. get_template_request6();
  2863. }
  2864. }
  2865. /* seed the random generator */
  2866. if (seeded == 0)
  2867. seed = (unsigned int) (boot.tv_sec + boot.tv_nsec);
  2868. srandom(seed);
  2869. /* preload the server with at least one packet */
  2870. compsend = preload + 1;
  2871. for (i = 0; i <= preload; i++) {
  2872. if (ipversion == 4)
  2873. ret = send4();
  2874. else
  2875. ret = send6();
  2876. if (ret < 0) {
  2877. /* failure at the first packet is fatal */
  2878. if (i == 0) {
  2879. fprintf(stderr,
  2880. "initial send failed: %s\n",
  2881. strerror(-ret));
  2882. exit(1);
  2883. }
  2884. if ((errno == EAGAIN) ||
  2885. (errno == EWOULDBLOCK) ||
  2886. (errno == ENOBUFS) ||
  2887. (errno == ENOMEM))
  2888. locallimit++;
  2889. fprintf(stderr, "preload send: %s\n", strerror(-ret));
  2890. break;
  2891. }
  2892. }
  2893. /* required only before the interrupted flag check */
  2894. (void) signal(SIGINT, interrupt);
  2895. /* main loop */
  2896. for (;;) {
  2897. struct timespec now, ts;
  2898. fd_set rfds;
  2899. /* immediate loop exit conditions */
  2900. if (interrupted) {
  2901. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2902. printf("interrupted\n");
  2903. break;
  2904. }
  2905. if (fatal) {
  2906. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2907. printf("got a fatal error\n");
  2908. break;
  2909. }
  2910. /* get the date and use it */
  2911. if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
  2912. perror("clock_gettime(now)");
  2913. fatal = 1;
  2914. continue;
  2915. }
  2916. if ((period != 0) &&
  2917. ((boot.tv_sec + period < now.tv_sec) ||
  2918. ((boot.tv_sec + period == now.tv_sec) &&
  2919. (boot.tv_nsec < now.tv_nsec)))) {
  2920. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2921. printf("reached test-period\n");
  2922. break;
  2923. }
  2924. if ((report != 0) &&
  2925. ((dreport.tv_sec < now.tv_sec) ||
  2926. ((dreport.tv_sec == now.tv_sec) &&
  2927. (dreport.tv_nsec < now.tv_nsec))))
  2928. reporting();
  2929. /* compute the delay for the next send */
  2930. due = last;
  2931. if (rate == 1)
  2932. due.tv_sec += 1;
  2933. else if (rate != 0)
  2934. due.tv_nsec += 1010000000 / rate;
  2935. else
  2936. due.tv_nsec += 1;
  2937. while (due.tv_nsec >= 1000000000) {
  2938. due.tv_sec += 1;
  2939. due.tv_nsec -= 1000000000;
  2940. }
  2941. ts = due;
  2942. ts.tv_sec -= now.tv_sec;
  2943. ts.tv_nsec -= now.tv_nsec;
  2944. while (ts.tv_nsec < 0) {
  2945. ts.tv_sec -= 1;
  2946. ts.tv_nsec += 1000000000;
  2947. }
  2948. /* the send was already due? */
  2949. if (ts.tv_sec < 0) {
  2950. ts.tv_sec = ts.tv_nsec = 0;
  2951. latesent++;
  2952. }
  2953. /* pselect() */
  2954. FD_ZERO(&rfds);
  2955. FD_SET(sock, &rfds);
  2956. ret = pselect(sock + 1, &rfds, NULL, NULL, &ts, NULL);
  2957. if (ret < 0) {
  2958. if (errno == EINTR)
  2959. continue;
  2960. perror("pselect");
  2961. fatal = 1;
  2962. continue;
  2963. }
  2964. /* packet(s) to receive */
  2965. while (ret == 1) {
  2966. if (ipversion == 4)
  2967. receive4();
  2968. else
  2969. receive6();
  2970. if (recv(sock, ibuf, sizeof(ibuf), MSG_PEEK) <= 0)
  2971. ret = 0;
  2972. else
  2973. multrcvd++;
  2974. }
  2975. if (fatal)
  2976. continue;
  2977. /* check receive loop exit conditions */
  2978. if ((numreq[0] != 0) && ((int) xscount0 >= numreq[0])) {
  2979. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2980. printf("reached num-request0\n");
  2981. break;
  2982. }
  2983. if ((numreq[1] != 0) && ((int) xscount2 >= numreq[1])) {
  2984. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2985. printf("reached num-request2\n");
  2986. break;
  2987. }
  2988. if ((maxdrop[0] != 0) &&
  2989. ((int) (xscount0 - xrcount0) > maxdrop[0])) {
  2990. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2991. printf("reached max-drop%s (absolute)\n",
  2992. simple != 0 ? "" : "0");
  2993. break;
  2994. }
  2995. if ((maxdrop[1] != 0) &&
  2996. ((int) (xscount2 - xrcount2) > maxdrop[1])) {
  2997. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2998. printf("reached max-drop2 (absolute)\n");
  2999. break;
  3000. }
  3001. if ((maxpdrop[0] != 0.) &&
  3002. (xscount0 > 10) &&
  3003. (((100. * (xscount0 - xrcount0)) / xscount0)
  3004. > maxpdrop[0])) {
  3005. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  3006. printf("reached max-drop%s (percent)\n",
  3007. simple != 0 ? "" : "0");
  3008. break;
  3009. }
  3010. if ((maxpdrop[1] != 0.) &&
  3011. (xscount2 > 10) &&
  3012. (((100. * (xscount2 - xrcount2)) / xscount2)
  3013. > maxpdrop[1])) {
  3014. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  3015. printf("reached max-drop2 (percent)\n");
  3016. break;
  3017. }
  3018. /* compute how many packets to send */
  3019. if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
  3020. perror("clock_gettime(now2)");
  3021. fatal = 1;
  3022. continue;
  3023. }
  3024. if ((now.tv_sec > due.tv_sec) ||
  3025. ((now.tv_sec == due.tv_sec) &&
  3026. (now.tv_nsec >= due.tv_nsec))) {
  3027. double tosend;
  3028. if (rate != 0) {
  3029. tosend = (now.tv_nsec - due.tv_nsec) / 1e9;
  3030. tosend += now.tv_sec - due.tv_sec;
  3031. tosend *= rate;
  3032. tosend += 1;
  3033. if (tosend > (double) aggressivity)
  3034. i = aggressivity;
  3035. else
  3036. i = (int) tosend;
  3037. } else
  3038. i = aggressivity;
  3039. compsend += i;
  3040. /* send packets */
  3041. for (;;) {
  3042. if (ipversion == 4)
  3043. ret = send4();
  3044. else
  3045. ret = send6();
  3046. if (ret < 0) {
  3047. if ((errno == EAGAIN) ||
  3048. (errno == EWOULDBLOCK) ||
  3049. (errno == ENOBUFS) ||
  3050. (errno == ENOMEM))
  3051. locallimit++;
  3052. fprintf(stderr,
  3053. "send: %s\n", strerror(-ret));
  3054. break;
  3055. }
  3056. i--;
  3057. if (i == 0)
  3058. break;
  3059. /* check for late packets to receive */
  3060. if (recv(sock, ibuf, sizeof(ibuf),
  3061. MSG_PEEK) > 0) {
  3062. latercvd++;
  3063. if (ipversion == 4)
  3064. receive4();
  3065. else
  3066. receive6();
  3067. }
  3068. }
  3069. } else
  3070. /* there was no packet to send */
  3071. shortwait++;
  3072. }
  3073. /* after main loop: finished */
  3074. if (clock_gettime(CLOCK_REALTIME, &finished) < 0)
  3075. perror("clock_gettime(finished)");
  3076. /* wrapped stop */
  3077. if (wrapped != NULL) {
  3078. pid_t pid;
  3079. pid = fork();
  3080. if (pid == 0)
  3081. (void) execlp(wrapped, "stop", (char *) NULL);
  3082. }
  3083. /* main statictics */
  3084. if (xscount2 == 0)
  3085. printf("sent: %llu, received: %llu (drops: %lld)\n",
  3086. (unsigned long long) xscount0,
  3087. (unsigned long long) xrcount0,
  3088. (long long) (xscount0 - xrcount0));
  3089. else
  3090. printf("sent: %llu/%llu, received: %llu/%llu "
  3091. "(drops: %lld/%lld)\n",
  3092. (unsigned long long) xscount0,
  3093. (unsigned long long) xscount2,
  3094. (unsigned long long) xrcount0,
  3095. (unsigned long long) xrcount2,
  3096. (long long) (xscount0 - xrcount0),
  3097. (long long) (xscount2 - xrcount2));
  3098. printf("tooshort: %llu, orphans: %llu, local limits: %llu\n",
  3099. (unsigned long long) tooshort,
  3100. (unsigned long long) orphans,
  3101. (unsigned long long) locallimit);
  3102. /* print the rate */
  3103. if (finished.tv_sec != 0) {
  3104. double dall, erate;
  3105. dall = (finished.tv_nsec - boot.tv_nsec) / 1e9;
  3106. dall += finished.tv_sec - boot.tv_sec;
  3107. erate = xrcount0 / dall;
  3108. if (rate != 0)
  3109. printf("rate: %f (expected %d)\n", erate, rate);
  3110. else
  3111. printf("rate: %f\n", erate);
  3112. }
  3113. /* rate processing instrumentation */
  3114. if ((diags != NULL) && (strchr(diags, 'i') != NULL)) {
  3115. printf("latesent: %llu, compsend: %llu, shortwait: %llu\n"
  3116. "multrcvd: %llu, latercvd: %llu, collected:%llu/%llu\n",
  3117. (unsigned long long) latesent,
  3118. (unsigned long long) compsend,
  3119. (unsigned long long) shortwait,
  3120. (unsigned long long) multrcvd,
  3121. (unsigned long long) latercvd,
  3122. (unsigned long long) collected[0],
  3123. (unsigned long long) collected[1]);
  3124. }
  3125. /* round-time trip statistics */
  3126. if (xrcount2 != 0) {
  3127. double avg0, avg2, stddev0, stddev2;
  3128. avg0 = dsum0 / xrcount0;
  3129. avg2 = dsum2 / xrcount2;
  3130. stddev0 = sqrt(dsumsq0 / xrcount0 - avg0 * avg0);
  3131. stddev2 = sqrt(dsumsq2 / xrcount2 - avg2 * avg2);
  3132. printf("RTT0: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
  3133. dmin0 * 1e3, avg0 * 1e3, dmax0 * 1e3, stddev0 * 1e3);
  3134. printf("RTT2: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
  3135. dmin2 * 1e3, avg2 * 1e3, dmax2 * 1e3, stddev2 * 1e3);
  3136. } else if (xrcount0 != 0) {
  3137. double avg, stddev;
  3138. avg = dsum0 / xrcount0;
  3139. stddev = sqrt(dsumsq0 / xrcount0 - avg * avg);
  3140. printf("RTT%s: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
  3141. simple != 0 ? "" : "0",
  3142. dmin0 * 1e3, avg * 1e3, dmax0 * 1e3, stddev * 1e3);
  3143. }
  3144. /* (first) server-ID option content */
  3145. if ((diags != NULL) && (strchr(diags, 's') != NULL) &&
  3146. !ISC_TAILQ_EMPTY(&xrcvd0)) {
  3147. struct exchange *x;
  3148. size_t n;
  3149. printf("server-id: ");
  3150. x = ISC_TAILQ_FIRST(&xrcvd0);
  3151. if (ipversion == 4)
  3152. n = 2;
  3153. else
  3154. n = 4;
  3155. for (; n < x->sidlen; n++)
  3156. printf("%02hhx", x->sid[n]);
  3157. printf("\n");
  3158. }
  3159. /* all time-stamps */
  3160. if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
  3161. !ISC_TAILQ_EMPTY(&xrcvd0)) {
  3162. struct exchange *x;
  3163. printf("\n\n");
  3164. ISC_TAILQ_FOREACH(x, &xrcvd0, gchain)
  3165. printf("%ld.%09ld %ld.%09ld\n",
  3166. (long) x->ts0.tv_sec, x->ts0.tv_nsec,
  3167. (long) x->ts1.tv_sec, x->ts1.tv_nsec);
  3168. }
  3169. if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
  3170. !ISC_TAILQ_EMPTY(&xrcvd2)) {
  3171. struct exchange *x;
  3172. printf("--\n");
  3173. ISC_TAILQ_FOREACH(x, &xrcvd2, gchain)
  3174. printf("%ld.%09ld %ld.%09ld %ld.%09ld %ld.%09ld\n",
  3175. (long) x->ts0.tv_sec, x->ts0.tv_nsec,
  3176. (long) x->ts1.tv_sec, x->ts1.tv_nsec,
  3177. (long) x->ts2.tv_sec, x->ts2.tv_nsec,
  3178. (long) x->ts3.tv_sec, x->ts3.tv_nsec);
  3179. }
  3180. if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
  3181. !ISC_TAILQ_EMPTY(&xrcvd0))
  3182. printf("\n\n");
  3183. /* template(s) */
  3184. if ((diags != NULL) && (strchr(diags, 'T') != NULL)) {
  3185. size_t n;
  3186. if (ipversion == 4) {
  3187. printf("length = %zu\n", length_discover4);
  3188. printf("xid offset = %d\n", DHCP_OFF_XID);
  3189. printf("xid length = 4\n");
  3190. printf("random offset = %zu\n", random_discover4);
  3191. printf("content:\n");
  3192. for (n = 0; n < length_discover4; n++) {
  3193. printf("%s%02hhx",
  3194. (n & 15) == 0 ? "" : " ",
  3195. template_discover4[n]);
  3196. if ((n & 15) == 15)
  3197. printf("\n");
  3198. }
  3199. if ((n & 15) != 15)
  3200. printf("\n");
  3201. if (simple != 0)
  3202. goto doneT;
  3203. printf("--\n");
  3204. printf("length = %zu\n", length_request4);
  3205. printf("xid offset = %d\n", DHCP_OFF_XID);
  3206. printf("xid length = 4\n");
  3207. printf("random offset = %zu\n", random_request4);
  3208. if (elapsed_request4 > 0)
  3209. printf("secs offset = %zu\n",
  3210. elapsed_request4);
  3211. printf("server-id offset = %zu\n", serverid_request4);
  3212. printf("server-id length = %d\n", DHCP_OPTLEN_SRVID);
  3213. printf("content:\n");
  3214. printf("requested-ip-address offset = %zu\n",
  3215. reqaddr_request4);
  3216. printf("requested-ip-address length = %d\n", 4);
  3217. for (n = 0; n < length_request4; n++) {
  3218. printf("%s%02hhx",
  3219. (n & 15) == 0 ? "" : " ",
  3220. template_request4[n]);
  3221. if ((n & 15) == 15)
  3222. printf("\n");
  3223. }
  3224. printf("\n");
  3225. } else {
  3226. printf("length = %zu\n", length_solicit6);
  3227. printf("xid offset = %d\n", DHCP6_OFF_XID);
  3228. printf("xid length = 3\n");
  3229. printf("random offset = %zu\n", random_solicit6);
  3230. for (n = 0; n < length_solicit6; n++) {
  3231. printf("%s%02hhx",
  3232. (n & 15) == 0 ? "" : " ",
  3233. template_solicit6[n]);
  3234. if ((n & 15) == 15)
  3235. printf("\n");
  3236. }
  3237. if ((n & 15) != 15)
  3238. printf("\n");
  3239. if (simple != 0)
  3240. goto doneT;
  3241. printf("--\n");
  3242. printf("length = %zu\n", length_request6);
  3243. printf("xid offset = %d\n", DHCP_OFF_XID);
  3244. printf("xid length = 4\n");
  3245. printf("random offset = %zu\n", random_request6);
  3246. if (elapsed_request6 > 0)
  3247. printf("secs offset = %zu\n",
  3248. elapsed_request6);
  3249. printf("server-id offset = %zu\n", serverid_request6);
  3250. printf("content:\n");
  3251. printf("requested-ip-address offset = %zu\n",
  3252. reqaddr_request6);
  3253. for (n = 0; n < length_request6; n++) {
  3254. printf("%s%02hhx",
  3255. (n & 15) == 0 ? "" : " ",
  3256. template_request6[n]);
  3257. if ((n & 15) == 15)
  3258. printf("\n");
  3259. }
  3260. printf("\n");
  3261. }
  3262. }
  3263. doneT:
  3264. /* compute the exit code (and exit) */
  3265. if (fatal)
  3266. exit(1);
  3267. else if ((xscount0 == xrcount0) && (xscount2 == xrcount2))
  3268. exit(0);
  3269. else
  3270. exit(3);
  3271. }
  3272. #endif /* HAVE_GETIFADDRS */