6ebf60217f79291de1bf0d516bcd062f8f4589eb
[people/mcb30/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 <gpxe/if_ether.h>
27 #include <gpxe/netdevice.h>
28 #include <gpxe/device.h>
29 #include <gpxe/xfer.h>
30 #include <gpxe/open.h>
31 #include <gpxe/job.h>
32 #include <gpxe/retry.h>
33 #include <gpxe/tcpip.h>
34 #include <gpxe/ip.h>
35 #include <gpxe/uuid.h>
36 #include <gpxe/timer.h>
37 #include <gpxe/settings.h>
38 #include <gpxe/dhcp.h>
39 #include <gpxe/dhcpopts.h>
40 #include <gpxe/dhcppkt.h>
41 #include <gpxe/features.h>
42
43 /** @file
44  *
45  * Dynamic Host Configuration Protocol
46  *
47  */
48
49 struct dhcp_session;
50 static int dhcp_tx ( struct dhcp_session *dhcp );
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 /** DHCP feature codes */
90 static uint8_t dhcp_features[0] __table_start ( uint8_t, dhcp_features );
91 static uint8_t dhcp_features_end[0] __table_end ( uint8_t, dhcp_features );
92
93 /** Version number feature */
94 FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH );
95
96 /** DHCP server address setting */
97 struct setting dhcp_server_setting __setting = {
98         .name = "dhcp-server",
99         .description = "DHCP server address",
100         .tag = DHCP_SERVER_IDENTIFIER,
101         .type = &setting_type_ipv4,
102 };
103
104 /** DHCP user class setting */
105 struct setting user_class_setting __setting = {
106         .name = "user-class",
107         .description = "User class identifier",
108         .tag = DHCP_USER_CLASS_ID,
109         .type = &setting_type_string,
110 };
111
112 /**
113  * Name a DHCP packet type
114  *
115  * @v msgtype           DHCP message type
116  * @ret string          DHCP mesasge type name
117  */
118 static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) {
119         switch ( msgtype ) {
120         case DHCPNONE:          return "BOOTP"; /* Non-DHCP packet */
121         case DHCPDISCOVER:      return "DHCPDISCOVER";
122         case DHCPOFFER:         return "DHCPOFFER";
123         case DHCPREQUEST:       return "DHCPREQUEST";
124         case DHCPDECLINE:       return "DHCPDECLINE";
125         case DHCPACK:           return "DHCPACK";
126         case DHCPNAK:           return "DHCPNAK";
127         case DHCPRELEASE:       return "DHCPRELEASE";
128         case DHCPINFORM:        return "DHCPINFORM";
129         default:                return "DHCP<invalid>";
130         }
131 }
132
133 /**
134  * Calculate DHCP transaction ID for a network device
135  *
136  * @v netdev            Network device
137  * @ret xid             DHCP XID
138  *
139  * Extract the least significant bits of the hardware address for use
140  * as the transaction ID.
141  */
142 static uint32_t dhcp_xid ( struct net_device *netdev ) {
143         uint32_t xid;
144
145         memcpy ( &xid, ( netdev->ll_addr + netdev->ll_protocol->ll_addr_len
146                          - sizeof ( xid ) ), sizeof ( xid ) );
147         return xid;
148 }
149
150 /****************************************************************************
151  *
152  * DHCP session
153  *
154  */
155
156 struct dhcp_session;
157
158 /** DHCP session state operations */
159 struct dhcp_session_state {
160         /** State name */
161         const char *name;
162         /**
163          * Construct transmitted packet
164          *
165          * @v dhcp              DHCP session
166          * @v dhcppkt           DHCP packet
167          * @v peer              Destination address
168          */
169         int ( * tx ) ( struct dhcp_session *dhcp,
170                        struct dhcp_packet *dhcppkt,
171                        struct sockaddr_in *peer );
172         /** Handle received packet
173          *
174          * @v dhcp              DHCP session
175          * @v dhcppkt           DHCP packet
176          * @v peer              DHCP server address
177          * @v msgtype           DHCP message type
178          */
179         void ( * rx ) ( struct dhcp_session *dhcp,
180                         struct dhcp_packet *dhcppkt,
181                         struct sockaddr_in *peer,
182                         uint8_t msgtype );
183         /** Handle timer expiry
184          *
185          * @v dhcp              DHCP session
186          */
187         void ( * expired ) ( struct dhcp_session *dhcp );
188         /** Transmitted message type */
189         uint8_t tx_msgtype;
190         /** Apply minimum timeout */
191         uint8_t apply_min_timeout;
192 };
193
194 static struct dhcp_session_state dhcp_state_discover;
195 static struct dhcp_session_state dhcp_state_request;
196 static struct dhcp_session_state dhcp_state_proxy;
197 static struct dhcp_session_state dhcp_state_pxebs;
198
199 /** A DHCP session */
200 struct dhcp_session {
201         /** Reference counter */
202         struct refcnt refcnt;
203         /** Job control interface */
204         struct job_interface job;
205         /** Data transfer interface */
206         struct xfer_interface xfer;
207
208         /** Network device being configured */
209         struct net_device *netdev;
210         /** Local socket address */
211         struct sockaddr_in local;
212         /** State of the session */
213         struct dhcp_session_state *state;
214
215         /** Offered IP address */
216         struct in_addr offer;
217         /** DHCP server */
218         struct in_addr server;
219         /** DHCP offer priority */
220         int priority;
221
222         /** ProxyDHCP protocol extensions should be ignored */
223         int no_pxedhcp;
224         /** ProxyDHCP server */
225         struct in_addr proxy_server;
226         /** ProxyDHCP server priority */
227         int proxy_priority;
228
229         /** PXE Boot Server */
230         struct in_addr pxe_server;
231         /** PXE Boot Server type */
232         uint16_t pxe_type;
233
234         /** Retransmission timer */
235         struct retry_timer timer;
236         /** Start time of the current state (in ticks) */
237         unsigned long start;
238 };
239
240 /**
241  * Free DHCP session
242  *
243  * @v refcnt            Reference counter
244  */
245 static void dhcp_free ( struct refcnt *refcnt ) {
246         struct dhcp_session *dhcp =
247                 container_of ( refcnt, struct dhcp_session, refcnt );
248
249         netdev_put ( dhcp->netdev );
250         free ( dhcp );
251 }
252
253 /**
254  * Mark DHCP session as complete
255  *
256  * @v dhcp              DHCP session
257  * @v rc                Return status code
258  */
259 static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
260
261         /* Block futher incoming messages */
262         job_nullify ( &dhcp->job );
263         xfer_nullify ( &dhcp->xfer );
264
265         /* Stop retry timer */
266         stop_timer ( &dhcp->timer );
267
268         /* Free resources and close interfaces */
269         xfer_close ( &dhcp->xfer, rc );
270         job_done ( &dhcp->job, rc );
271 }
272
273 /**
274  * Transition to new DHCP session state
275  *
276  * @v dhcp              DHCP session
277  * @v state             New session state
278  */
279 static void dhcp_set_state ( struct dhcp_session *dhcp,
280                              struct dhcp_session_state *state ) {
281
282         DBGC ( dhcp, "DHCP %p entering %s state\n", dhcp, state->name );
283         dhcp->state = state;
284         dhcp->start = currticks();
285         stop_timer ( &dhcp->timer );
286         dhcp->timer.min_timeout =
287                 ( state->apply_min_timeout ? DHCP_MIN_TIMEOUT : 0 );
288         dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT;
289         start_timer_nodelay ( &dhcp->timer );
290 }
291
292 /****************************************************************************
293  *
294  * DHCP state machine
295  *
296  */
297
298 /**
299  * Construct transmitted packet for DHCP discovery
300  *
301  * @v dhcp              DHCP session
302  * @v dhcppkt           DHCP packet
303  * @v peer              Destination address
304  */
305 static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
306                                struct dhcp_packet *dhcppkt __unused,
307                                struct sockaddr_in *peer ) {
308
309         DBGC ( dhcp, "DHCP %p DHCPDISCOVER\n", dhcp );
310
311         /* Set server address */
312         peer->sin_addr.s_addr = INADDR_BROADCAST;
313         peer->sin_port = htons ( BOOTPS_PORT );
314
315         return 0;
316 }
317
318 /**
319  * Handle received packet during DHCP discovery
320  *
321  * @v dhcp              DHCP session
322  * @v dhcppkt           DHCP packet
323  * @v peer              DHCP server address
324  * @v msgtype           DHCP message type
325  */
326 static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
327                                 struct dhcp_packet *dhcppkt,
328                                 struct sockaddr_in *peer, uint8_t msgtype ) {
329         struct in_addr server_id = { 0 };
330         struct in_addr ip;
331         char vci[9]; /* "PXEClient" */
332         int vci_len;
333         int has_pxeclient;
334         int8_t priority = 0;
335         uint8_t no_pxedhcp = 0;
336         unsigned long elapsed;
337
338         DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
339                dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
340                ntohs ( peer->sin_port ) );
341
342         /* Identify server ID */
343         dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
344                         &server_id, sizeof ( server_id ) );
345         if ( server_id.s_addr != peer->sin_addr.s_addr )
346                 DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
347
348         /* Identify offered IP address */
349         ip = dhcppkt->dhcphdr->yiaddr;
350         if ( ip.s_addr )
351                 DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
352
353         /* Identify "PXEClient" vendor class */
354         vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID,
355                                   vci, sizeof ( vci ) );
356         has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) &&
357                           ( strncmp ( "PXEClient", vci, sizeof (vci) ) == 0 ));
358         if ( has_pxeclient )
359                 DBGC ( dhcp, " pxe" );
360
361         /* Identify priority */
362         dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
363                         sizeof ( priority ) );
364         if ( priority )
365                 DBGC ( dhcp, " pri %d", priority );
366
367         /* Identify ignore-PXE flag */
368         dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp,
369                         sizeof ( no_pxedhcp ) );
370         if ( no_pxedhcp )
371                 DBGC ( dhcp, " nopxe" );
372         DBGC ( dhcp, "\n" );
373
374         /* Select as DHCP offer, if applicable */
375         if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
376              ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) &&
377              ( priority >= dhcp->priority ) ) {
378                 dhcp->offer = ip;
379                 dhcp->server = server_id;
380                 dhcp->priority = priority;
381                 dhcp->no_pxedhcp = no_pxedhcp;
382         }
383
384         /* Select as ProxyDHCP offer, if applicable */
385         if ( has_pxeclient && ( msgtype == DHCPOFFER ) &&
386              ( priority >= dhcp->proxy_priority ) ) {
387                 dhcp->proxy_server = server_id;
388                 dhcp->proxy_priority = priority;
389         }
390
391         /* We can exit the discovery state when we have a valid
392          * DHCPOFFER, and either:
393          *
394          *  o  The DHCPOFFER instructs us to ignore ProxyDHCPOFFERs, or
395          *  o  We have a valid ProxyDHCPOFFER, or
396          *  o  We have allowed sufficient time for ProxyDHCPOFFERs.
397          */
398
399         /* If we don't yet have a DHCPOFFER, do nothing */
400         if ( ! dhcp->offer.s_addr )
401                 return;
402
403         /* If we can't yet transition to DHCPREQUEST, do nothing */
404         elapsed = ( currticks() - dhcp->start );
405         if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_server.s_addr ||
406                  ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
407                 return;
408
409         /* Transition to DHCPREQUEST */
410         dhcp_set_state ( dhcp, &dhcp_state_request );
411 }
412
413 /**
414  * Handle timer expiry during DHCP discovery
415  *
416  * @v dhcp              DHCP session
417  */
418 static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) {
419         unsigned long elapsed = ( currticks() - dhcp->start );
420
421         /* Give up waiting for ProxyDHCP before we reach the failure point */
422         if ( dhcp->offer.s_addr && ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) {
423                 dhcp_set_state ( dhcp, &dhcp_state_request );
424                 return;
425         }
426
427         /* Otherwise, retransmit current packet */
428         dhcp_tx ( dhcp );
429 }
430
431 /** DHCP discovery state operations */
432 static struct dhcp_session_state dhcp_state_discover = {
433         .name                   = "discovery",
434         .tx                     = dhcp_discovery_tx,
435         .rx                     = dhcp_discovery_rx,
436         .expired                = dhcp_discovery_expired,
437         .tx_msgtype             = DHCPDISCOVER,
438         .apply_min_timeout      = 1,
439 };
440
441 /**
442  * Construct transmitted packet for DHCP request
443  *
444  * @v dhcp              DHCP session
445  * @v dhcppkt           DHCP packet
446  * @v peer              Destination address
447  */
448 static int dhcp_request_tx ( struct dhcp_session *dhcp,
449                              struct dhcp_packet *dhcppkt,
450                              struct sockaddr_in *peer ) {
451         int rc;
452
453         DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
454                dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT );
455         DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) );
456
457         /* Set server ID */
458         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
459                                     &dhcp->server,
460                                     sizeof ( dhcp->server ) ) ) != 0 )
461                 return rc;
462
463         /* Set requested IP address */
464         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
465                                     &dhcp->offer,
466                                     sizeof ( dhcp->offer ) ) ) != 0 )
467                 return rc;
468
469         /* Set server address */
470         peer->sin_addr.s_addr = INADDR_BROADCAST;
471         peer->sin_port = htons ( BOOTPS_PORT );
472
473         return 0;
474 }
475
476 /**
477  * Handle received packet during DHCP request
478  *
479  * @v dhcp              DHCP session
480  * @v dhcppkt           DHCP packet
481  * @v peer              DHCP server address
482  * @v msgtype           DHCP message type
483  */
484 static void dhcp_request_rx ( struct dhcp_session *dhcp,
485                               struct dhcp_packet *dhcppkt,
486                               struct sockaddr_in *peer, uint8_t msgtype ) {
487         struct in_addr server_id = { 0 };
488         struct in_addr ip;
489         struct settings *parent;
490         int rc;
491
492         DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
493                dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
494                ntohs ( peer->sin_port ) );
495
496         /* Identify server ID */
497         dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
498                         &server_id, sizeof ( server_id ) );
499         if ( server_id.s_addr != peer->sin_addr.s_addr )
500                 DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
501
502         /* Identify leased IP address */
503         ip = dhcppkt->dhcphdr->yiaddr;
504         if ( ip.s_addr )
505                 DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
506         DBGC ( dhcp, "\n" );
507
508         /* Filter out unacceptable responses */
509         if ( peer->sin_port != htons ( BOOTPS_PORT ) )
510                 return;
511         if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
512                 return;
513         if ( server_id.s_addr != dhcp->server.s_addr )
514                 return;
515
516         /* Record assigned address */
517         dhcp->local.sin_addr = ip;
518
519         /* Register settings */
520         parent = netdev_settings ( dhcp->netdev );
521         if ( ( rc = register_settings ( &dhcppkt->settings, parent ) ) != 0 ){
522                 DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
523                        dhcp, strerror ( rc ) );
524                 dhcp_finished ( dhcp, rc );
525                 return;
526         }
527
528         /* Start ProxyDHCPREQUEST if applicable */
529         if ( dhcp->proxy_server.s_addr && ( ! dhcp->no_pxedhcp ) ) {
530                 dhcp_set_state ( dhcp, &dhcp_state_proxy );
531                 return;
532         }
533
534         /* Terminate DHCP */
535         dhcp_finished ( dhcp, 0 );
536 }
537
538 /**
539  * Handle timer expiry during DHCP discovery
540  *
541  * @v dhcp              DHCP session
542  */
543 static void dhcp_request_expired ( struct dhcp_session *dhcp ) {
544
545         /* Retransmit current packet */
546         dhcp_tx ( dhcp );
547 }
548
549 /** DHCP request state operations */
550 static struct dhcp_session_state dhcp_state_request = {
551         .name                   = "request",
552         .tx                     = dhcp_request_tx,
553         .rx                     = dhcp_request_rx,
554         .expired                = dhcp_request_expired,
555         .tx_msgtype             = DHCPREQUEST,
556         .apply_min_timeout      = 0,
557 };
558
559 /**
560  * Construct transmitted packet for ProxyDHCP request
561  *
562  * @v dhcp              DHCP session
563  * @v dhcppkt           DHCP packet
564  * @v peer              Destination address
565  */
566 static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
567                            struct dhcp_packet *dhcppkt,
568                            struct sockaddr_in *peer ) {
569         int rc;
570
571         DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s:%d\n",
572                dhcp, inet_ntoa ( dhcp->proxy_server ), PXE_PORT );
573
574         /* Set server ID */
575         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
576                                     &dhcp->proxy_server,
577                                     sizeof ( dhcp->proxy_server ) ) ) != 0 )
578                 return rc;
579
580         /* Set server address */
581         peer->sin_addr = dhcp->proxy_server;
582         peer->sin_port = htons ( PXE_PORT );
583
584         return 0;
585 }
586
587 /**
588  * Handle received packet during ProxyDHCP request
589  *
590  * @v dhcp              DHCP session
591  * @v dhcppkt           DHCP packet
592  * @v peer              DHCP server address
593  * @v msgtype           DHCP message type
594  */
595 static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
596                             struct dhcp_packet *dhcppkt,
597                             struct sockaddr_in *peer, uint8_t msgtype ) {
598         struct in_addr server_id = { 0 };
599         int rc;
600
601         DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
602                dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
603                ntohs ( peer->sin_port ) );
604
605         /* Identify server ID */
606         dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
607                         &server_id, sizeof ( server_id ) );
608         if ( server_id.s_addr != peer->sin_addr.s_addr )
609                 DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
610         DBGC ( dhcp, "\n" );
611
612         /* Filter out unacceptable responses */
613         if ( peer->sin_port != htons ( PXE_PORT ) )
614                 return;
615         if ( msgtype != DHCPACK )
616                 return;
617         if ( server_id.s_addr /* Linux PXE server omits server ID */ &&
618              ( server_id.s_addr != dhcp->proxy_server.s_addr ) )
619                 return;
620
621         /* Register settings */
622         dhcppkt->settings.name = PROXYDHCP_SETTINGS_NAME;
623         if ( ( rc = register_settings ( &dhcppkt->settings, NULL ) ) != 0 ) {
624                 DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
625                        dhcp, strerror ( rc ) );
626                 dhcp_finished ( dhcp, rc );
627                 return;
628         }
629
630         /* Terminate DHCP */
631         dhcp_finished ( dhcp, 0 );
632 }
633
634 /**
635  * Handle timer expiry during ProxyDHCP request
636  *
637  * @v dhcp              DHCP session
638  */
639 static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) {
640         unsigned long elapsed = ( currticks() - dhcp->start );
641
642         /* Give up waiting for ProxyDHCP before we reach the failure point */
643         if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) {
644                 dhcp_finished ( dhcp, 0 );
645                 return;
646         }
647
648         /* Retransmit current packet */
649         dhcp_tx ( dhcp );
650 }
651
652 /** ProxyDHCP request state operations */
653 static struct dhcp_session_state dhcp_state_proxy = {
654         .name                   = "ProxyDHCP",
655         .tx                     = dhcp_proxy_tx,
656         .rx                     = dhcp_proxy_rx,
657         .expired                = dhcp_proxy_expired,
658         .tx_msgtype             = DHCPREQUEST,
659         .apply_min_timeout      = 0,
660 };
661
662 /**
663  * Construct transmitted packet for PXE Boot Server Discovery
664  *
665  * @v dhcp              DHCP session
666  * @v dhcppkt           DHCP packet
667  * @v peer              Destination address
668  */
669 static int dhcp_pxebs_tx ( struct dhcp_session *dhcp,
670                            struct dhcp_packet *dhcppkt,
671                            struct sockaddr_in *peer ) {
672         struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
673         int rc;
674
675         DBGC ( dhcp, "DHCP %p PXEBS REQUEST to %s:%d for type %d\n",
676                dhcp, inet_ntoa ( dhcp->pxe_server ), PXE_PORT,
677                ntohs ( dhcp->pxe_type ) );
678
679         /* Set boot menu item */
680         menu_item.type = dhcp->pxe_type;
681         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
682                                     &menu_item, sizeof ( menu_item ) ) ) != 0 )
683                 return rc;
684
685         /* Set server address */
686         peer->sin_addr = dhcp->pxe_server;
687         peer->sin_port = htons ( PXE_PORT );
688
689         return 0;
690 }
691
692 /**
693  * Handle received packet during PXE Boot Server Discovery
694  *
695  * @v dhcp              DHCP session
696  * @v dhcppkt           DHCP packet
697  * @v peer              DHCP server address
698  * @v msgtype           DHCP message type
699  */
700 static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
701                             struct dhcp_packet *dhcppkt,
702                             struct sockaddr_in *peer, uint8_t msgtype ) {
703         struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
704         int rc;
705
706         DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
707                dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
708                ntohs ( peer->sin_port ) );
709
710         /* Identify boot menu item */
711         dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
712                         &menu_item, sizeof ( menu_item ) );
713         if ( menu_item.type )
714                 DBGC ( dhcp, " for type %d", ntohs ( menu_item.type ) );
715         DBGC ( dhcp, "\n" );
716
717         /* Filter out unacceptable responses */
718         if ( peer->sin_port != htons ( PXE_PORT ) )
719                 return;
720         if ( msgtype != DHCPACK )
721                 return;
722         if ( menu_item.type != dhcp->pxe_type )
723                 return;
724
725         /* Register settings */
726         dhcppkt->settings.name = PXEBS_SETTINGS_NAME;
727         if ( ( rc = register_settings ( &dhcppkt->settings, NULL ) ) != 0 ) {
728                 DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
729                        dhcp, strerror ( rc ) );
730                 dhcp_finished ( dhcp, rc );
731                 return;
732         }
733
734         /* Terminate DHCP */
735         dhcp_finished ( dhcp, 0 );
736 }
737
738 /**
739  * Handle timer expiry during PXE Boot Server Discovery
740  *
741  * @v dhcp              DHCP session
742  */
743 static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) {
744
745         /* Retransmit current packet */
746         dhcp_tx ( dhcp );
747 }
748
749 /** PXE Boot Server Discovery state operations */
750 static struct dhcp_session_state dhcp_state_pxebs = {
751         .name                   = "PXEBS",
752         .tx                     = dhcp_pxebs_tx,
753         .rx                     = dhcp_pxebs_rx,
754         .expired                = dhcp_pxebs_expired,
755         .tx_msgtype             = DHCPREQUEST,
756         .apply_min_timeout      = 1,
757 };
758
759 /****************************************************************************
760  *
761  * Packet construction
762  *
763  */
764
765 /**
766  * Create a DHCP packet
767  *
768  * @v dhcppkt           DHCP packet structure to fill in
769  * @v netdev            Network device
770  * @v msgtype           DHCP message type
771  * @v options           Initial options to include (or NULL)
772  * @v options_len       Length of initial options
773  * @v data              Buffer for DHCP packet
774  * @v max_len           Size of DHCP packet buffer
775  * @ret rc              Return status code
776  *
777  * Creates a DHCP packet in the specified buffer, and initialise a
778  * DHCP packet structure.
779  */
780 int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
781                          struct net_device *netdev, uint8_t msgtype,
782                          const void *options, size_t options_len,
783                          void *data, size_t max_len ) {
784         struct dhcphdr *dhcphdr = data;
785         unsigned int hlen;
786         int rc;
787
788         /* Sanity check */
789         if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
790                 return -ENOSPC;
791
792         /* Initialise DHCP packet content */
793         memset ( dhcphdr, 0, max_len );
794         dhcphdr->xid = dhcp_xid ( netdev );
795         dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
796         dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
797         dhcphdr->op = dhcp_op[msgtype];
798         /* If hardware length exceeds the chaddr field length, don't
799          * use the chaddr field.  This is as per RFC4390.
800          */
801         hlen = netdev->ll_protocol->ll_addr_len;
802         if ( hlen > sizeof ( dhcphdr->chaddr ) ) {
803                 hlen = 0;
804                 dhcphdr->flags = htons ( BOOTP_FL_BROADCAST );
805         }
806         dhcphdr->hlen = hlen;
807         memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
808         memcpy ( dhcphdr->options, options, options_len );
809
810         /* Initialise DHCP packet structure */
811         memset ( dhcppkt, 0, sizeof ( *dhcppkt ) );
812         dhcppkt_init ( dhcppkt, data, max_len );
813         
814         /* Set DHCP_MESSAGE_TYPE option */
815         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE,
816                                     &msgtype, sizeof ( msgtype ) ) ) != 0 )
817                 return rc;
818
819         return 0;
820 }
821
822 /**
823  * Create DHCP request packet
824  *
825  * @v dhcppkt           DHCP packet structure to fill in
826  * @v netdev            Network device
827  * @v msgtype           DHCP message type
828  * @v ciaddr            Client IP address
829  * @v data              Buffer for DHCP packet
830  * @v max_len           Size of DHCP packet buffer
831  * @ret rc              Return status code
832  *
833  * Creates a DHCP request packet in the specified buffer, and
834  * initialise a DHCP packet structure.
835  */
836 int dhcp_create_request ( struct dhcp_packet *dhcppkt,
837                           struct net_device *netdev, unsigned int msgtype,
838                           struct in_addr ciaddr, void *data, size_t max_len ) {
839         struct device_description *desc = &netdev->dev->desc;
840         struct dhcp_netdev_desc dhcp_desc;
841         struct dhcp_client_id client_id;
842         struct dhcp_client_uuid client_uuid;
843         size_t dhcp_features_len;
844         size_t ll_addr_len;
845         ssize_t len;
846         int rc;
847
848         /* Create DHCP packet */
849         if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype,
850                                          dhcp_request_options_data,
851                                          sizeof ( dhcp_request_options_data ),
852                                          data, max_len ) ) != 0 ) {
853                 DBG ( "DHCP could not create DHCP packet: %s\n",
854                       strerror ( rc ) );
855                 return rc;
856         }
857
858         /* Set client IP address */
859         dhcppkt->dhcphdr->ciaddr = ciaddr;
860
861         /* Add options to identify the feature list */
862         dhcp_features_len = ( dhcp_features_end - dhcp_features );
863         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features,
864                                     dhcp_features_len ) ) != 0 ) {
865                 DBG ( "DHCP could not set features list option: %s\n",
866                       strerror ( rc ) );
867                 return rc;
868         }
869
870         /* Add options to identify the network device */
871         dhcp_desc.type = desc->bus_type;
872         dhcp_desc.vendor = htons ( desc->vendor );
873         dhcp_desc.device = htons ( desc->device );
874         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc,
875                                     sizeof ( dhcp_desc ) ) ) != 0 ) {
876                 DBG ( "DHCP could not set bus ID option: %s\n",
877                       strerror ( rc ) );
878                 return rc;
879         }
880
881         /* Add DHCP client identifier.  Required for Infiniband, and
882          * doesn't hurt other link layers.
883          */
884         client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto );
885         ll_addr_len = netdev->ll_protocol->ll_addr_len;
886         assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) );
887         memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len );
888         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id,
889                                     ( ll_addr_len + 1 ) ) ) != 0 ) {
890                 DBG ( "DHCP could not set client ID: %s\n",
891                       strerror ( rc ) );
892                 return rc;
893         }
894
895         /* Add client UUID, if we have one.  Required for PXE. */
896         client_uuid.type = DHCP_CLIENT_UUID_TYPE;
897         if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting,
898                                           &client_uuid.uuid ) ) >= 0 ) {
899                 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID,
900                                             &client_uuid,
901                                             sizeof ( client_uuid ) ) ) != 0 ) {
902                         DBG ( "DHCP could not set client UUID: %s\n",
903                               strerror ( rc ) );
904                         return rc;
905                 }
906         }
907
908         /* Add user class, if we have one. */
909         if ( ( len = fetch_setting_len ( NULL, &user_class_setting ) ) >= 0 ) {
910                 char user_class[len];
911                 fetch_setting ( NULL, &user_class_setting, user_class,
912                                 sizeof ( user_class ) );
913                 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_USER_CLASS_ID,
914                                             &user_class,
915                                             sizeof ( user_class ) ) ) != 0 ) {
916                         DBG ( "DHCP could not set user class: %s\n",
917                               strerror ( rc ) );
918                         return rc;
919                 }
920         }
921
922         return 0;
923 }
924
925 /****************************************************************************
926  *
927  * Data transfer interface
928  *
929  */
930
931 /**
932  * Transmit DHCP request
933  *
934  * @v dhcp              DHCP session
935  * @ret rc              Return status code
936  */
937 static int dhcp_tx ( struct dhcp_session *dhcp ) {
938         static struct sockaddr_in peer = {
939                 .sin_family = AF_INET,
940         };
941         struct xfer_metadata meta = {
942                 .netdev = dhcp->netdev,
943                 .src = ( struct sockaddr * ) &dhcp->local,
944                 .dest = ( struct sockaddr * ) &peer,
945         };
946         struct io_buffer *iobuf;
947         uint8_t msgtype = dhcp->state->tx_msgtype;
948         struct dhcp_packet dhcppkt;
949         int rc;
950
951         /* Start retry timer.  Do this first so that failures to
952          * transmit will be retried.
953          */
954         start_timer ( &dhcp->timer );
955
956         /* Allocate buffer for packet */
957         iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
958         if ( ! iobuf )
959                 return -ENOMEM;
960
961         /* Create basic DHCP packet in temporary buffer */
962         if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype,
963                                           dhcp->local.sin_addr, iobuf->data,
964                                           iob_tailroom ( iobuf ) ) ) != 0 ) {
965                 DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
966                        dhcp, strerror ( rc ) );
967                 goto done;
968         }
969
970         /* Fill in packet based on current state */
971         if ( ( rc = dhcp->state->tx ( dhcp, &dhcppkt, &peer ) ) != 0 ) {
972                 DBGC ( dhcp, "DHCP %p could not fill DHCP request: %s\n",
973                        dhcp, strerror ( rc ) );
974                 goto done;
975         }
976
977         /* Transmit the packet */
978         iob_put ( iobuf, dhcppkt.len );
979         if ( ( rc = xfer_deliver_iob_meta ( &dhcp->xfer, iob_disown ( iobuf ),
980                                             &meta ) ) != 0 ) {
981                 DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
982                        dhcp, strerror ( rc ) );
983                 goto done;
984         }
985
986  done:
987         free_iob ( iobuf );
988         return rc;
989 }
990
991 /**
992  * Receive new data
993  *
994  * @v xfer              Data transfer interface
995  * @v iobuf             I/O buffer
996  * @v meta              Transfer metadata
997  * @ret rc              Return status code
998  */
999 static int dhcp_deliver_iob ( struct xfer_interface *xfer,
1000                               struct io_buffer *iobuf,
1001                               struct xfer_metadata *meta ) {
1002         struct dhcp_session *dhcp =
1003                 container_of ( xfer, struct dhcp_session, xfer );
1004         struct sockaddr_in *peer;
1005         size_t data_len;
1006         struct dhcp_packet *dhcppkt;
1007         struct dhcphdr *dhcphdr;
1008         uint8_t msgtype = 0;
1009         int rc = 0;
1010
1011         /* Sanity checks */
1012         if ( ! meta ) {
1013                 DBGC ( dhcp, "DHCP %p received packet without metadata\n",
1014                        dhcp );
1015                 rc = -EINVAL;
1016                 goto err_no_meta;
1017         }
1018         if ( ! meta->src ) {
1019                 DBGC ( dhcp, "DHCP %p received packet without source port\n",
1020                        dhcp );
1021                 rc = -EINVAL;
1022                 goto err_no_src;
1023         }
1024         peer = ( struct sockaddr_in * ) meta->src;
1025
1026         /* Create a DHCP packet containing the I/O buffer contents.
1027          * Whilst we could just use the original buffer in situ, that
1028          * would waste the unused space in the packet buffer, and also
1029          * waste a relatively scarce fully-aligned I/O buffer.
1030          */
1031         data_len = iob_len ( iobuf );
1032         dhcppkt = zalloc ( sizeof ( *dhcppkt ) + data_len );
1033         if ( ! dhcppkt ) {
1034                 rc = -ENOMEM;
1035                 goto err_alloc_dhcppkt;
1036         }
1037         dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
1038         memcpy ( dhcphdr, iobuf->data, data_len );
1039         dhcppkt_init ( dhcppkt, dhcphdr, data_len );
1040
1041         /* Identify message type */
1042         dhcppkt_fetch ( dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
1043                         sizeof ( msgtype ) );
1044
1045         /* Check for matching transaction ID */
1046         if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
1047                 DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction "
1048                        "ID\n", dhcp, dhcp_msgtype_name ( msgtype ),
1049                        inet_ntoa ( peer->sin_addr ),
1050                        ntohs ( peer->sin_port ) );
1051                 rc = -EINVAL;
1052                 goto err_xid;
1053         };
1054
1055         /* Handle packet based on current state */
1056         dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype );
1057
1058  err_xid:
1059         dhcppkt_put ( dhcppkt );
1060  err_alloc_dhcppkt:
1061  err_no_src:
1062  err_no_meta:
1063         free_iob ( iobuf );
1064         return rc;
1065 }
1066
1067 /** DHCP data transfer interface operations */
1068 static struct xfer_interface_operations dhcp_xfer_operations = {
1069         .close          = ignore_xfer_close,
1070         .vredirect      = xfer_vopen,
1071         .window         = unlimited_xfer_window,
1072         .alloc_iob      = default_xfer_alloc_iob,
1073         .deliver_iob    = dhcp_deliver_iob,
1074         .deliver_raw    = xfer_deliver_as_iob,
1075 };
1076
1077 /**
1078  * Handle DHCP retry timer expiry
1079  *
1080  * @v timer             DHCP retry timer
1081  * @v fail              Failure indicator
1082  */
1083 static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
1084         struct dhcp_session *dhcp =
1085                 container_of ( timer, struct dhcp_session, timer );
1086
1087         /* If we have failed, terminate DHCP */
1088         if ( fail ) {
1089                 dhcp_finished ( dhcp, -ETIMEDOUT );
1090                 return;
1091         }
1092
1093         /* Handle timer expiry based on current state */
1094         dhcp->state->expired ( dhcp );
1095 }
1096
1097 /****************************************************************************
1098  *
1099  * Job control interface
1100  *
1101  */
1102
1103 /**
1104  * Handle kill() event received via job control interface
1105  *
1106  * @v job               DHCP job control interface
1107  */
1108 static void dhcp_job_kill ( struct job_interface *job ) {
1109         struct dhcp_session *dhcp =
1110                 container_of ( job, struct dhcp_session, job );
1111
1112         /* Terminate DHCP session */
1113         dhcp_finished ( dhcp, -ECANCELED );
1114 }
1115
1116 /** DHCP job control interface operations */
1117 static struct job_interface_operations dhcp_job_operations = {
1118         .done           = ignore_job_done,
1119         .kill           = dhcp_job_kill,
1120         .progress       = ignore_job_progress,
1121 };
1122
1123 /****************************************************************************
1124  *
1125  * Instantiators
1126  *
1127  */
1128
1129 /**
1130  * DHCP peer address for socket opening
1131  *
1132  * This is a dummy address; the only useful portion is the socket
1133  * family (so that we get a UDP connection).  The DHCP client will set
1134  * the IP address and source port explicitly on each transmission.
1135  */
1136 static struct sockaddr dhcp_peer = {
1137         .sa_family = AF_INET,
1138 };
1139
1140 /**
1141  * Start DHCP state machine on a network device
1142  *
1143  * @v job               Job control interface
1144  * @v netdev            Network device
1145  * @ret rc              Return status code
1146  *
1147  * Starts DHCP on the specified network device.  If successful, the
1148  * DHCPACK (and ProxyDHCPACK, if applicable) will be registered as
1149  * option sources.
1150  */
1151 int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
1152         struct dhcp_session *dhcp;
1153         int rc;
1154
1155         /* Allocate and initialise structure */
1156         dhcp = zalloc ( sizeof ( *dhcp ) );
1157         if ( ! dhcp )
1158                 return -ENOMEM;
1159         dhcp->refcnt.free = dhcp_free;
1160         job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
1161         xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
1162         dhcp->netdev = netdev_get ( netdev );
1163         dhcp->local.sin_family = AF_INET;
1164         dhcp->local.sin_port = htons ( BOOTPC_PORT );
1165         dhcp->timer.expired = dhcp_timer_expired;
1166
1167         /* Instantiate child objects and attach to our interfaces */
1168         if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
1169                                   ( struct sockaddr * ) &dhcp->local ) ) != 0 )
1170                 goto err;
1171
1172         /* Enter DHCPDISCOVER state */
1173         dhcp_set_state ( dhcp, &dhcp_state_discover );
1174
1175         /* Attach parent interface, mortalise self, and return */
1176         job_plug_plug ( &dhcp->job, job );
1177         ref_put ( &dhcp->refcnt );
1178         return 0;
1179
1180  err:
1181         dhcp_finished ( dhcp, rc );
1182         ref_put ( &dhcp->refcnt );
1183         return rc;
1184 }
1185
1186 /**
1187  * Start PXE Boot Server Discovery on a network device
1188  *
1189  * @v job               Job control interface
1190  * @v netdev            Network device
1191  * @v pxe_server        PXE server (may be a multicast address)
1192  * @v pxe_type          PXE server type
1193  * @ret rc              Return status code
1194  *
1195  * Starts PXE Boot Server Discovery on the specified network device.
1196  * If successful, the Boot Server ACK will be registered as an option
1197  * source.
1198  */
1199 int start_pxebs ( struct job_interface *job, struct net_device *netdev,
1200                   struct in_addr pxe_server, unsigned int pxe_type ) {
1201         struct dhcp_session *dhcp;
1202         int rc;
1203
1204         /* Allocate and initialise structure */
1205         dhcp = zalloc ( sizeof ( *dhcp ) );
1206         if ( ! dhcp )
1207                 return -ENOMEM;
1208         dhcp->refcnt.free = dhcp_free;
1209         job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
1210         xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
1211         dhcp->netdev = netdev_get ( netdev );
1212         dhcp->local.sin_family = AF_INET;
1213         fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting,
1214                              &dhcp->local.sin_addr );
1215         dhcp->local.sin_port = htons ( BOOTPC_PORT );
1216         dhcp->pxe_server = pxe_server;
1217         dhcp->pxe_type = htons ( pxe_type );
1218         dhcp->timer.expired = dhcp_timer_expired;
1219
1220         /* Instantiate child objects and attach to our interfaces */
1221         if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
1222                                   ( struct sockaddr * ) &dhcp->local ) ) != 0 )
1223                 goto err;
1224
1225         /* Enter PXEBS state */
1226         dhcp_set_state ( dhcp, &dhcp_state_pxebs );
1227
1228         /* Attach parent interface, mortalise self, and return */
1229         job_plug_plug ( &dhcp->job, job );
1230         ref_put ( &dhcp->refcnt );
1231         return 0;
1232
1233  err:
1234         dhcp_finished ( dhcp, rc );
1235         ref_put ( &dhcp->refcnt );
1236         return rc;
1237 }