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