c7e1f8864d2e70ae1369c0f6a0ecb93806579860
[people/asdlkf/gpxe.git] / src / net / udp / dhcp.c
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
19 #include <string.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <byteswap.h>
26 #include <console.h>
27 #include <gpxe/if_ether.h>
28 #include <gpxe/netdevice.h>
29 #include <gpxe/device.h>
30 #include <gpxe/xfer.h>
31 #include <gpxe/open.h>
32 #include <gpxe/job.h>
33 #include <gpxe/retry.h>
34 #include <gpxe/tcpip.h>
35 #include <gpxe/ip.h>
36 #include <gpxe/uuid.h>
37 #include <gpxe/dhcp.h>
38 #include <gpxe/timer.h>
39 #include <gpxe/settings.h>
40 #include <gpxe/dhcp.h>
41 #include <gpxe/dhcpopts.h>
42 #include <gpxe/dhcppkt.h>
43 #include <gpxe/features.h>
44 #include <gpxe/keys.h>
45
46 /** @file
47  *
48  * Dynamic Host Configuration Protocol
49  *
50  */
51
52 /**
53  * DHCP operation types
54  *
55  * This table maps from DHCP message types (i.e. values of the @c
56  * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP
57  * packet.
58  */
59 static const uint8_t dhcp_op[] = {
60         [DHCPDISCOVER]  = BOOTP_REQUEST,
61         [DHCPOFFER]     = BOOTP_REPLY,
62         [DHCPREQUEST]   = BOOTP_REQUEST,
63         [DHCPDECLINE]   = BOOTP_REQUEST,
64         [DHCPACK]       = BOOTP_REPLY,
65         [DHCPNAK]       = BOOTP_REPLY,
66         [DHCPRELEASE]   = BOOTP_REQUEST,
67         [DHCPINFORM]    = BOOTP_REQUEST,
68 };
69
70 /** Raw option data for options common to all DHCP requests */
71 static uint8_t dhcp_request_options_data[] = {
72         DHCP_MAX_MESSAGE_SIZE,
73         DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ),
74         DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ),
75         DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ),
76         DHCP_VENDOR_CLASS_ID,
77         DHCP_STRING (  'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':',
78                        'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':',
79                        'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ),
80         DHCP_PARAMETER_REQUEST_LIST,
81         DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS,
82                       DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME,
83                       DHCP_ROOT_PATH, DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID,
84                       DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME,
85                       DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ),
86         DHCP_END
87 };
88
89 /** Options common to all DHCP requests */
90 static struct dhcp_options dhcp_request_options = {
91         .data = dhcp_request_options_data,
92         .max_len = sizeof ( dhcp_request_options_data ),
93         .len = sizeof ( dhcp_request_options_data ),
94 };
95
96 /** DHCP feature codes */
97 static uint8_t dhcp_features[0] __table_start ( uint8_t, dhcp_features );
98 static uint8_t dhcp_features_end[0] __table_end ( uint8_t, dhcp_features );
99
100 /** Version number feature */
101 FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH );
102
103 /** DHCP network device descriptor */
104 struct dhcp_netdev_desc {
105         /** Bus type ID */
106         uint8_t type;
107         /** Vendor ID */
108         uint16_t vendor;
109         /** Device ID */
110         uint16_t device;
111 } __attribute__ (( packed ));
112
113 /** DHCP client identifier */
114 struct dhcp_client_id {
115         /** Link-layer protocol */
116         uint8_t ll_proto;
117         /** Link-layer address */
118         uint8_t ll_addr[MAX_LL_ADDR_LEN];
119 } __attribute__ (( packed ));
120
121 /** DHCP client UUID */
122 struct dhcp_client_uuid {
123         /** Identifier type */
124         uint8_t type;
125         /** UUID */
126         union uuid uuid;
127 } __attribute__ (( packed ));
128
129 #define DHCP_CLIENT_UUID_TYPE 0
130
131 /** DHCP PXE boot prompt */
132 struct dhcp_pxe_boot_prompt {
133         /** Timeout
134          *
135          * A value of 0 means "time out immediately and select first
136          * boot item, without displaying the prompt".  A value of 255
137          * means "display menu immediately with no timeout".  Any
138          * other value means "display prompt, wait this many seconds
139          * for keypress, if key is F8, display menu, otherwise select
140          * first boot item".
141          */
142         uint8_t timeout;
143         /** Prompt to press F8 */
144         char prompt[0];
145 } __attribute__ (( packed ));
146
147 /** DHCP PXE boot menu item description */
148 struct dhcp_pxe_boot_menu_item_desc {
149         /** "Type" */
150         uint16_t type;
151         /** Description length */
152         uint8_t desc_len;
153         /** Description */
154         char desc[0];
155 } __attribute__ (( packed ));
156
157 /** DHCP PXE boot menu item */
158 struct dhcp_pxe_boot_menu_item {
159         /** "Type"
160          *
161          * This field actually identifies the specific boot server (or
162          * cluster of boot servers offering identical boot files).
163          */
164         uint16_t type;
165         /** "Layer"
166          *
167          * Just don't ask.
168          */
169         uint16_t layer;
170 } __attribute__ (( packed ));
171
172 /** Maximum allowed number of PXE boot menu items */
173 #define PXE_BOOT_MENU_MAX_ITEMS 20
174
175 /**
176  * Name a DHCP packet type
177  *
178  * @v msgtype           DHCP message type
179  * @ret string          DHCP mesasge type name
180  */
181 static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) {
182         switch ( msgtype ) {
183         case DHCPNONE:          return "BOOTP"; /* Non-DHCP packet */
184         case DHCPDISCOVER:      return "DHCPDISCOVER";
185         case DHCPOFFER:         return "DHCPOFFER";
186         case DHCPREQUEST:       return "DHCPREQUEST";
187         case DHCPDECLINE:       return "DHCPDECLINE";
188         case DHCPACK:           return "DHCPACK";
189         case DHCPNAK:           return "DHCPNAK";
190         case DHCPRELEASE:       return "DHCPRELEASE";
191         case DHCPINFORM:        return "DHCPINFORM";
192         default:                return "DHCP<invalid>";
193         }
194 }
195
196 /**
197  * Calculate DHCP transaction ID for a network device
198  *
199  * @v netdev            Network device
200  * @ret xid             DHCP XID
201  *
202  * Extract the least significant bits of the hardware address for use
203  * as the transaction ID.
204  */
205 static uint32_t dhcp_xid ( struct net_device *netdev ) {
206         uint32_t xid;
207
208         memcpy ( &xid, ( netdev->ll_addr + netdev->ll_protocol->ll_addr_len
209                          - sizeof ( xid ) ), sizeof ( xid ) );
210         return xid;
211 }
212
213 /****************************************************************************
214  *
215  * DHCP settings
216  *
217  */
218
219 /** A DHCP settings block */
220 struct dhcp_settings {
221         /** Reference counter */
222         struct refcnt refcnt;
223         /** DHCP packet */
224         struct dhcp_packet dhcppkt;
225         /** Setting interface */
226         struct settings settings;
227 };
228
229 /**
230  * Increment reference count on DHCP settings block
231  *
232  * @v dhcpset           DHCP settings block
233  * @ret dhcpset         DHCP settings block
234  */
235 static inline __attribute__ (( always_inline )) struct dhcp_settings *
236 dhcpset_get ( struct dhcp_settings *dhcpset ) {
237         ref_get ( &dhcpset->refcnt );
238         return dhcpset;
239 }
240
241 /**
242  * Decrement reference count on DHCP settings block
243  *
244  * @v dhcpset           DHCP settings block
245  */
246 static inline __attribute__ (( always_inline )) void
247 dhcpset_put ( struct dhcp_settings *dhcpset ) {
248         ref_put ( &dhcpset->refcnt );
249 }
250
251 /**
252  * Store value of DHCP setting
253  *
254  * @v settings          Settings block
255  * @v setting           Setting to store
256  * @v data              Setting data, or NULL to clear setting
257  * @v len               Length of setting data
258  * @ret rc              Return status code
259  */
260 static int dhcpset_store ( struct settings *settings, struct setting *setting,
261                            const void *data, size_t len ) {
262         struct dhcp_settings *dhcpset =
263                 container_of ( settings, struct dhcp_settings, settings );
264
265         return dhcppkt_store ( &dhcpset->dhcppkt, setting->tag, data, len );
266 }
267
268 /**
269  * Fetch value of DHCP setting
270  *
271  * @v settings          Settings block, or NULL to search all blocks
272  * @v setting           Setting to fetch
273  * @v data              Buffer to fill with setting data
274  * @v len               Length of buffer
275  * @ret len             Length of setting data, or negative error
276  */
277 static int dhcpset_fetch ( struct settings *settings, struct setting *setting,
278                            void *data, size_t len ) {
279         struct dhcp_settings *dhcpset =
280                 container_of ( settings, struct dhcp_settings, settings );
281
282         return dhcppkt_fetch ( &dhcpset->dhcppkt, setting->tag, data, len );
283 }
284
285 /** DHCP settings operations */
286 static struct settings_operations dhcpset_settings_operations = {
287         .store = dhcpset_store,
288         .fetch = dhcpset_fetch,
289 };
290
291 /**
292  * Create DHCP setting block
293  *
294  * @v dhcphdr           DHCP packet
295  * @v len               Length of DHCP packet
296  * @ret dhcpset         DHCP settings block
297  */
298 static struct dhcp_settings * dhcpset_create ( const struct dhcphdr *dhcphdr,
299                                                size_t len ) {
300         struct dhcp_settings *dhcpset;
301         void *data;
302
303         dhcpset = zalloc ( sizeof ( *dhcpset ) + len );
304         if ( dhcpset ) {
305                 data = ( ( ( void * ) dhcpset ) + sizeof ( *dhcpset ) );
306                 memcpy ( data, dhcphdr, len );
307                 dhcppkt_init ( &dhcpset->dhcppkt, data, len );
308                 settings_init ( &dhcpset->settings,
309                                 &dhcpset_settings_operations, &dhcpset->refcnt,
310                                 DHCP_SETTINGS_NAME, 0 );
311         }
312         return dhcpset;
313 }
314
315 /** DHCP server address setting */
316 struct setting dhcp_server_setting __setting = {
317         .name = "dhcp-server",
318         .description = "DHCP server address",
319         .tag = DHCP_SERVER_IDENTIFIER,
320         .type = &setting_type_ipv4,
321 };
322
323 /****************************************************************************
324  *
325  * DHCP session
326  *
327  */
328
329 /** DHCP session states */
330 enum dhcp_session_state {
331         /** Sending DHCPDISCOVERs, collecting DHCPOFFERs and ProxyDHCPOFFERs */
332         DHCP_STATE_DISCOVER = 0,
333         /** Sending DHCPREQUESTs, waiting for DHCPACK */
334         DHCP_STATE_REQUEST,
335         /** Sending ProxyDHCPREQUESTs, waiting for ProxyDHCPACK */
336         DHCP_STATE_PROXYREQUEST,
337         /** Sending BootServerDHCPREQUESTs, waiting for BootServerDHCPACK */
338         DHCP_STATE_BSREQUEST,
339 };
340
341 /**
342  * Name a DHCP session state
343  *
344  * @v state             DHCP session state
345  * @ret string          DHCP session state name
346  */
347 static inline const char * dhcp_state_name ( enum dhcp_session_state state ) {
348         switch ( state ) {
349         case DHCP_STATE_DISCOVER:       return "DHCPDISCOVER";
350         case DHCP_STATE_REQUEST:        return "DHCPREQUEST";
351         case DHCP_STATE_PROXYREQUEST:   return "ProxyDHCPREQUEST";
352         case DHCP_STATE_BSREQUEST:      return "BootServerREQUEST";
353         default:                        return "<invalid>";
354         }
355 }
356
357 /** A DHCP session */
358 struct dhcp_session {
359         /** Reference counter */
360         struct refcnt refcnt;
361         /** Job control interface */
362         struct job_interface job;
363         /** Data transfer interface */
364         struct xfer_interface xfer;
365
366         /** Network device being configured */
367         struct net_device *netdev;
368
369         /** State of the session
370          *
371          * This is a value for the @c DHCP_MESSAGE_TYPE option
372          * (e.g. @c DHCPDISCOVER).
373          */
374         enum dhcp_session_state state;
375         /** DHCPOFFER obtained during DHCPDISCOVER containing IP address */
376         struct dhcp_settings *dhcpoffer;
377         /** DHCPOFFER obtained during DHCPDISCOVER containing "PXEClient" */
378         struct dhcp_settings *pxedhcpoffer;
379         /** DHCPACK obtained during DHCPREQUEST containing IP address */
380         struct dhcp_settings *dhcpack;
381         /** DHCPACK obtained during DHCPREQUEST or ProxyDHCPREQUEST
382          * containing "PXEClient"
383          */
384         struct dhcp_settings *pxedhcpack;
385         /** BootServerDHCPACK obtained during BootServerDHCPREQUEST */
386         struct dhcp_settings *bsdhcpack;
387         /** PXE boot menu item */
388         struct dhcp_pxe_boot_menu_item menu_item;
389
390         /** Retransmission timer */
391         struct retry_timer timer;
392         /** Start time of the current state (in ticks) */
393         unsigned long start;
394 };
395
396 /**
397  * Free DHCP session
398  *
399  * @v refcnt            Reference counter
400  */
401 static void dhcp_free ( struct refcnt *refcnt ) {
402         struct dhcp_session *dhcp =
403                 container_of ( refcnt, struct dhcp_session, refcnt );
404
405         netdev_put ( dhcp->netdev );
406         dhcpset_put ( dhcp->dhcpoffer );
407         dhcpset_put ( dhcp->pxedhcpoffer );
408         dhcpset_put ( dhcp->dhcpack );
409         dhcpset_put ( dhcp->pxedhcpack );
410         dhcpset_put ( dhcp->bsdhcpack );
411         free ( dhcp );
412 }
413
414 /**
415  * Mark DHCP session as complete
416  *
417  * @v dhcp              DHCP session
418  * @v rc                Return status code
419  */
420 static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
421
422         /* Block futher incoming messages */
423         job_nullify ( &dhcp->job );
424         xfer_nullify ( &dhcp->xfer );
425
426         /* Stop retry timer */
427         stop_timer ( &dhcp->timer );
428
429         /* Free resources and close interfaces */
430         xfer_close ( &dhcp->xfer, rc );
431         job_done ( &dhcp->job, rc );
432 }
433
434 /****************************************************************************
435  *
436  * Data transfer interface
437  *
438  */
439
440 /**
441  * Create a DHCP packet
442  *
443  * @v dhcppkt           DHCP packet structure to fill in
444  * @v netdev            Network device
445  * @v msgtype           DHCP message type
446  * @v options           Initial options to include (or NULL)
447  * @v data              Buffer for DHCP packet
448  * @v max_len           Size of DHCP packet buffer
449  * @ret rc              Return status code
450  *
451  * Creates a DHCP packet in the specified buffer, and fills out a @c
452  * dhcp_packet structure.
453  */
454 int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
455                          struct net_device *netdev, uint8_t msgtype,
456                          struct dhcp_options *options, 
457                          void *data, size_t max_len ) {
458         struct dhcphdr *dhcphdr = data;
459         size_t options_len;
460         unsigned int hlen;
461         int rc;
462
463         /* Sanity check */
464         options_len = ( options ? options->len : 0 );
465         if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
466                 return -ENOSPC;
467
468         /* Initialise DHCP packet content */
469         memset ( dhcphdr, 0, max_len );
470         dhcphdr->xid = dhcp_xid ( netdev );
471         dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
472         dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
473         dhcphdr->op = dhcp_op[msgtype];
474         /* If hardware length exceeds the chaddr field length, don't
475          * use the chaddr field.  This is as per RFC4390.
476          */
477         hlen = netdev->ll_protocol->ll_addr_len;
478         if ( hlen > sizeof ( dhcphdr->chaddr ) ) {
479                 hlen = 0;
480                 dhcphdr->flags = htons ( BOOTP_FL_BROADCAST );
481         }
482         dhcphdr->hlen = hlen;
483         memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
484         memcpy ( dhcphdr->options, options->data, options_len );
485
486         /* Initialise DHCP packet structure */
487         memset ( dhcppkt, 0, sizeof ( *dhcppkt ) );
488         dhcppkt_init ( dhcppkt, data, max_len );
489         
490         /* Set DHCP_MESSAGE_TYPE option */
491         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE,
492                                     &msgtype, sizeof ( msgtype ) ) ) != 0 )
493                 return rc;
494
495         return 0;
496 }
497
498 /**
499  * Create DHCP request packet
500  *
501  * @v dhcppkt           DHCP packet structure to fill in
502  * @v netdev            Network device
503  * @v msgtype           DHCP message type
504  * @v ciaddr            Client IP address, if applicable
505  * @v server            Server identifier, if applicable
506  * @v requested_ip      Requested address, if applicable
507  * @v menu_item         PXE menu item, if applicable
508  * @v data              Buffer for DHCP packet
509  * @v max_len           Size of DHCP packet buffer
510  * @ret rc              Return status code
511  */
512 int dhcp_create_request ( struct dhcp_packet *dhcppkt,
513                           struct net_device *netdev, unsigned int msgtype,
514                           struct in_addr ciaddr, struct in_addr server,
515                           struct in_addr requested_ip,
516                           struct dhcp_pxe_boot_menu_item *menu_item,
517                           void *data, size_t max_len ) {
518         struct device_description *desc = &netdev->dev->desc;
519         struct dhcp_netdev_desc dhcp_desc;
520         struct dhcp_client_id client_id;
521         struct dhcp_client_uuid client_uuid;
522         size_t dhcp_features_len;
523         size_t ll_addr_len;
524         int rc;
525
526         /* Create DHCP packet */
527         if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype,
528                                          &dhcp_request_options, data,
529                                          max_len ) ) != 0 ) {
530                 DBG ( "DHCP could not create DHCP packet: %s\n",
531                       strerror ( rc ) );
532                 return rc;
533         }
534
535         /* Set client IP address */
536         dhcppkt->dhcphdr->ciaddr = ciaddr;
537
538         /* Set server ID, if present */
539         if ( server.s_addr &&
540              ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
541                                       &server, sizeof ( server ) ) ) != 0 ) ) {
542                         DBG ( "DHCP could not set server ID: %s\n",
543                               strerror ( rc ) );
544                         return rc;
545         }
546
547         /* Set requested IP address, if present */
548         if ( requested_ip.s_addr &&
549              ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
550                                       &requested_ip,
551                                       sizeof ( requested_ip ) ) ) != 0 ) ) {
552                 DBG ( "DHCP could not set requested address: %s\n",
553                       strerror ( rc ) );
554                 return rc;
555         }
556
557         /* Add options to identify the feature list */
558         dhcp_features_len = ( dhcp_features_end - dhcp_features );
559         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features,
560                                     dhcp_features_len ) ) != 0 ) {
561                 DBG ( "DHCP could not set features list option: %s\n",
562                       strerror ( rc ) );
563                 return rc;
564         }
565
566         /* Add options to identify the network device */
567         dhcp_desc.type = desc->bus_type;
568         dhcp_desc.vendor = htons ( desc->vendor );
569         dhcp_desc.device = htons ( desc->device );
570         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc,
571                                     sizeof ( dhcp_desc ) ) ) != 0 ) {
572                 DBG ( "DHCP could not set bus ID option: %s\n",
573                       strerror ( rc ) );
574                 return rc;
575         }
576
577         /* Add DHCP client identifier.  Required for Infiniband, and
578          * doesn't hurt other link layers.
579          */
580         client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto );
581         ll_addr_len = netdev->ll_protocol->ll_addr_len;
582         assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) );
583         memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len );
584         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id,
585                                     ( ll_addr_len + 1 ) ) ) != 0 ) {
586                 DBG ( "DHCP could not set client ID: %s\n",
587                       strerror ( rc ) );
588                 return rc;
589         }
590
591         /* Add client UUID, if we have one.  Required for PXE. */
592         client_uuid.type = DHCP_CLIENT_UUID_TYPE;
593         if ( ( rc = fetch_uuid_setting ( NULL, &uuid_setting,
594                                          &client_uuid.uuid ) ) >= 0 ) {
595                 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID,
596                                             &client_uuid,
597                                             sizeof ( client_uuid ) ) ) != 0 ) {
598                         DBG ( "DHCP could not set client UUID: %s\n",
599                               strerror ( rc ) );
600                         return rc;
601                 }
602         }
603
604         /* Set PXE boot menu item, if present */
605         if ( menu_item && menu_item->type &&
606              ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
607                                       menu_item,
608                                       sizeof ( *menu_item ) ) ) != 0 ) ) {
609                 DBG ( "DHCP could not set PXE menu item: %s\n",
610                       strerror ( rc ) );
611                 return rc;
612         }
613
614         return 0;
615 }
616
617 /**
618  * Transmit DHCP request
619  *
620  * @v dhcp              DHCP session
621  * @ret rc              Return status code
622  */
623 static int dhcp_tx ( struct dhcp_session *dhcp ) {
624         static struct sockaddr_in dest = {
625                 .sin_family = AF_INET,
626                 .sin_port = htons ( PXE_PORT ),
627         };
628         static struct sockaddr_in src = {
629                 .sin_family = AF_INET,
630                 .sin_port = htons ( BOOTPC_PORT ),
631         };
632         struct xfer_metadata meta = {
633                 .netdev = dhcp->netdev,
634         };
635         struct io_buffer *iobuf;
636         struct dhcp_packet dhcppkt;
637         struct in_addr ciaddr = { 0 };
638         struct in_addr server = { 0 };
639         struct in_addr requested_ip = { 0 };
640         unsigned int msgtype;
641         int rc;
642
643         /* Start retry timer.  Do this first so that failures to
644          * transmit will be retried.
645          */
646         start_timer ( &dhcp->timer );
647
648         /* Determine packet contents based on current state */
649         switch ( dhcp->state ) {
650         case DHCP_STATE_DISCOVER:
651                 msgtype = DHCPDISCOVER;
652                 break;
653         case DHCP_STATE_REQUEST:
654                 assert ( dhcp->dhcpoffer );
655                 msgtype = DHCPREQUEST;
656                 dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt,
657                                 DHCP_SERVER_IDENTIFIER, &server,
658                                 sizeof ( server ) );
659                 requested_ip = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
660                 break;
661         case DHCP_STATE_PROXYREQUEST:
662                 assert ( dhcp->dhcpoffer );
663                 assert ( dhcp->pxedhcpoffer );
664                 assert ( dhcp->dhcpack );
665                 msgtype = DHCPREQUEST;
666                 ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
667                 dhcppkt_fetch ( &dhcp->pxedhcpoffer->dhcppkt,
668                                 DHCP_SERVER_IDENTIFIER, &dest.sin_addr,
669                                 sizeof ( dest.sin_addr ) );
670                 meta.dest = ( struct sockaddr * ) &dest;
671                 server = dest.sin_addr;
672                 assert ( dest.sin_addr.s_addr );
673                 assert ( ciaddr.s_addr );
674                 break;
675         case DHCP_STATE_BSREQUEST:
676                 assert ( dhcp->dhcpoffer );
677                 assert ( dhcp->pxedhcpoffer );
678                 assert ( dhcp->dhcpack );
679                 assert ( dhcp->pxedhcpack );
680                 msgtype = DHCPREQUEST;
681                 ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
682                 dhcppkt_fetch ( &dhcp->pxedhcpack->dhcppkt,
683                                 DHCP_PXE_BOOT_SERVER_MCAST,
684                                 &dest.sin_addr, sizeof ( dest.sin_addr ) );
685                 meta.dest = ( struct sockaddr * ) &dest;
686                 assert ( dest.sin_addr.s_addr );
687                 assert ( ciaddr.s_addr );
688                 break;
689         default:
690                 assert ( 0 );
691                 return -EINVAL;
692         }
693
694         DBGC ( dhcp, "DHCP %p %s", dhcp, dhcp_msgtype_name ( msgtype ) );
695         if ( server.s_addr )
696                 DBGC ( dhcp, " to %s", inet_ntoa ( server ) );
697         if ( meta.dest ) {
698                 if ( dest.sin_addr.s_addr == server.s_addr ) {
699                         DBGC ( dhcp, ":%d (unicast)",
700                                ntohs ( dest.sin_port ) );
701                 } else {
702                         DBGC ( dhcp, " via %s:%d", inet_ntoa ( dest.sin_addr ),
703                                ntohs ( dest.sin_port ) );
704                 }
705         } else {
706                 DBGC ( dhcp, " (broadcast)" );
707         }
708         if ( requested_ip.s_addr )
709                 DBGC ( dhcp, " for %s", inet_ntoa ( requested_ip ) );
710         if ( dhcp->menu_item.type ) {
711                 DBGC ( dhcp, " for item %04x",
712                        ntohs ( dhcp->menu_item.type ) );
713         }
714         DBGC ( dhcp, "\n" );
715
716         /* Allocate buffer for packet */
717         iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
718         if ( ! iobuf )
719                 return -ENOMEM;
720
721         /* Create DHCP packet in temporary buffer */
722         if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype,
723                                           ciaddr, server, requested_ip,
724                                           &dhcp->menu_item, iobuf->data,
725                                           iob_tailroom ( iobuf ) ) ) != 0 ) {
726                 DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
727                        dhcp, strerror ( rc ) );
728                 goto done;
729         }
730
731         /* Explicitly specify source address, if available. */
732         if ( ciaddr.s_addr ) {
733                 src.sin_addr = ciaddr;
734                 meta.src = ( struct sockaddr * ) &src;
735         }
736
737         /* Transmit the packet */
738         iob_put ( iobuf, dhcppkt.len );
739         rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
740         iobuf = NULL;
741         if ( rc != 0 ) {
742                 DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
743                        dhcp, strerror ( rc ) );
744                 goto done;
745         }
746
747  done:
748         free_iob ( iobuf );
749         return rc;
750 }
751
752 /**
753  * Prompt for PXE boot menu selection
754  *
755  * @v pxedhcpack        PXEDHCPACK packet
756  * @ret rc              Return status code
757  *
758  * Note that a success return status indicates that the PXE boot menu
759  * should be displayed.
760  */
761 static int dhcp_pxe_boot_menu_prompt ( struct dhcp_packet *pxedhcpack ) {
762         union {
763                 uint8_t buf[80];
764                 struct dhcp_pxe_boot_prompt prompt;
765         } u;
766         ssize_t slen;
767         unsigned long start;
768         int key;
769
770         /* Parse menu prompt */
771         memset ( &u, 0, sizeof ( u ) );
772         if ( ( slen = dhcppkt_fetch ( pxedhcpack, DHCP_PXE_BOOT_MENU_PROMPT,
773                                       &u, sizeof ( u ) ) ) <= 0 ) {
774                 /* If prompt is not present, we should always display
775                  * the menu.
776                  */
777                 return 0;
778         }
779
780         /* Display prompt, if applicable */
781         if ( u.prompt.timeout )
782                 printf ( "\n%s\n", u.prompt.prompt );
783
784         /* Timeout==0xff means display menu immediately */
785         if ( u.prompt.timeout == 0xff )
786                 return 0;
787
788         /* Wait for F8 or other key press */
789         start = currticks();
790         while ( ( currticks() - start ) <
791                 ( u.prompt.timeout * TICKS_PER_SEC ) ) {
792                 if ( iskey() ) {
793                         key = getkey();
794                         return ( ( key == KEY_F8 ) ? 0 : -ECANCELED );
795                 }
796         }
797
798         return -ECANCELED;
799 }
800
801 /**
802  * Perform PXE boot menu selection
803  *
804  * @v pxedhcpack        PXEDHCPACK packet
805  * @v menu_item         PXE boot menu item to fill in
806  * @ret rc              Return status code
807  *
808  * Note that a success return status indicates that a PXE boot menu
809  * item has been selected, and that the DHCP session should perform a
810  * boot server request/ack.
811  */
812 static int dhcp_pxe_boot_menu ( struct dhcp_packet *pxedhcpack,
813                                 struct dhcp_pxe_boot_menu_item *menu_item ) {
814         uint8_t buf[256];
815         ssize_t slen;
816         size_t menu_len;
817         struct dhcp_pxe_boot_menu_item_desc *menu_item_desc;
818         size_t menu_item_desc_len;
819         struct {
820                 uint16_t type;
821                 char *desc;
822         } menu[PXE_BOOT_MENU_MAX_ITEMS];
823         size_t offset = 0;
824         unsigned int num_menu_items = 0;
825         unsigned int i;
826         unsigned int selected_menu_item;
827         int key;
828         int rc;
829
830         /* Check for boot menu */
831         memset ( &buf, 0, sizeof ( buf ) );
832         if ( ( slen = dhcppkt_fetch ( pxedhcpack, DHCP_PXE_BOOT_MENU,
833                                       &buf, sizeof ( buf ) ) ) <= 0 ) {
834                 DBGC2 ( pxedhcpack, "PXEDHCPACK %p has no boot menu\n",
835                         pxedhcpack );
836                 return slen;
837         }
838         menu_len = slen;
839
840         /* Parse boot menu */
841         while ( offset < menu_len ) {
842                 menu_item_desc = ( ( void * ) ( buf + offset ) );
843                 menu_item_desc_len = ( sizeof ( *menu_item_desc ) +
844                                        menu_item_desc->desc_len );
845                 if ( ( offset + menu_item_desc_len ) > menu_len ) {
846                         DBGC ( pxedhcpack, "PXEDHCPACK %p has malformed "
847                                "boot menu\n", pxedhcpack );
848                         return -EINVAL;
849                 }
850                 menu[num_menu_items].type = menu_item_desc->type;
851                 menu[num_menu_items].desc = menu_item_desc->desc;
852                 /* Set type to 0; this ensures that the description
853                  * for the previous menu item is NUL-terminated.
854                  * (Final item is NUL-terminated anyway.)
855                  */
856                 menu_item_desc->type = 0;
857                 offset += menu_item_desc_len;
858                 num_menu_items++;
859                 if ( num_menu_items == ( sizeof ( menu ) /
860                                          sizeof ( menu[0] ) ) ) {
861                         DBGC ( pxedhcpack, "PXEDHCPACK %p has too many "
862                                "menu items\n", pxedhcpack );
863                         /* Silently ignore remaining items */
864                         break;
865                 }
866         }
867         if ( ! num_menu_items ) {
868                 DBGC ( pxedhcpack, "PXEDHCPACK %p has no menu items\n",
869                        pxedhcpack );
870                 return -EINVAL;
871         }
872
873         /* Default to first menu item */
874         menu_item->type = menu[0].type;
875
876         /* Prompt for menu, if necessary */
877         if ( ( rc = dhcp_pxe_boot_menu_prompt ( pxedhcpack ) ) != 0 ) {
878                 /* Failure to display menu means we should just
879                  * continue with the boot.
880                  */
881                 return 0;
882         }
883
884         /* Display menu */
885         for ( i = 0 ; i < num_menu_items ; i++ ) {
886                 printf ( "%c. %s\n", ( 'A' + i ), menu[i].desc );
887         }
888
889         /* Obtain selection */
890         while ( 1 ) {
891                 key = getkey();
892                 selected_menu_item = ( toupper ( key ) - 'A' );
893                 if ( selected_menu_item < num_menu_items ) {
894                         menu_item->type = menu[selected_menu_item].type;
895                         return 0;
896                 }
897         }
898 }
899
900 /**
901  * Transition to new DHCP session state
902  *
903  * @v dhcp              DHCP session
904  * @v state             New session state
905  */
906 static void dhcp_set_state ( struct dhcp_session *dhcp,
907                              enum dhcp_session_state state ) {
908         DBGC ( dhcp, "DHCP %p entering %s state\n",
909                dhcp, dhcp_state_name ( state ) );
910         dhcp->state = state;
911         dhcp->start = currticks();
912         dhcp->timer.min_timeout = 0;
913         start_timer_nodelay ( &dhcp->timer );
914 }
915
916 /**
917  * Transition to next DHCP state
918  *
919  * @v dhcp              DHCP session
920  */
921 static void dhcp_next_state ( struct dhcp_session *dhcp ) {
922
923         stop_timer ( &dhcp->timer );
924
925         switch ( dhcp->state ) {
926         case DHCP_STATE_DISCOVER:
927                 dhcp_set_state ( dhcp, DHCP_STATE_REQUEST );
928                 break;
929         case DHCP_STATE_REQUEST:
930                 if ( dhcp->pxedhcpoffer ) {
931                         /* Store DHCPACK as PXEDHCPACK.  This handles
932                          * the case in which the DHCP server itself
933                          * responds with "PXEClient" and PXE options
934                          * but there is no ProxyDHCP server resident
935                          * on the machine.
936                          */
937                         dhcp->pxedhcpack = dhcpset_get ( dhcp->dhcpack );
938                         dhcp_set_state ( dhcp, DHCP_STATE_PROXYREQUEST );
939                         break;
940                 }
941                 /* Fall through */
942         case DHCP_STATE_PROXYREQUEST:
943                 if ( dhcp->pxedhcpack ) {
944                         dhcp_pxe_boot_menu ( &dhcp->pxedhcpack->dhcppkt,
945                                              &dhcp->menu_item );
946                         if ( dhcp->menu_item.type ) {
947                                 dhcp_set_state ( dhcp, DHCP_STATE_BSREQUEST );
948                                 break;
949                         }
950                 }
951                 /* Fall through */
952         case DHCP_STATE_BSREQUEST:
953                 dhcp_finished ( dhcp, 0 );
954                 break;
955         default:
956                 assert ( 0 );
957                 return;
958         }
959
960 }
961
962 /**
963  * Store received DHCPOFFER
964  *
965  * @v dhcp              DHCP session
966  * @v dhcpoffer         Received DHCPOFFER
967  * @v stored_dhcpoffer  Location to store DHCPOFFER
968  *
969  * The DHCPOFFER will be stored in place of the existing stored
970  * DHCPOFFER if its priority is equal to or greater than the stored
971  * DHCPOFFER.
972  */
973 static void dhcp_store_dhcpoffer ( struct dhcp_session *dhcp,
974                                    struct dhcp_settings *dhcpoffer,
975                                    struct dhcp_settings **stored_dhcpoffer ) {
976         uint8_t stored_priority = 0;
977         uint8_t priority = 0;
978
979         /* Get priorities of the two DHCPOFFERs */
980         if ( *stored_dhcpoffer ) {
981                 dhcppkt_fetch ( &(*stored_dhcpoffer)->dhcppkt,
982                                 DHCP_EB_PRIORITY, &stored_priority,
983                                 sizeof ( stored_priority ) );
984         }
985         dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_EB_PRIORITY, &priority,
986                         sizeof ( priority ) );
987
988         /* Replace stored offer only if priority is equal or greater */
989         if ( priority >= stored_priority ) {
990                 if ( *stored_dhcpoffer ) {
991                         DBGC ( dhcp, "DHCP %p stored DHCPOFFER %p discarded\n",
992                                dhcp, *stored_dhcpoffer );
993                 }
994                 DBGC ( dhcp, "DHCP %p DHCPOFFER %p stored\n",
995                        dhcp, dhcpoffer );
996                 dhcpset_put ( *stored_dhcpoffer );
997                 *stored_dhcpoffer = dhcpset_get ( dhcpoffer );
998         }
999 }
1000
1001 /**
1002  * Handle received DHCPOFFER
1003  *
1004  * @v dhcp              DHCP session
1005  * @v dhcpoffer         Received DHCPOFFER
1006  */
1007 static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
1008                                 struct dhcp_settings *dhcpoffer ) {
1009         struct in_addr server_id = { 0 };
1010         char vci[9]; /* "PXEClient" */
1011         int len;
1012         uint8_t ignore_pxe = 0;
1013         unsigned long elapsed;
1014
1015         /* Check for presence of DHCP server ID */
1016         if ( dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER,
1017                              &server_id, sizeof ( server_id ) )
1018              != sizeof ( server_id ) ) {
1019                 DBGC ( dhcp, "DHCP %p DHCPOFFER %p missing server ID\n",
1020                        dhcp, dhcpoffer );
1021                 /* Could be a valid BOOTP offer; do not abort processing */
1022         }
1023
1024         /* If there is an IP address, it's a normal DHCPOFFER */
1025         if ( dhcpoffer->dhcppkt.dhcphdr->yiaddr.s_addr != 0 ) {
1026                 DBGC ( dhcp, "DHCP %p DHCPOFFER %p from %s",
1027                        dhcp, dhcpoffer, inet_ntoa ( server_id ) );
1028                 DBGC ( dhcp, " has IP %s\n",
1029                        inet_ntoa ( dhcpoffer->dhcppkt.dhcphdr->yiaddr ) );
1030                 dhcp_store_dhcpoffer ( dhcp, dhcpoffer, &dhcp->dhcpoffer );
1031         }
1032
1033         /* If there is a "PXEClient" vendor class ID, it's a
1034          * PXEDHCPOFFER.  Note that it could be both a normal
1035          * DHCPOFFER and a PXEDHCPOFFER.
1036          */
1037         len = dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_VENDOR_CLASS_ID,
1038                               vci, sizeof ( vci ) );
1039         if ( ( server_id.s_addr != 0 ) &&
1040              ( len >= ( int ) sizeof ( vci ) ) &&
1041              ( strncmp ( "PXEClient", vci, sizeof ( vci ) ) == 0 ) ) {
1042                 DBGC ( dhcp, "DHCP %p DHCPOFFER %p from %s has PXE options\n",
1043                        dhcp, dhcpoffer, inet_ntoa ( server_id ) );
1044                 dhcp_store_dhcpoffer ( dhcp, dhcpoffer,
1045                                        &dhcp->pxedhcpoffer );
1046         }
1047
1048         /* We can transition to making the DHCPREQUEST when we have a
1049          * valid DHCPOFFER, and either:
1050          *
1051          *  o  The DHCPOFFER instructs us to ignore PXEDHCPOFFERs, or
1052          *  o  We have a valid PXEDHCPOFFER, or
1053          *  o  We have allowed sufficient time for ProxyDHCPOFFERs.
1054          */
1055
1056         /* If we don't yet have a DHCPOFFER, do nothing */
1057         if ( ! dhcp->dhcpoffer )
1058                 return;
1059
1060         /* If the DHCPOFFER instructs us to ignore PXEDHCP, discard
1061          * any PXEDHCPOFFER
1062          */
1063         dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt, DHCP_EB_NO_PXEDHCP,
1064                         &ignore_pxe, sizeof ( ignore_pxe ) );
1065         if ( ignore_pxe && dhcp->pxedhcpoffer ) {
1066                 DBGC ( dhcp, "DHCP %p discarding PXEDHCPOFFER\n", dhcp );
1067                 dhcpset_put ( dhcp->pxedhcpoffer );
1068                 dhcp->pxedhcpoffer = NULL;
1069         }
1070
1071         /* If we can't yet transition to DHCPREQUEST, do nothing */
1072         elapsed = ( currticks() - dhcp->start );
1073         if ( ! ( ignore_pxe || dhcp->pxedhcpoffer ||
1074                  ( elapsed > PROXYDHCP_WAIT_TIME ) ) )
1075                 return;
1076
1077         /* Transition to DHCPREQUEST */
1078         dhcp_next_state ( dhcp );
1079 }
1080
1081 /**
1082  * Store received DHCPACK
1083  *
1084  * @v dhcp              DHCP session
1085  * @v dhcpack           Received DHCPACK
1086  *
1087  * The DHCPACK will be registered as a settings block.
1088  */
1089 static int dhcp_store_dhcpack ( struct dhcp_session *dhcp,
1090                                 struct dhcp_settings *dhcpack,
1091                                 struct settings *parent ) {
1092         struct settings *settings = &dhcpack->settings;
1093         struct settings *old_settings;
1094         int rc;
1095
1096         /* Unregister any old settings obtained via DHCP */
1097         if ( ( old_settings = find_child_settings ( parent, settings->name ) ))
1098                 unregister_settings ( old_settings );
1099
1100         /* Register new settings */
1101         if ( ( rc = register_settings ( settings, parent ) ) != 0 ) {
1102                 DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
1103                        dhcp, strerror ( rc ) );
1104                 dhcp_finished ( dhcp, rc ); /* This is a fatal error */
1105                 return rc;
1106         }
1107
1108         return 0;
1109 }
1110
1111 /**
1112  * Handle received DHCPACK
1113  *
1114  * @v dhcp              DHCP session
1115  * @v dhcpack           Received DHCPACK
1116  */
1117 static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp,
1118                               struct dhcp_settings *dhcpack ) {
1119         struct settings *parent;
1120         struct in_addr offer_server_id = { 0 };
1121         struct in_addr ack_server_id = { 0 };
1122         int rc;
1123
1124         /* Verify server ID matches */
1125         assert ( dhcp->dhcpoffer != NULL );
1126         dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER,
1127                         &offer_server_id, sizeof ( offer_server_id ) );
1128         dhcppkt_fetch ( &dhcpack->dhcppkt, DHCP_SERVER_IDENTIFIER,
1129                         &ack_server_id, sizeof ( ack_server_id ) );
1130         if ( offer_server_id.s_addr != ack_server_id.s_addr ) {
1131                 DBGC ( dhcp, "DHCP %p ignoring DHCPACK with wrong server ID "
1132                        "%s\n", dhcp, inet_ntoa ( ack_server_id ) );
1133                 return;
1134         }
1135
1136         /* Record DHCPACK */
1137         assert ( dhcp->dhcpack == NULL );
1138         dhcp->dhcpack = dhcpset_get ( dhcpack );
1139
1140         /* Register settings */
1141         parent = netdev_settings ( dhcp->netdev );
1142         if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) != 0 )
1143                 return;
1144
1145         /* Transition to next state */
1146         dhcp_next_state ( dhcp );
1147 }
1148
1149 /**
1150  * Handle received ProxyDHCPACK
1151  *
1152  * @v dhcp              DHCP session
1153  * @v proxydhcpack      Received ProxyDHCPACK
1154  */
1155 static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
1156                                    struct dhcp_settings *proxydhcpack ) {
1157         struct in_addr offer_server_id = { 0 };
1158         struct in_addr ack_server_id = { 0 };
1159         int rc;
1160
1161         /* Verify server ID matches, if present */
1162         assert ( dhcp->pxedhcpoffer != NULL );
1163         if ( ( rc = dhcppkt_fetch ( &proxydhcpack->dhcppkt,
1164                                     DHCP_SERVER_IDENTIFIER, &ack_server_id,
1165                                     sizeof ( ack_server_id ) ) ) > 0 ) {
1166                 dhcppkt_fetch ( &dhcp->pxedhcpoffer->dhcppkt,
1167                                 DHCP_SERVER_IDENTIFIER, &offer_server_id,
1168                                 sizeof ( offer_server_id ) );
1169                 if ( offer_server_id.s_addr != ack_server_id.s_addr ) {
1170                         DBGC ( dhcp, "DHCP %p ignoring ProxyDHCPACK with "
1171                                "wrong server ID %s\n",
1172                                dhcp, inet_ntoa ( ack_server_id ) );
1173                         return;
1174                 }
1175         }
1176
1177         /* Rename settings */
1178         proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME;
1179
1180         /* Record ProxyDHCPACK as PXEDHCPACK */
1181         dhcpset_put ( dhcp->pxedhcpack );
1182         dhcp->pxedhcpack = dhcpset_get ( proxydhcpack );
1183
1184         /* Register settings */
1185         if ( ( rc = dhcp_store_dhcpack ( dhcp, proxydhcpack, NULL ) ) != 0 )
1186                 return;
1187
1188         /* Transition to next state */
1189         dhcp_next_state ( dhcp );
1190 }
1191
1192 /**
1193  * Handle received BootServerDHCPACK
1194  *
1195  * @v dhcp              DHCP session
1196  * @v bsdhcpack         Received BootServerDHCPACK
1197  */
1198 static void dhcp_rx_bsdhcpack ( struct dhcp_session *dhcp,
1199                                 struct dhcp_settings *bsdhcpack ) {
1200         int rc;
1201
1202         /* Rename settings */
1203         bsdhcpack->settings.name = BSDHCP_SETTINGS_NAME;
1204
1205         /* Record BootServerDHCPACK */
1206         assert ( dhcp->bsdhcpack == NULL );
1207         dhcp->bsdhcpack = dhcpset_get ( bsdhcpack );
1208
1209         /* Register settings */
1210         if ( ( rc = dhcp_store_dhcpack ( dhcp, bsdhcpack, NULL ) ) != 0 )
1211                 return;
1212
1213         /* Transition to next state */
1214         dhcp_next_state ( dhcp );
1215 }
1216
1217 /**
1218  * Receive new data
1219  *
1220  * @v xfer              Data transfer interface
1221  * @v iobuf             I/O buffer
1222  * @v meta              Transfer metadata
1223  * @ret rc              Return status code
1224  */
1225 static int dhcp_deliver_iob ( struct xfer_interface *xfer,
1226                               struct io_buffer *iobuf,
1227                               struct xfer_metadata *meta ) {
1228         struct dhcp_session *dhcp =
1229                 container_of ( xfer, struct dhcp_session, xfer );
1230         struct sockaddr_in *sin_src;
1231         unsigned int src_port;
1232         struct dhcp_settings *dhcpset;
1233         struct dhcphdr *dhcphdr;
1234         uint8_t msgtype = 0;
1235         int rc = 0;
1236
1237         /* Sanity checks */
1238         if ( ! meta ) {
1239                 DBGC ( dhcp, "DHCP %p received packet without metadata\n",
1240                        dhcp );
1241                 rc = -EINVAL;
1242                 goto err_no_meta;
1243         }
1244         if ( ! meta->src ) {
1245                 DBGC ( dhcp, "DHCP %p received packet without source port\n",
1246                        dhcp );
1247                 rc = -EINVAL;
1248                 goto err_no_src;
1249         }
1250         sin_src = ( struct sockaddr_in * ) meta->src;
1251         src_port = sin_src->sin_port;
1252
1253         /* Convert packet into a DHCP settings block */
1254         dhcpset = dhcpset_create ( iobuf->data, iob_len ( iobuf ) );
1255         if ( ! dhcpset ) {
1256                 DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp );
1257                 rc = -ENOMEM;
1258                 goto err_dhcpset_create;
1259         }
1260         dhcphdr = dhcpset->dhcppkt.dhcphdr;
1261
1262         /* Identify message type */
1263         dhcppkt_fetch ( &dhcpset->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
1264                         sizeof ( msgtype ) );
1265         DBGC ( dhcp, "DHCP %p %s %p from %s:%d\n", dhcp,
1266                dhcp_msgtype_name ( msgtype ), dhcpset,
1267                inet_ntoa ( sin_src->sin_addr ), ntohs ( src_port ) );
1268
1269         /* Check for matching transaction ID */
1270         if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
1271                 DBGC ( dhcp, "DHCP %p %s %p has bad transaction ID\n",
1272                        dhcp, dhcp_msgtype_name ( msgtype ), dhcpset );
1273                 rc = -EINVAL;
1274                 goto err_xid;
1275         };
1276
1277         /* Handle packet based on current state */
1278         switch ( dhcp->state ) {
1279         case DHCP_STATE_DISCOVER:
1280                 if ( ( ( msgtype == DHCPOFFER ) || ( msgtype == DHCPNONE ) ) &&
1281                      ( src_port == htons ( BOOTPS_PORT ) ) )
1282                         dhcp_rx_dhcpoffer ( dhcp, dhcpset );
1283                 break;
1284         case DHCP_STATE_REQUEST:
1285                 if ( ( ( msgtype == DHCPACK ) || ( msgtype == DHCPNONE ) ) &&
1286                      ( src_port == htons ( BOOTPS_PORT ) ) )
1287                         dhcp_rx_dhcpack ( dhcp, dhcpset );
1288                 break;
1289         case DHCP_STATE_PROXYREQUEST:
1290                 if ( ( msgtype == DHCPACK ) &&
1291                      ( src_port == htons ( PXE_PORT ) ) )
1292                         dhcp_rx_proxydhcpack ( dhcp, dhcpset );
1293                 break;
1294         case DHCP_STATE_BSREQUEST:
1295                 if ( ( msgtype == DHCPACK ) &&
1296                      ( src_port == htons ( PXE_PORT ) ) )
1297                         dhcp_rx_bsdhcpack ( dhcp, dhcpset );
1298                 break;
1299         default:
1300                 assert ( 0 );
1301                 break;
1302         }
1303
1304  err_xid:
1305         dhcpset_put ( dhcpset );
1306  err_dhcpset_create:
1307  err_no_src:
1308  err_no_meta:
1309         free_iob ( iobuf );
1310         return rc;
1311 }
1312
1313 /** DHCP data transfer interface operations */
1314 static struct xfer_interface_operations dhcp_xfer_operations = {
1315         .close          = ignore_xfer_close,
1316         .vredirect      = xfer_vopen,
1317         .window         = unlimited_xfer_window,
1318         .alloc_iob      = default_xfer_alloc_iob,
1319         .deliver_iob    = dhcp_deliver_iob,
1320         .deliver_raw    = xfer_deliver_as_iob,
1321 };
1322
1323 /**
1324  * Handle DHCP retry timer expiry
1325  *
1326  * @v timer             DHCP retry timer
1327  * @v fail              Failure indicator
1328  */
1329 static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
1330         struct dhcp_session *dhcp =
1331                 container_of ( timer, struct dhcp_session, timer );
1332         unsigned long elapsed = ( currticks() - dhcp->start );
1333
1334         /* If we have failed, terminate DHCP */
1335         if ( fail ) {
1336                 dhcp_finished ( dhcp, -ETIMEDOUT );
1337                 return;
1338         }
1339
1340         /* Give up waiting for ProxyDHCP before we reach the failure point */
1341         if ( dhcp->dhcpoffer && ( elapsed > PROXYDHCP_WAIT_TIME ) ) {
1342                 dhcp_next_state ( dhcp );
1343                 return;
1344         }
1345
1346         /* Otherwise, retransmit current packet */
1347         dhcp_tx ( dhcp );
1348 }
1349
1350 /****************************************************************************
1351  *
1352  * Job control interface
1353  *
1354  */
1355
1356 /**
1357  * Handle kill() event received via job control interface
1358  *
1359  * @v job               DHCP job control interface
1360  */
1361 static void dhcp_job_kill ( struct job_interface *job ) {
1362         struct dhcp_session *dhcp =
1363                 container_of ( job, struct dhcp_session, job );
1364
1365         /* Terminate DHCP session */
1366         dhcp_finished ( dhcp, -ECANCELED );
1367 }
1368
1369 /** DHCP job control interface operations */
1370 static struct job_interface_operations dhcp_job_operations = {
1371         .done           = ignore_job_done,
1372         .kill           = dhcp_job_kill,
1373         .progress       = ignore_job_progress,
1374 };
1375
1376 /****************************************************************************
1377  *
1378  * Instantiator
1379  *
1380  */
1381
1382 /**
1383  * Start DHCP on a network device
1384  *
1385  * @v job               Job control interface
1386  * @v netdev            Network device
1387  * @v register_options  DHCP option block registration routine
1388  * @ret rc              Return status code
1389  *
1390  * Starts DHCP on the specified network device.  If successful, the @c
1391  * register_options() routine will be called with the acquired
1392  * options.
1393  */
1394 int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
1395         static struct sockaddr_in server = {
1396                 .sin_family = AF_INET,
1397                 .sin_addr.s_addr = INADDR_BROADCAST,
1398                 .sin_port = htons ( BOOTPS_PORT ),
1399         };
1400         static struct sockaddr_in client = {
1401                 .sin_family = AF_INET,
1402                 .sin_port = htons ( BOOTPC_PORT ),
1403         };
1404         struct dhcp_session *dhcp;
1405         int rc;
1406
1407         /* Allocate and initialise structure */
1408         dhcp = zalloc ( sizeof ( *dhcp ) );
1409         if ( ! dhcp )
1410                 return -ENOMEM;
1411         dhcp->refcnt.free = dhcp_free;
1412         job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
1413         xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
1414         dhcp->netdev = netdev_get ( netdev );
1415         dhcp->timer.expired = dhcp_timer_expired;
1416         dhcp->timer.min_timeout = DHCP_MIN_TIMEOUT;
1417         dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT;
1418         dhcp->start = currticks();
1419
1420         /* Instantiate child objects and attach to our interfaces */
1421         if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM,
1422                                        ( struct sockaddr * ) &server,
1423                                        ( struct sockaddr * ) &client ) ) != 0 )
1424                 goto err;
1425
1426         /* Start timer to initiate initial DHCPREQUEST */
1427         start_timer_nodelay ( &dhcp->timer );
1428
1429         /* Attach parent interface, mortalise self, and return */
1430         job_plug_plug ( &dhcp->job, job );
1431         ref_put ( &dhcp->refcnt );
1432         return 0;
1433
1434  err:
1435         dhcp_finished ( dhcp, rc );
1436         ref_put ( &dhcp->refcnt );
1437         return rc;
1438 }