dhcpc.c 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092
  1. /*
  2. * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. *
  32. * -
  33. * Portions Copyright (c) 1983, 1993 by
  34. * The Regents of the University of California. All rights reserved.
  35. *
  36. * Redistribution and use in source and binary forms, with or without
  37. * modification, are permitted provided that the following conditions
  38. * are met:
  39. * 1. Redistributions of source code must retain the above copyright
  40. * notice, this list of conditions and the following disclaimer.
  41. * 2. Redistributions in binary form must reproduce the above copyright
  42. * notice, this list of conditions and the following disclaimer in the
  43. * documentation and/or other materials provided with the distribution.
  44. * 3. Neither the name of the University nor the names of its contributors
  45. * may be used to endorse or promote products derived from this software
  46. * without specific prior written permission.
  47. *
  48. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  49. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  50. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  51. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  52. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  53. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  54. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  55. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  56. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  57. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  58. * SUCH DAMAGE.
  59. * -
  60. * Portions Copyright (c) 1993 by Digital Equipment Corporation.
  61. *
  62. * Permission to use, copy, modify, and distribute this software for any
  63. * purpose with or without fee is hereby granted, provided that the above
  64. * copyright notice and this permission notice appear in all copies, and that
  65. * the name of Digital Equipment Corporation not be used in advertising or
  66. * publicity pertaining to distribution of the document or software without
  67. * specific, written prior permission.
  68. *
  69. * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
  70. * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
  71. * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
  72. * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  73. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  74. * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  75. * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  76. * SOFTWARE.
  77. */
  78. /*!
  79. * \file pro/dhcpc.c
  80. * \brief DHCP client.
  81. *
  82. * \verbatim
  83. * $Id: dhcpc.c 5131 2013-05-10 07:46:14Z haraldkipp $
  84. * \endverbatim
  85. */
  86. #include <cfg/os.h>
  87. #include <sys/thread.h>
  88. #include <sys/event.h>
  89. #include <sys/timer.h>
  90. #include <sys/confnet.h>
  91. #include <sys/confos.h>
  92. #include <stdlib.h>
  93. #include <string.h>
  94. #include <time.h>
  95. #include <memdebug.h>
  96. #include <arpa/inet.h>
  97. #include <netinet/in.h>
  98. #include <netinet/if_ether.h>
  99. #include <netdb.h>
  100. #include <net/route.h>
  101. #include <sys/socket.h>
  102. #include <pro/dhcp.h>
  103. #ifdef NUTDEBUG
  104. #include <net/netdebug.h>
  105. #endif
  106. #if 0
  107. /* Use for local debugging. */
  108. #define NUTDEBUG
  109. #include <stdio.h>
  110. #define __tcp_trs stdout
  111. static uint_fast8_t __tcp_trf = 1;
  112. #endif
  113. /*!
  114. * \addtogroup xgDHCPC
  115. */
  116. /*@{*/
  117. /*!
  118. * \name DHCP Client Configuration
  119. *
  120. * The Nut/OS Configurator may be used to override the default values.
  121. */
  122. /*@{*/
  123. /*!
  124. * \brief UDP port of DHCP server.
  125. *
  126. * \showinitializer
  127. */
  128. #ifndef DHCP_SERVERPORT
  129. #define DHCP_SERVERPORT 67
  130. #endif
  131. /*!
  132. * \brief UDP port of DHCP client.
  133. *
  134. * \showinitializer
  135. */
  136. #ifndef DHCP_CLIENTPORT
  137. #define DHCP_CLIENTPORT 68
  138. #endif
  139. /*!
  140. * \brief Maximum DHCP message size we can accept.
  141. *
  142. * RFC 2131 demands, that a DHCP client must be prepared to receive DHCP
  143. * messages with an options field length of at least 312 octets. This
  144. * implies that we must be able to accept messages of up to 576 octets.
  145. *
  146. * \showinitializer
  147. */
  148. #ifndef MAX_DHCP_MSGSIZE
  149. #define MAX_DHCP_MSGSIZE 576
  150. #endif
  151. /*!
  152. * \brief Minimum DHCP message length.
  153. *
  154. * Used to maintain BOOTP compatibility of outgoing messages.
  155. *
  156. * \showinitializer
  157. */
  158. #ifndef MIN_DHCP_MSGSIZE
  159. #define MIN_DHCP_MSGSIZE 300
  160. #endif
  161. /*!
  162. * \brief Maximum UDP buffer size used by the DHCP client.
  163. *
  164. * If this item is not equal zero, the DHCP client will use its value to
  165. * set #SO_RCVBUF by calling NutUdpSetSockOpt().
  166. *
  167. * If this item is set to zero, NutUdpSetSockOpt() is not called and the
  168. * UDP socket interface will buffer the last incoming datagram on the
  169. * #DHCP_CLIENTPORT socket port only. Any previously received datagram is
  170. * silently discarded. As long as one DHCP server is expected in the local
  171. * network, this will be fine and save some heap memory while DHCP is
  172. * active.
  173. *
  174. * \showinitializer
  175. */
  176. #ifndef MAX_DHCP_BUFSIZE
  177. #define MAX_DHCP_BUFSIZE 1728
  178. #endif
  179. /*!
  180. * \brief Minimum number of milliseconds to wait for a response.
  181. *
  182. * If we receive no response from a DHCP server within this time, we
  183. * will double this value up to \ref MAX_DHCP_WAIT and repeat our
  184. * request up to \ref MAX_DCHP_RETRIES times before giving up.
  185. *
  186. * \showinitializer
  187. */
  188. #ifndef MIN_DHCP_WAIT
  189. #define MIN_DHCP_WAIT 4000
  190. #endif
  191. /*!
  192. * \brief Maximum number of milliseconds to wait for a response.
  193. *
  194. * The timeout value for receiving server responses will be doubled
  195. * on each retry but limited by this value.
  196. *
  197. * \showinitializer
  198. */
  199. #ifndef MAX_DHCP_WAIT
  200. #define MAX_DHCP_WAIT 64000
  201. #endif
  202. /*!
  203. * \brief Maximum number of request retries.
  204. *
  205. * We will give up after resending this number of requests without
  206. * receiving a response.
  207. *
  208. * \showinitializer
  209. */
  210. #ifndef MAX_DCHP_RETRIES
  211. #define MAX_DCHP_RETRIES 3
  212. #endif
  213. /*!
  214. * \brief Maximum number of release retries.
  215. *
  216. * RFC 2131 doesn't specify a server response to release messages from
  217. * the client. If the message gets lost, then the lease isn't released.
  218. *
  219. * \showinitializer
  220. */
  221. #ifndef MAX_DCHP_RELEASE_RETRIES
  222. #define MAX_DCHP_RELEASE_RETRIES 0
  223. #endif
  224. /*!
  225. * \brief Default lease time in seconds.
  226. *
  227. * This value is used if the server doesn't provide a lease time.
  228. *
  229. * \showinitializer
  230. */
  231. #ifndef DHCP_DEFAULT_LEASE
  232. #define DHCP_DEFAULT_LEASE 43200
  233. #endif
  234. /*!
  235. * \brief Maximum sleep time in seconds.
  236. *
  237. * \showinitializer
  238. */
  239. #ifndef MAX_DHCP_NAPTIME
  240. #define MAX_DHCP_NAPTIME 4294967
  241. #endif
  242. /*!
  243. * \brief Stack size of the DHCP client thread.
  244. *
  245. * \showinitializer
  246. */
  247. #ifndef NUT_THREAD_DHCPSTACK
  248. #if defined(__AVR__)
  249. #if defined(__GNUC__)
  250. /* avr-gcc size optimized code used 192 bytes. */
  251. #define NUT_THREAD_DHCPSTACK 288
  252. #else
  253. /* icc-avr v7.19 used 360 bytes. */
  254. #define NUT_THREAD_DHCPSTACK 512
  255. #endif
  256. #else
  257. /* arm-elf-gcc used 276 bytes with size optimized, 680 bytes with debug code. */
  258. /* arm-none-eabi creates stack overflow with 384 bytes on EIR 1.0 board. */
  259. #define NUT_THREAD_DHCPSTACK 512
  260. #endif
  261. #endif
  262. /*@}*/
  263. /*!
  264. * \name DHCP Message Types
  265. *
  266. * See RFC 2131.
  267. */
  268. /*@{*/
  269. /*! \brief Client broadcast to locate available servers.
  270. */
  271. #define DHCP_DISCOVER 1
  272. /*! \brief Server to client in response to DHCP_DISCOVER.
  273. *
  274. * Contains an offer of configuration parameters.
  275. */
  276. #define DHCP_OFFER 2
  277. /*! \brief Client message to servers.
  278. *
  279. * Used for
  280. * - requesting offered parameters from one server and implicitly declining offers from all others.
  281. * - confirming correctness of previously allocated address after, e.g., system reboot.
  282. * - extending the lease on a particular network address.
  283. */
  284. #define DHCP_REQUEST 3
  285. /*! \brief Client to server indicating network address is already in use.
  286. *
  287. * Not used by Nut/Net.
  288. */
  289. #define DHCP_DECLINE 4
  290. /*! \brief Server to client with configuration parameters.
  291. *
  292. * Contains committed network address.
  293. */
  294. #define DHCP_ACK 5
  295. /*! \brief Server to client indicating client's notion of network address is incorrect.
  296. *
  297. * May be caused by the client's move to new subnet or by expiration of the client's lease.
  298. */
  299. #define DHCP_NAK 6
  300. /*! \brief Client to server relinquishing network address and cancelling remaining lease.
  301. */
  302. #define DHCP_RELEASE 7
  303. /*! \brief Client to server, asking only for local configuration parameters.
  304. *
  305. * Used, if the client already has externally configured network address.
  306. */
  307. #define DHCP_INFORM 8
  308. /*@}*/
  309. /*!
  310. * \name DHCP Options
  311. *
  312. * Nut/Net recognizes a subset of options defined in RFC 2132.
  313. */
  314. /*@{*/
  315. /*!
  316. * \brief DHCP pad option.
  317. *
  318. * The pad option can be used to cause subsequent fields to align on
  319. * word boundaries.
  320. */
  321. #define DHCPOPT_PAD 0
  322. /*!
  323. * \brief DHCP subnet mask option.
  324. */
  325. #define DHCPOPT_NETMASK 1
  326. /*!
  327. * \brief DHCP router option.
  328. */
  329. #define DHCPOPT_GATEWAY 3
  330. /*!
  331. * \brief DHCP domain name server option.
  332. */
  333. #define DHCPOPT_DNS 6
  334. /*!
  335. * \brief DHCP host name option.
  336. */
  337. #define DHCPOPT_HOSTNAME 12
  338. /*!
  339. * \brief DHCP domain name option.
  340. */
  341. #define DHCPOPT_DOMAIN 15
  342. /*!
  343. * \brief DHCP broadcast address option.
  344. */
  345. #define DHCPOPT_BROADCAST 28
  346. /*!
  347. * \brief DHCP requested IP address option.
  348. */
  349. #define DHCPOPT_REQESTIP 50
  350. /*!
  351. * \brief DHCP IP address lease time option.
  352. */
  353. #define DHCPOPT_LEASETIME 51
  354. /*!
  355. * \brief DHCP message type option.
  356. */
  357. #define DHCPOPT_MSGTYPE 53
  358. /*!
  359. * \brief DHCP server identifier option.
  360. */
  361. #define DHCPOPT_SID 54
  362. /*!
  363. * \brief DHCP parameter request list option.
  364. */
  365. #define DHCPOPT_PARAMREQUEST 55
  366. /*!
  367. * \brief Maximum DHCP message size option.
  368. */
  369. #define DHCPOPT_MAXMSGSIZE 57
  370. /*!
  371. * \brief DHCP renewal time option.
  372. */
  373. #define DHCPOPT_RENEWALTIME 58
  374. /*!
  375. * \brief DHCP rebinding time option.
  376. */
  377. #define DHCPOPT_REBINDTIME 59
  378. /*!
  379. * \brief DHCP end option.
  380. */
  381. #define DHCPOPT_END 255
  382. /*@}*/
  383. /*!
  384. * \brief BOOTP message structure type.
  385. */
  386. typedef struct bootp BOOTP;
  387. /*!
  388. * \brief BOOTP message structure.
  389. */
  390. struct NUT_PACKED_TYPE bootp {
  391. uint8_t bp_op; /*!< \brief Packet opcode type: 1=request, 2=reply */
  392. uint8_t bp_htype; /*!< \brief Hardware address type: 1=Ethernet */
  393. uint8_t bp_hlen; /*!< \brief Hardware address length: 6 for Ethernet */
  394. uint8_t bp_hops; /*!< \brief Gateway hops */
  395. uint32_t bp_xid; /*!< \brief Transaction ID */
  396. uint16_t bp_secs; /*!< \brief Seconds since boot began */
  397. uint16_t bp_flags; /*!< \brief RFC1532 broadcast, etc. */
  398. uint32_t bp_ciaddr; /*!< \brief Client IP address */
  399. uint32_t bp_yiaddr; /*!< \brief 'Your' IP address */
  400. uint32_t bp_siaddr; /*!< \brief Server IP address */
  401. uint32_t bp_giaddr; /*!< \brief Gateway IP address */
  402. uint8_t bp_chaddr[16]; /*!< \brief Client hardware address */
  403. char bp_sname[64]; /*!< \brief Server host name */
  404. char bp_file[128]; /*!< \brief Boot file name */
  405. uint8_t bp_options[312]; /*!< \brief Vendor-specific area */
  406. };
  407. /*!
  408. * \brief Dynamic configuration structure type.
  409. */
  410. typedef struct dyn_cfg DYNCFG;
  411. /*!
  412. * \brief Dynamic configuration structure.
  413. */
  414. struct dyn_cfg {
  415. uint8_t dyn_msgtyp; /*!< \brief DHCP message type */
  416. uint32_t dyn_yiaddr; /*!< \brief Offered IP address. */
  417. uint32_t dyn_netmask; /*!< \brief Local IP netmask. */
  418. uint32_t dyn_broadcast; /*!< \brief Local IP broadcast address. */
  419. uint32_t dyn_gateway; /*!< \brief Default gate IP address. */
  420. uint32_t dyn_pdns; /*!< \brief Primary DNS IP address. */
  421. uint32_t dyn_sdns; /*!< \brief Secondary DNS IP address. */
  422. uint32_t dyn_sid; /*!< \brief Server identifier. */
  423. uint32_t dyn_renewalTime; /*!< \brief Renewal time in seconds. */
  424. uint32_t dyn_rebindTime; /*!< \brief Rebind time in seconds. */
  425. uint32_t dyn_leaseTime; /*!< \brief Offered lease time in seconds. */
  426. uint8_t *dyn_hostname; /*!< \brief Local hostname. */
  427. uint8_t *dyn_domain; /*!< \brief Name of local domain. */
  428. };
  429. /*!
  430. * \brief Current configuration.
  431. *
  432. * This structure is filled by parsing offer or acknowledge messages from
  433. * the server.
  434. */
  435. static DYNCFG *dhcpConfig;
  436. /*!
  437. * \brief Client thread identifier.
  438. *
  439. * Used to determine if the client thread is running. Zero indicates that
  440. * it is not.
  441. */
  442. static HANDLE dhcpThread;
  443. /*!
  444. * \brief Current state of the DHCP client state machine.
  445. */
  446. static uint8_t dhcpState;
  447. /*!
  448. * \brief Latest DHCP error code.
  449. */
  450. static int dhcpError;
  451. /*!
  452. * \brief DHCP wake up queue.
  453. *
  454. * The DHCP state machine can be woken up by posting to this queue.
  455. */
  456. static HANDLE dhcpWake;
  457. /*!
  458. * \brief DHCP waiting queue.
  459. *
  460. * Application threads wait on this queue until DHCP success or failure.
  461. */
  462. static HANDLE dhcpDone;
  463. /*!
  464. * \brief Maximum number of milliseconds to wait on \ref dhcpDone.
  465. *
  466. * Specified by the application when calling the DHCP client API.
  467. */
  468. static uint32_t dhcpApiTimeout;
  469. /*!
  470. * \brief Time at which the application started to wait.
  471. *
  472. * Used in conjunction with \ref dhcpApiTimeout to limit the maximum
  473. * wait time for server responses.
  474. */
  475. static uint32_t dhcpApiStart;
  476. /*!
  477. * \brief Dynamic string copy.
  478. *
  479. * \param dst Points to a string pointer. If the pointer is not NULL, it
  480. * is assumed that it points to a previously allocated buffer
  481. * and this buffer will be released first. Then a new buffer
  482. * will be allocated and the source string will be copied to
  483. * this buffer.
  484. * \param src The source string. No delimiter required.
  485. * \param len Length of the source string.
  486. */
  487. static void copy_str(uint8_t ** dst, void *src, int len)
  488. {
  489. free(*dst);
  490. *dst = malloc(len + 1);
  491. if (*dst) {
  492. if (len) {
  493. memcpy(*dst, src, len);
  494. }
  495. *(*dst + len) = 0;
  496. }
  497. }
  498. /*!
  499. * \brief Release DYNCFG structure.
  500. *
  501. * Frees all memory occupied by a \ref DYNCFG structure.
  502. *
  503. * \param dyncfg This structure will be released.
  504. */
  505. static void ReleaseDynCfg(DYNCFG * dyncfg)
  506. {
  507. if (dyncfg) {
  508. free(dyncfg->dyn_hostname);
  509. free(dyncfg->dyn_domain);
  510. free(dyncfg);
  511. }
  512. }
  513. /*!
  514. * \brief Parse a DHCP reply message.
  515. *
  516. * \param bp Pointer to the reply message. The caller must make sure,
  517. * that this contains a valid BOOTP reply header with at
  518. * least five bytes in the options field.
  519. * \param len Number of valid bytes in the reply message.
  520. *
  521. * \return Pointer to config structure. Must be released by the caller.
  522. * NULL is returned in case of parsing errors.
  523. */
  524. static DYNCFG *ParseReply(BOOTP *bp, int len)
  525. {
  526. uint8_t *op;
  527. int left;
  528. DYNCFG *cfgp;
  529. /* Allocate and initialize a new structure. */
  530. cfgp = malloc(sizeof(DYNCFG));
  531. if (cfgp == NULL) {
  532. return NULL;
  533. }
  534. memset(cfgp, 0, sizeof(DYNCFG));
  535. cfgp->dyn_leaseTime = DHCP_DEFAULT_LEASE;
  536. /* Set the assigned IP address. */
  537. memcpy(&cfgp->dyn_yiaddr, &bp->bp_yiaddr, 4);
  538. /*
  539. * Parse options until an end option is found or until we reached
  540. * the end of the message.
  541. */
  542. op = bp->bp_options + 4;
  543. left = len - (sizeof(*bp) - sizeof(bp->bp_options)) - 4;
  544. while (*op != DHCPOPT_END && left > 0) {
  545. uint8_t ol;
  546. #ifdef NUTDEBUG
  547. if (__tcp_trf) {
  548. fprintf(__tcp_trs, "[DHCP-Opt-%u]", *op);
  549. }
  550. #endif
  551. /* Pad option is used for boundary alignment. */
  552. if (*op == DHCPOPT_PAD) {
  553. op++;
  554. left--;
  555. continue;
  556. }
  557. /* Reject if option length exceeds total length. */
  558. ol = *(op + 1);
  559. if (ol > left) {
  560. break;
  561. }
  562. /* Type of this message. */
  563. if (*op == DHCPOPT_MSGTYPE) {
  564. if (ol != 1) {
  565. break;
  566. }
  567. cfgp->dyn_msgtyp = *(op + 2);
  568. }
  569. /* Our host name. May or may not include the domain. */
  570. else if (*op == DHCPOPT_HOSTNAME) {
  571. copy_str(&cfgp->dyn_hostname, op + 2, ol);
  572. }
  573. /* Name of the domain we are in. */
  574. else if (*op == DHCPOPT_DOMAIN) {
  575. copy_str(&cfgp->dyn_domain, op + 2, ol);
  576. }
  577. /* All remaining options require at least 4 octets. */
  578. else if (ol >= 4) {
  579. /* Preset most often used long value. */
  580. uint32_t lval = *(op + 2);
  581. lval += (uint32_t)(*(op + 3)) << 8;
  582. lval += (uint32_t)(*(op + 4)) << 16;
  583. lval += (uint32_t)(*(op + 5)) << 24;
  584. /* Our IP network mask. */
  585. if (*op == DHCPOPT_NETMASK) {
  586. cfgp->dyn_netmask = lval;
  587. }
  588. /* Our IP broadcast address. */
  589. else if (*op == DHCPOPT_BROADCAST) {
  590. cfgp->dyn_broadcast = lval;
  591. }
  592. /* Our IP default gate. More than one gateway may be
  593. specified. We take the fist one only and ignore the
  594. rest. */
  595. else if (*op == DHCPOPT_GATEWAY) {
  596. cfgp->dyn_gateway = lval;
  597. }
  598. /* Our DNS server. Updated by Jelle Martijn Kok to
  599. support a secondary DNS. */
  600. else if (*op == DHCPOPT_DNS) {
  601. cfgp->dyn_pdns = lval;
  602. if (ol >= 8) {
  603. cfgp->dyn_sdns = *(op + 6);
  604. cfgp->dyn_sdns += (uint32_t)(*(op + 7)) << 8;
  605. cfgp->dyn_sdns += (uint32_t)(*(op + 8)) << 16;
  606. cfgp->dyn_sdns += (uint32_t)(*(op + 9)) << 24;
  607. }
  608. }
  609. /* Server identifier. */
  610. else if (*op == DHCPOPT_SID) {
  611. cfgp->dyn_sid = lval;
  612. }
  613. /* Renewal time. */
  614. else if (*op == DHCPOPT_RENEWALTIME) {
  615. cfgp->dyn_renewalTime = ntohl(lval);
  616. }
  617. /* Rebinding time. */
  618. else if (*op == DHCPOPT_REBINDTIME) {
  619. cfgp->dyn_rebindTime = ntohl(lval);
  620. }
  621. /* Total lease time granted. */
  622. else if (*op == DHCPOPT_LEASETIME) {
  623. cfgp->dyn_leaseTime = ntohl(lval);
  624. }
  625. }
  626. op += ol + 2;
  627. left -= ol + 2;
  628. }
  629. /*
  630. * Discard this configuration if parsing stopped before reaching
  631. * the end option or if we didn't receive an expected message type.
  632. */
  633. if (*op != DHCPOPT_END || /* */
  634. (cfgp->dyn_msgtyp != DHCP_OFFER && /* */
  635. cfgp->dyn_msgtyp != DHCP_ACK && /* */
  636. cfgp->dyn_msgtyp != DHCP_NAK)) {
  637. #ifdef NUTDEBUG
  638. if (__tcp_trf) {
  639. fprintf(__tcp_trs, "[DHCP-Parse Error]");
  640. }
  641. #endif
  642. ReleaseDynCfg(cfgp);
  643. return 0;
  644. }
  645. /* Calculate renewal and rebind times. */
  646. if (cfgp->dyn_renewalTime == 0) {
  647. cfgp->dyn_renewalTime = cfgp->dyn_leaseTime / 2;
  648. }
  649. if (cfgp->dyn_rebindTime == 0) {
  650. cfgp->dyn_rebindTime = cfgp->dyn_renewalTime + /* */
  651. cfgp->dyn_renewalTime / 2 + /* */
  652. cfgp->dyn_renewalTime / 4;
  653. }
  654. return cfgp;
  655. }
  656. /*!
  657. * \brief Add variable length option.
  658. *
  659. * \param op Pointer into the option buffer.
  660. * \param ot Option type.
  661. * \param ov Pointer to buffer to copy from.
  662. * \param len Number of octets to copy.
  663. *
  664. * \return Total number of octets added, include type and length.
  665. */
  666. static size_t DhcpAddOption(uint8_t * op, uint8_t ot, void *ov, uint8_t len)
  667. {
  668. *op++ = ot;
  669. *op++ = len;
  670. memcpy(op, ov, len);
  671. return 2 + len;
  672. }
  673. /*!
  674. * \brief Add single octet option.
  675. *
  676. * \param op Pointer into the option buffer.
  677. * \param ot Option type.
  678. * \param ov Option value.
  679. *
  680. * \return Total number of octets added, include type and length.
  681. */
  682. static size_t DhcpAddByteOption(uint8_t * op, uint8_t ot, uint8_t ov)
  683. {
  684. *op++ = ot;
  685. *op++ = 1;
  686. *op++ = ov;
  687. return 3;
  688. }
  689. /*!
  690. * \brief Add double octet option.
  691. *
  692. * \param op Pointer into the option buffer.
  693. * \param ot Option type.
  694. * \param ov Option value in host byte order. Will be converted to network
  695. * byte order.
  696. *
  697. * \return Total number of octets added, include type and length.
  698. */
  699. static size_t DhcpAddShortOption(uint8_t * op, uint8_t ot, uint16_t ov)
  700. {
  701. *op++ = ot;
  702. *op++ = 2;
  703. ov = htons(ov);
  704. memcpy(op, &ov, 2);
  705. return 4;
  706. }
  707. /*!
  708. * \brief Add parameter request option.
  709. *
  710. * RFC 2131 demands to use the same parameter request in discover and
  711. * request messages.
  712. *
  713. * \param op Pointer into the option buffer.
  714. * \param ot Option type.
  715. * \param ov Option value in host byte order.
  716. *
  717. * \return Total number of octets added, include type and length.
  718. */
  719. static size_t DhcpAddParmReqOption(uint8_t * op)
  720. {
  721. *op++ = DHCPOPT_PARAMREQUEST;
  722. *op++ = 3; /* Adjust when adding more options! */
  723. *op++ = DHCPOPT_NETMASK; /* Typically sent by default, but play safe. */
  724. *op++ = DHCPOPT_GATEWAY; /* We want a default gateway. */
  725. *op++ = DHCPOPT_DNS; /* We want the DNS' IP. */
  726. return 5; /* Adjust when adding more options! */
  727. }
  728. /*!
  729. * \brief Prepare a BOOTP request message header.
  730. *
  731. * A BOOTP header will be initialized at the specified buffer address
  732. * and the routine will add the DHCP magic cookie (RFC 1497) and an
  733. * additional option containing the specified DHCP message type.
  734. *
  735. * All other fields are cleared to zero.
  736. *
  737. * \param bp Pointer to the buffer to initialize.
  738. * \param msgtyp DHCP message type, either \ref DHCP_DISCOVER,
  739. * \ref DHCP_REQUEST or \ref DHCP_RELEASE.
  740. * \param xid Random transaction identifier.
  741. * \param ciaddr Our IP address. Should be set to zero unless we are in
  742. * BOUND, RENEW or REBINDING state and can respond to ARP
  743. * requests.
  744. * \param secs Seconds elapsed, since we began address acquisition or
  745. * renewal.
  746. *
  747. * \return Total number of octets added.
  748. */
  749. static unsigned int DhcpPrepHeader(BOOTP *bp, uint8_t msgtyp, uint32_t xid, uint32_t ciaddr, uint16_t secs)
  750. {
  751. uint8_t *op;
  752. memset(bp, 0, sizeof(*bp));
  753. /* Clients send bootp requests (op code 1) only. */
  754. bp->bp_op = 1;
  755. /* Ethernet addresses are type 1 with 6 octets. */
  756. bp->bp_htype = 1;
  757. bp->bp_hlen = 6;
  758. memcpy(bp->bp_chaddr, confnet.cdn_mac, 6);
  759. /* Transaction identifier. */
  760. bp->bp_xid = xid;
  761. /* Seconds elapsed since address acquisition. */
  762. bp->bp_secs = htons(secs);
  763. #ifdef DHCP_BROADCAST_FLAG
  764. /*
  765. * We do not need the broadcast flag, because our stack accepts IP
  766. * messages to any destination if no local address has been assigned,
  767. * However, we continue supporting this compile time option.
  768. */
  769. bp->bp_flags = htons(0x8000);
  770. #endif
  771. bp->bp_ciaddr = ciaddr;
  772. /* Add the DHCP magic cookie according to RFC 1497. */
  773. op = bp->bp_options;
  774. *op++ = 0x63;
  775. *op++ = 0x82;
  776. *op++ = 0x53;
  777. *op++ = 0x63;
  778. /* Add the DHCP message type option. */
  779. return DhcpAddByteOption(op, DHCPOPT_MSGTYPE, msgtyp) + 4;
  780. }
  781. /*!
  782. * \brief Send a DHCP message to the server.
  783. *
  784. * This routine will also add an end of option identifier and take care,
  785. * that the message length will not fall below the minimum expected by
  786. * BOOTP.
  787. *
  788. * \param sock Socket descriptor. This pointer must have been
  789. * retrieved by calling NutUdpCreateSocket().
  790. * \param addr Destination IP addres.
  791. * \param bp Pointer to a buffer to be used for transmission.
  792. * Must contain a fully initialized header and option
  793. * fields.
  794. * \param len Total number of DHCP option octets.
  795. *
  796. * \return 0 on success. On errors -1 is returned and \ref dhcpError will
  797. * be set to \ref DHCPERR_TRANSMIT.
  798. */
  799. static int DhcpSendMessage(UDPSOCKET * sock, uint32_t addr, BOOTP *bp, size_t len)
  800. {
  801. /* Add 'end of options'. */
  802. bp->bp_options[len++] = DHCPOPT_END;
  803. /* Maintain a BOOTP compatible minimum packet size of 300 octets.
  804. Thanks to Tomohiro Haraikawa. */
  805. len += sizeof(BOOTP) - sizeof(bp->bp_options);
  806. if (len < MIN_DHCP_MSGSIZE) {
  807. len = MIN_DHCP_MSGSIZE;
  808. }
  809. #ifdef NUTDEBUG
  810. if (__tcp_trf) {
  811. fprintf(__tcp_trs, "[DHCP-Send to %s]", inet_ntoa(addr));
  812. }
  813. #endif
  814. if (NutUdpSendTo(sock, addr, DHCP_SERVERPORT, bp, len) < 0) {
  815. dhcpError = DHCPERR_TRANSMIT;
  816. return -1;
  817. }
  818. return 0;
  819. }
  820. /*!
  821. * \brief Receive a DHCP reply from the server.
  822. *
  823. * \param sock Socket descriptor.
  824. * \param xid Expected transaction identifier. Incoming messages with a
  825. * different identifier are silently discarded.
  826. * \param bp Pointer to the receive buffer.
  827. * \param tmo Maximum number of milliseconds to wait for a valid message.
  828. *
  829. * \return The number of bytes received, if successful. The return
  830. * value -1 indicates an error, in which case dhcpError is
  831. * set to an error code. On timeout 0 is returned.
  832. */
  833. static int DhcpRecvMessage(UDPSOCKET * sock, uint32_t xid, BOOTP *bp, uint32_t tmo)
  834. {
  835. int rc;
  836. uint16_t port;
  837. uint32_t addr;
  838. uint32_t etim;
  839. uint32_t wtim;
  840. /* Set our start time. */
  841. etim = NutGetMillis();
  842. /* Set the initial receive timeout. */
  843. wtim = tmo;
  844. for (;;) {
  845. rc = NutUdpReceiveFrom(sock, &addr, &port, bp, sizeof(BOOTP), wtim);
  846. #ifdef NUTDEBUG
  847. if (__tcp_trf) {
  848. if (rc > 0) {
  849. fprintf(__tcp_trs, "[DHCP-Recv from %s]", inet_ntoa(addr));
  850. } else if (rc < 0) {
  851. fprintf(__tcp_trs, "[DHCP-Recv Error]");
  852. } else {
  853. fprintf(__tcp_trs, "[DHCP-Recv Timeout %lu]", tmo);
  854. }
  855. }
  856. #endif
  857. /* Immediately return on receive errors and timeouts. */
  858. if (rc <= 0) {
  859. if (rc < 0) {
  860. dhcpError = DHCPERR_RECEIVE;
  861. }
  862. break;
  863. }
  864. /* The message must at least include the BOOTP header plus five
  865. bytes of options (magic and end). We are quite liberal here. */
  866. if (rc > sizeof(BOOTP) - sizeof(bp->bp_options) + 5) {
  867. /* The message must be a BOOTP reply with the expected XID. */
  868. if (bp->bp_op == 2 && bp->bp_xid == xid) {
  869. /* Message is acceptable. */
  870. break;
  871. }
  872. }
  873. /* Calculate the remaining timeout for not getting trapped here
  874. on a busy network, which regularly broadcasts DHCP messages. */
  875. wtim = NutGetMillis() - etim;
  876. if (wtim >= tmo - 250) {
  877. /* Less than 250 ms left, return timeout. */
  878. rc = 0;
  879. break;
  880. }
  881. wtim = tmo - wtim;
  882. }
  883. return rc;
  884. }
  885. /*!
  886. * \brief Broadcast a DHCP discover message.
  887. *
  888. * \param sock Socket descriptor. This pointer must have been retrieved
  889. * by calling NutUdpCreateSocket().
  890. * \param bp Pointer to a buffer to be used for transmission. No specific
  891. * initialization required.
  892. * \param xid Random transaction identifier.
  893. * \param raddr Requested IP address. Optional.
  894. * \param secs Seconds elapsed since start of address acquisition. Related
  895. * requests must use the same value.
  896. *
  897. * \return 0 on success, -1 if send failed.
  898. */
  899. static int DhcpBroadcastDiscover(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, uint32_t raddr, uint16_t secs)
  900. {
  901. size_t optlen;
  902. int len;
  903. uint8_t *op = bp->bp_options;
  904. optlen = DhcpPrepHeader(bp, DHCP_DISCOVER, xid, 0, secs);
  905. /* Request a specific IP if one had been assigned previously. */
  906. if (raddr) {
  907. optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
  908. }
  909. optlen += DhcpAddParmReqOption(op + optlen);
  910. /* Pass host name if specified in confos structure.
  911. * Win2k DHCP server can register this as dynamic DNS entry.
  912. * Also viewing DHCP lease table shows something sensible.
  913. */
  914. len = strlen(confos.hostname);
  915. if (len > 0) {
  916. optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
  917. }
  918. /* Request a maximum message size. */
  919. optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);
  920. return DhcpSendMessage(sock, INADDR_BROADCAST, bp, optlen);
  921. }
  922. /*!
  923. * \brief Send a DHCP request message.
  924. *
  925. * \param sock Socket descriptor. This pointer must have been retrieved by
  926. * calling NutUdpCreateSocket().
  927. * \param daddr IP address of the DHCP server or \ref INADDR_BROADCAST.
  928. * \param bp Pointer to a buffer to be used for transmission.
  929. * \param xid Random transaction identifier.
  930. * \param caddr Our IP address. Should be set only if we are able to respond
  931. * to ARP requests. Otherwise must be set to 0.
  932. * \param raddr Requested IP address. Required.
  933. * \param sid Server identifier. If this request is not an offer response,
  934. * then set it to zero.
  935. * \param secs Seconds elapsed since start of address acquisition. If this
  936. * request is sent in reponse to an offer, the same value must
  937. * be used.
  938. *
  939. * \return 0 on success, -1 if send failed.
  940. */
  941. static int DhcpSendRequest(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, /* */
  942. uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
  943. {
  944. size_t optlen;
  945. int len;
  946. uint8_t *op = bp->bp_options;
  947. /* Initialize the BOOTP header. */
  948. optlen = DhcpPrepHeader(bp, DHCP_REQUEST, xid, caddr, secs);
  949. /* Add specified options. */
  950. if (raddr) {
  951. optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
  952. }
  953. if (sid) {
  954. optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
  955. }
  956. optlen += DhcpAddParmReqOption(op + optlen);
  957. /* Pass host name if specified in confos structure. */
  958. /* viewing DHCP lease table shows something sensible. */
  959. len = strlen(confos.hostname);
  960. if (len > 0) {
  961. optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
  962. }
  963. return DhcpSendMessage(sock, daddr, bp, optlen);
  964. }
  965. /*!
  966. * \brief Broadcast a DHCP request message.
  967. *
  968. * \param sock Socket descriptor. This pointer must have been retrieved by
  969. * calling NutUdpCreateSocket().
  970. * \param bp Pointer to a buffer to be used for transmission.
  971. * \param xid Random transaction identifier.
  972. * \param caddr Our IP address. Should be set only if we are able to respond
  973. * to ARP requests. Otherwise must be set to 0.
  974. * \param raddr Requested IP address. Required.
  975. * \param sid Server identifier. If this request is not an offer response,
  976. * then set it to zero.
  977. * \param secs Seconds elapsed since start of address acquisition. If this
  978. * request is sent in reponse to an offer, the same value must
  979. * be used.
  980. *
  981. * \return 0 on success, -1 if send failed.
  982. */
  983. static int DhcpBroadcastRequest(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, /* */
  984. uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
  985. {
  986. return DhcpSendRequest(sock, INADDR_BROADCAST, bp, xid, caddr, raddr, sid, secs);
  987. }
  988. /*!
  989. * \brief Relinguish our DHCP lease.
  990. *
  991. * \param sock Socket descriptor. This pointer must have been retrieved by
  992. * calling NutUdpCreateSocket().
  993. * \param daddr IP address of the DHCP server.
  994. * \param bp Pointer to a buffer to be used for transmission.
  995. * \param xid Random transaction identifier.
  996. * \param caddr Our IP address. Should be set only if we are able to respond
  997. * to ARP requests. Otherwise must be set to 0.
  998. * \param sid Server identifier. If this request is not an offer response,
  999. * then set it to zero.
  1000. *
  1001. * \return 0 on success, -1 if send failed.
  1002. */
  1003. static int DhcpSendRelease(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr, uint32_t sid)
  1004. {
  1005. size_t optlen;
  1006. uint8_t *op = bp->bp_options;
  1007. /* Prepare BOOTP header. 'secs' is set to zero. */
  1008. optlen = DhcpPrepHeader(bp, DHCP_RELEASE, xid, caddr, 0);
  1009. /* Optionally add server identifier. */
  1010. if (sid) {
  1011. optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
  1012. }
  1013. return DhcpSendMessage(sock, daddr, bp, optlen);
  1014. }
  1015. /*!
  1016. * \brief Inform DHCP about an externally allocated address.
  1017. *
  1018. * \param sock Socket descriptor. This pointer must have been retrieved by
  1019. * calling NutUdpCreateSocket().
  1020. * \param daddr IP address of the DHCP server or INADDR_BROADCAST.
  1021. * \param bp Pointer to a buffer to be used for transmission.
  1022. * \param xid Random transaction identifier.
  1023. * \param caddr Our IP address. Required.
  1024. *
  1025. * \return 0 on success, -1 if send failed.
  1026. */
  1027. static int DhcpSendInform(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr)
  1028. {
  1029. size_t optlen;
  1030. size_t len;
  1031. uint8_t *op = bp->bp_options;
  1032. /* Prepare BOOTP header. 'secs' is set to zero. */
  1033. optlen = DhcpPrepHeader(bp, DHCP_INFORM, xid, caddr, 0);
  1034. /* Additional options we want. */
  1035. optlen += DhcpAddParmReqOption(op + optlen);
  1036. /* Add our configured host name. */
  1037. len = strlen(confos.hostname);
  1038. if (len > 0) {
  1039. optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
  1040. }
  1041. /* We should provide the maximum message size. */
  1042. optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);
  1043. return DhcpSendMessage(sock, daddr, bp, optlen);
  1044. }
  1045. /*!
  1046. * \brief Check a new offer.
  1047. *
  1048. * \param dyncfg Current configuration, may be NULL.
  1049. * \param ip DHCP server IP address.
  1050. * \param bp DHCP offer message.
  1051. * \param len DHCP message length.
  1052. *
  1053. * \return Updated configuration.
  1054. */
  1055. static DYNCFG *CheckOffer(DYNCFG * dyncfg, BOOTP *bp, size_t len)
  1056. {
  1057. DYNCFG *offer;
  1058. /* Parse the new offer. If it's invalid, return the current
  1059. configuration. */
  1060. offer = ParseReply(bp, len);
  1061. if (offer == NULL) {
  1062. return dyncfg;
  1063. }
  1064. /* Discard anything which is not an offer. */
  1065. if (offer->dyn_msgtyp != DHCP_OFFER) {
  1066. ReleaseDynCfg(offer);
  1067. return dyncfg;
  1068. }
  1069. /* First offer, take it. */
  1070. if (dyncfg == NULL) {
  1071. dyncfg = offer;
  1072. }
  1073. /*
  1074. * Check if the new offer is better than the current configuration:
  1075. */
  1076. else {
  1077. /* If we remember a previously allocated IP which isn't in the
  1078. current configuration but in the new offer, then let's take
  1079. the new one. */
  1080. if (confnet.cdn_ip_addr & confnet.cdn_ip_mask) {
  1081. if (dyncfg->dyn_yiaddr != confnet.cdn_ip_addr && /* */
  1082. offer->dyn_yiaddr == confnet.cdn_ip_addr) {
  1083. ReleaseDynCfg(dyncfg);
  1084. dyncfg = offer;
  1085. }
  1086. }
  1087. /* In the second place prefer long lease times. */
  1088. else if (offer->dyn_leaseTime > dyncfg->dyn_leaseTime) {
  1089. ReleaseDynCfg(dyncfg);
  1090. dyncfg = offer;
  1091. }
  1092. /* The new one deosn't offer anything interesting. Discard it. */
  1093. else {
  1094. ReleaseDynCfg(offer);
  1095. }
  1096. }
  1097. return dyncfg;
  1098. }
  1099. /*! \fn NutDhcpClient(void *arg)
  1100. * \brief DHCP client thread.
  1101. *
  1102. * This thread implements a DHCP state machine and is automatically started
  1103. * when calling NutDhcpIfConfig() or NutDhcpInform().
  1104. *
  1105. * \bug We are not able to shutdown our interface, which may cause problems
  1106. * if out original DHCP server dies.
  1107. *
  1108. * \todo We are using a bunch of global variables, which must be associated
  1109. * to a specific interfase if we want to support more than one
  1110. * Ethernet port.
  1111. */
  1112. THREAD(NutDhcpClient, arg)
  1113. {
  1114. DYNCFG *reply = NULL;
  1115. UDPSOCKET *sock = NULL;
  1116. BOOTP *bp = NULL;
  1117. int n;
  1118. uint32_t xid;
  1119. IFNET *nif;
  1120. uint16_t secs = 0;
  1121. uint32_t aquisStart = NutGetSeconds();
  1122. uint32_t leaseStart = 0;
  1123. uint32_t procStart;
  1124. uint32_t napTime;
  1125. ureg_t retries;
  1126. uint32_t tmo = MIN_DHCP_WAIT;
  1127. uint32_t last_ip = confnet.cdn_ip_addr;
  1128. uint32_t server_ip;
  1129. nif = ((NUTDEVICE *) arg)->dev_icb;
  1130. /*
  1131. * Generate a random transaction identifier based on our MAC
  1132. * address with the least significant byte of the MAC address
  1133. * becoming the most significant byte of the identifier. This
  1134. * should give a sufficient unique value when several Ethernuts
  1135. * are started concurrently.
  1136. */
  1137. xid = 0;
  1138. for (retries = 0; retries < sizeof(xid); retries++) {
  1139. xid <<= 8;
  1140. xid += nif->if_mac[5 - retries];
  1141. }
  1142. retries = 0;
  1143. for (;;) {
  1144. procStart = NutGetSeconds();
  1145. #ifdef NUTDEBUG
  1146. if (__tcp_trf) {
  1147. fprintf(__tcp_trs, "\n[%u.DHCP-", retries + 1);
  1148. switch (dhcpState) {
  1149. case DHCPST_INIT:
  1150. fprintf(__tcp_trs, "INIT]");
  1151. break;
  1152. case DHCPST_SELECTING:
  1153. fprintf(__tcp_trs, "SELECTING]");
  1154. break;
  1155. case DHCPST_REQUESTING:
  1156. fprintf(__tcp_trs, "REQUESTING]");
  1157. break;
  1158. case DHCPST_REBOOTING:
  1159. fprintf(__tcp_trs, "REBOOTING %s]", inet_ntoa(last_ip));
  1160. break;
  1161. case DHCPST_BOUND:
  1162. fprintf(__tcp_trs, "BOUND %lu]", procStart - leaseStart);
  1163. break;
  1164. case DHCPST_RENEWING:
  1165. fprintf(__tcp_trs, "RENEWING %lu]", procStart - leaseStart);
  1166. break;
  1167. case DHCPST_REBINDING:
  1168. fprintf(__tcp_trs, "REBINDING %lu]", procStart - leaseStart);
  1169. break;
  1170. case DHCPST_INFORMING:
  1171. fprintf(__tcp_trs, "INFORMING]");
  1172. break;
  1173. case DHCPST_RELEASING:
  1174. fprintf(__tcp_trs, "RELEASING]");
  1175. break;
  1176. case DHCPST_IDLE:
  1177. if (dhcpError) {
  1178. fprintf(__tcp_trs, "ERROR %u]", dhcpError);
  1179. } else {
  1180. fprintf(__tcp_trs, "IDLE]");
  1181. }
  1182. break;
  1183. default:
  1184. fprintf(__tcp_trs, "UNKNOWN %u]", dhcpState);
  1185. break;
  1186. }
  1187. }
  1188. #endif
  1189. /*
  1190. * Setup some values based on the number of retry attempts.
  1191. */
  1192. server_ip = INADDR_BROADCAST; /* Broadcasting is default. */
  1193. if (retries) {
  1194. /* Double our timeout on each retry. */
  1195. tmo += tmo;
  1196. if (tmo > MAX_DHCP_WAIT) {
  1197. tmo = MAX_DHCP_WAIT;
  1198. }
  1199. } else {
  1200. /* Start with minimum timeout first. */
  1201. tmo = MIN_DHCP_WAIT;
  1202. /* Use a new xid for the first message in each state except
  1203. * when requesting, where we should continue using the xid
  1204. * from the offer message we received.
  1205. */
  1206. if (dhcpState != DHCPST_REQUESTING) {
  1207. xid++;
  1208. }
  1209. /* If we know the server's IP, try to unicast on the first
  1210. attempt. */
  1211. if (dhcpConfig && dhcpConfig->dyn_sid) {
  1212. server_ip = dhcpConfig->dyn_sid;
  1213. }
  1214. }
  1215. /*
  1216. * Keep track of the API timeout.
  1217. */
  1218. if (dhcpState != DHCPST_IDLE && dhcpState != DHCPST_BOUND && dhcpApiTimeout != NUT_WAIT_INFINITE) {
  1219. uint32_t tt = NutGetMillis() - dhcpApiStart;
  1220. if (dhcpApiTimeout <= tt) {
  1221. dhcpError = DHCPERR_TIMEOUT;
  1222. dhcpState = DHCPST_IDLE;
  1223. continue;
  1224. }
  1225. tt = dhcpApiTimeout - tt;
  1226. if (tt < tmo) {
  1227. tmo = tt;
  1228. }
  1229. }
  1230. /*
  1231. * Keep track of acquisition time.
  1232. */
  1233. if (dhcpState == DHCPST_SELECTING || dhcpState == DHCPST_RENEWING || dhcpState == DHCPST_REBINDING) {
  1234. /* For retries make sure that secs doesn't overflow. */
  1235. if (retries) {
  1236. if (procStart - aquisStart > 0xffffUL) {
  1237. secs = 0xffff;
  1238. } else {
  1239. secs = (uint16_t) (procStart - aquisStart);
  1240. }
  1241. }
  1242. /* For first transmissions make sure that secs is zero. */
  1243. else {
  1244. aquisStart = procStart;
  1245. secs = 0;
  1246. }
  1247. }
  1248. /*
  1249. * Release UDP socket and buffer in states with long inactive time.
  1250. */
  1251. if (dhcpState == DHCPST_BOUND || dhcpState == DHCPST_IDLE) {
  1252. if (sock) {
  1253. NutUdpDestroySocket(sock);
  1254. sock = NULL;
  1255. }
  1256. free(bp);
  1257. bp = NULL;
  1258. }
  1259. /*
  1260. * In all other states we need the socket and the buffer.
  1261. */
  1262. else {
  1263. /*
  1264. * Check if something else configured our interface.
  1265. */
  1266. if (dhcpConfig == NULL && nif->if_local_ip) {
  1267. /* If we need additional configuration, we can sent
  1268. a DHCP Inform message here. */
  1269. dhcpState = DHCPST_IDLE;
  1270. continue;
  1271. }
  1272. if (sock == NULL || bp == NULL) {
  1273. if (sock == NULL) {
  1274. sock = NutUdpCreateSocket(DHCP_CLIENTPORT);
  1275. }
  1276. if (bp == NULL) {
  1277. bp = malloc(sizeof(BOOTP));
  1278. }
  1279. if (sock == NULL || bp == NULL) {
  1280. /* Looks like we are out of memory. */
  1281. dhcpError = DHCPERR_SYSTEM;
  1282. dhcpState = DHCPST_IDLE;
  1283. /* At this point either socket or buffer may be allocated.
  1284. Thus we need to jump back to the top of our state loop
  1285. to release it. */
  1286. continue;
  1287. }
  1288. #if MAX_DHCP_BUFSIZE
  1289. {
  1290. uint16_t max_ms = MAX_DHCP_BUFSIZE;
  1291. NutUdpSetSockOpt(sock, SO_RCVBUF, &max_ms, sizeof(max_ms));
  1292. }
  1293. #endif
  1294. }
  1295. }
  1296. /*
  1297. * (Re)Start.
  1298. */
  1299. if (dhcpState == DHCPST_INIT) {
  1300. /* Clear the retry counter. */
  1301. retries = 0;
  1302. /* Use a new XID on each attempt. */
  1303. xid++;
  1304. /* Determine whether this is an initial boot or a reboot. */
  1305. if ((last_ip & confnet.cdn_ip_mask) == 0) {
  1306. /* No previous IP, start from ground up. */
  1307. dhcpState = DHCPST_SELECTING;
  1308. } else {
  1309. /* We got a previously allocated IP configuration from
  1310. * non-volatile configuration memory. Try to re-use it. */
  1311. dhcpState = DHCPST_REBOOTING;
  1312. }
  1313. }
  1314. /*
  1315. * Broadcast discover and collect incoming offers.
  1316. */
  1317. else if (dhcpState == DHCPST_SELECTING) {
  1318. if (retries++ > MAX_DCHP_RETRIES) {
  1319. /* Too many retries while discovering DHCP. Give up. */
  1320. dhcpError = DHCPERR_TIMEOUT;
  1321. dhcpState = DHCPST_IDLE;
  1322. }
  1323. /* Send the discovering broadcast. */
  1324. else if (DhcpBroadcastDiscover(sock, bp, xid, last_ip, secs) < 0) {
  1325. /* Fatal transmit error on broadcast. */
  1326. dhcpState = DHCPST_IDLE;
  1327. } else {
  1328. /* Collect incoming offers. */
  1329. while ((n = DhcpRecvMessage(sock, xid, bp, tmo)) > 0) {
  1330. /* Check if this is a valid offer. */
  1331. dhcpConfig = CheckOffer(dhcpConfig, bp, n);
  1332. if (dhcpConfig) {
  1333. /* If the callers timeout is low, do not collect
  1334. more than one. Thanks to Jelle Kok. */
  1335. if (dhcpApiTimeout < MIN_DHCP_WAIT * 3) {
  1336. break;
  1337. }
  1338. /* Switch to lowest timeout after we received
  1339. a first response. */
  1340. tmo = MIN_DHCP_WAIT;
  1341. }
  1342. }
  1343. /* Change to ERROR state on fatal receive errors. */
  1344. if (n < 0) {
  1345. dhcpState = DHCPST_IDLE;
  1346. }
  1347. /* Change to REQUESTING state if we got a valid offer.
  1348. Otherwise we stay in SELECTING state. */
  1349. else if (dhcpConfig) {
  1350. retries = 0;
  1351. dhcpState = DHCPST_REQUESTING;
  1352. }
  1353. }
  1354. }
  1355. /*
  1356. * Send request and wait for an acknowledge.
  1357. */
  1358. else if (dhcpState == DHCPST_REQUESTING) {
  1359. if (retries++ > MAX_DCHP_RETRIES) {
  1360. /* Too many retries with this server, fall back to discovery. */
  1361. dhcpState = DHCPST_INIT;
  1362. }
  1363. /* Request an offered configuration. According to RFC 2131 this
  1364. has to be broadcasted. */
  1365. else if (DhcpBroadcastRequest(sock, bp, xid, 0, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid, secs) < 0) {
  1366. /* Fatal transmit error on broadcast. Give up. */
  1367. dhcpState = DHCPST_IDLE;
  1368. } else {
  1369. n = DhcpRecvMessage(sock, xid, bp, tmo);
  1370. if (n < 0) {
  1371. /* Fatal receive error. */
  1372. dhcpState = DHCPST_IDLE;
  1373. }
  1374. else if (n > 0) {
  1375. reply = ParseReply(bp, n);
  1376. if (reply) {
  1377. /* The server accepted our request. We are bound. */
  1378. if (reply->dyn_msgtyp == DHCP_ACK) {
  1379. ReleaseDynCfg(dhcpConfig);
  1380. dhcpConfig = reply;
  1381. reply = 0;
  1382. leaseStart = aquisStart;
  1383. dhcpState = DHCPST_BOUND;
  1384. }
  1385. /* The server declines a previously offered configuration.
  1386. Restart discovery. */
  1387. else if (reply->dyn_msgtyp == DHCP_NAK) {
  1388. dhcpState = DHCPST_INIT;
  1389. }
  1390. }
  1391. }
  1392. }
  1393. }
  1394. /*
  1395. * Reusing a previously allocated network address after reboot.
  1396. */
  1397. else if (dhcpState == DHCPST_REBOOTING) {
  1398. if (++retries > MAX_DCHP_RETRIES) {
  1399. /* Too many retries, fall back to discovery. */
  1400. last_ip = 0;
  1401. dhcpState = DHCPST_INIT;
  1402. }
  1403. /* Broadcast a request for our previous configuration. */
  1404. else if (DhcpBroadcastRequest(sock, bp, xid, 0, last_ip, 0, secs) < 0) {
  1405. /* Fatal transmit error on broadcast. Give up. */
  1406. dhcpState = DHCPST_IDLE;
  1407. } else {
  1408. n = DhcpRecvMessage(sock, xid, bp, tmo);
  1409. if (n < 0) {
  1410. /* Fatal receive error. */
  1411. dhcpState = DHCPST_IDLE;
  1412. }
  1413. else if (n > 0) {
  1414. reply = ParseReply(bp, n);
  1415. if (reply) {
  1416. if (reply->dyn_msgtyp == DHCP_ACK) {
  1417. ReleaseDynCfg(dhcpConfig);
  1418. dhcpConfig = reply;
  1419. reply = 0;
  1420. leaseStart = aquisStart;
  1421. dhcpState = DHCPST_BOUND;
  1422. }
  1423. else if (reply->dyn_msgtyp == DHCP_NAK) {
  1424. /* Either our previous address had been allocated by
  1425. someone else or we changed the network. Remove the
  1426. previous address and restart. */
  1427. last_ip = 0;
  1428. dhcpState = DHCPST_INIT;
  1429. }
  1430. }
  1431. }
  1432. }
  1433. }
  1434. /*
  1435. * Maintain lease time.
  1436. */
  1437. else if (dhcpState == DHCPST_BOUND) {
  1438. retries = 0;
  1439. dhcpApiTimeout = NUT_WAIT_INFINITE;
  1440. NutEventBroadcast(&dhcpDone);
  1441. if (dhcpConfig->dyn_renewalTime <= procStart - leaseStart) {
  1442. dhcpState = DHCPST_RENEWING;
  1443. } else {
  1444. /* Calculate the remaining lease time and take a nap. */
  1445. napTime = dhcpConfig->dyn_renewalTime - (procStart - leaseStart);
  1446. if (napTime > MAX_DHCP_NAPTIME) {
  1447. napTime = MAX_DHCP_NAPTIME;
  1448. }
  1449. NutEventWait(&dhcpWake, napTime * 1000UL);
  1450. }
  1451. }
  1452. /*
  1453. * Waiting for an acknowledge of our renewal request.
  1454. */
  1455. else if (dhcpState == DHCPST_RENEWING) {
  1456. retries++;
  1457. if (tmo / 1000 > dhcpConfig->dyn_rebindTime - (procStart - leaseStart)) {
  1458. tmo = (dhcpConfig->dyn_rebindTime - (procStart - leaseStart)) * 1000;
  1459. }
  1460. if (dhcpConfig->dyn_rebindTime <= procStart - leaseStart) {
  1461. retries = 0;
  1462. dhcpState = DHCPST_REBINDING;
  1463. }
  1464. /* Send a request to our leasing server. We must not include
  1465. the server identifier. */
  1466. else if (DhcpSendRequest(sock, dhcpConfig->dyn_sid, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) <
  1467. 0) {
  1468. /* Unicast transmit error. */
  1469. retries = 0;
  1470. dhcpState = DHCPST_REBINDING;
  1471. } else {
  1472. n = DhcpRecvMessage(sock, xid, bp, tmo);
  1473. if (n < 0) {
  1474. /* Fatal receive error. */
  1475. dhcpState = DHCPST_IDLE;
  1476. }
  1477. else if (n > 0) {
  1478. reply = ParseReply(bp, n);
  1479. if (reply) {
  1480. if (reply->dyn_msgtyp == DHCP_ACK) {
  1481. /* Got an acknowledge, return to bound state. */
  1482. ReleaseDynCfg(dhcpConfig);
  1483. dhcpConfig = reply;
  1484. reply = 0;
  1485. leaseStart = aquisStart;
  1486. dhcpState = DHCPST_BOUND;
  1487. }
  1488. else if (reply->dyn_msgtyp == DHCP_NAK) {
  1489. /* Unexpected NAK. */
  1490. retries = 0;
  1491. dhcpState = DHCPST_REBINDING;
  1492. }
  1493. }
  1494. }
  1495. }
  1496. }
  1497. /*
  1498. * Waiting for an acknowledge of our rebind request.
  1499. */
  1500. else if (dhcpState == DHCPST_REBINDING) {
  1501. retries++;
  1502. if (tmo / 1000 > dhcpConfig->dyn_leaseTime - (procStart - leaseStart)) {
  1503. tmo = (dhcpConfig->dyn_leaseTime - (procStart - leaseStart)) * 1000;
  1504. }
  1505. if (dhcpConfig->dyn_leaseTime <= procStart - leaseStart) {
  1506. retries = 0;
  1507. dhcpState = DHCPST_IDLE;
  1508. }
  1509. /* Broadcast a request for our previous configuration. We
  1510. must not include a server identifier. */
  1511. else if (DhcpBroadcastRequest(sock, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) < 0) {
  1512. /* Fatal transmit error on broadcast. Give up. */
  1513. dhcpState = DHCPST_IDLE;
  1514. } else {
  1515. n = DhcpRecvMessage(sock, xid, bp, tmo);
  1516. if (n < 0) {
  1517. /* Fatal receive error. */
  1518. dhcpState = DHCPST_IDLE;
  1519. }
  1520. else if (n > 0) {
  1521. reply = ParseReply(bp, n);
  1522. if (reply) {
  1523. if (reply->dyn_msgtyp == DHCP_ACK) {
  1524. /* Got an acknowledge, return to bound state. */
  1525. ReleaseDynCfg(dhcpConfig);
  1526. dhcpConfig = reply;
  1527. reply = 0;
  1528. leaseStart = aquisStart;
  1529. dhcpState = DHCPST_BOUND;
  1530. }
  1531. else if (reply->dyn_msgtyp == DHCP_NAK) {
  1532. /*
  1533. * We have a problem here if the last DHCP server died.
  1534. * If a backup server exists, it may probe our IP address
  1535. * using ARP or ICMP. Our interface is up and responding,
  1536. * so the backup server may think that the IP address
  1537. * is in use and respond with NAK. Without shutting
  1538. * down our interface (not yet implemented) we are stuck.
  1539. * We switch to discovery state, but the problem remains.
  1540. */
  1541. dhcpState = DHCPST_INIT;
  1542. }
  1543. }
  1544. }
  1545. }
  1546. }
  1547. /*
  1548. * Send an inform and wait for its (optional) echo.
  1549. */
  1550. else if (dhcpState == DHCPST_INFORMING) {
  1551. if (retries++ > MAX_DCHP_RETRIES) {
  1552. dhcpState = DHCPST_IDLE;
  1553. } else if (DhcpSendInform(sock, server_ip, bp, xid, nif->if_local_ip) < 0) {
  1554. if (server_ip == INADDR_BROADCAST) {
  1555. dhcpState = DHCPST_IDLE;
  1556. }
  1557. } else {
  1558. n = DhcpRecvMessage(sock, xid, bp, tmo);
  1559. if (n) {
  1560. if (n > 0) {
  1561. reply = ParseReply(bp, n);
  1562. if (reply && reply->dyn_msgtyp == DHCP_ACK) {
  1563. /* Take over this configuration. */
  1564. ReleaseDynCfg(dhcpConfig);
  1565. dhcpConfig = reply;
  1566. reply = 0;
  1567. }
  1568. }
  1569. }
  1570. dhcpState = DHCPST_IDLE;
  1571. }
  1572. }
  1573. /*
  1574. * Send a release and wait for its (optional) echo.
  1575. */
  1576. else if (dhcpState == DHCPST_RELEASING) {
  1577. if (dhcpConfig == NULL || /* Not configured. */
  1578. retries++ > MAX_DCHP_RELEASE_RETRIES || /* Too many retries. */
  1579. DhcpSendRelease(sock, server_ip, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid) < 0) {
  1580. if (server_ip == INADDR_BROADCAST) {
  1581. dhcpState = DHCPST_IDLE;
  1582. }
  1583. } else {
  1584. n = DhcpRecvMessage(sock, xid, bp, tmo);
  1585. if (n < 0) {
  1586. /* Fatal receive error. */
  1587. dhcpState = DHCPST_IDLE;
  1588. }
  1589. else if (n > 0) {
  1590. reply = ParseReply(bp, n);
  1591. if (reply) {
  1592. if (reply->dyn_msgtyp == DHCP_ACK) {
  1593. dhcpState = DHCPST_IDLE;
  1594. }
  1595. else if (reply->dyn_msgtyp == DHCP_NAK) {
  1596. dhcpState = DHCPST_IDLE;
  1597. }
  1598. }
  1599. }
  1600. }
  1601. }
  1602. /*
  1603. * We are done somehow. Either a fatal error occured or we
  1604. * reached the specified timeout time or our lease has been
  1605. * release or something else configured our interface.
  1606. * Release all resources and wait for a new API call to
  1607. * wake us up.
  1608. */
  1609. else if (dhcpState == DHCPST_IDLE) {
  1610. ReleaseDynCfg(dhcpConfig);
  1611. dhcpConfig = NULL;
  1612. retries = 0;
  1613. dhcpApiTimeout = NUT_WAIT_INFINITE;
  1614. NutEventBroadcast(&dhcpDone);
  1615. NutEventWait(&dhcpWake, NUT_WAIT_INFINITE);
  1616. }
  1617. /* Release any received reply. */
  1618. if (reply) {
  1619. ReleaseDynCfg(reply);
  1620. reply = 0;
  1621. }
  1622. }
  1623. }
  1624. /*!
  1625. * \brief Activate the DHCP client thread.
  1626. *
  1627. * Start the DHCP thread if not running or wake it up. Pass the caller's
  1628. * timeout to the thread and wait an infinite time. We rely on the thread
  1629. * to wake us up on timeout.
  1630. *
  1631. * \param name Name of the registered Ethernet device.
  1632. * \param state State to start with.
  1633. * \param timeout Maximum number of milliseconds to wait. To disable
  1634. * timeout, set this parameter to \ref NUT_WAIT_INFINITE.
  1635. * This value must be larger than 3 times of \ref MIN_DHCP_WAIT
  1636. * to enable collection of offers from multiple servers.
  1637. */
  1638. static int DhcpKick(const char *name, uint8_t state, uint32_t timeout)
  1639. {
  1640. NUTDEVICE *dev;
  1641. IFNET *nif;
  1642. /* Lookup the Ethernet device. */
  1643. dev = NutDeviceLookup(name);
  1644. if (dev == NULL || dev->dev_type != IFTYP_NET) {
  1645. dhcpError = DHCPERR_BADDEV;
  1646. return -1;
  1647. }
  1648. nif = dev->dev_icb;
  1649. if (nif == NULL || nif->if_type != IFT_ETHER) {
  1650. dhcpError = DHCPERR_BADDEV;
  1651. return -1;
  1652. }
  1653. /* Initialize timeout checking. */
  1654. dhcpApiStart = NutGetMillis();
  1655. dhcpApiTimeout = timeout;
  1656. dhcpState = state;
  1657. if (dhcpThread == NULL) {
  1658. dhcpThread = NutThreadCreate("dhcpc", NutDhcpClient, dev,
  1659. (NUT_THREAD_DHCPSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD);
  1660. }
  1661. /* Wake up the DHCP thread. Note, that we use an asynchronous post
  1662. without context switch. This is required because the DHCP thread
  1663. broadcasts an event to dhcpDone, which will not signal an empty
  1664. queue. If that broadcast happens before we enter the following
  1665. wait, then we will be trapped. Thanks to Henrik Maier for this
  1666. important fix. */
  1667. NutEventPostAsync(&dhcpWake);
  1668. NutEventWait(&dhcpDone, NUT_WAIT_INFINITE);
  1669. return 0;
  1670. }
  1671. /*!
  1672. * \brief Automatically configure an Ethernet network interface.
  1673. *
  1674. * If no MAC address is specified, this routine will try to read a
  1675. * previously stored configuration from the EEPROM. If this retrieves
  1676. * a fixed IP configuration, then the network interface will be
  1677. * immediately configured with these values by calling NutNetIfConfig().
  1678. * If no valid IP configuration has been read, then this routine will
  1679. * start the DHCP client thread and wait upto a given number of
  1680. * milliseconds for an acknowledged configuration from a DHCP server.
  1681. *
  1682. * If a MAC address has been specified, this routine will not read the
  1683. * EEPROM configuration. If the application has set the global
  1684. * \ref CONFNET structure to a valid IP configuration before calling
  1685. * this function, then the network interface will be immediately
  1686. * configured with these values by calling NutNetIfConfig(). Otherwise
  1687. * the DHCP client thread will be started and this routine will wait
  1688. * upto a given number of milliseconds for an acknowledged configuration
  1689. * from a DHCP server.
  1690. *
  1691. * \param name Name of the registered Ethernet device.
  1692. * \param mac MAC address of the destination. Set NULL to use the
  1693. * configuration stored in the EEPROM.
  1694. * \param timeout Maximum number of milliseconds to wait. To disable
  1695. * timeout, set this parameter to \ref NUT_WAIT_INFINITE.
  1696. * Otherwise the value must be larger than 3 times of
  1697. * \ref MIN_DHCP_WAIT to enable collection of offers
  1698. * from multiple servers.
  1699. *
  1700. * \return 0 if the interface had been successfully configured. In most
  1701. * cases this information is sufficient, because the application
  1702. * will not care whether the configuration had been provided by
  1703. * a DHCP server or EEPROM values. However, if EEPROM values had
  1704. * been used, then no DNS servers had been set. The application
  1705. * can call NutDhcpStatus() to check the DHCP bound state. -1 is
  1706. * returned if the interface configuration failed. In this case
  1707. * NutDhcpError() can be called to get a more specific error
  1708. * code.
  1709. */
  1710. int NutDhcpIfConfig(const char *name, uint8_t * mac, uint32_t timeout)
  1711. {
  1712. NUTDEVICE *dev;
  1713. IFNET *nif = NULL;
  1714. /*
  1715. * Verify the given Ethernet device.
  1716. */
  1717. dev = NutDeviceLookup(name);
  1718. if (dev && dev->dev_type == IFTYP_NET) {
  1719. nif = (IFNET *) dev->dev_icb;
  1720. }
  1721. if (nif == NULL || nif->if_type != IFT_ETHER) {
  1722. /* Not a network device or wrong interface type. */
  1723. dhcpError = DHCPERR_BADDEV;
  1724. return -1;
  1725. }
  1726. /*
  1727. * Determine the MAC address.
  1728. */
  1729. if (mac && !ETHER_IS_BROADCAST(mac)) {
  1730. /* If the caller specified a valid MAC address, we use it
  1731. to override the configuration. */
  1732. memcpy(confnet.cdn_mac, mac, sizeof(confnet.cdn_mac));
  1733. }
  1734. else if (ETHER_IS_ZERO(nif->if_mac)) {
  1735. /* The interface has not defined a MAC. Try to get one from
  1736. non-volatile memory. */
  1737. NutNetLoadConfig(name);
  1738. }
  1739. /*
  1740. * Check if we have a valid MAC address. In order to maintain
  1741. * backward compatibility, we accept anything which is neither
  1742. * zero nor the broadcast address. Later versions may become more
  1743. * restrictive and demand a valid unicast address.
  1744. */
  1745. if (ETHER_IS_ZERO(confnet.cdn_mac) || ETHER_IS_BROADCAST(confnet.cdn_mac)) {
  1746. dhcpError = DHCPERR_NOMAC;
  1747. return -1;
  1748. }
  1749. /*
  1750. * Copy the MAC address to the interface structure. This will
  1751. * magically enable the brain dead interface.
  1752. */
  1753. memcpy(nif->if_mac, confnet.cdn_mac, 6);
  1754. NutSleep(500);
  1755. /*
  1756. * Zero out the ip address and mask. This allows to switch between
  1757. * DHCP and static IP addresses without resetting/power cycling.
  1758. * See patch #2903940.
  1759. */
  1760. nif->if_local_ip = 0;
  1761. nif->if_mask = confnet.cdn_ip_mask;
  1762. /*
  1763. * If the EEPROM contains a fixed network configuration, we skip DHCP.
  1764. */
  1765. if (confnet.cdn_cip_addr & confnet.cdn_ip_mask) {
  1766. /* Give up a previously allocated lease. See patch #2903940. */
  1767. (void)NutDhcpRelease(name, (3*MIN_DHCP_WAIT));
  1768. confnet.cdn_ip_addr = confnet.cdn_cip_addr;
  1769. return NutNetIfConfig2(name,
  1770. confnet.cdn_mac,
  1771. confnet.cdn_ip_addr,
  1772. confnet.cdn_ip_mask,
  1773. confnet.cdn_gateway);
  1774. }
  1775. /*
  1776. * Start the DHCP thread if not running or wake it up. Pass the caller's
  1777. * timeout to the thread and wait an infinite time. We rely on the thread
  1778. * to wake us up on timeout.
  1779. */
  1780. if (DhcpKick(name, DHCPST_INIT, timeout) == 0) {
  1781. /*
  1782. * The thread finished its task. If it reached the bound state, then
  1783. * we got a valid configuration from DHCP.
  1784. */
  1785. if (dhcpState == DHCPST_BOUND) {
  1786. #ifdef NUTDEBUG
  1787. if (__tcp_trf) {
  1788. fprintf(__tcp_trs, "[DHCP-Config %s]", inet_ntoa(dhcpConfig->dyn_yiaddr));
  1789. }
  1790. #endif
  1791. NutNetIfSetup(dev, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_netmask, dhcpConfig->dyn_gateway);
  1792. NutDnsConfig2(NULL, NULL, dhcpConfig->dyn_pdns, dhcpConfig->dyn_sdns);
  1793. return 0;
  1794. }
  1795. /*
  1796. * Our interface has been configured externally, possibly by auto
  1797. * ARP or a similar function implemented by the application.
  1798. */
  1799. if (nif->if_local_ip) {
  1800. #ifdef NUTDEBUG
  1801. if (__tcp_trf) {
  1802. fprintf(__tcp_trs, "[DHCP-External %s]", inet_ntoa(nif->if_local_ip));
  1803. }
  1804. #endif
  1805. return 0;
  1806. }
  1807. /*
  1808. * DHCP failed. In case we remember a previously allocated address,
  1809. * then let's use it.
  1810. */
  1811. if ((confnet.cdn_ip_addr & confnet.cdn_ip_mask) != 0) {
  1812. #ifdef NUTDEBUG
  1813. if (__tcp_trf) {
  1814. fprintf(__tcp_trs, "[DHCP-Reusing %s]", inet_ntoa(confnet.cdn_ip_addr));
  1815. }
  1816. #endif
  1817. NutNetIfConfig2(name, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask, confnet.cdn_gateway);
  1818. return 0;
  1819. }
  1820. }
  1821. return -1;
  1822. }
  1823. /*!
  1824. * \brief Relinguish our DHCP lease.
  1825. *
  1826. * This function may be called by the application if we are moving
  1827. * to another network. It helps the DHCP server to tidy up his
  1828. * allocation table, but is not a required DHCP function.
  1829. *
  1830. * Upon return, the system should be shut down within 20 seconds.
  1831. *
  1832. * The client must reside in state \ref DHCPST_BOUND.
  1833. *
  1834. * \param name Name of the registered Ethernet device, currently ignored.
  1835. * \param timeout Maximum number of milliseconds to wait.
  1836. *
  1837. * \return 0 on success or -1 in case of an error.
  1838. */
  1839. int NutDhcpRelease(const char *name, uint32_t timeout)
  1840. {
  1841. /* Check the state. */
  1842. if (dhcpState != DHCPST_BOUND) {
  1843. dhcpError = DHCPERR_STATE;
  1844. return -1;
  1845. }
  1846. /* Action! */
  1847. return DhcpKick(name, DHCPST_RELEASING, timeout);
  1848. }
  1849. /*!
  1850. * \brief Inform DHCP about an allocated address.
  1851. *
  1852. * The client must reside in state \ref DHCPST_IDLE.
  1853. *
  1854. * \param name Name of the registered Ethernet device, currently ignored.
  1855. * \param timeout Maximum number of milliseconds to wait.
  1856. *
  1857. * \return 0 on success or -1 in case of an error.
  1858. */
  1859. int NutDhcpInform(const char *name, uint32_t timeout)
  1860. {
  1861. /* Check the state. */
  1862. if (dhcpState != DHCPST_IDLE) {
  1863. dhcpError = DHCPERR_STATE;
  1864. return -1;
  1865. }
  1866. /* Action! */
  1867. return DhcpKick(name, DHCPST_INFORMING, timeout);
  1868. }
  1869. /*!
  1870. * \brief Return DHCP client status.
  1871. *
  1872. * \param name Name of the registered Ethernet device, currently ignored.
  1873. *
  1874. * \return DHCP status code, which may be any of the following:
  1875. * - \ref DHCPST_INIT
  1876. * - \ref DHCPST_SELECTING
  1877. * - \ref DHCPST_REQUESTING
  1878. * - \ref DHCPST_REBOOTING
  1879. * - \ref DHCPST_BOUND
  1880. * - \ref DHCPST_RENEWING
  1881. * - \ref DHCPST_REBINDING
  1882. * - \ref DHCPST_INFORMING
  1883. * - \ref DHCPST_RELEASING
  1884. * - \ref DHCPST_IDLE
  1885. */
  1886. int NutDhcpStatus(const char *name)
  1887. {
  1888. return dhcpState;
  1889. }
  1890. /*!
  1891. * \brief Return DHCP error code.
  1892. *
  1893. * Possible error codes are
  1894. *
  1895. * - \ref DHCPERR_TIMEOUT
  1896. * - \ref DHCPERR_NOMAC
  1897. * - \ref DHCPERR_BADDEV
  1898. * - \ref DHCPERR_SYSTEM
  1899. * - \ref DHCPERR_TRANSMIT
  1900. * - \ref DHCPERR_RECEIVE
  1901. *
  1902. * The error will be cleared upon return.
  1903. *
  1904. * \param name Name of the registered Ethernet device, currently ignored.
  1905. *
  1906. * \return DHCP error code or 0 if no error occured.
  1907. */
  1908. int NutDhcpError(const char *name)
  1909. {
  1910. int rc = dhcpError;
  1911. dhcpError = 0;
  1912. return rc;
  1913. }
  1914. /*!
  1915. * \brief Check if DHCP has configured our interface
  1916. *
  1917. * \deprecated Applications should use NutDhcpStatus().
  1918. *
  1919. * \return 0 if DHCP is in bound state.
  1920. */
  1921. int NutDhcpIsConfigured(void)
  1922. {
  1923. return (dhcpState == DHCPST_BOUND);
  1924. }
  1925. /*@}*/