dhcp.c 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. /*
  2. * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. */
  18. #include <string.h>
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <errno.h>
  22. #include <assert.h>
  23. #include <byteswap.h>
  24. #include <gpxe/if_ether.h>
  25. #include <gpxe/netdevice.h>
  26. #include <gpxe/device.h>
  27. #include <gpxe/xfer.h>
  28. #include <gpxe/open.h>
  29. #include <gpxe/job.h>
  30. #include <gpxe/retry.h>
  31. #include <gpxe/tcpip.h>
  32. #include <gpxe/ip.h>
  33. #include <gpxe/uuid.h>
  34. #include <gpxe/dhcp.h>
  35. #include <gpxe/timer.h>
  36. #include <gpxe/settings.h>
  37. #include <gpxe/dhcp.h>
  38. #include <gpxe/dhcpopts.h>
  39. #include <gpxe/dhcppkt.h>
  40. #include <gpxe/features.h>
  41. /** @file
  42. *
  43. * Dynamic Host Configuration Protocol
  44. *
  45. */
  46. /**
  47. * DHCP operation types
  48. *
  49. * This table maps from DHCP message types (i.e. values of the @c
  50. * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP
  51. * packet.
  52. */
  53. static const uint8_t dhcp_op[] = {
  54. [DHCPDISCOVER] = BOOTP_REQUEST,
  55. [DHCPOFFER] = BOOTP_REPLY,
  56. [DHCPREQUEST] = BOOTP_REQUEST,
  57. [DHCPDECLINE] = BOOTP_REQUEST,
  58. [DHCPACK] = BOOTP_REPLY,
  59. [DHCPNAK] = BOOTP_REPLY,
  60. [DHCPRELEASE] = BOOTP_REQUEST,
  61. [DHCPINFORM] = BOOTP_REQUEST,
  62. };
  63. /** Raw option data for options common to all DHCP requests */
  64. static uint8_t dhcp_request_options_data[] = {
  65. DHCP_MAX_MESSAGE_SIZE,
  66. DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ),
  67. DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ),
  68. DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ),
  69. DHCP_VENDOR_CLASS_ID,
  70. DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':',
  71. 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':',
  72. 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ),
  73. DHCP_PARAMETER_REQUEST_LIST,
  74. DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS,
  75. DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME,
  76. DHCP_ROOT_PATH, DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID,
  77. DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME,
  78. DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ),
  79. DHCP_END
  80. };
  81. /** Options common to all DHCP requests */
  82. static struct dhcp_options dhcp_request_options = {
  83. .data = dhcp_request_options_data,
  84. .max_len = sizeof ( dhcp_request_options_data ),
  85. .len = sizeof ( dhcp_request_options_data ),
  86. };
  87. /** DHCP feature codes */
  88. static uint8_t dhcp_features[0] __table_start ( uint8_t, dhcp_features );
  89. static uint8_t dhcp_features_end[0] __table_end ( uint8_t, dhcp_features );
  90. /** Version number feature */
  91. FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH );
  92. /** DHCP network device descriptor */
  93. struct dhcp_netdev_desc {
  94. /** Bus type ID */
  95. uint8_t type;
  96. /** Vendor ID */
  97. uint16_t vendor;
  98. /** Device ID */
  99. uint16_t device;
  100. } __attribute__ (( packed ));
  101. /** DHCP client identifier */
  102. struct dhcp_client_id {
  103. /** Link-layer protocol */
  104. uint8_t ll_proto;
  105. /** Link-layer address */
  106. uint8_t ll_addr[MAX_LL_ADDR_LEN];
  107. } __attribute__ (( packed ));
  108. /** DHCP client UUID */
  109. struct dhcp_client_uuid {
  110. /** Identifier type */
  111. uint8_t type;
  112. /** UUID */
  113. union uuid uuid;
  114. } __attribute__ (( packed ));
  115. #define DHCP_CLIENT_UUID_TYPE 0
  116. /** DHCP PXE boot menu item */
  117. struct dhcp_pxe_boot_menu_item {
  118. /** "Type"
  119. *
  120. * This field actually identifies the specific boot server (or
  121. * cluster of boot servers offering identical boot files).
  122. */
  123. uint16_t type;
  124. /** "Layer"
  125. *
  126. * Just don't ask.
  127. */
  128. uint16_t layer;
  129. } __attribute__ (( packed ));
  130. /**
  131. * Name a DHCP packet type
  132. *
  133. * @v msgtype DHCP message type
  134. * @ret string DHCP mesasge type name
  135. */
  136. static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) {
  137. switch ( msgtype ) {
  138. case DHCPNONE: return "BOOTP"; /* Non-DHCP packet */
  139. case DHCPDISCOVER: return "DHCPDISCOVER";
  140. case DHCPOFFER: return "DHCPOFFER";
  141. case DHCPREQUEST: return "DHCPREQUEST";
  142. case DHCPDECLINE: return "DHCPDECLINE";
  143. case DHCPACK: return "DHCPACK";
  144. case DHCPNAK: return "DHCPNAK";
  145. case DHCPRELEASE: return "DHCPRELEASE";
  146. case DHCPINFORM: return "DHCPINFORM";
  147. default: return "DHCP<invalid>";
  148. }
  149. }
  150. /**
  151. * Calculate DHCP transaction ID for a network device
  152. *
  153. * @v netdev Network device
  154. * @ret xid DHCP XID
  155. *
  156. * Extract the least significant bits of the hardware address for use
  157. * as the transaction ID.
  158. */
  159. static uint32_t dhcp_xid ( struct net_device *netdev ) {
  160. uint32_t xid;
  161. memcpy ( &xid, ( netdev->ll_addr + netdev->ll_protocol->ll_addr_len
  162. - sizeof ( xid ) ), sizeof ( xid ) );
  163. return xid;
  164. }
  165. /****************************************************************************
  166. *
  167. * DHCP settings
  168. *
  169. */
  170. /** A DHCP settings block */
  171. struct dhcp_settings {
  172. /** Reference counter */
  173. struct refcnt refcnt;
  174. /** DHCP packet */
  175. struct dhcp_packet dhcppkt;
  176. /** Setting interface */
  177. struct settings settings;
  178. };
  179. /**
  180. * Increment reference count on DHCP settings block
  181. *
  182. * @v dhcpset DHCP settings block
  183. * @ret dhcpset DHCP settings block
  184. */
  185. static inline __attribute__ (( always_inline )) struct dhcp_settings *
  186. dhcpset_get ( struct dhcp_settings *dhcpset ) {
  187. ref_get ( &dhcpset->refcnt );
  188. return dhcpset;
  189. }
  190. /**
  191. * Decrement reference count on DHCP settings block
  192. *
  193. * @v dhcpset DHCP settings block
  194. */
  195. static inline __attribute__ (( always_inline )) void
  196. dhcpset_put ( struct dhcp_settings *dhcpset ) {
  197. ref_put ( &dhcpset->refcnt );
  198. }
  199. /**
  200. * Store value of DHCP setting
  201. *
  202. * @v settings Settings block
  203. * @v setting Setting to store
  204. * @v data Setting data, or NULL to clear setting
  205. * @v len Length of setting data
  206. * @ret rc Return status code
  207. */
  208. static int dhcpset_store ( struct settings *settings, struct setting *setting,
  209. const void *data, size_t len ) {
  210. struct dhcp_settings *dhcpset =
  211. container_of ( settings, struct dhcp_settings, settings );
  212. return dhcppkt_store ( &dhcpset->dhcppkt, setting->tag, data, len );
  213. }
  214. /**
  215. * Fetch value of DHCP setting
  216. *
  217. * @v settings Settings block, or NULL to search all blocks
  218. * @v setting Setting to fetch
  219. * @v data Buffer to fill with setting data
  220. * @v len Length of buffer
  221. * @ret len Length of setting data, or negative error
  222. */
  223. static int dhcpset_fetch ( struct settings *settings, struct setting *setting,
  224. void *data, size_t len ) {
  225. struct dhcp_settings *dhcpset =
  226. container_of ( settings, struct dhcp_settings, settings );
  227. return dhcppkt_fetch ( &dhcpset->dhcppkt, setting->tag, data, len );
  228. }
  229. /** DHCP settings operations */
  230. static struct settings_operations dhcpset_settings_operations = {
  231. .store = dhcpset_store,
  232. .fetch = dhcpset_fetch,
  233. };
  234. /**
  235. * Create DHCP setting block
  236. *
  237. * @v dhcphdr DHCP packet
  238. * @v len Length of DHCP packet
  239. * @ret dhcpset DHCP settings block
  240. */
  241. static struct dhcp_settings * dhcpset_create ( const struct dhcphdr *dhcphdr,
  242. size_t len ) {
  243. struct dhcp_settings *dhcpset;
  244. void *data;
  245. dhcpset = zalloc ( sizeof ( *dhcpset ) + len );
  246. if ( dhcpset ) {
  247. data = ( ( ( void * ) dhcpset ) + sizeof ( *dhcpset ) );
  248. memcpy ( data, dhcphdr, len );
  249. dhcppkt_init ( &dhcpset->dhcppkt, data, len );
  250. settings_init ( &dhcpset->settings,
  251. &dhcpset_settings_operations, &dhcpset->refcnt,
  252. DHCP_SETTINGS_NAME, 0 );
  253. }
  254. return dhcpset;
  255. }
  256. /** DHCP server address setting */
  257. struct setting dhcp_server_setting __setting = {
  258. .name = "dhcp-server",
  259. .description = "DHCP server address",
  260. .tag = DHCP_SERVER_IDENTIFIER,
  261. .type = &setting_type_ipv4,
  262. };
  263. /****************************************************************************
  264. *
  265. * DHCP session
  266. *
  267. */
  268. /** DHCP session states */
  269. enum dhcp_session_state {
  270. /** Sending DHCPDISCOVERs, collecting DHCPOFFERs and ProxyDHCPOFFERs */
  271. DHCP_STATE_DISCOVER = 0,
  272. /** Sending DHCPREQUESTs, waiting for DHCPACK */
  273. DHCP_STATE_REQUEST,
  274. /** Sending ProxyDHCPREQUESTs, waiting for ProxyDHCPACK */
  275. DHCP_STATE_PROXYREQUEST,
  276. /** Sending BootServerDHCPREQUESTs, waiting for BootServerDHCPACK */
  277. DHCP_STATE_BSREQUEST,
  278. };
  279. /**
  280. * Name a DHCP session state
  281. *
  282. * @v state DHCP session state
  283. * @ret string DHCP session state name
  284. */
  285. static inline const char * dhcp_state_name ( enum dhcp_session_state state ) {
  286. switch ( state ) {
  287. case DHCP_STATE_DISCOVER: return "DHCPDISCOVER";
  288. case DHCP_STATE_REQUEST: return "DHCPREQUEST";
  289. case DHCP_STATE_PROXYREQUEST: return "ProxyDHCPREQUEST";
  290. case DHCP_STATE_BSREQUEST: return "BootServerREQUEST";
  291. default: return "<invalid>";
  292. }
  293. }
  294. /** A DHCP session */
  295. struct dhcp_session {
  296. /** Reference counter */
  297. struct refcnt refcnt;
  298. /** Job control interface */
  299. struct job_interface job;
  300. /** Data transfer interface */
  301. struct xfer_interface xfer;
  302. /** Network device being configured */
  303. struct net_device *netdev;
  304. /** State of the session
  305. *
  306. * This is a value for the @c DHCP_MESSAGE_TYPE option
  307. * (e.g. @c DHCPDISCOVER).
  308. */
  309. enum dhcp_session_state state;
  310. /** DHCPOFFER obtained during DHCPDISCOVER */
  311. struct dhcp_settings *dhcpoffer;
  312. /** ProxyDHCPOFFER obtained during DHCPDISCOVER */
  313. struct dhcp_settings *proxydhcpoffer;
  314. /** DHCPACK obtained during DHCPREQUEST */
  315. struct dhcp_settings *dhcpack;
  316. /** ProxyDHCPACK obtained during ProxyDHCPREQUEST */
  317. struct dhcp_settings *proxydhcpack;
  318. /** BootServerDHCPACK obtained during BootServerDHCPREQUEST */
  319. struct dhcp_settings *bsdhcpack;
  320. /** Retransmission timer */
  321. struct retry_timer timer;
  322. /** Start time of the current state (in ticks) */
  323. unsigned long start;
  324. };
  325. /**
  326. * Free DHCP session
  327. *
  328. * @v refcnt Reference counter
  329. */
  330. static void dhcp_free ( struct refcnt *refcnt ) {
  331. struct dhcp_session *dhcp =
  332. container_of ( refcnt, struct dhcp_session, refcnt );
  333. netdev_put ( dhcp->netdev );
  334. dhcpset_put ( dhcp->dhcpoffer );
  335. dhcpset_put ( dhcp->proxydhcpoffer );
  336. dhcpset_put ( dhcp->dhcpack );
  337. dhcpset_put ( dhcp->proxydhcpack );
  338. dhcpset_put ( dhcp->bsdhcpack );
  339. free ( dhcp );
  340. }
  341. /**
  342. * Mark DHCP session as complete
  343. *
  344. * @v dhcp DHCP session
  345. * @v rc Return status code
  346. */
  347. static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
  348. /* Block futher incoming messages */
  349. job_nullify ( &dhcp->job );
  350. xfer_nullify ( &dhcp->xfer );
  351. /* Stop retry timer */
  352. stop_timer ( &dhcp->timer );
  353. /* Free resources and close interfaces */
  354. xfer_close ( &dhcp->xfer, rc );
  355. job_done ( &dhcp->job, rc );
  356. }
  357. /****************************************************************************
  358. *
  359. * Data transfer interface
  360. *
  361. */
  362. /**
  363. * Create a DHCP packet
  364. *
  365. * @v dhcppkt DHCP packet structure to fill in
  366. * @v netdev Network device
  367. * @v msgtype DHCP message type
  368. * @v options Initial options to include (or NULL)
  369. * @v data Buffer for DHCP packet
  370. * @v max_len Size of DHCP packet buffer
  371. * @ret rc Return status code
  372. *
  373. * Creates a DHCP packet in the specified buffer, and fills out a @c
  374. * dhcp_packet structure.
  375. */
  376. int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
  377. struct net_device *netdev, uint8_t msgtype,
  378. struct dhcp_options *options,
  379. void *data, size_t max_len ) {
  380. struct dhcphdr *dhcphdr = data;
  381. size_t options_len;
  382. unsigned int hlen;
  383. int rc;
  384. /* Sanity check */
  385. options_len = ( options ? options->len : 0 );
  386. if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
  387. return -ENOSPC;
  388. /* Initialise DHCP packet content */
  389. memset ( dhcphdr, 0, max_len );
  390. dhcphdr->xid = dhcp_xid ( netdev );
  391. dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
  392. dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
  393. dhcphdr->op = dhcp_op[msgtype];
  394. /* If hardware length exceeds the chaddr field length, don't
  395. * use the chaddr field. This is as per RFC4390.
  396. */
  397. hlen = netdev->ll_protocol->ll_addr_len;
  398. if ( hlen > sizeof ( dhcphdr->chaddr ) ) {
  399. hlen = 0;
  400. dhcphdr->flags = htons ( BOOTP_FL_BROADCAST );
  401. }
  402. dhcphdr->hlen = hlen;
  403. memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
  404. memcpy ( dhcphdr->options, options->data, options_len );
  405. /* Initialise DHCP packet structure */
  406. memset ( dhcppkt, 0, sizeof ( *dhcppkt ) );
  407. dhcppkt_init ( dhcppkt, data, max_len );
  408. /* Set DHCP_MESSAGE_TYPE option */
  409. if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE,
  410. &msgtype, sizeof ( msgtype ) ) ) != 0 )
  411. return rc;
  412. return 0;
  413. }
  414. /**
  415. * Create DHCP request packet
  416. *
  417. * @v dhcppkt DHCP packet structure to fill in
  418. * @v netdev Network device
  419. * @v msgtype DHCP message type
  420. * @v ciaddr Client IP address, if applicable
  421. * @v server Server identifier, if applicable
  422. * @v requested_ip Requested address, if applicable
  423. * @v menu_item PXE menu item, if applicable
  424. * @v data Buffer for DHCP packet
  425. * @v max_len Size of DHCP packet buffer
  426. * @ret rc Return status code
  427. */
  428. int dhcp_create_request ( struct dhcp_packet *dhcppkt,
  429. struct net_device *netdev, unsigned int msgtype,
  430. struct in_addr ciaddr, struct in_addr server,
  431. struct in_addr requested_ip,
  432. struct dhcp_pxe_boot_menu_item *menu_item,
  433. void *data, size_t max_len ) {
  434. struct device_description *desc = &netdev->dev->desc;
  435. struct dhcp_netdev_desc dhcp_desc;
  436. struct dhcp_client_id client_id;
  437. struct dhcp_client_uuid client_uuid;
  438. size_t dhcp_features_len;
  439. size_t ll_addr_len;
  440. int rc;
  441. /* Create DHCP packet */
  442. if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype,
  443. &dhcp_request_options, data,
  444. max_len ) ) != 0 ) {
  445. DBG ( "DHCP could not create DHCP packet: %s\n",
  446. strerror ( rc ) );
  447. return rc;
  448. }
  449. /* Set client IP address */
  450. dhcppkt->dhcphdr->ciaddr = ciaddr;
  451. /* Set server ID, if present */
  452. if ( server.s_addr &&
  453. ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
  454. &server, sizeof ( server ) ) ) != 0 ) ) {
  455. DBG ( "DHCP could not set server ID: %s\n",
  456. strerror ( rc ) );
  457. return rc;
  458. }
  459. /* Set requested IP address, if present */
  460. if ( requested_ip.s_addr &&
  461. ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
  462. &requested_ip,
  463. sizeof ( requested_ip ) ) ) != 0 ) ) {
  464. DBG ( "DHCP could not set requested address: %s\n",
  465. strerror ( rc ) );
  466. return rc;
  467. }
  468. /* Add options to identify the feature list */
  469. dhcp_features_len = ( dhcp_features_end - dhcp_features );
  470. if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features,
  471. dhcp_features_len ) ) != 0 ) {
  472. DBG ( "DHCP could not set features list option: %s\n",
  473. strerror ( rc ) );
  474. return rc;
  475. }
  476. /* Add options to identify the network device */
  477. dhcp_desc.type = desc->bus_type;
  478. dhcp_desc.vendor = htons ( desc->vendor );
  479. dhcp_desc.device = htons ( desc->device );
  480. if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc,
  481. sizeof ( dhcp_desc ) ) ) != 0 ) {
  482. DBG ( "DHCP could not set bus ID option: %s\n",
  483. strerror ( rc ) );
  484. return rc;
  485. }
  486. /* Add DHCP client identifier. Required for Infiniband, and
  487. * doesn't hurt other link layers.
  488. */
  489. client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto );
  490. ll_addr_len = netdev->ll_protocol->ll_addr_len;
  491. assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) );
  492. memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len );
  493. if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id,
  494. ( ll_addr_len + 1 ) ) ) != 0 ) {
  495. DBG ( "DHCP could not set client ID: %s\n",
  496. strerror ( rc ) );
  497. return rc;
  498. }
  499. /* Add client UUID, if we have one. Required for PXE. */
  500. client_uuid.type = DHCP_CLIENT_UUID_TYPE;
  501. if ( ( rc = fetch_uuid_setting ( NULL, &uuid_setting,
  502. &client_uuid.uuid ) ) >= 0 ) {
  503. if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID,
  504. &client_uuid,
  505. sizeof ( client_uuid ) ) ) != 0 ) {
  506. DBG ( "DHCP could not set client UUID: %s\n",
  507. strerror ( rc ) );
  508. return rc;
  509. }
  510. }
  511. /* Set PXE boot menu item, if present */
  512. if ( menu_item && menu_item->type &&
  513. ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
  514. menu_item,
  515. sizeof ( *menu_item ) ) ) != 0 ) ) {
  516. DBG ( "DHCP could not set PXE menu item: %s\n",
  517. strerror ( rc ) );
  518. return rc;
  519. }
  520. return 0;
  521. }
  522. /**
  523. * Transmit DHCP request
  524. *
  525. * @v dhcp DHCP session
  526. * @ret rc Return status code
  527. */
  528. static int dhcp_tx ( struct dhcp_session *dhcp ) {
  529. static struct sockaddr_in dest = {
  530. .sin_family = AF_INET,
  531. .sin_port = htons ( PROXYDHCP_PORT ),
  532. };
  533. static struct sockaddr_in src = {
  534. .sin_family = AF_INET,
  535. .sin_port = htons ( BOOTPC_PORT ),
  536. };
  537. struct xfer_metadata meta = {
  538. .netdev = dhcp->netdev,
  539. };
  540. struct io_buffer *iobuf;
  541. struct dhcp_packet dhcppkt;
  542. struct in_addr ciaddr = { 0 };
  543. struct in_addr server = { 0 };
  544. struct in_addr requested_ip = { 0 };
  545. struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
  546. unsigned int msgtype;
  547. int rc;
  548. /* Start retry timer. Do this first so that failures to
  549. * transmit will be retried.
  550. */
  551. start_timer ( &dhcp->timer );
  552. /* Determine packet contents based on current state */
  553. switch ( dhcp->state ) {
  554. case DHCP_STATE_DISCOVER:
  555. msgtype = DHCPDISCOVER;
  556. break;
  557. case DHCP_STATE_REQUEST:
  558. assert ( dhcp->dhcpoffer );
  559. msgtype = DHCPREQUEST;
  560. dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt,
  561. DHCP_SERVER_IDENTIFIER, &server,
  562. sizeof ( server ) );
  563. requested_ip = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
  564. break;
  565. case DHCP_STATE_PROXYREQUEST:
  566. assert ( dhcp->dhcpoffer );
  567. assert ( dhcp->proxydhcpoffer );
  568. assert ( dhcp->dhcpack );
  569. msgtype = DHCPREQUEST;
  570. ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
  571. dhcppkt_fetch ( &dhcp->proxydhcpoffer->dhcppkt,
  572. DHCP_SERVER_IDENTIFIER, &dest.sin_addr,
  573. sizeof ( dest.sin_addr ) );
  574. meta.dest = ( struct sockaddr * ) &dest;
  575. server = dest.sin_addr;
  576. assert ( dest.sin_addr.s_addr );
  577. assert ( ciaddr.s_addr );
  578. break;
  579. case DHCP_STATE_BSREQUEST:
  580. assert ( dhcp->dhcpoffer );
  581. assert ( dhcp->proxydhcpoffer );
  582. assert ( dhcp->dhcpack );
  583. assert ( dhcp->proxydhcpack );
  584. msgtype = DHCPREQUEST;
  585. ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
  586. dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt,
  587. DHCP_PXE_BOOT_SERVER_MCAST,
  588. &dest.sin_addr, sizeof ( dest.sin_addr ) );
  589. meta.dest = ( struct sockaddr * ) &dest;
  590. dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt,
  591. DHCP_PXE_BOOT_MENU, &menu_item.type,
  592. sizeof ( menu_item.type ) );
  593. assert ( dest.sin_addr.s_addr );
  594. assert ( menu_item.type );
  595. assert ( ciaddr.s_addr );
  596. break;
  597. default:
  598. assert ( 0 );
  599. return -EINVAL;
  600. }
  601. DBGC ( dhcp, "DHCP %p %s", dhcp, dhcp_msgtype_name ( msgtype ) );
  602. if ( server.s_addr )
  603. DBGC ( dhcp, " to %s", inet_ntoa ( server ) );
  604. if ( meta.dest ) {
  605. if ( dest.sin_addr.s_addr == server.s_addr ) {
  606. DBGC ( dhcp, ":%d (unicast)",
  607. ntohs ( dest.sin_port ) );
  608. } else {
  609. DBGC ( dhcp, " via %s:%d", inet_ntoa ( dest.sin_addr ),
  610. ntohs ( dest.sin_port ) );
  611. }
  612. } else {
  613. DBGC ( dhcp, " (broadcast)" );
  614. }
  615. if ( requested_ip.s_addr )
  616. DBGC ( dhcp, " for %s", inet_ntoa ( requested_ip ) );
  617. if ( menu_item.type )
  618. DBGC ( dhcp, " for item %04x", ntohs ( menu_item.type ) );
  619. DBGC ( dhcp, "\n" );
  620. /* Allocate buffer for packet */
  621. iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
  622. if ( ! iobuf )
  623. return -ENOMEM;
  624. /* Create DHCP packet in temporary buffer */
  625. if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype,
  626. ciaddr, server, requested_ip,
  627. &menu_item, iobuf->data,
  628. iob_tailroom ( iobuf ) ) ) != 0 ) {
  629. DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
  630. dhcp, strerror ( rc ) );
  631. goto done;
  632. }
  633. /* Explicitly specify source address, if available. */
  634. if ( ciaddr.s_addr ) {
  635. src.sin_addr = ciaddr;
  636. meta.src = ( struct sockaddr * ) &src;
  637. }
  638. /* Transmit the packet */
  639. iob_put ( iobuf, dhcppkt.len );
  640. rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
  641. iobuf = NULL;
  642. if ( rc != 0 ) {
  643. DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
  644. dhcp, strerror ( rc ) );
  645. goto done;
  646. }
  647. done:
  648. free_iob ( iobuf );
  649. return rc;
  650. }
  651. /**
  652. * Transition to new DHCP session state
  653. *
  654. * @v dhcp DHCP session
  655. * @v state New session state
  656. */
  657. static void dhcp_set_state ( struct dhcp_session *dhcp,
  658. enum dhcp_session_state state ) {
  659. DBGC ( dhcp, "DHCP %p entering %s state\n",
  660. dhcp, dhcp_state_name ( state ) );
  661. dhcp->state = state;
  662. dhcp->start = currticks();
  663. dhcp->timer.min_timeout = 0;
  664. start_timer_nodelay ( &dhcp->timer );
  665. }
  666. /**
  667. * Transition to next DHCP state
  668. *
  669. * @v dhcp DHCP session
  670. */
  671. static void dhcp_next_state ( struct dhcp_session *dhcp ) {
  672. struct in_addr bs_mcast = { 0 };
  673. switch ( dhcp->state ) {
  674. case DHCP_STATE_DISCOVER:
  675. dhcp_set_state ( dhcp, DHCP_STATE_REQUEST );
  676. break;
  677. case DHCP_STATE_REQUEST:
  678. if ( dhcp->proxydhcpoffer ) {
  679. /* Store DHCPACK as ProxyDHCPACK. This
  680. * handles the case in which the DHCP server
  681. * itself responds with "PXEClient" and PXE
  682. * options but there is no actual ProxyDHCP
  683. * server resident on the machine.
  684. */
  685. dhcp->proxydhcpack = dhcpset_get ( dhcp->dhcpack );
  686. dhcp_set_state ( dhcp, DHCP_STATE_PROXYREQUEST );
  687. break;
  688. }
  689. /* Fall through */
  690. case DHCP_STATE_PROXYREQUEST:
  691. if ( dhcp->proxydhcpack ) {
  692. dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt,
  693. DHCP_PXE_BOOT_SERVER_MCAST,
  694. &bs_mcast, sizeof ( bs_mcast ) );
  695. if ( bs_mcast.s_addr ) {
  696. dhcp_set_state ( dhcp, DHCP_STATE_BSREQUEST );
  697. break;
  698. }
  699. }
  700. /* Fall through */
  701. case DHCP_STATE_BSREQUEST:
  702. dhcp_finished ( dhcp, 0 );
  703. break;
  704. default:
  705. assert ( 0 );
  706. return;
  707. }
  708. }
  709. /**
  710. * Store received DHCPOFFER
  711. *
  712. * @v dhcp DHCP session
  713. * @v dhcpoffer Received DHCPOFFER
  714. * @v stored_dhcpoffer Location to store DHCPOFFER
  715. *
  716. * The DHCPOFFER will be stored in place of the existing stored
  717. * DHCPOFFER if its priority is equal to or greater than the stored
  718. * DHCPOFFER.
  719. */
  720. static void dhcp_store_dhcpoffer ( struct dhcp_session *dhcp,
  721. struct dhcp_settings *dhcpoffer,
  722. struct dhcp_settings **stored_dhcpoffer ) {
  723. uint8_t stored_priority = 0;
  724. uint8_t priority = 0;
  725. /* Get priorities of the two DHCPOFFERs */
  726. if ( *stored_dhcpoffer ) {
  727. dhcppkt_fetch ( &(*stored_dhcpoffer)->dhcppkt,
  728. DHCP_EB_PRIORITY, &stored_priority,
  729. sizeof ( stored_priority ) );
  730. }
  731. dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_EB_PRIORITY, &priority,
  732. sizeof ( priority ) );
  733. /* Replace stored offer only if priority is equal or greater */
  734. if ( priority >= stored_priority ) {
  735. if ( *stored_dhcpoffer ) {
  736. DBGC ( dhcp, "DHCP %p stored DHCPOFFER %p discarded\n",
  737. dhcp, *stored_dhcpoffer );
  738. }
  739. DBGC ( dhcp, "DHCP %p DHCPOFFER %p stored\n",
  740. dhcp, dhcpoffer );
  741. dhcpset_put ( *stored_dhcpoffer );
  742. *stored_dhcpoffer = dhcpset_get ( dhcpoffer );
  743. }
  744. }
  745. /**
  746. * Handle received DHCPOFFER
  747. *
  748. * @v dhcp DHCP session
  749. * @v dhcpoffer Received DHCPOFFER
  750. */
  751. static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
  752. struct dhcp_settings *dhcpoffer ) {
  753. struct in_addr server_id = { 0 };
  754. char vci[9]; /* "PXEClient" */
  755. int len;
  756. uint8_t ignore_proxy = 0;
  757. unsigned long elapsed;
  758. /* Check for presence of DHCP server ID */
  759. if ( dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER,
  760. &server_id, sizeof ( server_id ) )
  761. != sizeof ( server_id ) ) {
  762. DBGC ( dhcp, "DHCP %p DHCPOFFER %p missing server ID\n",
  763. dhcp, dhcpoffer );
  764. /* Could be a valid BOOTP offer; do not abort processing */
  765. }
  766. /* If there is an IP address, it's a normal DHCPOFFER */
  767. if ( dhcpoffer->dhcppkt.dhcphdr->yiaddr.s_addr != 0 ) {
  768. DBGC ( dhcp, "DHCP %p DHCPOFFER %p from %s",
  769. dhcp, dhcpoffer, inet_ntoa ( server_id ) );
  770. DBGC ( dhcp, " has IP %s\n",
  771. inet_ntoa ( dhcpoffer->dhcppkt.dhcphdr->yiaddr ) );
  772. dhcp_store_dhcpoffer ( dhcp, dhcpoffer, &dhcp->dhcpoffer );
  773. }
  774. /* If there is a "PXEClient" vendor class ID, it's a
  775. * ProxyDHCPOFFER. Note that it could be both a normal
  776. * DHCPOFFER and a ProxyDHCPOFFER.
  777. */
  778. len = dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_VENDOR_CLASS_ID,
  779. vci, sizeof ( vci ) );
  780. if ( ( server_id.s_addr != 0 ) &&
  781. ( len >= ( int ) sizeof ( vci ) ) &&
  782. ( strncmp ( "PXEClient", vci, sizeof ( vci ) ) == 0 ) ) {
  783. DBGC ( dhcp, "DHCP %p DHCPOFFER %p from %s is a "
  784. "ProxyDHCPOFFER\n",
  785. dhcp, dhcpoffer, inet_ntoa ( server_id ) );
  786. dhcp_store_dhcpoffer ( dhcp, dhcpoffer,
  787. &dhcp->proxydhcpoffer );
  788. }
  789. /* We can transition to making the DHCPREQUEST when we have a
  790. * valid DHCPOFFER, and either:
  791. *
  792. * o The DHCPOFFER instructs us to not wait for ProxyDHCP, or
  793. * o We have a valid ProxyDHCPOFFER, or
  794. * o We have allowed sufficient time for ProxyDHCPOFFERs.
  795. */
  796. /* If we don't yet have a DHCPOFFER, do nothing */
  797. if ( ! dhcp->dhcpoffer )
  798. return;
  799. /* If the DHCPOFFER instructs us to ignore ProxyDHCP, discard
  800. * any ProxyDHCPOFFER
  801. */
  802. dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt, DHCP_EB_NO_PROXYDHCP,
  803. &ignore_proxy, sizeof ( ignore_proxy ) );
  804. if ( ignore_proxy && dhcp->proxydhcpoffer ) {
  805. DBGC ( dhcp, "DHCP %p discarding ProxyDHCPOFFER\n", dhcp );
  806. dhcpset_put ( dhcp->proxydhcpoffer );
  807. dhcp->proxydhcpoffer = NULL;
  808. }
  809. /* If we can't yet transition to DHCPREQUEST, do nothing */
  810. elapsed = ( currticks() - dhcp->start );
  811. if ( ! ( ignore_proxy || dhcp->proxydhcpoffer ||
  812. ( elapsed > PROXYDHCP_WAIT_TIME ) ) )
  813. return;
  814. /* Transition to DHCPREQUEST */
  815. dhcp_next_state ( dhcp );
  816. }
  817. /**
  818. * Store received DHCPACK
  819. *
  820. * @v dhcp DHCP session
  821. * @v dhcpack Received DHCPACK
  822. *
  823. * The DHCPACK will be registered as a settings block.
  824. */
  825. static int dhcp_store_dhcpack ( struct dhcp_session *dhcp,
  826. struct dhcp_settings *dhcpack,
  827. struct settings *parent ) {
  828. struct settings *settings = &dhcpack->settings;
  829. struct settings *old_settings;
  830. int rc;
  831. /* Unregister any old settings obtained via DHCP */
  832. if ( ( old_settings = find_child_settings ( parent, settings->name ) ))
  833. unregister_settings ( old_settings );
  834. /* Register new settings */
  835. if ( ( rc = register_settings ( settings, parent ) ) != 0 ) {
  836. DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
  837. dhcp, strerror ( rc ) );
  838. dhcp_finished ( dhcp, rc ); /* This is a fatal error */
  839. return rc;
  840. }
  841. return 0;
  842. }
  843. /**
  844. * Handle received DHCPACK
  845. *
  846. * @v dhcp DHCP session
  847. * @v dhcpack Received DHCPACK
  848. */
  849. static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp,
  850. struct dhcp_settings *dhcpack ) {
  851. struct settings *parent;
  852. struct in_addr offer_server_id = { 0 };
  853. struct in_addr ack_server_id = { 0 };
  854. int rc;
  855. /* Verify server ID matches */
  856. assert ( dhcp->dhcpoffer != NULL );
  857. dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER,
  858. &offer_server_id, sizeof ( offer_server_id ) );
  859. dhcppkt_fetch ( &dhcpack->dhcppkt, DHCP_SERVER_IDENTIFIER,
  860. &ack_server_id, sizeof ( ack_server_id ) );
  861. if ( offer_server_id.s_addr != ack_server_id.s_addr ) {
  862. DBGC ( dhcp, "DHCP %p ignoring DHCPACK with wrong server ID "
  863. "%s\n", dhcp, inet_ntoa ( ack_server_id ) );
  864. return;
  865. }
  866. /* Record DHCPACK */
  867. assert ( dhcp->dhcpack == NULL );
  868. dhcp->dhcpack = dhcpset_get ( dhcpack );
  869. /* Register settings */
  870. parent = netdev_settings ( dhcp->netdev );
  871. if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) != 0 )
  872. return;
  873. /* Transition to next state */
  874. dhcp_next_state ( dhcp );
  875. }
  876. /**
  877. * Handle received ProxyDHCPACK
  878. *
  879. * @v dhcp DHCP session
  880. * @v proxydhcpack Received ProxyDHCPACK
  881. */
  882. static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
  883. struct dhcp_settings *proxydhcpack ) {
  884. struct in_addr offer_server_id = { 0 };
  885. struct in_addr ack_server_id = { 0 };
  886. int rc;
  887. /* Verify server ID matches, if present */
  888. assert ( dhcp->proxydhcpoffer != NULL );
  889. if ( ( rc = dhcppkt_fetch ( &proxydhcpack->dhcppkt,
  890. DHCP_SERVER_IDENTIFIER, &ack_server_id,
  891. sizeof ( ack_server_id ) ) ) > 0 ) {
  892. dhcppkt_fetch ( &dhcp->proxydhcpoffer->dhcppkt,
  893. DHCP_SERVER_IDENTIFIER, &offer_server_id,
  894. sizeof ( offer_server_id ) );
  895. if ( offer_server_id.s_addr != ack_server_id.s_addr ) {
  896. DBGC ( dhcp, "DHCP %p ignoring ProxyDHCPACK with "
  897. "wrong server ID %s\n",
  898. dhcp, inet_ntoa ( ack_server_id ) );
  899. return;
  900. }
  901. }
  902. /* Rename settings */
  903. proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME;
  904. /* Record ProxyDHCPACK */
  905. dhcpset_put ( dhcp->proxydhcpack );
  906. dhcp->proxydhcpack = dhcpset_get ( proxydhcpack );
  907. /* Register settings */
  908. if ( ( rc = dhcp_store_dhcpack ( dhcp, proxydhcpack, NULL ) ) != 0 )
  909. return;
  910. /* Transition to next state */
  911. dhcp_next_state ( dhcp );
  912. }
  913. /**
  914. * Handle received BootServerDHCPACK
  915. *
  916. * @v dhcp DHCP session
  917. * @v bsdhcpack Received BootServerDHCPACK
  918. */
  919. static void dhcp_rx_bsdhcpack ( struct dhcp_session *dhcp,
  920. struct dhcp_settings *bsdhcpack ) {
  921. int rc;
  922. /* Rename settings */
  923. bsdhcpack->settings.name = BSDHCP_SETTINGS_NAME;
  924. /* Record ProxyDHCPACK */
  925. assert ( dhcp->bsdhcpack == NULL );
  926. dhcp->bsdhcpack = dhcpset_get ( bsdhcpack );
  927. /* Register settings */
  928. if ( ( rc = dhcp_store_dhcpack ( dhcp, bsdhcpack, NULL ) ) != 0 )
  929. return;
  930. /* Transition to next state */
  931. dhcp_next_state ( dhcp );
  932. }
  933. /**
  934. * Receive new data
  935. *
  936. * @v xfer Data transfer interface
  937. * @v iobuf I/O buffer
  938. * @v meta Transfer metadata
  939. * @ret rc Return status code
  940. */
  941. static int dhcp_deliver_iob ( struct xfer_interface *xfer,
  942. struct io_buffer *iobuf,
  943. struct xfer_metadata *meta ) {
  944. struct dhcp_session *dhcp =
  945. container_of ( xfer, struct dhcp_session, xfer );
  946. struct sockaddr_in *sin_src;
  947. unsigned int src_port;
  948. struct dhcp_settings *dhcpset;
  949. struct dhcphdr *dhcphdr;
  950. uint8_t msgtype = 0;
  951. int rc = 0;
  952. /* Sanity checks */
  953. if ( ! meta ) {
  954. DBGC ( dhcp, "DHCP %p received packet without metadata\n",
  955. dhcp );
  956. rc = -EINVAL;
  957. goto err_no_meta;
  958. }
  959. if ( ! meta->src ) {
  960. DBGC ( dhcp, "DHCP %p received packet without source port\n",
  961. dhcp );
  962. rc = -EINVAL;
  963. goto err_no_src;
  964. }
  965. sin_src = ( struct sockaddr_in * ) meta->src;
  966. src_port = sin_src->sin_port;
  967. /* Convert packet into a DHCP settings block */
  968. dhcpset = dhcpset_create ( iobuf->data, iob_len ( iobuf ) );
  969. if ( ! dhcpset ) {
  970. DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp );
  971. rc = -ENOMEM;
  972. goto err_dhcpset_create;
  973. }
  974. dhcphdr = dhcpset->dhcppkt.dhcphdr;
  975. /* Identify message type */
  976. dhcppkt_fetch ( &dhcpset->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
  977. sizeof ( msgtype ) );
  978. DBGC ( dhcp, "DHCP %p %s %p from %s:%d\n", dhcp,
  979. dhcp_msgtype_name ( msgtype ), dhcpset,
  980. inet_ntoa ( sin_src->sin_addr ), ntohs ( src_port ) );
  981. /* Check for matching transaction ID */
  982. if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
  983. DBGC ( dhcp, "DHCP %p %s %p has bad transaction ID\n",
  984. dhcp, dhcp_msgtype_name ( msgtype ), dhcpset );
  985. rc = -EINVAL;
  986. goto err_xid;
  987. };
  988. /* Handle packet based on current state */
  989. switch ( dhcp->state ) {
  990. case DHCP_STATE_DISCOVER:
  991. if ( ( ( msgtype == DHCPOFFER ) || ( msgtype == DHCPNONE ) ) &&
  992. ( src_port == htons ( BOOTPS_PORT ) ) )
  993. dhcp_rx_dhcpoffer ( dhcp, dhcpset );
  994. break;
  995. case DHCP_STATE_REQUEST:
  996. if ( ( ( msgtype == DHCPACK ) || ( msgtype == DHCPNONE ) ) &&
  997. ( src_port == htons ( BOOTPS_PORT ) ) )
  998. dhcp_rx_dhcpack ( dhcp, dhcpset );
  999. break;
  1000. case DHCP_STATE_PROXYREQUEST:
  1001. if ( ( msgtype == DHCPACK ) &&
  1002. ( src_port == htons ( PROXYDHCP_PORT ) ) )
  1003. dhcp_rx_proxydhcpack ( dhcp, dhcpset );
  1004. break;
  1005. case DHCP_STATE_BSREQUEST:
  1006. if ( ( msgtype == DHCPACK ) &&
  1007. ( src_port == htons ( PROXYDHCP_PORT ) ) )
  1008. dhcp_rx_bsdhcpack ( dhcp, dhcpset );
  1009. break;
  1010. default:
  1011. assert ( 0 );
  1012. break;
  1013. }
  1014. err_xid:
  1015. dhcpset_put ( dhcpset );
  1016. err_dhcpset_create:
  1017. err_no_src:
  1018. err_no_meta:
  1019. free_iob ( iobuf );
  1020. return rc;
  1021. }
  1022. /** DHCP data transfer interface operations */
  1023. static struct xfer_interface_operations dhcp_xfer_operations = {
  1024. .close = ignore_xfer_close,
  1025. .vredirect = xfer_vopen,
  1026. .window = unlimited_xfer_window,
  1027. .alloc_iob = default_xfer_alloc_iob,
  1028. .deliver_iob = dhcp_deliver_iob,
  1029. .deliver_raw = xfer_deliver_as_iob,
  1030. };
  1031. /**
  1032. * Handle DHCP retry timer expiry
  1033. *
  1034. * @v timer DHCP retry timer
  1035. * @v fail Failure indicator
  1036. */
  1037. static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
  1038. struct dhcp_session *dhcp =
  1039. container_of ( timer, struct dhcp_session, timer );
  1040. unsigned long elapsed = ( currticks() - dhcp->start );
  1041. /* If we have failed, terminate DHCP */
  1042. if ( fail ) {
  1043. dhcp_finished ( dhcp, -ETIMEDOUT );
  1044. return;
  1045. }
  1046. /* Give up waiting for ProxyDHCP before we reach the failure point */
  1047. if ( dhcp->dhcpoffer && ( elapsed > PROXYDHCP_WAIT_TIME ) ) {
  1048. dhcp_next_state ( dhcp );
  1049. return;
  1050. }
  1051. /* Otherwise, retransmit current packet */
  1052. dhcp_tx ( dhcp );
  1053. }
  1054. /****************************************************************************
  1055. *
  1056. * Job control interface
  1057. *
  1058. */
  1059. /**
  1060. * Handle kill() event received via job control interface
  1061. *
  1062. * @v job DHCP job control interface
  1063. */
  1064. static void dhcp_job_kill ( struct job_interface *job ) {
  1065. struct dhcp_session *dhcp =
  1066. container_of ( job, struct dhcp_session, job );
  1067. /* Terminate DHCP session */
  1068. dhcp_finished ( dhcp, -ECANCELED );
  1069. }
  1070. /** DHCP job control interface operations */
  1071. static struct job_interface_operations dhcp_job_operations = {
  1072. .done = ignore_job_done,
  1073. .kill = dhcp_job_kill,
  1074. .progress = ignore_job_progress,
  1075. };
  1076. /****************************************************************************
  1077. *
  1078. * Instantiator
  1079. *
  1080. */
  1081. /**
  1082. * Start DHCP on a network device
  1083. *
  1084. * @v job Job control interface
  1085. * @v netdev Network device
  1086. * @v register_options DHCP option block registration routine
  1087. * @ret rc Return status code
  1088. *
  1089. * Starts DHCP on the specified network device. If successful, the @c
  1090. * register_options() routine will be called with the acquired
  1091. * options.
  1092. */
  1093. int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
  1094. static struct sockaddr_in server = {
  1095. .sin_family = AF_INET,
  1096. .sin_addr.s_addr = INADDR_BROADCAST,
  1097. .sin_port = htons ( BOOTPS_PORT ),
  1098. };
  1099. static struct sockaddr_in client = {
  1100. .sin_family = AF_INET,
  1101. .sin_port = htons ( BOOTPC_PORT ),
  1102. };
  1103. struct dhcp_session *dhcp;
  1104. int rc;
  1105. /* Allocate and initialise structure */
  1106. dhcp = zalloc ( sizeof ( *dhcp ) );
  1107. if ( ! dhcp )
  1108. return -ENOMEM;
  1109. dhcp->refcnt.free = dhcp_free;
  1110. job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
  1111. xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
  1112. dhcp->netdev = netdev_get ( netdev );
  1113. dhcp->timer.expired = dhcp_timer_expired;
  1114. dhcp->timer.min_timeout = DHCP_MIN_TIMEOUT;
  1115. dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT;
  1116. dhcp->start = currticks();
  1117. /* Instantiate child objects and attach to our interfaces */
  1118. if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM,
  1119. ( struct sockaddr * ) &server,
  1120. ( struct sockaddr * ) &client ) ) != 0 )
  1121. goto err;
  1122. /* Start timer to initiate initial DHCPREQUEST */
  1123. start_timer_nodelay ( &dhcp->timer );
  1124. /* Attach parent interface, mortalise self, and return */
  1125. job_plug_plug ( &dhcp->job, job );
  1126. ref_put ( &dhcp->refcnt );
  1127. return 0;
  1128. err:
  1129. dhcp_finished ( dhcp, rc );
  1130. ref_put ( &dhcp->refcnt );
  1131. return rc;
  1132. }