perfdhcp.c 87 KB

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