perfdhcp.cc 87 KB

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