perfdhcp.c 87 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559
  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. ret = bind(sock,
  1113. (struct sockaddr *) &localaddr,
  1114. sizeof(struct sockaddr_in6));
  1115. if (ret < 0) {
  1116. perror("Failed to bind v6 socket to local-link address");
  1117. exit(1);
  1118. }
  1119. /* perform the multicast stuff when the destination is multicast */
  1120. if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
  1121. int hops = 1;
  1122. ret = setsockopt(sock,
  1123. IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
  1124. &hops, sizeof(hops));
  1125. if (ret < 0) {
  1126. perror("setsockopt(IPV6_MULTICAST_HOPS)");
  1127. exit(1);
  1128. }
  1129. }
  1130. if (isinterface && IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
  1131. unsigned idx = if_nametoindex(localname);
  1132. if (idx == 0) {
  1133. fprintf(stderr,
  1134. "if_nametoindex(%s) failed\n",
  1135. localname);
  1136. exit(1);
  1137. }
  1138. ret = setsockopt(sock,
  1139. IPPROTO_IPV6, IPV6_MULTICAST_IF,
  1140. &idx, sizeof(idx));
  1141. if (ret < 0) {
  1142. perror("setsockopt(IPV6_MULTICAST_IF)");
  1143. exit(1);
  1144. }
  1145. }
  1146. }
  1147. /*
  1148. * build a DHCPv6 SOLICIT template
  1149. * (implicit parameter is the DUID, don't assume an Ethernet link)
  1150. */
  1151. void
  1152. build_template_solicit6(void)
  1153. {
  1154. uint8_t *p = template_solicit6;
  1155. xid_solicit6 = DHCP6_OFF_XID;
  1156. /* message type */
  1157. p[DHCP6_OFF_MSGTYP] = DHCP6_OP_SOLICIT;
  1158. /* options */
  1159. p += DHCP6_OFF_OPTIONS;
  1160. /* elapsed time */
  1161. p[1] = DHCP6_OPT_ELAPSED_TIME;
  1162. p[3] = 2;
  1163. p += 6;
  1164. /* rapid commit */
  1165. if (rapidcommit != 0) {
  1166. p[1] = DHCP6_OPT_RAPID_COMMIT;
  1167. p += 4;
  1168. }
  1169. /* client ID */
  1170. p[1] = DHCP6_OPT_CLIENTID;
  1171. p[3] = duid_length;
  1172. memcpy(p + 4, duid_prefix, duid_length);
  1173. p += 4 + duid_length;
  1174. random_solicit6 = p - template_solicit6;
  1175. /* option request option */
  1176. p[1] = DHCP6_OPT_ORO;
  1177. p[3] = 4;
  1178. p[5] = DHCP6_OPT_NAME_SERVERS;
  1179. p[7] = DHCP6_OPT_DOMAIN_SEARCH;
  1180. p += 8;
  1181. /* IA_NA (IAID = 1, T1 = 3600, T2 = 5400) */
  1182. p[1] = DHCP6_OPT_IA_NA;
  1183. p[3] = 12;
  1184. p[7] = 1;
  1185. p[10] = 3600 >> 8;
  1186. p[11] = 3600 & 0xff;
  1187. p[14] = 5400 >> 8;
  1188. p[15] = 5400 & 0xff;
  1189. p += 16;
  1190. /* set length */
  1191. length_solicit6 = p - template_solicit6;
  1192. }
  1193. /*
  1194. * get a DHCPv6 first packet (usually a SOLICIT) template
  1195. * from the file given in the command line (-T<template-file>)
  1196. * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
  1197. */
  1198. void
  1199. get_template_solicit6(void)
  1200. {
  1201. uint8_t *p = template_solicit6;
  1202. int fd, cc, i, j;
  1203. fd = open(templatefile[0], O_RDONLY);
  1204. if (fd < 0) {
  1205. fprintf(stderr, "open(%s): %s\n",
  1206. templatefile[0], strerror(errno));
  1207. exit(2);
  1208. }
  1209. cc = read(fd, tbuf, sizeof(tbuf));
  1210. (void) close(fd);
  1211. if (cc < 0) {
  1212. fprintf(stderr, "read(%s): %s\n",
  1213. templatefile[0], strerror(errno));
  1214. exit(1);
  1215. }
  1216. if (cc < 10) {
  1217. fprintf(stderr, "file '%s' too short\n", templatefile[0]);
  1218. exit(2);
  1219. }
  1220. if (cc > 8193) {
  1221. fprintf(stderr,"file '%s' too large\n", templatefile[0]);
  1222. exit(2);
  1223. }
  1224. j = 0;
  1225. for (i = 0; i < cc; i++) {
  1226. if (isspace((int) tbuf[i]))
  1227. continue;
  1228. if (!isxdigit((int) tbuf[i])) {
  1229. fprintf(stderr,
  1230. "illegal char[%d]='%c' in file '%s'\n",
  1231. i, (int) tbuf[i], templatefile[0]);
  1232. exit(2);
  1233. }
  1234. tbuf[j] = tbuf[i];
  1235. j++;
  1236. }
  1237. cc = j;
  1238. if ((cc & 1) != 0) {
  1239. fprintf(stderr,
  1240. "odd number of hexadecimal digits in file '%s'\n",
  1241. templatefile[0]);
  1242. exit(2);
  1243. }
  1244. length_solicit6 = cc >> 1;
  1245. for (i = 0; i < cc; i += 2)
  1246. (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
  1247. if (xidoffset[0] >= 0)
  1248. xid_solicit6 = (size_t) xidoffset[0];
  1249. else
  1250. xid_solicit6 = DHCP6_OFF_XID;
  1251. if (xid_solicit6 + 3 > length_solicit6) {
  1252. fprintf(stderr,
  1253. "xid (at %zu) is outside the template (length %zu)?\n",
  1254. xid_solicit6, length_solicit6);
  1255. exit(2);
  1256. }
  1257. if (rndoffset[0] >= 0)
  1258. random_solicit6 = (size_t) rndoffset[0];
  1259. else
  1260. random_solicit6 = 0;
  1261. if (random_solicit6 > length_solicit6) {
  1262. fprintf(stderr,
  1263. "random (at %zu) outside the template (length %zu)?\n",
  1264. random_solicit6, length_solicit6);
  1265. exit(2);
  1266. }
  1267. }
  1268. /*
  1269. * build a DHCPv6 REQUEST template
  1270. * (implicit parameter is the DUID, don't assume an Ethernet link)
  1271. */
  1272. void
  1273. build_template_request6(void)
  1274. {
  1275. uint8_t *p = template_request6;
  1276. xid_request6 = DHCP6_OFF_XID;
  1277. /* message type */
  1278. p[DHCP6_OFF_MSGTYP] = DHCP6_OP_REQUEST;
  1279. /* options */
  1280. p += DHCP6_OFF_OPTIONS;
  1281. /* elapsed time */
  1282. p[1] = DHCP6_OPT_ELAPSED_TIME;
  1283. p[3] = 2;
  1284. p += 4;
  1285. elapsed_request6 = p - template_request6;
  1286. p += 2;
  1287. /* client ID */
  1288. p[1] = DHCP6_OPT_CLIENTID;
  1289. p[3] = duid_length;
  1290. memcpy(p + 4, duid_prefix, duid_length);
  1291. p += 4 + duid_length;
  1292. random_request6 = p - template_request6;
  1293. /* option request option */
  1294. p[1] = DHCP6_OPT_ORO;
  1295. p[3] = 4;
  1296. p[5] = DHCP6_OPT_NAME_SERVERS;
  1297. p[7] = DHCP6_OPT_DOMAIN_SEARCH;
  1298. p += 8;
  1299. /* server ID and IA_NA */
  1300. serverid_request6 = p - template_request6;
  1301. reqaddr_request6 = p - template_request6;
  1302. /* set length */
  1303. length_request6 = p - template_request6;
  1304. }
  1305. /*
  1306. * get a DHCPv6 third packet (usually a REQUEST) template
  1307. * from the file given in the command line (-T<template-file>)
  1308. * and offsets (-X,-O,-E,-S,-I).
  1309. */
  1310. void
  1311. get_template_request6(void)
  1312. {
  1313. uint8_t *p = template_request6;
  1314. int fd, cc, i, j;
  1315. fd = open(templatefile[1], O_RDONLY);
  1316. if (fd < 0) {
  1317. fprintf(stderr, "open(%s): %s\n",
  1318. templatefile[1], strerror(errno));
  1319. exit(2);
  1320. }
  1321. cc = read(fd, tbuf, sizeof(tbuf));
  1322. (void) close(fd);
  1323. if (cc < 0) {
  1324. fprintf(stderr, "read(%s): %s\n",
  1325. templatefile[1], strerror(errno));
  1326. exit(1);
  1327. }
  1328. if (cc < 10) {
  1329. fprintf(stderr, "file '%s' too short\n", templatefile[1]);
  1330. exit(2);
  1331. }
  1332. if (cc > 8193) {
  1333. fprintf(stderr,"file '%s' too large\n", templatefile[1]);
  1334. exit(2);
  1335. }
  1336. j = 0;
  1337. for (i = 0; i < cc; i++) {
  1338. if (isspace((int) tbuf[i]))
  1339. continue;
  1340. if (!isxdigit((int) tbuf[i])) {
  1341. fprintf(stderr,
  1342. "illegal char[%d]='%c' in file '%s'\n",
  1343. i, (int) tbuf[i], templatefile[1]);
  1344. exit(2);
  1345. }
  1346. tbuf[j] = tbuf[i];
  1347. j++;
  1348. }
  1349. cc = j;
  1350. if ((cc & 1) != 0) {
  1351. fprintf(stderr,
  1352. "odd number of hexadecimal digits in file '%s'\n",
  1353. templatefile[1]);
  1354. exit(2);
  1355. }
  1356. length_request6 = cc >> 1;
  1357. for (i = 0; i < cc; i += 2)
  1358. (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
  1359. if (xidoffset[1] >= 0)
  1360. xid_request6 = (size_t) xidoffset[1];
  1361. else
  1362. xid_request6 = DHCP6_OFF_XID;
  1363. if (xid_request6 + 3 > length_request6) {
  1364. fprintf(stderr,
  1365. "xid (at %zu) is outside the template (length %zu)?\n",
  1366. xid_request6, length_request6);
  1367. exit(2);
  1368. }
  1369. if (rndoffset[1] >= 0)
  1370. random_request6 = (size_t) rndoffset[1];
  1371. else
  1372. random_request6 = 0;
  1373. if (random_request6 > length_request6) {
  1374. fprintf(stderr,
  1375. "random (at %zu) outside the template (length %zu)?\n",
  1376. random_request6, length_request6);
  1377. exit(2);
  1378. }
  1379. if (elpoffset >= 0)
  1380. elapsed_request6 = (size_t) elpoffset;
  1381. if (elapsed_request6 + 2 > length_request6) {
  1382. fprintf(stderr,
  1383. "secs (at %zu) outside the template (length %zu)?\n",
  1384. elapsed_request6, length_request6);
  1385. exit(2);
  1386. }
  1387. serverid_request6 = (size_t) sidoffset;
  1388. if (serverid_request6 > length_request6) {
  1389. fprintf(stderr,
  1390. "server-id option (at %zu) outside the template "
  1391. "(length %zu)?\n",
  1392. serverid_request6, length_request6);
  1393. exit(2);
  1394. }
  1395. reqaddr_request6 = (size_t) ripoffset;
  1396. if (reqaddr_request6 > length_request6) {
  1397. fprintf(stderr,
  1398. "requested-ip-address option (at %zu) outside "
  1399. "the template (length %zu)?\n",
  1400. reqaddr_request6, length_request6);
  1401. exit(2);
  1402. }
  1403. }
  1404. /*
  1405. * send the DHCPv6 REQUEST third packet
  1406. * (the transaction ID is odd)
  1407. * (TODO: check for errors in the ADVERTISE)
  1408. */
  1409. void
  1410. send_request6(struct exchange *x0)
  1411. {
  1412. struct exchange *x;
  1413. struct xlist *bucket;
  1414. size_t len;
  1415. uint32_t hash;
  1416. ssize_t ret;
  1417. x = (struct exchange *) malloc(sizeof(*x));
  1418. if (x == NULL) {
  1419. locallimit++;
  1420. perror("send2");
  1421. return;
  1422. }
  1423. memcpy(x, x0, sizeof(*x));
  1424. x->order2 = xscount2++;
  1425. x->xid |= 1;
  1426. hash = x->xid >> 1;
  1427. ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
  1428. hash &= hashsize2 - 1;
  1429. bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
  1430. ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
  1431. len = length_request6;
  1432. memcpy(obuf, template_request6, len);
  1433. /* xid */
  1434. obuf[xid_request6] = x->xid >> 16;
  1435. obuf[xid_request6 + 1] = x->xid >> 8;
  1436. obuf[xid_request6 + 2] = x->xid;
  1437. /* random */
  1438. randomize(random_request6, x->rnd);
  1439. /* elapsed time */
  1440. if (elapsed_request6 > 0) {
  1441. int et;
  1442. et = (x->ts1.tv_sec - x->ts0.tv_sec) * 100;
  1443. et += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 10000000;
  1444. if (et > 65535) {
  1445. obuf[elapsed_request6] = 0xff;
  1446. obuf[elapsed_request6 + 1] = 0xff;
  1447. } else if (et > 0) {
  1448. obuf[elapsed_request6] = et >> 8;
  1449. obuf[elapsed_request6 + 1] = et & 0xff;
  1450. }
  1451. }
  1452. /* server ID */
  1453. if (serverid_request6 < length_request6)
  1454. memmove(obuf + serverid_request6 + x->sidlen,
  1455. obuf + serverid_request6,
  1456. x->sidlen);
  1457. memcpy(obuf + serverid_request6, x->sid, x->sidlen);
  1458. len += x->sidlen;
  1459. /* IA_NA */
  1460. if (reqaddr_request6 < serverid_request6) {
  1461. memmove(obuf + reqaddr_request6 + x->ianalen,
  1462. obuf + reqaddr_request6,
  1463. x->ianalen);
  1464. memcpy(obuf + reqaddr_request6, x->iana, x->ianalen);
  1465. } else if (reqaddr_request6 < length_request6) {
  1466. memmove(obuf + reqaddr_request6 + x->sidlen + x->ianalen,
  1467. obuf + reqaddr_request6 + x->sidlen,
  1468. x->ianalen);
  1469. memcpy(obuf + reqaddr_request6+ x->sidlen,
  1470. x->iana,
  1471. x->ianalen);
  1472. } else
  1473. memcpy(obuf + len, x->iana, x->ianalen);
  1474. len += x->ianalen;
  1475. /* timestamp */
  1476. ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
  1477. if (ret < 0) {
  1478. perror("clock_gettime(send2)");
  1479. fatal = 1;
  1480. return;
  1481. }
  1482. ret = sendto(sock, obuf, len, 0,
  1483. (struct sockaddr *) &serveraddr,
  1484. sizeof(struct sockaddr_in6));
  1485. if (ret >= 0)
  1486. return;
  1487. if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
  1488. (errno == ENOBUFS) || (errno == ENOMEM))
  1489. locallimit++;
  1490. perror("send2");
  1491. }
  1492. /*
  1493. * send the DHCPv6 SOLICIT first packet
  1494. * (for 4-exchange, the transaction ID xid is even)
  1495. */
  1496. int
  1497. send6(void)
  1498. {
  1499. struct exchange *x;
  1500. struct xlist *bucket;
  1501. uint32_t hash;
  1502. ssize_t ret;
  1503. x = (struct exchange *) malloc(sizeof(*x));
  1504. if (x == NULL)
  1505. return -ENOMEM;
  1506. memset(x, 0, sizeof(*x));
  1507. x->order0 = xscount0++;
  1508. hash = x->rnd = (uint32_t) random();
  1509. if (simple == 0)
  1510. x->xid = (hash << 1) & 0x00ffffff;
  1511. else
  1512. x->xid = hash & 0x00ffffff;
  1513. ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
  1514. hash &= hashsize0 - 1;
  1515. bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
  1516. ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
  1517. memcpy(obuf, template_solicit6, length_solicit6);
  1518. /* xid */
  1519. obuf[xid_solicit6] = x->xid >> 16;
  1520. obuf[xid_solicit6 + 1] = x->xid >> 8;
  1521. obuf[xid_solicit6 + 2] = x->xid;
  1522. /* random */
  1523. x->rnd = randomize(random_solicit6, x->rnd);
  1524. /* timestamp */
  1525. ret = clock_gettime(CLOCK_REALTIME, &last);
  1526. if (ret < 0) {
  1527. perror("clock_gettime(send)");
  1528. fatal = 1;
  1529. return -errno;
  1530. }
  1531. x->ts0 = last;
  1532. errno = 0;
  1533. ret = sendto(sock, obuf, length_solicit6, 0,
  1534. (struct sockaddr *) &serveraddr,
  1535. sizeof(struct sockaddr_in6));
  1536. if (ret == (ssize_t) length_solicit6)
  1537. return 0;
  1538. return -errno;
  1539. }
  1540. /*
  1541. * scan a DHCPv6 ADVERTISE to get its server-id option
  1542. */
  1543. int
  1544. scan_for_srvid6(struct exchange *x, size_t cc)
  1545. {
  1546. size_t off = DHCP6_OFF_OPTIONS, len;
  1547. for (;;) {
  1548. if (off + 4 > cc) {
  1549. fprintf(stderr, "server-id not found\n");
  1550. return -1;
  1551. }
  1552. if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_SERVERID))
  1553. break;
  1554. off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
  1555. }
  1556. len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
  1557. /* cache it in the global variables when required and not yet done */
  1558. if ((usefirst != 0) && (gsrvid == NULL)) {
  1559. if (len <= sizeof(gsrvidbuf)) {
  1560. memcpy(gsrvidbuf, ibuf + off, len);
  1561. gsrvid = gsrvidbuf;
  1562. gsrvidlen = len;
  1563. } else {
  1564. gsrvid = (uint8_t *) malloc(len);
  1565. if (gsrvid == NULL) {
  1566. perror("malloc(gsrvid");
  1567. return -1;
  1568. }
  1569. memcpy(gsrvid, ibuf + off, len);
  1570. gsrvidlen = len;
  1571. }
  1572. }
  1573. x->sid = ibuf + off;
  1574. x->sidlen = len;
  1575. return 0;
  1576. }
  1577. /*
  1578. * scan a DHCPv6 ADVERTISE to get its IA_NA option
  1579. * (TODO: check for errors)
  1580. */
  1581. int
  1582. scan_for_ia_na(struct exchange *x, size_t cc)
  1583. {
  1584. size_t off = DHCP6_OFF_OPTIONS, len;
  1585. for (;;) {
  1586. if (off + 4 > cc) {
  1587. fprintf(stderr, "ia-na not found\n");
  1588. return -1;
  1589. }
  1590. if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_IA_NA))
  1591. break;
  1592. off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
  1593. }
  1594. len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
  1595. x->iana = ibuf + off;
  1596. x->ianalen = len;
  1597. return 0;
  1598. }
  1599. /*
  1600. * receive a DHCPv6 packet
  1601. */
  1602. void
  1603. receive6(void)
  1604. {
  1605. struct exchange *x, *t;
  1606. struct xlist *bucket;
  1607. struct timespec now;
  1608. ssize_t cc;
  1609. uint32_t xid, hash;
  1610. int checklost = 0;
  1611. double delta;
  1612. cc = recv(sock, ibuf, sizeof(ibuf), 0);
  1613. if (cc < 0) {
  1614. if ((errno == EAGAIN) ||
  1615. (errno == EWOULDBLOCK) ||
  1616. (errno == EINTR))
  1617. return;
  1618. perror("recv");
  1619. fatal = 1;
  1620. return;
  1621. }
  1622. /* enforce a reasonable length */
  1623. if (cc < 22) {
  1624. tooshort++;
  1625. return;
  1626. }
  1627. if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
  1628. perror("clock_gettime(receive)");
  1629. fatal = 1;
  1630. return;
  1631. }
  1632. xid = ibuf[xid_solicit6] << 16;
  1633. xid |= ibuf[xid_solicit6 + 1] << 8;
  1634. xid |= ibuf[xid_solicit6 + 2];
  1635. /* 4-packet exchange even/odd xid */
  1636. if (simple == 0) {
  1637. if ((xid & 1) != 0) {
  1638. receive_reply(xid, &now);
  1639. return;
  1640. }
  1641. hash = (xid >> 1) & (hashsize0 - 1);
  1642. } else
  1643. hash = xid & (hashsize0 - 1);
  1644. /* now it is the second packet, get the bucket which is needed */
  1645. bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
  1646. /* try the 'next to be received' cache */
  1647. if ((xnext0 != NULL) && (xnext0->xid == xid)) {
  1648. x = xnext0;
  1649. goto found;
  1650. }
  1651. /* if the rate is not illimited, garbage collect up to 3
  1652. timed-out exchanges */
  1653. if (rate != 0)
  1654. checklost = 3;
  1655. /* look for the exchange */
  1656. ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
  1657. double waited;
  1658. if (x->xid == xid)
  1659. goto found;
  1660. if (checklost <= 0)
  1661. continue;
  1662. /* check for a timed-out exchange */
  1663. waited = now.tv_sec - x->ts0.tv_sec;
  1664. waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
  1665. if (waited < losttime[0]) {
  1666. checklost = 0;
  1667. continue;
  1668. }
  1669. /* garbage collect timed-out exchange */
  1670. checklost--;
  1671. ISC_TAILQ_REMOVE(bucket, x, hchain);
  1672. ISC_TAILQ_REMOVE(&xsent0, x, gchain);
  1673. free(x);
  1674. collected[0] += 1;
  1675. }
  1676. /* no match? very late or not for us */
  1677. orphans++;
  1678. return;
  1679. /* got it: update stats and move to the received queue */
  1680. found:
  1681. xrcount0++;
  1682. x->ts1 = now;
  1683. delta = x->ts1.tv_sec - x->ts0.tv_sec;
  1684. delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
  1685. if (delta < dmin0)
  1686. dmin0 = delta;
  1687. if (delta > dmax0)
  1688. dmax0 = delta;
  1689. dsum0 += delta;
  1690. dsumsq0 += delta * delta;
  1691. xnext0 = ISC_TAILQ_NEXT(x, gchain);
  1692. ISC_TAILQ_REMOVE(bucket, x, hchain);
  1693. ISC_TAILQ_REMOVE(&xsent0, x, gchain);
  1694. ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
  1695. /* if the exchange is not finished, go to the second part */
  1696. if (simple == 0) {
  1697. int ret = 0;
  1698. /* the server-ID option is needed */
  1699. if ((usefirst != 0) && (gsrvid != NULL)) {
  1700. x->sid = gsrvid;
  1701. x->sidlen = gsrvidlen;
  1702. } else
  1703. ret = scan_for_srvid6(x, cc);
  1704. /* and the IA_NA option too */
  1705. if (ret >= 0)
  1706. ret = scan_for_ia_na(x, cc);
  1707. if (ret >= 0)
  1708. send_request6(x);
  1709. }
  1710. }
  1711. /*
  1712. * decode a base command line parameter
  1713. * (currently only MAC address and DUID are supported)
  1714. */
  1715. void
  1716. decodebase(void)
  1717. {
  1718. char *b0 = base[basecnt];
  1719. /* MAC address (alias Ethernet address) */
  1720. if ((strncasecmp(b0, "mac=", 4) == 0) ||
  1721. (strncasecmp(b0, "ether=", 6) == 0)) {
  1722. char *b;
  1723. b = strchr(b0, '=') + 1;
  1724. if (sscanf(b, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
  1725. &mac_prefix[0], &mac_prefix[1],
  1726. &mac_prefix[2], &mac_prefix[3],
  1727. &mac_prefix[4], &mac_prefix[5]) == 6) {
  1728. if ((diags != NULL) && (strchr(diags, 'a') != NULL))
  1729. printf("set MAC to %s\n", b);
  1730. return;
  1731. }
  1732. fprintf(stderr,
  1733. "expected base in '%*s=xx:xx:xx:xx:xx:xx' format\n",
  1734. (int) (b - b0 - 1), b0);
  1735. exit(2);
  1736. }
  1737. /* DUID */
  1738. if (strncasecmp(b0, "duid=", 5) == 0) {
  1739. char *b;
  1740. size_t i, l;
  1741. if (duid_prefix != NULL) {
  1742. fprintf(stderr, "duid was already set?\n");
  1743. exit(2);
  1744. }
  1745. b = strchr(b0, '=') + 1;
  1746. l = 0;
  1747. while (*b != '\0') {
  1748. if (!isxdigit((int) *b)) {
  1749. fprintf(stderr,
  1750. "illegal char '%c' in duid\n",
  1751. (int) *b);
  1752. exit(2);
  1753. }
  1754. b++;
  1755. l++;
  1756. }
  1757. if ((l & 1) != 0) {
  1758. fprintf(stderr,
  1759. "odd number of hexadecimal digits in duid\n");
  1760. exit(2);
  1761. }
  1762. l /= 2;
  1763. if (l > 64) {
  1764. fprintf(stderr, "duid too large\n");
  1765. exit(2);
  1766. }
  1767. duid_prefix = (uint8_t *) malloc(l);
  1768. if (duid_prefix == NULL) {
  1769. perror("malloc(duid)");
  1770. exit(1);
  1771. }
  1772. b = strchr(b0, '=') + 1;
  1773. for (i = 0; i < l; i++, b += 2)
  1774. (void) sscanf(b, "%02hhx", &duid_prefix[i]);
  1775. duid_length = l;
  1776. if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
  1777. b = strchr(b0, '=') + 1;
  1778. printf("set DUID[%d] to %s\n",
  1779. duid_length, b);
  1780. }
  1781. return;
  1782. }
  1783. /* other */
  1784. fprintf(stderr, "not yet supported base '%s'\n", b0);
  1785. exit(2);
  1786. }
  1787. /*
  1788. * get the server socket address from the command line:
  1789. * - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
  1790. */
  1791. void
  1792. getserveraddr(const int flags)
  1793. {
  1794. struct addrinfo hints, *res;
  1795. char *service;
  1796. int ret;
  1797. memset(&hints, 0, sizeof(hints));
  1798. if (ipversion == 4) {
  1799. hints.ai_family = AF_INET;
  1800. service = "67";
  1801. } else {
  1802. hints.ai_family = AF_INET6;
  1803. service = "547";
  1804. }
  1805. hints.ai_socktype = SOCK_DGRAM;
  1806. hints.ai_flags = AI_NUMERICSERV | flags;
  1807. #if defined(AI_ADDRCONFIG)
  1808. hints.ai_flags |= AI_ADDRCONFIG;
  1809. #endif
  1810. hints.ai_protocol = IPPROTO_UDP;
  1811. ret = getaddrinfo(servername, service, &hints, &res);
  1812. if (ret != 0) {
  1813. fprintf(stderr, "bad server=%s: %s\n",
  1814. servername, gai_strerror(ret));
  1815. exit(2);
  1816. }
  1817. if (res->ai_next != NULL) {
  1818. fprintf(stderr, "ambiguous server=%s\n", servername);
  1819. exit(2);
  1820. }
  1821. memcpy(&serveraddr, res->ai_addr, res->ai_addrlen);
  1822. freeaddrinfo(res);
  1823. }
  1824. /*
  1825. * get the interface from the command line:
  1826. * - name of the interface
  1827. * - socket address to fill
  1828. * (in IPv6, get the first link-local address)
  1829. */
  1830. void
  1831. getinterface(const char *name, struct sockaddr_storage *ss)
  1832. {
  1833. struct ifaddrs *ifaddr, *ifa;
  1834. int ret;
  1835. size_t len;
  1836. ret = getifaddrs(&ifaddr);
  1837. if (ret < 0) {
  1838. perror("getifaddrs");
  1839. exit(1);
  1840. }
  1841. for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
  1842. if (ifa->ifa_name == NULL)
  1843. continue;
  1844. if (strcmp(ifa->ifa_name, name) != 0)
  1845. continue;
  1846. if ((ifa->ifa_flags & IFF_UP) == 0)
  1847. continue;
  1848. if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
  1849. continue;
  1850. if (ifa->ifa_addr == NULL)
  1851. continue;
  1852. if ((ipversion == 4) &&
  1853. (ifa->ifa_addr->sa_family != AF_INET))
  1854. continue;
  1855. if (ipversion == 6) {
  1856. struct sockaddr_in6 *a6;
  1857. a6 = (struct sockaddr_in6 *) ifa->ifa_addr;
  1858. if (a6->sin6_family != AF_INET6)
  1859. continue;
  1860. if (!IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr))
  1861. continue;
  1862. }
  1863. if (ipversion == 4)
  1864. len = sizeof(struct sockaddr_in);
  1865. else
  1866. len = sizeof(struct sockaddr_in6);
  1867. memcpy(ss, ifa->ifa_addr, len);
  1868. /* fill the server port */
  1869. if (ipversion == 4)
  1870. ((struct sockaddr_in *) ss)->sin_port = htons(67);
  1871. else
  1872. ((struct sockaddr_in6 *) ss)->sin6_port = htons(546);
  1873. return;
  1874. }
  1875. fprintf(stderr, "can't find interface %s\n", name);
  1876. exit(1);
  1877. }
  1878. /*
  1879. * get the local socket address from the command line
  1880. * (if it doesn't work, try an interface name)
  1881. */
  1882. void
  1883. getlocaladdr(void)
  1884. {
  1885. struct addrinfo hints, *res;
  1886. char *service;
  1887. int ret;
  1888. memset(&hints, 0, sizeof(hints));
  1889. if (ipversion == 4) {
  1890. hints.ai_family = AF_INET;
  1891. service = "67";
  1892. } else {
  1893. hints.ai_family = AF_INET6;
  1894. service = "546";
  1895. }
  1896. hints.ai_socktype = SOCK_DGRAM;
  1897. hints.ai_flags = AI_NUMERICSERV;
  1898. #if defined(AI_ADDRCONFIG)
  1899. hints.ai_flags |= AI_ADDRCONFIG;
  1900. #endif
  1901. hints.ai_protocol = IPPROTO_UDP;
  1902. ret = getaddrinfo(localname, service, &hints, &res);
  1903. if ((ret == EAI_NONAME)
  1904. #ifdef EAI_NODATA
  1905. || (ret == EAI_NODATA)
  1906. #endif
  1907. ) {
  1908. isinterface = 1;
  1909. getinterface(localname, &localaddr);
  1910. return;
  1911. }
  1912. if (ret != 0) {
  1913. fprintf(stderr,
  1914. "bad -l<local-addr=%s>: %s\n",
  1915. localname,
  1916. gai_strerror(ret));
  1917. exit(2);
  1918. }
  1919. /* refuse multiple addresses */
  1920. if (res->ai_next != NULL) {
  1921. fprintf(stderr,
  1922. "ambiguous -l<local-addr=%s>\n",
  1923. localname);
  1924. exit(2);
  1925. }
  1926. memcpy(&localaddr, res->ai_addr, res->ai_addrlen);
  1927. freeaddrinfo(res);
  1928. return;
  1929. }
  1930. /*
  1931. * get the local socket address from the server one
  1932. */
  1933. void
  1934. getlocal(void)
  1935. {
  1936. int ret, s;
  1937. socklen_t l;
  1938. if (ipversion == 4) {
  1939. l = sizeof(struct sockaddr_in);
  1940. s = socket(PF_INET, SOCK_DGRAM, 0);
  1941. } else {
  1942. l = sizeof(struct sockaddr_in6);
  1943. s = socket(PF_INET6, SOCK_DGRAM, 0);
  1944. }
  1945. if (s < 0) {
  1946. perror("socket");
  1947. exit(1);
  1948. }
  1949. if ((ipversion == 4) && (isbroadcast != 0)) {
  1950. int on = 1;
  1951. ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
  1952. if (ret < 0) {
  1953. perror("setsockopt(SO_BROADCAST)");
  1954. exit(1);
  1955. }
  1956. }
  1957. ret = connect(s, (struct sockaddr *) &serveraddr, l);
  1958. if (ret < 0) {
  1959. perror("connect");
  1960. exit(1);
  1961. }
  1962. l = sizeof(localaddr);
  1963. ret = getsockname(s, (struct sockaddr *) &localaddr, &l);
  1964. if (ret < 0) {
  1965. perror("getsockname");
  1966. exit(1);
  1967. }
  1968. (void) close(s);
  1969. /* fill the local port */
  1970. if (ipversion == 4)
  1971. ((struct sockaddr_in *) &localaddr)->sin_port = htons(67);
  1972. else
  1973. ((struct sockaddr_in6 *) &localaddr)->sin6_port = htons(546);
  1974. }
  1975. /*
  1976. * intermediate reporting
  1977. * (note: an in-transit packet can be reported as dropped)
  1978. */
  1979. void
  1980. reporting(void)
  1981. {
  1982. dreport.tv_sec += report;
  1983. if (xscount2 == 0) {
  1984. printf("sent: %llu, received: %llu (drops: %lld)",
  1985. (unsigned long long) xscount0,
  1986. (unsigned long long) xrcount0,
  1987. (long long) (xscount0 - xrcount0));
  1988. if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
  1989. double avg;
  1990. avg = dsum0 / xrcount0;
  1991. printf(" average: %.3f ms", avg * 1e3);
  1992. }
  1993. } else {
  1994. printf("sent: %llu/%llu received: %llu/%llu "
  1995. "(drops: %lld/%lld)",
  1996. (unsigned long long) xscount0,
  1997. (unsigned long long) xscount2,
  1998. (unsigned long long) xrcount0,
  1999. (unsigned long long) xrcount2,
  2000. (long long) (xscount0 - xrcount0),
  2001. (long long) (xscount2 - xrcount2));
  2002. if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
  2003. double avg0, avg2;
  2004. avg0 = dsum0 / xrcount0;
  2005. if (xrcount2 != 0)
  2006. avg2 = dsum2 / xrcount2;
  2007. else
  2008. avg2 = 0.;
  2009. printf(" average: %.3f/%.3f ms",
  2010. avg0 * 1e3, avg2 * 1e3);
  2011. }
  2012. }
  2013. printf("\n");
  2014. }
  2015. /*
  2016. * SIGCHLD handler
  2017. */
  2018. void
  2019. reapchild(int sig)
  2020. {
  2021. int status;
  2022. sig = sig;
  2023. while (wait3(&status, WNOHANG, NULL) > 0)
  2024. /* continue */;
  2025. }
  2026. /*
  2027. * SIGINT handler
  2028. */
  2029. void
  2030. interrupt(int sig)
  2031. {
  2032. sig = sig;
  2033. interrupted = 1;
  2034. }
  2035. /*
  2036. * '-v' handler
  2037. */
  2038. void
  2039. version(void)
  2040. {
  2041. fprintf(stderr, "version 0.01\n");
  2042. }
  2043. /*
  2044. * usage (from the wiki)
  2045. */
  2046. void
  2047. usage(void)
  2048. {
  2049. fprintf(stderr, "%s",
  2050. "perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
  2051. " [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
  2052. " [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
  2053. " [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
  2054. " [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
  2055. " [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
  2056. " [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
  2057. "\f\n"
  2058. "The [server] argument is the name/address of the DHCP server to\n"
  2059. "contact. For DHCPv4 operation, exchanges are initiated by\n"
  2060. "transmitting a DHCP DISCOVER to this address.\n"
  2061. "\n"
  2062. "For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
  2063. "SOLICIT to this address. In the DHCPv6 case, the special name 'all'\n"
  2064. "can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
  2065. "multicast address FF02::1:2), or the special name 'servers' to refer\n"
  2066. "to All_DHCP_Servers (the multicast address FF05::1:3). The [server]\n"
  2067. "argument is optional only in the case that -l is used to specify an\n"
  2068. "interface, in which case [server] defaults to 'all'.\n"
  2069. "\n"
  2070. "The default is to perform a single 4-way exchange, effectively pinging\n"
  2071. "the server.\n"
  2072. "The -r option is used to set up a performance test, without\n"
  2073. "it exchanges are initiated as fast as possible.\n"
  2074. "\n"
  2075. "Options:\n"
  2076. "-1: Take the server-ID option from the first received message.\n"
  2077. "-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
  2078. "-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
  2079. "-a<aggressivity>: When the target sending rate is not yet reached,\n"
  2080. " control how many exchanges are initiated before the next pause.\n"
  2081. "-b<base>: The base MAC, DUID, IP, etc, used to simulate different\n"
  2082. " clients. This can be specified multiple times, each instance is\n"
  2083. " in the <type>=<value> form, for instance:\n"
  2084. " (and default) MAC=00:0c:01:02:03:04.\n"
  2085. "-d<drop-time>: Specify the time after which a request is treated as\n"
  2086. " having been lost. The value is given in seconds and may contain a\n"
  2087. " fractional component. The default is 1 second.\n"
  2088. "-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
  2089. " elapsed-time option in the (second/request) template.\n"
  2090. " The value 0 disables it.\n"
  2091. "-h: Print this help.\n"
  2092. "-i: Do only the initial part of an exchange: DO or SA, depending on\n"
  2093. " whether -6 is given.\n"
  2094. "-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
  2095. " option / (DHCPv6) IA_NA option in the (second/request) template.\n"
  2096. "-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
  2097. " hostname/address to use when communicating with the server. By\n"
  2098. " default, the interface address through which traffic would\n"
  2099. " normally be routed to the server is used.\n"
  2100. " For DHCPv6 operation, specify the name of the network interface\n"
  2101. " via which exchanges are initiated.\n"
  2102. "-L<local-port>: Specify the local port to use\n"
  2103. " (the value 0 means to use the default).\n"
  2104. "-O<random-offset>: Offset of the last octet to randomize in the template.\n"
  2105. "-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
  2106. "-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
  2107. " exchanges per second. A periodic report is generated showing the\n"
  2108. " number of exchanges which were not completed, as well as the\n"
  2109. " average response latency. The program continues until\n"
  2110. " interrupted, at which point a final report is generated.\n"
  2111. "-R<range>: Specify how many different clients are used. With 1\n"
  2112. " (the default), all requests seem to come from the same client.\n"
  2113. "-s<seed>: Specify the seed for randomization, making it repeatable.\n"
  2114. "-S<srvid-offset>: Offset of the server-ID option in the\n"
  2115. " (second/request) template.\n"
  2116. "-T<template-file>: The name of a file containing the template to use\n"
  2117. " as a stream of hexadecimal digits.\n"
  2118. "-v: Report the version number of this program.\n"
  2119. "-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
  2120. " the program.\n"
  2121. "-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
  2122. " <diagnostic-selector> is a string of single-keywords specifying\n"
  2123. " the operations for which verbose output is desired. The selector\n"
  2124. " keyletters are:\n"
  2125. " * 'a': print the decoded command line arguments\n"
  2126. " * 'e': print the exit reason\n"
  2127. " * 'i': print rate processing details\n"
  2128. " * 'r': print randomization details\n"
  2129. " * 's': print first server-id\n"
  2130. " * 't': when finished, print timers of all successful exchanges\n"
  2131. " * 'T': when finished, print templates\n"
  2132. "-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
  2133. "\n"
  2134. "DHCPv4 only options:\n"
  2135. "-B: Force broadcast handling.\n"
  2136. "\n"
  2137. "DHCPv6 only options:\n"
  2138. "-c: Add a rapid commit option (exchanges will be SA).\n"
  2139. "\n"
  2140. "The remaining options are used only in conjunction with -r:\n"
  2141. "\n"
  2142. "-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
  2143. " been dropped. Use -D0 to abort if even a single request has been\n"
  2144. " dropped. If <max-drop> includes the suffix '%', it specifies a\n"
  2145. " maximum percentage of requests that may be dropped before abort.\n"
  2146. " In this case, testing of the threshold begins after 10 requests\n"
  2147. " have been expected to be received.\n"
  2148. "-n<num-request>: Initiate <num-request> transactions. No report is\n"
  2149. " generated until all transactions have been initiated/waited-for,\n"
  2150. " after which a report is generated and the program terminates.\n"
  2151. "-p<test-period>: Send requests for the given test period, which is\n"
  2152. " specified in the same manner as -d. This can be used as an\n"
  2153. " alternative to -n, or both options can be given, in which case the\n"
  2154. " testing is completed when either limit is reached.\n"
  2155. "-t<report>: Delay in seconds between two periodic reports.\n"
  2156. "\n"
  2157. "Errors:\n"
  2158. "- tooshort: received a too short message\n"
  2159. "- orphans: received a message which doesn't match an exchange\n"
  2160. " (duplicate, late or not related)\n"
  2161. "- locallimit: reached to local system limits when sending a message.\n"
  2162. "\n"
  2163. "Exit status:\n"
  2164. "The exit status is:\n"
  2165. "0 on complete success.\n"
  2166. "1 for a general error.\n"
  2167. "2 if an error is found in the command line arguments.\n"
  2168. "3 if there are no general failures in operation, but one or more\n"
  2169. " exchanges are not successfully completed.\n");
  2170. }
  2171. /*
  2172. * main function / entry point
  2173. */
  2174. int
  2175. main(const int argc, char * const argv[])
  2176. {
  2177. int opt, flags = 0, ret, i;
  2178. long long r;
  2179. char *pc;
  2180. extern char *optarg;
  2181. extern int optind;
  2182. #define OPTIONS "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:"
  2183. /* decode options */
  2184. while ((opt = getopt(argc, argv, OPTIONS)) != -1)
  2185. switch (opt) {
  2186. case 'h':
  2187. usage();
  2188. exit(0);
  2189. case 'v':
  2190. version();
  2191. exit(0);
  2192. case '4':
  2193. if (ipversion == 6) {
  2194. fprintf(stderr, "IP version already set to 6\n");
  2195. usage();
  2196. exit(2);
  2197. }
  2198. ipversion = 4;
  2199. break;
  2200. case '6':
  2201. if (ipversion == 4) {
  2202. fprintf(stderr, "IP version already set to 4\n");
  2203. usage();
  2204. exit(2);
  2205. }
  2206. ipversion = 6;
  2207. break;
  2208. case 'r':
  2209. rate = atoi(optarg);
  2210. if (rate <= 0) {
  2211. fprintf(stderr, "rate must be a positive integer\n");
  2212. usage();
  2213. exit(2);
  2214. }
  2215. break;
  2216. case 't':
  2217. report = atoi(optarg);
  2218. if (report <= 0) {
  2219. fprintf(stderr, "report must be a positive integer\n");
  2220. usage();
  2221. exit(2);
  2222. }
  2223. break;
  2224. case 'R':
  2225. r = atoll(optarg);
  2226. if (r < 0) {
  2227. fprintf(stderr,
  2228. "range must not be a negative integer\n");
  2229. usage();
  2230. exit(2);
  2231. }
  2232. range = (uint32_t) r;
  2233. if ((range != 0) && (range != UINT32_MAX)) {
  2234. uint32_t s = range + 1;
  2235. uint64_t b = UINT32_MAX + 1, m;
  2236. m = (b / s) * s;
  2237. if (m == b)
  2238. maxrandom = 0;
  2239. else
  2240. maxrandom = (uint32_t) m;
  2241. }
  2242. break;
  2243. case 'b':
  2244. if (basecnt > 3) {
  2245. fprintf(stderr, "too many bases\n");
  2246. usage();
  2247. exit(2);
  2248. }
  2249. base[basecnt] = optarg;
  2250. decodebase();
  2251. basecnt++;
  2252. break;
  2253. case 'n':
  2254. numreq[gotnumreq] = atoi(optarg);
  2255. if (numreq[gotnumreq] <= 0) {
  2256. fprintf(stderr,
  2257. "num-request must be a positive integer\n");
  2258. usage();
  2259. exit(2);
  2260. }
  2261. gotnumreq = 1;
  2262. break;
  2263. case 'p':
  2264. period = atoi(optarg);
  2265. if (period <= 0) {
  2266. fprintf(stderr,
  2267. "test-period must be a positive integer\n");
  2268. usage();
  2269. exit(2);
  2270. }
  2271. break;
  2272. case 'd':
  2273. losttime[gotlosttime] = atof(optarg);
  2274. if (losttime[gotlosttime] <= 0.) {
  2275. fprintf(stderr,
  2276. "drop-time must be a positive number\n");
  2277. usage();
  2278. exit(2);
  2279. }
  2280. gotlosttime = 1;
  2281. break;
  2282. case 'D':
  2283. pc = strchr(optarg, '%');
  2284. if (pc != NULL) {
  2285. *pc = '\0';
  2286. maxpdrop[gotmaxdrop] = atof(optarg);
  2287. if ((maxpdrop[gotmaxdrop] <= 0) ||
  2288. (maxpdrop[gotmaxdrop] >= 100)) {
  2289. fprintf(stderr,
  2290. "invalid drop-time percentage\n");
  2291. usage();
  2292. exit(2);
  2293. }
  2294. gotmaxdrop = 1;
  2295. break;
  2296. }
  2297. maxdrop[gotmaxdrop] = atoi(optarg);
  2298. if (maxdrop[gotmaxdrop] <= 0) {
  2299. fprintf(stderr,
  2300. "max-drop must be a positive integer\n");
  2301. usage();
  2302. exit(2);
  2303. }
  2304. gotmaxdrop = 1;
  2305. break;
  2306. case 'l':
  2307. localname = optarg;
  2308. break;
  2309. case 'P':
  2310. preload = atoi(optarg);
  2311. if (preload < 0) {
  2312. fprintf(stderr,
  2313. "preload must not be a negative integer\n");
  2314. usage();
  2315. exit(2);
  2316. }
  2317. break;
  2318. case 'a':
  2319. aggressivity = atoi(optarg);
  2320. if (aggressivity <= 0) {
  2321. fprintf(stderr,
  2322. "aggressivity must be a positive integer\n");
  2323. usage();
  2324. exit(2);
  2325. }
  2326. break;
  2327. case 'L':
  2328. localport = atoi(optarg);
  2329. if (localport < 0) {
  2330. fprintf(stderr,
  2331. "local-port must not be a negative integer\n");
  2332. usage();
  2333. exit(2);
  2334. }
  2335. if (localport > (int) UINT16_MAX) {
  2336. fprintf(stderr,
  2337. "local-port must be lower than %d\n",
  2338. (int) UINT16_MAX);
  2339. usage();
  2340. exit(2);
  2341. }
  2342. break;
  2343. case 's':
  2344. seeded = 1;
  2345. seed = (unsigned int) atol(optarg);
  2346. break;
  2347. case 'i':
  2348. simple = 1;
  2349. break;
  2350. case 'B':
  2351. isbroadcast = 1;
  2352. break;
  2353. case 'c':
  2354. rapidcommit = 1;
  2355. break;
  2356. case '1':
  2357. usefirst = 1;
  2358. break;
  2359. case 'T':
  2360. if (templatefile[0] != NULL) {
  2361. if (templatefile[1] != NULL) {
  2362. fprintf(stderr,
  2363. "template-files are already set\n");
  2364. usage();
  2365. exit(2);
  2366. }
  2367. templatefile[1] = optarg;
  2368. } else
  2369. templatefile[0] = optarg;
  2370. break;
  2371. case 'X':
  2372. if (xidoffset[0] >= 0)
  2373. i = 1;
  2374. else
  2375. i = 0;
  2376. xidoffset[i] = atoi(optarg);
  2377. if (xidoffset[i] <= 0) {
  2378. fprintf(stderr,
  2379. "xid-offset must be a positive integer\n");
  2380. usage();
  2381. exit(2);
  2382. }
  2383. break;
  2384. case 'O':
  2385. if (rndoffset[0] >= 0)
  2386. i = 1;
  2387. else
  2388. i = 0;
  2389. rndoffset[i] = atoi(optarg);
  2390. if (rndoffset[i] < 3) {
  2391. fprintf(stderr,
  2392. "random-offset must be greater than 3\n");
  2393. usage();
  2394. exit(2);
  2395. }
  2396. break;
  2397. case 'E':
  2398. elpoffset = atoi(optarg);
  2399. if (elpoffset < 0) {
  2400. fprintf(stderr,
  2401. "time-offset must not be a "
  2402. "negative integer\n");
  2403. usage();
  2404. exit(2);
  2405. }
  2406. break;
  2407. case 'S':
  2408. sidoffset = atoi(optarg);
  2409. if (sidoffset <= 0) {
  2410. fprintf(stderr,
  2411. "srvid-offset must be a positive integer\n");
  2412. usage();
  2413. exit(2);
  2414. }
  2415. break;
  2416. case 'I':
  2417. ripoffset = atoi(optarg);
  2418. if (ripoffset <= 0) {
  2419. fprintf(stderr,
  2420. "ip-offset must be a positive integer\n");
  2421. usage();
  2422. exit(2);
  2423. }
  2424. break;
  2425. case 'x':
  2426. diags = optarg;
  2427. break;
  2428. case 'w':
  2429. wrapped = optarg;
  2430. break;
  2431. default:
  2432. usage();
  2433. exit(2);
  2434. }
  2435. /* adjust some global variables */
  2436. if (ipversion == 0)
  2437. ipversion = 4;
  2438. if (templatefile[1] != NULL) {
  2439. if (xidoffset[1] < 0)
  2440. xidoffset[1] = xidoffset[0];
  2441. if (rndoffset[1] < 0)
  2442. rndoffset[1] = rndoffset[0];
  2443. }
  2444. /* when required, print the internal view of the command line */
  2445. if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
  2446. printf("IPv%d", ipversion);
  2447. if (simple != 0) {
  2448. if (ipversion == 4)
  2449. printf(" DO only");
  2450. else
  2451. printf(" SA only");
  2452. }
  2453. if (rate != 0)
  2454. printf(" rate=%d", rate);
  2455. if (report != 0)
  2456. printf(" report=%d", report);
  2457. if (range != 0) {
  2458. if (strchr(diags, 'r') != NULL)
  2459. printf(" range=0..%d [0x%x]",
  2460. range,
  2461. (unsigned int) maxrandom);
  2462. else
  2463. printf(" range=0..%d", range);
  2464. }
  2465. if (basecnt != 0)
  2466. for (i = 0; i < basecnt; i++)
  2467. printf(" base[%d]='%s'", i, base[i]);
  2468. if (gotnumreq != 0)
  2469. printf(" num-request=%d,%d", numreq[0], numreq[1]);
  2470. if (period != 0)
  2471. printf(" test-period=%d", period);
  2472. printf(" drop-time=%g,%g", losttime[0], losttime[1]);
  2473. if ((maxdrop[0] != 0) || (maxdrop[1] != 0))
  2474. printf(" max-drop=%d,%d", maxdrop[0], maxdrop[1]);
  2475. if ((maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))
  2476. printf(" max-drop=%2.2f%%,%2.2f%%",
  2477. maxpdrop[0], maxpdrop[1]);
  2478. if (preload != 0)
  2479. printf(" preload=%d", preload);
  2480. printf(" aggressivity=%d", aggressivity);
  2481. if (localport != 0)
  2482. printf(" local-port=%d", localport);
  2483. if (seeded)
  2484. printf(" seed=%u", seed);
  2485. if (isbroadcast != 0)
  2486. printf(" broadcast");
  2487. if (rapidcommit != 0)
  2488. printf(" rapid-commit");
  2489. if (usefirst != 0)
  2490. printf(" use-first");
  2491. if ((templatefile[0] != NULL) && (templatefile[1] == NULL))
  2492. printf(" template-file='%s'", templatefile[0]);
  2493. else if (templatefile[1] != NULL)
  2494. printf(" template-file='%s','%s'",
  2495. templatefile[0], templatefile[1]);
  2496. if ((xidoffset[0] >= 0) && (xidoffset[1] < 0))
  2497. printf(" xid-offset=%d", xidoffset[0]);
  2498. else if (xidoffset[1] >= 0)
  2499. printf(" xid-offset=%d,%d",
  2500. xidoffset[0], xidoffset[1]);
  2501. if ((rndoffset[0] >= 0) && (rndoffset[1] < 0))
  2502. printf(" xid-offset=%d", rndoffset[0]);
  2503. else if (rndoffset[1] >= 0)
  2504. printf(" xid-offset=%d,%d",
  2505. rndoffset[0], rndoffset[1]);
  2506. if (elpoffset >= 0)
  2507. printf(" time-offset=%d", elpoffset);
  2508. if (sidoffset >= 0)
  2509. printf(" srvid-offset=%d", sidoffset);
  2510. if (ripoffset >= 0)
  2511. printf(" ip-offset=%d", ripoffset);
  2512. printf(" diagnotic-selectors='%s'", diags);
  2513. if (wrapped != NULL)
  2514. printf(" wrapped='%s'", wrapped);
  2515. printf("\n");
  2516. }
  2517. /* check DHCPv4 only options */
  2518. if ((ipversion != 4) && (isbroadcast != 0)) {
  2519. fprintf(stderr, "-b is not compatible with IPv6 (-6)\n");
  2520. usage();
  2521. exit(2);
  2522. }
  2523. /* check DHCPv6 only options */
  2524. if ((ipversion != 6) && (rapidcommit != 0)) {
  2525. fprintf(stderr, "-6 (IPv6) must be set to use -c\n");
  2526. usage();
  2527. exit(2);
  2528. }
  2529. /* check 4-packet (aka not simple) mode options */
  2530. if ((simple != 0) && (numreq[1] != 0)) {
  2531. fprintf(stderr,
  2532. "second -n<num-request> is not compatible with -i\n");
  2533. usage();
  2534. exit(2);
  2535. }
  2536. if ((simple != 0) && (losttime[1] != 1.)) {
  2537. fprintf(stderr,
  2538. "second -d<drop-time> is not compatible with -i\n");
  2539. usage();
  2540. exit(2);
  2541. }
  2542. if ((simple != 0) &&
  2543. ((maxdrop[1] != 0) || (maxpdrop[1] != 0.))) {
  2544. fprintf(stderr,
  2545. "second -D<max-drop> is not compatible with -i\n");
  2546. usage();
  2547. exit(2);
  2548. }
  2549. if ((simple != 0) && (usefirst != 0)) {
  2550. fprintf(stderr,
  2551. "-1 is not compatible with -i\n");
  2552. usage();
  2553. exit(2);
  2554. }
  2555. if ((simple != 0) && (templatefile[1] != NULL)) {
  2556. fprintf(stderr,
  2557. "second -T<template-file> is not "
  2558. "compatible with -i\n");
  2559. usage();
  2560. exit(2);
  2561. }
  2562. if ((simple != 0) && (xidoffset[1] >= 0)) {
  2563. fprintf(stderr,
  2564. "second -X<xid-offset> is not compatible with -i\n");
  2565. usage();
  2566. exit(2);
  2567. }
  2568. if ((simple != 0) && (rndoffset[1] >= 0)) {
  2569. fprintf(stderr,
  2570. "second -O<random-offset is not compatible with -i\n");
  2571. usage();
  2572. exit(2);
  2573. }
  2574. if ((simple != 0) && (elpoffset >= 0)) {
  2575. fprintf(stderr,
  2576. "-E<time-offset> is not compatible with -i\n");
  2577. usage();
  2578. exit(2);
  2579. }
  2580. if ((simple != 0) && (sidoffset >= 0)) {
  2581. fprintf(stderr,
  2582. "-S<srvid-offset> is not compatible with -i\n");
  2583. usage();
  2584. exit(2);
  2585. }
  2586. if ((simple != 0) && (ripoffset >= 0)) {
  2587. fprintf(stderr,
  2588. "-I<ip-offset> is not compatible with -i\n");
  2589. usage();
  2590. exit(2);
  2591. }
  2592. /* check simple mode options */
  2593. if ((simple == 0) && (rapidcommit != 0)) {
  2594. fprintf(stderr, "-i must be set to use -c\n");
  2595. usage();
  2596. exit(2);
  2597. }
  2598. /* check rate '-r' options */
  2599. if ((rate == 0) && (report != 0)) {
  2600. fprintf(stderr,
  2601. "-r<rate> must be set to use -t<report>\n");
  2602. usage();
  2603. exit(2);
  2604. }
  2605. if ((rate == 0) && ((numreq[0] != 0) || (numreq[1] != 0))) {
  2606. fprintf(stderr,
  2607. "-r<rate> must be set to use -n<num-request>\n");
  2608. usage();
  2609. exit(2);
  2610. }
  2611. if ((rate == 0) && (period != 0)) {
  2612. fprintf(stderr,
  2613. "-r<rate> must be set to use -p<test-period>\n");
  2614. usage();
  2615. exit(2);
  2616. }
  2617. if ((rate == 0) &&
  2618. ((maxdrop[0] != 0) || (maxdrop[1] != 0) ||
  2619. (maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))) {
  2620. fprintf(stderr,
  2621. "-r<rate> must be set to use -D<max-drop>\n");
  2622. usage();
  2623. exit(2);
  2624. }
  2625. /* check (first) template file options */
  2626. if ((templatefile[0] == NULL) && (xidoffset[0] >= 0)) {
  2627. fprintf(stderr,
  2628. "-T<template-file> must be set to "
  2629. "use -X<xid-offset>\n");
  2630. usage();
  2631. exit(2);
  2632. }
  2633. if ((templatefile[0] == NULL) && (rndoffset[0] >= 0)) {
  2634. fprintf(stderr,
  2635. "-T<template-file> must be set to "
  2636. "use -O<random-offset>\n");
  2637. usage();
  2638. exit(2);
  2639. }
  2640. /* check (second) template file options */
  2641. if ((templatefile[1] == NULL) && (elpoffset >= 0)) {
  2642. fprintf(stderr,
  2643. "second/request -T<template-file> must be set to "
  2644. "use -E<time-offset>\n");
  2645. usage();
  2646. exit(2);
  2647. }
  2648. if ((templatefile[1] == NULL) && (sidoffset >= 0)) {
  2649. fprintf(stderr,
  2650. "second/request -T<template-file> must be set to "
  2651. "use -S<srvid-offset>\n");
  2652. usage();
  2653. exit(2);
  2654. }
  2655. if ((templatefile[1] == NULL) && (ripoffset >= 0)) {
  2656. fprintf(stderr,
  2657. "second/request -T<template-file> must be set to "
  2658. "use -I<ip-offset>\n");
  2659. usage();
  2660. exit(2);
  2661. }
  2662. /* check various template file(s) and other condition(s) options */
  2663. if ((templatefile[0] != NULL) && (range > 0) && (rndoffset[0] < 0)) {
  2664. fprintf(stderr,
  2665. "-O<random-offset> must be set when "
  2666. "-T<template-file> and -R<range> are used\n");
  2667. usage();
  2668. exit(2);
  2669. }
  2670. if ((templatefile[1] != NULL) && (sidoffset < 0)) {
  2671. fprintf(stderr,
  2672. "-S<srvid-offset> must be set when second "
  2673. "-T<template-file> is used\n");
  2674. usage();
  2675. exit(2);
  2676. }
  2677. if ((templatefile[1] != NULL) && (ripoffset < 0)) {
  2678. fprintf(stderr,
  2679. "-I<ip-offset> must be set when second "
  2680. "-T<template-file> is used\n");
  2681. usage();
  2682. exit(2);
  2683. }
  2684. /* get the server argument */
  2685. if (optind < argc - 1) {
  2686. fprintf(stderr, "extra arguments?\n");
  2687. usage();
  2688. exit(2);
  2689. }
  2690. if (optind == argc - 1) {
  2691. servername = argv[optind];
  2692. /* decode special cases */
  2693. if ((ipversion == 4) &&
  2694. (strcmp(servername, "all") == 0)) {
  2695. flags = AI_NUMERICHOST;
  2696. isbroadcast = 1;
  2697. servername = "255.255.255.255";
  2698. } else if ((ipversion == 6) &&
  2699. (strcmp(servername, "all") == 0)) {
  2700. flags = AI_NUMERICHOST;
  2701. servername = "FF02::1:2";
  2702. } else if ((ipversion == 6) &&
  2703. (strcmp(servername, "servers") == 0)) {
  2704. flags = AI_NUMERICHOST;
  2705. servername = "FF05::1:3";
  2706. }
  2707. }
  2708. /* handle the local '-l' address/interface */
  2709. if (localname != NULL) {
  2710. /* given */
  2711. getlocaladdr();
  2712. if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
  2713. if (isinterface)
  2714. printf("interface='%s'\n", localname);
  2715. else
  2716. printf("local-addr='%s'\n", localname);
  2717. }
  2718. /* get the not given server from it */
  2719. if (servername == NULL) {
  2720. if (isinterface && (ipversion == 4)) {
  2721. flags = AI_NUMERICHOST;
  2722. isbroadcast = 1;
  2723. servername = "255.255.255.255";
  2724. } else if (isinterface && (ipversion == 6)) {
  2725. flags = AI_NUMERICHOST;
  2726. servername = "FF02::1:2";
  2727. } else {
  2728. fprintf(stderr,
  2729. "without an interface "
  2730. "server is required\n");
  2731. usage();
  2732. exit(2);
  2733. }
  2734. }
  2735. } else if (servername == NULL) {
  2736. fprintf(stderr, "without an interface server is required\n");
  2737. usage();
  2738. exit(2);
  2739. }
  2740. /* get the server socket address */
  2741. getserveraddr(flags);
  2742. /* finish local/server socket address stuff and print it */
  2743. if ((diags != NULL) && (strchr(diags, 'a') != NULL))
  2744. printf("server='%s'\n", servername);
  2745. if (localname == NULL)
  2746. getlocal();
  2747. if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
  2748. char addr[NI_MAXHOST];
  2749. ret = getnameinfo((struct sockaddr *) &localaddr,
  2750. sizeof(localaddr),
  2751. addr,
  2752. NI_MAXHOST,
  2753. NULL,
  2754. 0,
  2755. NI_NUMERICHOST);
  2756. if (ret != 0) {
  2757. fprintf(stderr,
  2758. "can't get the local address: %s\n",
  2759. gai_strerror(ret));
  2760. exit(1);
  2761. }
  2762. printf("local address='%s'\n", addr);
  2763. }
  2764. /* initialize exchange structures */
  2765. inits();
  2766. /* get the socket descriptor and template(s) */
  2767. if (ipversion == 4) {
  2768. getsock4();
  2769. if (templatefile[0] == NULL)
  2770. build_template_discover4();
  2771. else
  2772. get_template_discover4();
  2773. if (simple == 0) {
  2774. if (templatefile[1] == NULL)
  2775. build_template_request4();
  2776. else
  2777. get_template_request4();
  2778. }
  2779. } else {
  2780. getsock6();
  2781. if (duid_prefix != NULL) {
  2782. if (templatefile[0] == NULL)
  2783. build_template_solicit6();
  2784. else
  2785. get_template_solicit6();
  2786. if (simple == 0) {
  2787. if (templatefile[1] == NULL)
  2788. build_template_request6();
  2789. else
  2790. get_template_request6();
  2791. }
  2792. }
  2793. }
  2794. /* sanity check */
  2795. if ((unsigned) sock > FD_SETSIZE) {
  2796. fprintf(stderr, "socket descriptor (%d) too large?!\n", sock);
  2797. exit(1);
  2798. }
  2799. /* make the socket descriptor not blocking */
  2800. flags = fcntl(sock, F_GETFL, 0);
  2801. if (flags < 0) {
  2802. perror("fcntl(F_GETFL)");
  2803. exit(1);
  2804. }
  2805. if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
  2806. perror("fcntl(F_SETFL)");
  2807. exit(1);
  2808. }
  2809. /* wrapped start */
  2810. if (wrapped != NULL) {
  2811. pid_t pid;
  2812. (void) signal(SIGCHLD, reapchild);
  2813. pid = fork();
  2814. if (pid < 0) {
  2815. perror("fork");
  2816. exit(1);
  2817. } else if (pid == 0)
  2818. (void) execlp(wrapped, "start", (char *) NULL);
  2819. }
  2820. /* boot is done! */
  2821. if (clock_gettime(CLOCK_REALTIME, &boot) < 0) {
  2822. perror("clock_gettime(boot)");
  2823. exit(1);
  2824. }
  2825. /* compute the next intermediate reporting date */
  2826. if (report != 0) {
  2827. dreport.tv_sec = boot.tv_sec + report;
  2828. dreport.tv_nsec = boot.tv_nsec;
  2829. }
  2830. /* compute the DUID (the current date is needed) */
  2831. if ((ipversion == 6) && (duid_prefix == NULL)) {
  2832. uint32_t curdate;
  2833. duid_length = 14;
  2834. duid_prefix = (uint8_t *) malloc(duid_length);
  2835. if (duid_prefix == NULL) {
  2836. perror("malloc(duid)");
  2837. exit(1);
  2838. }
  2839. duid_prefix[0] = DHCP6_DUID_LLT >> 8;
  2840. duid_prefix[1] = DHCP6_DUID_LLT;
  2841. duid_prefix[2] = DHCP_HTYPE_ETHER >> 8;
  2842. duid_prefix[3] = DHCP_HTYPE_ETHER;
  2843. curdate = htonl(boot.tv_sec - DHCP6_DUID_EPOCH);
  2844. memcpy(duid_prefix + 4, &curdate, 4);
  2845. memcpy(duid_prefix + 8, mac_prefix, 6);
  2846. /* the DUID is in template(s) */
  2847. if (templatefile[0] == NULL)
  2848. build_template_solicit6();
  2849. else
  2850. get_template_solicit6();
  2851. if (simple == 0) {
  2852. if (templatefile[1] == NULL)
  2853. build_template_request6();
  2854. else
  2855. get_template_request6();
  2856. }
  2857. }
  2858. /* seed the random generator */
  2859. if (seeded == 0)
  2860. seed = (unsigned int) (boot.tv_sec + boot.tv_nsec);
  2861. srandom(seed);
  2862. /* preload the server with at least one packet */
  2863. compsend = preload + 1;
  2864. for (i = 0; i <= preload; i++) {
  2865. if (ipversion == 4)
  2866. ret = send4();
  2867. else
  2868. ret = send6();
  2869. if (ret < 0) {
  2870. /* failure at the first packet is fatal */
  2871. if (i == 0) {
  2872. fprintf(stderr,
  2873. "initial send failed: %s\n",
  2874. strerror(-ret));
  2875. exit(1);
  2876. }
  2877. if ((errno == EAGAIN) ||
  2878. (errno == EWOULDBLOCK) ||
  2879. (errno == ENOBUFS) ||
  2880. (errno == ENOMEM))
  2881. locallimit++;
  2882. fprintf(stderr, "preload send: %s\n", strerror(-ret));
  2883. break;
  2884. }
  2885. }
  2886. /* required only before the interrupted flag check */
  2887. (void) signal(SIGINT, interrupt);
  2888. /* main loop */
  2889. for (;;) {
  2890. struct timespec now, ts;
  2891. fd_set rfds;
  2892. /* immediate loop exit conditions */
  2893. if (interrupted) {
  2894. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2895. printf("interrupted\n");
  2896. break;
  2897. }
  2898. if (fatal) {
  2899. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2900. printf("got a fatal error\n");
  2901. break;
  2902. }
  2903. /* get the date and use it */
  2904. if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
  2905. perror("clock_gettime(now)");
  2906. fatal = 1;
  2907. continue;
  2908. }
  2909. if ((period != 0) &&
  2910. ((boot.tv_sec + period < now.tv_sec) ||
  2911. ((boot.tv_sec + period == now.tv_sec) &&
  2912. (boot.tv_nsec < now.tv_nsec)))) {
  2913. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2914. printf("reached test-period\n");
  2915. break;
  2916. }
  2917. if ((report != 0) &&
  2918. ((dreport.tv_sec < now.tv_sec) ||
  2919. ((dreport.tv_sec == now.tv_sec) &&
  2920. (dreport.tv_nsec < now.tv_nsec))))
  2921. reporting();
  2922. /* compute the delay for the next send */
  2923. due = last;
  2924. if (rate == 1)
  2925. due.tv_sec += 1;
  2926. else if (rate != 0)
  2927. due.tv_nsec += 1010000000 / rate;
  2928. else
  2929. due.tv_nsec += 1;
  2930. while (due.tv_nsec >= 1000000000) {
  2931. due.tv_sec += 1;
  2932. due.tv_nsec -= 1000000000;
  2933. }
  2934. ts = due;
  2935. ts.tv_sec -= now.tv_sec;
  2936. ts.tv_nsec -= now.tv_nsec;
  2937. while (ts.tv_nsec < 0) {
  2938. ts.tv_sec -= 1;
  2939. ts.tv_nsec += 1000000000;
  2940. }
  2941. /* the send was already due? */
  2942. if (ts.tv_sec < 0) {
  2943. ts.tv_sec = ts.tv_nsec = 0;
  2944. latesent++;
  2945. }
  2946. /* pselect() */
  2947. FD_ZERO(&rfds);
  2948. FD_SET(sock, &rfds);
  2949. ret = pselect(sock + 1, &rfds, NULL, NULL, &ts, NULL);
  2950. if (ret < 0) {
  2951. if (errno == EINTR)
  2952. continue;
  2953. perror("pselect");
  2954. fatal = 1;
  2955. continue;
  2956. }
  2957. /* packet(s) to receive */
  2958. while (ret == 1) {
  2959. if (ipversion == 4)
  2960. receive4();
  2961. else
  2962. receive6();
  2963. if (recv(sock, ibuf, sizeof(ibuf), MSG_PEEK) <= 0)
  2964. ret = 0;
  2965. else
  2966. multrcvd++;
  2967. }
  2968. if (fatal)
  2969. continue;
  2970. /* check receive loop exit conditions */
  2971. if ((numreq[0] != 0) && ((int) xscount0 >= numreq[0])) {
  2972. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2973. printf("reached num-request0\n");
  2974. break;
  2975. }
  2976. if ((numreq[1] != 0) && ((int) xscount2 >= numreq[1])) {
  2977. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2978. printf("reached num-request2\n");
  2979. break;
  2980. }
  2981. if ((maxdrop[0] != 0) &&
  2982. ((int) (xscount0 - xrcount0) > maxdrop[0])) {
  2983. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2984. printf("reached max-drop%s (absolute)\n",
  2985. simple != 0 ? "" : "0");
  2986. break;
  2987. }
  2988. if ((maxdrop[1] != 0) &&
  2989. ((int) (xscount2 - xrcount2) > maxdrop[1])) {
  2990. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2991. printf("reached max-drop2 (absolute)\n");
  2992. break;
  2993. }
  2994. if ((maxpdrop[0] != 0.) &&
  2995. (xscount0 > 10) &&
  2996. (((100. * (xscount0 - xrcount0)) / xscount0)
  2997. > maxpdrop[0])) {
  2998. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  2999. printf("reached max-drop%s (percent)\n",
  3000. simple != 0 ? "" : "0");
  3001. break;
  3002. }
  3003. if ((maxpdrop[1] != 0.) &&
  3004. (xscount2 > 10) &&
  3005. (((100. * (xscount2 - xrcount2)) / xscount2)
  3006. > maxpdrop[1])) {
  3007. if ((diags != NULL) && (strchr(diags, 'e') != NULL))
  3008. printf("reached max-drop2 (percent)\n");
  3009. break;
  3010. }
  3011. /* compute how many packets to send */
  3012. if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
  3013. perror("clock_gettime(now2)");
  3014. fatal = 1;
  3015. continue;
  3016. }
  3017. if ((now.tv_sec > due.tv_sec) ||
  3018. ((now.tv_sec == due.tv_sec) &&
  3019. (now.tv_nsec >= due.tv_nsec))) {
  3020. double tosend;
  3021. if (rate != 0) {
  3022. tosend = (now.tv_nsec - due.tv_nsec) / 1e9;
  3023. tosend += now.tv_sec - due.tv_sec;
  3024. tosend *= rate;
  3025. tosend += 1;
  3026. if (tosend > (double) aggressivity)
  3027. i = aggressivity;
  3028. else
  3029. i = (int) tosend;
  3030. } else
  3031. i = aggressivity;
  3032. compsend += i;
  3033. /* send packets */
  3034. for (;;) {
  3035. if (ipversion == 4)
  3036. ret = send4();
  3037. else
  3038. ret = send6();
  3039. if (ret < 0) {
  3040. if ((errno == EAGAIN) ||
  3041. (errno == EWOULDBLOCK) ||
  3042. (errno == ENOBUFS) ||
  3043. (errno == ENOMEM))
  3044. locallimit++;
  3045. fprintf(stderr,
  3046. "send: %s\n", strerror(-ret));
  3047. break;
  3048. }
  3049. i--;
  3050. if (i == 0)
  3051. break;
  3052. /* check for late packets to receive */
  3053. if (recv(sock, ibuf, sizeof(ibuf),
  3054. MSG_PEEK) > 0) {
  3055. latercvd++;
  3056. if (ipversion == 4)
  3057. receive4();
  3058. else
  3059. receive6();
  3060. }
  3061. }
  3062. } else
  3063. /* there was no packet to send */
  3064. shortwait++;
  3065. }
  3066. /* after main loop: finished */
  3067. if (clock_gettime(CLOCK_REALTIME, &finished) < 0)
  3068. perror("clock_gettime(finished)");
  3069. /* wrapped stop */
  3070. if (wrapped != NULL) {
  3071. pid_t pid;
  3072. pid = fork();
  3073. if (pid == 0)
  3074. (void) execlp(wrapped, "stop", (char *) NULL);
  3075. }
  3076. /* main statictics */
  3077. if (xscount2 == 0)
  3078. printf("sent: %llu, received: %llu (drops: %lld)\n",
  3079. (unsigned long long) xscount0,
  3080. (unsigned long long) xrcount0,
  3081. (long long) (xscount0 - xrcount0));
  3082. else
  3083. printf("sent: %llu/%llu, received: %llu/%llu "
  3084. "(drops: %lld/%lld)\n",
  3085. (unsigned long long) xscount0,
  3086. (unsigned long long) xscount2,
  3087. (unsigned long long) xrcount0,
  3088. (unsigned long long) xrcount2,
  3089. (long long) (xscount0 - xrcount0),
  3090. (long long) (xscount2 - xrcount2));
  3091. printf("tooshort: %llu, orphans: %llu, local limits: %llu\n",
  3092. (unsigned long long) tooshort,
  3093. (unsigned long long) orphans,
  3094. (unsigned long long) locallimit);
  3095. /* print the rate */
  3096. if (finished.tv_sec != 0) {
  3097. double dall, erate;
  3098. dall = (finished.tv_nsec - boot.tv_nsec) / 1e9;
  3099. dall += finished.tv_sec - boot.tv_sec;
  3100. erate = xrcount0 / dall;
  3101. if (rate != 0)
  3102. printf("rate: %f (expected %d)\n", erate, rate);
  3103. else
  3104. printf("rate: %f\n", erate);
  3105. }
  3106. /* rate processing instrumentation */
  3107. if ((diags != NULL) && (strchr(diags, 'i') != NULL)) {
  3108. printf("latesent: %llu, compsend: %llu, shortwait: %llu\n"
  3109. "multrcvd: %llu, latercvd: %llu, collected:%llu/%llu\n",
  3110. (unsigned long long) latesent,
  3111. (unsigned long long) compsend,
  3112. (unsigned long long) shortwait,
  3113. (unsigned long long) multrcvd,
  3114. (unsigned long long) latercvd,
  3115. (unsigned long long) collected[0],
  3116. (unsigned long long) collected[1]);
  3117. }
  3118. /* round-time trip statistics */
  3119. if (xrcount2 != 0) {
  3120. double avg0, avg2, stddev0, stddev2;
  3121. avg0 = dsum0 / xrcount0;
  3122. avg2 = dsum2 / xrcount2;
  3123. stddev0 = sqrt(dsumsq0 / xrcount0 - avg0 * avg0);
  3124. stddev2 = sqrt(dsumsq2 / xrcount2 - avg2 * avg2);
  3125. printf("RTT0: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
  3126. dmin0 * 1e3, avg0 * 1e3, dmax0 * 1e3, stddev0 * 1e3);
  3127. printf("RTT2: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
  3128. dmin2 * 1e3, avg2 * 1e3, dmax2 * 1e3, stddev2 * 1e3);
  3129. } else if (xrcount0 != 0) {
  3130. double avg, stddev;
  3131. avg = dsum0 / xrcount0;
  3132. stddev = sqrt(dsumsq0 / xrcount0 - avg * avg);
  3133. printf("RTT%s: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
  3134. simple != 0 ? "" : "0",
  3135. dmin0 * 1e3, avg * 1e3, dmax0 * 1e3, stddev * 1e3);
  3136. }
  3137. /* (first) server-ID option content */
  3138. if ((diags != NULL) && (strchr(diags, 's') != NULL) &&
  3139. !ISC_TAILQ_EMPTY(&xrcvd0)) {
  3140. struct exchange *x;
  3141. size_t n;
  3142. printf("server-id: ");
  3143. x = ISC_TAILQ_FIRST(&xrcvd0);
  3144. if (ipversion == 4)
  3145. n = 2;
  3146. else
  3147. n = 4;
  3148. for (; n < x->sidlen; n++)
  3149. printf("%02hhx", x->sid[n]);
  3150. printf("\n");
  3151. }
  3152. /* all time-stamps */
  3153. if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
  3154. !ISC_TAILQ_EMPTY(&xrcvd0)) {
  3155. struct exchange *x;
  3156. printf("\n\n");
  3157. ISC_TAILQ_FOREACH(x, &xrcvd0, gchain)
  3158. printf("%ld.%09ld %ld.%09ld\n",
  3159. (long) x->ts0.tv_sec, x->ts0.tv_nsec,
  3160. (long) x->ts1.tv_sec, x->ts1.tv_nsec);
  3161. }
  3162. if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
  3163. !ISC_TAILQ_EMPTY(&xrcvd2)) {
  3164. struct exchange *x;
  3165. printf("--\n");
  3166. ISC_TAILQ_FOREACH(x, &xrcvd2, gchain)
  3167. printf("%ld.%09ld %ld.%09ld %ld.%09ld %ld.%09ld\n",
  3168. (long) x->ts0.tv_sec, x->ts0.tv_nsec,
  3169. (long) x->ts1.tv_sec, x->ts1.tv_nsec,
  3170. (long) x->ts2.tv_sec, x->ts2.tv_nsec,
  3171. (long) x->ts3.tv_sec, x->ts3.tv_nsec);
  3172. }
  3173. if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
  3174. !ISC_TAILQ_EMPTY(&xrcvd0))
  3175. printf("\n\n");
  3176. /* template(s) */
  3177. if ((diags != NULL) && (strchr(diags, 'T') != NULL)) {
  3178. size_t n;
  3179. if (ipversion == 4) {
  3180. printf("length = %zu\n", length_discover4);
  3181. printf("xid offset = %d\n", DHCP_OFF_XID);
  3182. printf("xid length = 4\n");
  3183. printf("random offset = %zu\n", random_discover4);
  3184. printf("content:\n");
  3185. for (n = 0; n < length_discover4; n++) {
  3186. printf("%s%02hhx",
  3187. (n & 15) == 0 ? "" : " ",
  3188. template_discover4[n]);
  3189. if ((n & 15) == 15)
  3190. printf("\n");
  3191. }
  3192. if ((n & 15) != 15)
  3193. printf("\n");
  3194. if (simple != 0)
  3195. goto doneT;
  3196. printf("--\n");
  3197. printf("length = %zu\n", length_request4);
  3198. printf("xid offset = %d\n", DHCP_OFF_XID);
  3199. printf("xid length = 4\n");
  3200. printf("random offset = %zu\n", random_request4);
  3201. if (elapsed_request4 > 0)
  3202. printf("secs offset = %zu\n",
  3203. elapsed_request4);
  3204. printf("server-id offset = %zu\n", serverid_request4);
  3205. printf("server-id length = %d\n", DHCP_OPTLEN_SRVID);
  3206. printf("content:\n");
  3207. printf("requested-ip-address offset = %zu\n",
  3208. reqaddr_request4);
  3209. printf("requested-ip-address length = %d\n", 4);
  3210. for (n = 0; n < length_request4; n++) {
  3211. printf("%s%02hhx",
  3212. (n & 15) == 0 ? "" : " ",
  3213. template_request4[n]);
  3214. if ((n & 15) == 15)
  3215. printf("\n");
  3216. }
  3217. printf("\n");
  3218. } else {
  3219. printf("length = %zu\n", length_solicit6);
  3220. printf("xid offset = %d\n", DHCP6_OFF_XID);
  3221. printf("xid length = 3\n");
  3222. printf("random offset = %zu\n", random_solicit6);
  3223. for (n = 0; n < length_solicit6; n++) {
  3224. printf("%s%02hhx",
  3225. (n & 15) == 0 ? "" : " ",
  3226. template_solicit6[n]);
  3227. if ((n & 15) == 15)
  3228. printf("\n");
  3229. }
  3230. if ((n & 15) != 15)
  3231. printf("\n");
  3232. if (simple != 0)
  3233. goto doneT;
  3234. printf("--\n");
  3235. printf("length = %zu\n", length_request6);
  3236. printf("xid offset = %d\n", DHCP_OFF_XID);
  3237. printf("xid length = 4\n");
  3238. printf("random offset = %zu\n", random_request6);
  3239. if (elapsed_request6 > 0)
  3240. printf("secs offset = %zu\n",
  3241. elapsed_request6);
  3242. printf("server-id offset = %zu\n", serverid_request6);
  3243. printf("content:\n");
  3244. printf("requested-ip-address offset = %zu\n",
  3245. reqaddr_request6);
  3246. for (n = 0; n < length_request6; n++) {
  3247. printf("%s%02hhx",
  3248. (n & 15) == 0 ? "" : " ",
  3249. template_request6[n]);
  3250. if ((n & 15) == 15)
  3251. printf("\n");
  3252. }
  3253. printf("\n");
  3254. }
  3255. }
  3256. doneT:
  3257. /* compute the exit code (and exit) */
  3258. if (fatal)
  3259. exit(1);
  3260. else if ((xscount0 == xrcount0) && (xscount2 == xrcount2))
  3261. exit(0);
  3262. else
  3263. exit(3);
  3264. }
  3265. #endif /* HAVE_GETIFADDRS */