[efi] Include installed protocol list in unknown handle names
[ipxe.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., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <byteswap.h>
33 #include <ipxe/if_ether.h>
34 #include <ipxe/iobuf.h>
35 #include <ipxe/netdevice.h>
36 #include <ipxe/device.h>
37 #include <ipxe/xfer.h>
38 #include <ipxe/open.h>
39 #include <ipxe/job.h>
40 #include <ipxe/retry.h>
41 #include <ipxe/tcpip.h>
42 #include <ipxe/ip.h>
43 #include <ipxe/uuid.h>
44 #include <ipxe/timer.h>
45 #include <ipxe/settings.h>
46 #include <ipxe/dhcp.h>
47 #include <ipxe/dhcpopts.h>
48 #include <ipxe/dhcppkt.h>
49 #include <ipxe/dhcp_arch.h>
50 #include <ipxe/features.h>
51 #include <config/dhcp.h>
52
53 /** @file
54 *
55 * Dynamic Host Configuration Protocol
56 *
57 */
58
59 struct dhcp_session;
60 static int dhcp_tx ( struct dhcp_session *dhcp );
61
62 /**
63 * DHCP operation types
64 *
65 * This table maps from DHCP message types (i.e. values of the @c
66 * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP
67 * packet.
68 */
69 static const uint8_t dhcp_op[] = {
70 [DHCPDISCOVER] = BOOTP_REQUEST,
71 [DHCPOFFER] = BOOTP_REPLY,
72 [DHCPREQUEST] = BOOTP_REQUEST,
73 [DHCPDECLINE] = BOOTP_REQUEST,
74 [DHCPACK] = BOOTP_REPLY,
75 [DHCPNAK] = BOOTP_REPLY,
76 [DHCPRELEASE] = BOOTP_REQUEST,
77 [DHCPINFORM] = BOOTP_REQUEST,
78 };
79
80 /** Raw option data for options common to all DHCP requests */
81 static uint8_t dhcp_request_options_data[] = {
82 DHCP_MESSAGE_TYPE, DHCP_BYTE ( 0 ),
83 DHCP_MAX_MESSAGE_SIZE,
84 DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ),
85 DHCP_CLIENT_ARCHITECTURE, DHCP_ARCH_CLIENT_ARCHITECTURE,
86 DHCP_CLIENT_NDI, DHCP_ARCH_CLIENT_NDI,
87 DHCP_VENDOR_CLASS_ID, DHCP_ARCH_VENDOR_CLASS_ID,
88 DHCP_USER_CLASS_ID, DHCP_STRING ( 'i', 'P', 'X', 'E' ),
89 DHCP_PARAMETER_REQUEST_LIST,
90 DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS,
91 DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME,
92 DHCP_ROOT_PATH, DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID,
93 DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME,
94 DHCP_DOMAIN_SEARCH,
95 128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */
96 DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ),
97 DHCP_END
98 };
99
100 /** DHCP server address setting */
101 const struct setting dhcp_server_setting __setting ( SETTING_MISC,
102 dhcp-server ) = {
103 .name = "dhcp-server",
104 .description = "DHCP server",
105 .tag = DHCP_SERVER_IDENTIFIER,
106 .type = &setting_type_ipv4,
107 };
108
109 /**
110 * Most recent DHCP transaction ID
111 *
112 * This is exposed for use by the fakedhcp code when reconstructing
113 * DHCP packets for PXE NBPs.
114 */
115 uint32_t dhcp_last_xid;
116
117 /**
118 * Name a DHCP packet type
119 *
120 * @v msgtype DHCP message type
121 * @ret string DHCP mesasge type name
122 */
123 static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) {
124 switch ( msgtype ) {
125 case DHCPNONE: return "BOOTP"; /* Non-DHCP packet */
126 case DHCPDISCOVER: return "DHCPDISCOVER";
127 case DHCPOFFER: return "DHCPOFFER";
128 case DHCPREQUEST: return "DHCPREQUEST";
129 case DHCPDECLINE: return "DHCPDECLINE";
130 case DHCPACK: return "DHCPACK";
131 case DHCPNAK: return "DHCPNAK";
132 case DHCPRELEASE: return "DHCPRELEASE";
133 case DHCPINFORM: return "DHCPINFORM";
134 default: return "DHCP<invalid>";
135 }
136 }
137
138 /****************************************************************************
139 *
140 * DHCP session
141 *
142 */
143
144 struct dhcp_session;
145
146 /** DHCP session state operations */
147 struct dhcp_session_state {
148 /** State name */
149 const char *name;
150 /**
151 * Construct transmitted packet
152 *
153 * @v dhcp DHCP session
154 * @v dhcppkt DHCP packet
155 * @v peer Destination address
156 */
157 int ( * tx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt,
158 struct sockaddr_in *peer );
159 /**
160 * Handle received packet
161 *
162 * @v dhcp DHCP session
163 * @v dhcppkt DHCP packet
164 * @v peer DHCP server address
165 * @v msgtype DHCP message type
166 * @v server_id DHCP server ID
167 * @v pseudo_id DHCP server pseudo-ID
168 */
169 void ( * rx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt,
170 struct sockaddr_in *peer, uint8_t msgtype,
171 struct in_addr server_id, struct in_addr pseudo_id );
172 /**
173 * Handle timer expiry
174 *
175 * @v dhcp DHCP session
176 */
177 void ( * expired ) ( struct dhcp_session *dhcp );
178 /** Transmitted message type */
179 uint8_t tx_msgtype;
180 /** Timeout parameters */
181 uint8_t min_timeout_sec;
182 uint8_t max_timeout_sec;
183 };
184
185 static struct dhcp_session_state dhcp_state_discover;
186 static struct dhcp_session_state dhcp_state_request;
187 static struct dhcp_session_state dhcp_state_proxy;
188 static struct dhcp_session_state dhcp_state_pxebs;
189
190 /** A DHCP session */
191 struct dhcp_session {
192 /** Reference counter */
193 struct refcnt refcnt;
194 /** Job control interface */
195 struct interface job;
196 /** Data transfer interface */
197 struct interface xfer;
198
199 /** Network device being configured */
200 struct net_device *netdev;
201 /** Local socket address */
202 struct sockaddr_in local;
203 /** State of the session */
204 struct dhcp_session_state *state;
205 /** Transaction ID (in network-endian order) */
206 uint32_t xid;
207
208 /** Offered IP address */
209 struct in_addr offer;
210 /** DHCP server */
211 struct in_addr server;
212 /** DHCP offer priority */
213 int priority;
214
215 /** ProxyDHCP protocol extensions should be ignored */
216 int no_pxedhcp;
217 /** ProxyDHCP server */
218 struct in_addr proxy_server;
219 /** ProxyDHCP offer */
220 struct dhcp_packet *proxy_offer;
221 /** ProxyDHCP offer priority */
222 int proxy_priority;
223
224 /** PXE Boot Server type */
225 uint16_t pxe_type;
226 /** List of PXE Boot Servers to attempt */
227 struct in_addr *pxe_attempt;
228 /** List of PXE Boot Servers to accept */
229 struct in_addr *pxe_accept;
230
231 /** Retransmission timer */
232 struct retry_timer timer;
233 /** Transmission counter */
234 unsigned int count;
235 /** Start time of the current state (in ticks) */
236 unsigned long start;
237 };
238
239 /**
240 * Free DHCP session
241 *
242 * @v refcnt Reference counter
243 */
244 static void dhcp_free ( struct refcnt *refcnt ) {
245 struct dhcp_session *dhcp =
246 container_of ( refcnt, struct dhcp_session, refcnt );
247
248 netdev_put ( dhcp->netdev );
249 dhcppkt_put ( dhcp->proxy_offer );
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 /* Stop retry timer */
262 stop_timer ( &dhcp->timer );
263
264 /* Shut down interfaces */
265 intf_shutdown ( &dhcp->xfer, rc );
266 intf_shutdown ( &dhcp->job, rc );
267 }
268
269 /**
270 * Transition to new DHCP session state
271 *
272 * @v dhcp DHCP session
273 * @v state New session state
274 */
275 static void dhcp_set_state ( struct dhcp_session *dhcp,
276 struct dhcp_session_state *state ) {
277
278 DBGC ( dhcp, "DHCP %p entering %s state\n", dhcp, state->name );
279 dhcp->state = state;
280 dhcp->start = currticks();
281 stop_timer ( &dhcp->timer );
282 set_timer_limits ( &dhcp->timer,
283 ( state->min_timeout_sec * TICKS_PER_SEC ),
284 ( state->max_timeout_sec * TICKS_PER_SEC ) );
285 start_timer_nodelay ( &dhcp->timer );
286 }
287
288 /**
289 * Check if DHCP packet contains PXE options
290 *
291 * @v dhcppkt DHCP packet
292 * @ret has_pxeopts DHCP packet contains PXE options
293 *
294 * It is assumed that the packet is already known to contain option 60
295 * set to "PXEClient".
296 */
297 static int dhcp_has_pxeopts ( struct dhcp_packet *dhcppkt ) {
298
299 /* Check for a next-server and boot filename */
300 if ( dhcppkt->dhcphdr->siaddr.s_addr &&
301 ( dhcppkt_fetch ( dhcppkt, DHCP_BOOTFILE_NAME, NULL, 0 ) > 0 ) )
302 return 1;
303
304 /* Check for a PXE boot menu */
305 if ( dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU, NULL, 0 ) > 0 )
306 return 1;
307
308 return 0;
309 }
310
311 /****************************************************************************
312 *
313 * DHCP state machine
314 *
315 */
316
317 /**
318 * Construct transmitted packet for DHCP discovery
319 *
320 * @v dhcp DHCP session
321 * @v dhcppkt DHCP packet
322 * @v peer Destination address
323 */
324 static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
325 struct dhcp_packet *dhcppkt __unused,
326 struct sockaddr_in *peer ) {
327
328 DBGC ( dhcp, "DHCP %p DHCPDISCOVER\n", dhcp );
329
330 /* Set server address */
331 peer->sin_addr.s_addr = INADDR_BROADCAST;
332 peer->sin_port = htons ( BOOTPS_PORT );
333
334 return 0;
335 }
336
337 /**
338 * Handle received packet during DHCP discovery
339 *
340 * @v dhcp DHCP session
341 * @v dhcppkt DHCP packet
342 * @v peer DHCP server address
343 * @v msgtype DHCP message type
344 * @v server_id DHCP server ID
345 * @v pseudo_id DHCP server pseudo-ID
346 */
347 static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
348 struct dhcp_packet *dhcppkt,
349 struct sockaddr_in *peer, uint8_t msgtype,
350 struct in_addr server_id,
351 struct in_addr pseudo_id ) {
352 struct in_addr ip;
353 char vci[9]; /* "PXEClient" */
354 int vci_len;
355 int has_pxeclient;
356 int8_t priority = 0;
357 uint8_t no_pxedhcp = 0;
358 unsigned long elapsed;
359
360 DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
361 dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
362 ntohs ( peer->sin_port ) );
363 if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
364 ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
365 DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
366 DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
367 }
368
369 /* Identify offered IP address */
370 ip = dhcppkt->dhcphdr->yiaddr;
371 if ( ip.s_addr )
372 DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
373
374 /* Identify "PXEClient" vendor class */
375 vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID,
376 vci, sizeof ( vci ) );
377 has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) &&
378 ( strncmp ( "PXEClient", vci, sizeof (vci) ) == 0 ));
379 if ( has_pxeclient ) {
380 DBGC ( dhcp, "%s",
381 ( dhcp_has_pxeopts ( dhcppkt ) ? " pxe" : " proxy" ) );
382 }
383
384 /* Identify priority */
385 dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
386 sizeof ( priority ) );
387 if ( priority )
388 DBGC ( dhcp, " pri %d", priority );
389
390 /* Identify ignore-PXE flag */
391 dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp,
392 sizeof ( no_pxedhcp ) );
393 if ( no_pxedhcp )
394 DBGC ( dhcp, " nopxe" );
395 DBGC ( dhcp, "\n" );
396
397 /* Select as DHCP offer, if applicable */
398 if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
399 ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) &&
400 ( priority >= dhcp->priority ) ) {
401 dhcp->offer = ip;
402 dhcp->server = server_id;
403 dhcp->priority = priority;
404 dhcp->no_pxedhcp = no_pxedhcp;
405 }
406
407 /* Select as ProxyDHCP offer, if applicable */
408 if ( pseudo_id.s_addr && has_pxeclient &&
409 ( priority >= dhcp->proxy_priority ) ) {
410 dhcppkt_put ( dhcp->proxy_offer );
411 dhcp->proxy_server = pseudo_id;
412 dhcp->proxy_offer = dhcppkt_get ( dhcppkt );
413 dhcp->proxy_priority = priority;
414 }
415
416 /* We can exit the discovery state when we have a valid
417 * DHCPOFFER, and either:
418 *
419 * o The DHCPOFFER instructs us to ignore ProxyDHCPOFFERs, or
420 * o We have a valid ProxyDHCPOFFER, or
421 * o We have allowed sufficient time for ProxyDHCPOFFERs.
422 */
423
424 /* If we don't yet have a DHCPOFFER, do nothing */
425 if ( ! dhcp->offer.s_addr )
426 return;
427
428 /* If we can't yet transition to DHCPREQUEST, do nothing */
429 elapsed = ( currticks() - dhcp->start );
430 if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_offer ||
431 ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) )
432 return;
433
434 /* Transition to DHCPREQUEST */
435 dhcp_set_state ( dhcp, &dhcp_state_request );
436 }
437
438 /**
439 * Handle timer expiry during DHCP discovery
440 *
441 * @v dhcp DHCP session
442 */
443 static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) {
444 unsigned long elapsed = ( currticks() - dhcp->start );
445
446 /* If link is blocked, defer DHCP discovery (and reset timeout) */
447 if ( netdev_link_blocked ( dhcp->netdev ) ) {
448 DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp );
449 start_timer_fixed ( &dhcp->timer,
450 ( DHCP_DISC_START_TIMEOUT_SEC *
451 TICKS_PER_SEC ) );
452 return;
453 }
454
455 /* Give up waiting for ProxyDHCP before we reach the failure point */
456 if ( dhcp->offer.s_addr &&
457 ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) {
458 dhcp_set_state ( dhcp, &dhcp_state_request );
459 return;
460 }
461
462 /* Otherwise, retransmit current packet */
463 dhcp_tx ( dhcp );
464 }
465
466 /** DHCP discovery state operations */
467 static struct dhcp_session_state dhcp_state_discover = {
468 .name = "discovery",
469 .tx = dhcp_discovery_tx,
470 .rx = dhcp_discovery_rx,
471 .expired = dhcp_discovery_expired,
472 .tx_msgtype = DHCPDISCOVER,
473 .min_timeout_sec = DHCP_DISC_START_TIMEOUT_SEC,
474 .max_timeout_sec = DHCP_DISC_END_TIMEOUT_SEC,
475 };
476
477 /**
478 * Construct transmitted packet for DHCP request
479 *
480 * @v dhcp DHCP session
481 * @v dhcppkt DHCP packet
482 * @v peer Destination address
483 */
484 static int dhcp_request_tx ( struct dhcp_session *dhcp,
485 struct dhcp_packet *dhcppkt,
486 struct sockaddr_in *peer ) {
487 int rc;
488
489 DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
490 dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT );
491 DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) );
492
493 /* Set server ID */
494 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
495 &dhcp->server,
496 sizeof ( dhcp->server ) ) ) != 0 )
497 return rc;
498
499 /* Set requested IP address */
500 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
501 &dhcp->offer,
502 sizeof ( dhcp->offer ) ) ) != 0 )
503 return rc;
504
505 /* Set server address */
506 peer->sin_addr.s_addr = INADDR_BROADCAST;
507 peer->sin_port = htons ( BOOTPS_PORT );
508
509 return 0;
510 }
511
512 /**
513 * Handle received packet during DHCP request
514 *
515 * @v dhcp DHCP session
516 * @v dhcppkt DHCP packet
517 * @v peer DHCP server address
518 * @v msgtype DHCP message type
519 * @v server_id DHCP server ID
520 * @v pseudo_id DHCP server pseudo-ID
521 */
522 static void dhcp_request_rx ( struct dhcp_session *dhcp,
523 struct dhcp_packet *dhcppkt,
524 struct sockaddr_in *peer, uint8_t msgtype,
525 struct in_addr server_id,
526 struct in_addr pseudo_id ) {
527 struct in_addr ip;
528 struct settings *parent;
529 struct settings *settings;
530 int rc;
531
532 DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
533 dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
534 ntohs ( peer->sin_port ) );
535 if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
536 ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
537 DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
538 DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
539 }
540
541 /* Identify leased IP address */
542 ip = dhcppkt->dhcphdr->yiaddr;
543 if ( ip.s_addr )
544 DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
545 DBGC ( dhcp, "\n" );
546
547 /* Filter out unacceptable responses */
548 if ( peer->sin_port != htons ( BOOTPS_PORT ) )
549 return;
550 if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
551 return;
552 if ( server_id.s_addr != dhcp->server.s_addr )
553 return;
554 if ( ip.s_addr != dhcp->offer.s_addr )
555 return;
556
557 /* Record assigned address */
558 dhcp->local.sin_addr = ip;
559
560 /* Register settings */
561 parent = netdev_settings ( dhcp->netdev );
562 settings = &dhcppkt->settings;
563 if ( ( rc = register_settings ( settings, parent,
564 DHCP_SETTINGS_NAME ) ) != 0 ) {
565 DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
566 dhcp, strerror ( rc ) );
567 dhcp_finished ( dhcp, rc );
568 return;
569 }
570
571 /* Perform ProxyDHCP if applicable */
572 if ( dhcp->proxy_offer /* Have ProxyDHCP offer */ &&
573 ( ! dhcp->no_pxedhcp ) /* ProxyDHCP not disabled */ ) {
574 if ( dhcp_has_pxeopts ( dhcp->proxy_offer ) ) {
575 /* PXE options already present; register settings
576 * without performing a ProxyDHCPREQUEST
577 */
578 settings = &dhcp->proxy_offer->settings;
579 if ( ( rc = register_settings ( settings, NULL,
580 PROXYDHCP_SETTINGS_NAME ) ) != 0 ) {
581 DBGC ( dhcp, "DHCP %p could not register "
582 "proxy settings: %s\n",
583 dhcp, strerror ( rc ) );
584 dhcp_finished ( dhcp, rc );
585 return;
586 }
587 } else {
588 /* PXE options not present; use a ProxyDHCPREQUEST */
589 dhcp_set_state ( dhcp, &dhcp_state_proxy );
590 return;
591 }
592 }
593
594 /* Terminate DHCP */
595 dhcp_finished ( dhcp, 0 );
596 }
597
598 /**
599 * Handle timer expiry during DHCP discovery
600 *
601 * @v dhcp DHCP session
602 */
603 static void dhcp_request_expired ( struct dhcp_session *dhcp ) {
604
605 /* Retransmit current packet */
606 dhcp_tx ( dhcp );
607 }
608
609 /** DHCP request state operations */
610 static struct dhcp_session_state dhcp_state_request = {
611 .name = "request",
612 .tx = dhcp_request_tx,
613 .rx = dhcp_request_rx,
614 .expired = dhcp_request_expired,
615 .tx_msgtype = DHCPREQUEST,
616 .min_timeout_sec = DHCP_REQ_START_TIMEOUT_SEC,
617 .max_timeout_sec = DHCP_REQ_END_TIMEOUT_SEC,
618 };
619
620 /**
621 * Construct transmitted packet for ProxyDHCP request
622 *
623 * @v dhcp DHCP session
624 * @v dhcppkt DHCP packet
625 * @v peer Destination address
626 */
627 static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
628 struct dhcp_packet *dhcppkt,
629 struct sockaddr_in *peer ) {
630 int rc;
631
632 DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s\n", dhcp,
633 inet_ntoa ( dhcp->proxy_server ) );
634
635 /* Set server ID */
636 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
637 &dhcp->proxy_server,
638 sizeof ( dhcp->proxy_server ) ) ) != 0 )
639 return rc;
640
641 /* Set server address */
642 peer->sin_addr = dhcp->proxy_server;
643 peer->sin_port = htons ( PXE_PORT );
644
645 return 0;
646 }
647
648 /**
649 * Handle received packet during ProxyDHCP request
650 *
651 * @v dhcp DHCP session
652 * @v dhcppkt DHCP packet
653 * @v peer DHCP server address
654 * @v msgtype DHCP message type
655 * @v server_id DHCP server ID
656 * @v pseudo_id DHCP server pseudo-ID
657 */
658 static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
659 struct dhcp_packet *dhcppkt,
660 struct sockaddr_in *peer, uint8_t msgtype,
661 struct in_addr server_id,
662 struct in_addr pseudo_id ) {
663 struct settings *settings = &dhcppkt->settings;
664 int rc;
665
666 DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
667 dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
668 ntohs ( peer->sin_port ) );
669 if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
670 ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
671 DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
672 DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
673 }
674 if ( dhcp_has_pxeopts ( dhcppkt ) )
675 DBGC ( dhcp, " pxe" );
676 DBGC ( dhcp, "\n" );
677
678 /* Filter out unacceptable responses */
679 if ( peer->sin_port != ntohs ( PXE_PORT ) )
680 return;
681 if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) )
682 return;
683 if ( ( pseudo_id.s_addr != dhcp->proxy_server.s_addr ) )
684 return;
685 if ( ! dhcp_has_pxeopts ( dhcppkt ) )
686 return;
687
688 /* Register settings */
689 if ( ( rc = register_settings ( settings, NULL,
690 PROXYDHCP_SETTINGS_NAME ) ) != 0 ) {
691 DBGC ( dhcp, "DHCP %p could not register proxy settings: %s\n",
692 dhcp, strerror ( rc ) );
693 dhcp_finished ( dhcp, rc );
694 return;
695 }
696
697 /* Terminate DHCP */
698 dhcp_finished ( dhcp, 0 );
699 }
700
701 /**
702 * Handle timer expiry during ProxyDHCP request
703 *
704 * @v dhcp DHCP session
705 */
706 static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) {
707 unsigned long elapsed = ( currticks() - dhcp->start );
708
709 /* Give up waiting for ProxyDHCP before we reach the failure point */
710 if ( elapsed > DHCP_REQ_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) {
711 dhcp_finished ( dhcp, 0 );
712 return;
713 }
714
715 /* Retransmit current packet */
716 dhcp_tx ( dhcp );
717 }
718
719 /** ProxyDHCP request state operations */
720 static struct dhcp_session_state dhcp_state_proxy = {
721 .name = "ProxyDHCP",
722 .tx = dhcp_proxy_tx,
723 .rx = dhcp_proxy_rx,
724 .expired = dhcp_proxy_expired,
725 .tx_msgtype = DHCPREQUEST,
726 .min_timeout_sec = DHCP_PROXY_START_TIMEOUT_SEC,
727 .max_timeout_sec = DHCP_PROXY_END_TIMEOUT_SEC,
728 };
729
730 /**
731 * Construct transmitted packet for PXE Boot Server Discovery
732 *
733 * @v dhcp DHCP session
734 * @v dhcppkt DHCP packet
735 * @v peer Destination address
736 */
737 static int dhcp_pxebs_tx ( struct dhcp_session *dhcp,
738 struct dhcp_packet *dhcppkt,
739 struct sockaddr_in *peer ) {
740 struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
741 int rc;
742
743 /* Set server address */
744 peer->sin_addr = *(dhcp->pxe_attempt);
745 peer->sin_port = ( ( peer->sin_addr.s_addr == INADDR_BROADCAST ) ?
746 htons ( BOOTPS_PORT ) : htons ( PXE_PORT ) );
747
748 DBGC ( dhcp, "DHCP %p PXEBS REQUEST to %s:%d for type %d\n",
749 dhcp, inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ),
750 le16_to_cpu ( dhcp->pxe_type ) );
751
752 /* Set boot menu item */
753 menu_item.type = dhcp->pxe_type;
754 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
755 &menu_item, sizeof ( menu_item ) ) ) != 0 )
756 return rc;
757
758 return 0;
759 }
760
761 /**
762 * Check to see if PXE Boot Server address is acceptable
763 *
764 * @v dhcp DHCP session
765 * @v bs Boot Server address
766 * @ret accept Boot Server is acceptable
767 */
768 static int dhcp_pxebs_accept ( struct dhcp_session *dhcp,
769 struct in_addr bs ) {
770 struct in_addr *accept;
771
772 /* Accept if we have no acceptance filter */
773 if ( ! dhcp->pxe_accept )
774 return 1;
775
776 /* Scan through acceptance list */
777 for ( accept = dhcp->pxe_accept ; accept->s_addr ; accept++ ) {
778 if ( accept->s_addr == bs.s_addr )
779 return 1;
780 }
781
782 DBGC ( dhcp, "DHCP %p rejecting server %s\n",
783 dhcp, inet_ntoa ( bs ) );
784 return 0;
785 }
786
787 /**
788 * Handle received packet during PXE Boot Server Discovery
789 *
790 * @v dhcp DHCP session
791 * @v dhcppkt DHCP packet
792 * @v peer DHCP server address
793 * @v msgtype DHCP message type
794 * @v server_id DHCP server ID
795 * @v pseudo_id DHCP server pseudo-ID
796 */
797 static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
798 struct dhcp_packet *dhcppkt,
799 struct sockaddr_in *peer, uint8_t msgtype,
800 struct in_addr server_id,
801 struct in_addr pseudo_id ) {
802 struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
803 int rc;
804
805 DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
806 dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
807 ntohs ( peer->sin_port ) );
808 if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
809 ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
810 DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
811 DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
812 }
813
814 /* Identify boot menu item */
815 dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
816 &menu_item, sizeof ( menu_item ) );
817 if ( menu_item.type )
818 DBGC ( dhcp, " for type %d", ntohs ( menu_item.type ) );
819 DBGC ( dhcp, "\n" );
820
821 /* Filter out unacceptable responses */
822 if ( ( peer->sin_port != htons ( BOOTPS_PORT ) ) &&
823 ( peer->sin_port != htons ( PXE_PORT ) ) )
824 return;
825 if ( msgtype != DHCPACK )
826 return;
827 if ( menu_item.type != dhcp->pxe_type )
828 return;
829 if ( ! dhcp_pxebs_accept ( dhcp, pseudo_id ) )
830 return;
831
832 /* Register settings */
833 if ( ( rc = register_settings ( &dhcppkt->settings, NULL,
834 PXEBS_SETTINGS_NAME ) ) != 0 ) {
835 DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
836 dhcp, strerror ( rc ) );
837 dhcp_finished ( dhcp, rc );
838 return;
839 }
840
841 /* Terminate DHCP */
842 dhcp_finished ( dhcp, 0 );
843 }
844
845 /**
846 * Handle timer expiry during PXE Boot Server Discovery
847 *
848 * @v dhcp DHCP session
849 */
850 static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) {
851 unsigned long elapsed = ( currticks() - dhcp->start );
852
853 /* Give up waiting before we reach the failure point, and fail
854 * over to the next server in the attempt list
855 */
856 if ( elapsed > PXEBS_MAX_TIMEOUT_SEC * TICKS_PER_SEC ) {
857 dhcp->pxe_attempt++;
858 if ( dhcp->pxe_attempt->s_addr ) {
859 dhcp_set_state ( dhcp, &dhcp_state_pxebs );
860 return;
861 } else {
862 dhcp_finished ( dhcp, -ETIMEDOUT );
863 return;
864 }
865 }
866
867 /* Retransmit current packet */
868 dhcp_tx ( dhcp );
869 }
870
871 /** PXE Boot Server Discovery state operations */
872 static struct dhcp_session_state dhcp_state_pxebs = {
873 .name = "PXEBS",
874 .tx = dhcp_pxebs_tx,
875 .rx = dhcp_pxebs_rx,
876 .expired = dhcp_pxebs_expired,
877 .tx_msgtype = DHCPREQUEST,
878 .min_timeout_sec = PXEBS_START_TIMEOUT_SEC,
879 .max_timeout_sec = PXEBS_END_TIMEOUT_SEC,
880 };
881
882 /****************************************************************************
883 *
884 * Packet construction
885 *
886 */
887
888 /**
889 * Create a DHCP packet
890 *
891 * @v dhcppkt DHCP packet structure to fill in
892 * @v netdev Network device
893 * @v msgtype DHCP message type
894 * @v xid Transaction ID (in network-endian order)
895 * @v options Initial options to include (or NULL)
896 * @v options_len Length of initial options
897 * @v data Buffer for DHCP packet
898 * @v max_len Size of DHCP packet buffer
899 * @ret rc Return status code
900 *
901 * Creates a DHCP packet in the specified buffer, and initialise a
902 * DHCP packet structure.
903 */
904 int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
905 struct net_device *netdev, uint8_t msgtype,
906 uint32_t xid, const void *options, size_t options_len,
907 void *data, size_t max_len ) {
908 struct dhcphdr *dhcphdr = data;
909 int rc;
910
911 /* Sanity check */
912 if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
913 return -ENOSPC;
914
915 /* Initialise DHCP packet content */
916 memset ( dhcphdr, 0, max_len );
917 dhcphdr->xid = xid;
918 dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
919 dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
920 dhcphdr->op = dhcp_op[msgtype];
921 dhcphdr->hlen = netdev->ll_protocol->ll_addr_len;
922 memcpy ( dhcphdr->chaddr, netdev->ll_addr,
923 netdev->ll_protocol->ll_addr_len );
924 memcpy ( dhcphdr->options, options, options_len );
925
926 /* If the local link-layer address functions only as a name
927 * (i.e. cannot be used as a destination address), then
928 * request broadcast responses.
929 */
930 if ( netdev->ll_protocol->flags & LL_NAME_ONLY )
931 dhcphdr->flags |= htons ( BOOTP_FL_BROADCAST );
932
933 /* If the network device already has an IPv4 address then
934 * unicast responses from the DHCP server may be rejected, so
935 * request broadcast responses.
936 */
937 if ( ipv4_has_any_addr ( netdev ) )
938 dhcphdr->flags |= htons ( BOOTP_FL_BROADCAST );
939
940 /* Initialise DHCP packet structure */
941 memset ( dhcppkt, 0, sizeof ( *dhcppkt ) );
942 dhcppkt_init ( dhcppkt, data, max_len );
943
944 /* Set DHCP_MESSAGE_TYPE option */
945 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE,
946 &msgtype, sizeof ( msgtype ) ) ) != 0 )
947 return rc;
948
949 return 0;
950 }
951
952 /**
953 * Create DHCP request packet
954 *
955 * @v dhcppkt DHCP packet structure to fill in
956 * @v netdev Network device
957 * @v msgtype DHCP message type
958 * @v xid Transaction ID (in network-endian order)
959 * @v ciaddr Client IP address
960 * @v data Buffer for DHCP packet
961 * @v max_len Size of DHCP packet buffer
962 * @ret rc Return status code
963 *
964 * Creates a DHCP request packet in the specified buffer, and
965 * initialise a DHCP packet structure.
966 */
967 int dhcp_create_request ( struct dhcp_packet *dhcppkt,
968 struct net_device *netdev, unsigned int msgtype,
969 uint32_t xid, struct in_addr ciaddr,
970 void *data, size_t max_len ) {
971 struct dhcp_netdev_desc dhcp_desc;
972 struct dhcp_client_id client_id;
973 struct dhcp_client_uuid client_uuid;
974 uint8_t *dhcp_features;
975 size_t dhcp_features_len;
976 size_t ll_addr_len;
977 void *user_class;
978 ssize_t len;
979 int rc;
980
981 /* Create DHCP packet */
982 if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype, xid,
983 dhcp_request_options_data,
984 sizeof ( dhcp_request_options_data ),
985 data, max_len ) ) != 0 ) {
986 DBG ( "DHCP could not create DHCP packet: %s\n",
987 strerror ( rc ) );
988 goto err_create_packet;
989 }
990
991 /* Set client IP address */
992 dhcppkt->dhcphdr->ciaddr = ciaddr;
993
994 /* Add options to identify the feature list */
995 dhcp_features = table_start ( DHCP_FEATURES );
996 dhcp_features_len = table_num_entries ( DHCP_FEATURES );
997 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features,
998 dhcp_features_len ) ) != 0 ) {
999 DBG ( "DHCP could not set features list option: %s\n",
1000 strerror ( rc ) );
1001 goto err_store_features;
1002 }
1003
1004 /* Add options to identify the network device */
1005 fetch_raw_setting ( netdev_settings ( netdev ), &busid_setting,
1006 &dhcp_desc, sizeof ( dhcp_desc ) );
1007 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc,
1008 sizeof ( dhcp_desc ) ) ) != 0 ) {
1009 DBG ( "DHCP could not set bus ID option: %s\n",
1010 strerror ( rc ) );
1011 goto err_store_busid;
1012 }
1013
1014 /* Add DHCP client identifier. Required for Infiniband, and
1015 * doesn't hurt other link layers.
1016 */
1017 client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto );
1018 ll_addr_len = netdev->ll_protocol->ll_addr_len;
1019 assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) );
1020 memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len );
1021 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id,
1022 ( ll_addr_len + 1 ) ) ) != 0 ) {
1023 DBG ( "DHCP could not set client ID: %s\n",
1024 strerror ( rc ) );
1025 goto err_store_client_id;
1026 }
1027
1028 /* Add client UUID, if we have one. Required for PXE. The
1029 * PXE spec does not specify a byte ordering for UUIDs, but
1030 * RFC4578 suggests that it follows the EFI spec, in which the
1031 * first three fields are little-endian.
1032 */
1033 client_uuid.type = DHCP_CLIENT_UUID_TYPE;
1034 if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting,
1035 &client_uuid.uuid ) ) >= 0 ) {
1036 uuid_mangle ( &client_uuid.uuid );
1037 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID,
1038 &client_uuid,
1039 sizeof ( client_uuid ) ) ) != 0 ) {
1040 DBG ( "DHCP could not set client UUID: %s\n",
1041 strerror ( rc ) );
1042 goto err_store_client_uuid;
1043 }
1044 }
1045
1046 /* Add user class, if we have one. */
1047 if ( ( len = fetch_raw_setting_copy ( NULL, &user_class_setting,
1048 &user_class ) ) >= 0 ) {
1049 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_USER_CLASS_ID,
1050 user_class, len ) ) != 0 ) {
1051 DBG ( "DHCP could not set user class: %s\n",
1052 strerror ( rc ) );
1053 goto err_store_user_class;
1054 }
1055 }
1056
1057 err_store_user_class:
1058 free ( user_class );
1059 err_store_client_uuid:
1060 err_store_client_id:
1061 err_store_busid:
1062 err_store_features:
1063 err_create_packet:
1064 return rc;
1065 }
1066
1067 /****************************************************************************
1068 *
1069 * Data transfer interface
1070 *
1071 */
1072
1073 /**
1074 * Transmit DHCP request
1075 *
1076 * @v dhcp DHCP session
1077 * @ret rc Return status code
1078 */
1079 static int dhcp_tx ( struct dhcp_session *dhcp ) {
1080 static struct sockaddr_in peer = {
1081 .sin_family = AF_INET,
1082 };
1083 struct xfer_metadata meta = {
1084 .netdev = dhcp->netdev,
1085 .src = ( struct sockaddr * ) &dhcp->local,
1086 .dest = ( struct sockaddr * ) &peer,
1087 };
1088 struct io_buffer *iobuf;
1089 uint8_t msgtype = dhcp->state->tx_msgtype;
1090 struct dhcp_packet dhcppkt;
1091 int rc;
1092
1093 /* Start retry timer. Do this first so that failures to
1094 * transmit will be retried.
1095 */
1096 start_timer ( &dhcp->timer );
1097
1098 /* Allocate buffer for packet */
1099 iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
1100 if ( ! iobuf )
1101 return -ENOMEM;
1102
1103 /* Create basic DHCP packet in temporary buffer */
1104 if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype,
1105 dhcp->xid, dhcp->local.sin_addr,
1106 iobuf->data,
1107 iob_tailroom ( iobuf ) ) ) != 0 ) {
1108 DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
1109 dhcp, strerror ( rc ) );
1110 goto done;
1111 }
1112
1113 /* (Ab)use the "secs" field to convey metadata about the DHCP
1114 * session state into packet traces. Useful for extracting
1115 * debug information from non-debug builds.
1116 */
1117 dhcppkt.dhcphdr->secs = htons ( ( ++(dhcp->count) << 2 ) |
1118 ( dhcp->offer.s_addr ? 0x02 : 0 ) |
1119 ( dhcp->proxy_offer ? 0x01 : 0 ) );
1120
1121 /* Fill in packet based on current state */
1122 if ( ( rc = dhcp->state->tx ( dhcp, &dhcppkt, &peer ) ) != 0 ) {
1123 DBGC ( dhcp, "DHCP %p could not fill DHCP request: %s\n",
1124 dhcp, strerror ( rc ) );
1125 goto done;
1126 }
1127
1128 /* Transmit the packet */
1129 iob_put ( iobuf, dhcppkt_len ( &dhcppkt ) );
1130 if ( ( rc = xfer_deliver ( &dhcp->xfer, iob_disown ( iobuf ),
1131 &meta ) ) != 0 ) {
1132 DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
1133 dhcp, strerror ( rc ) );
1134 goto done;
1135 }
1136
1137 done:
1138 free_iob ( iobuf );
1139 return rc;
1140 }
1141
1142 /**
1143 * Receive new data
1144 *
1145 * @v dhcp DHCP session
1146 * @v iobuf I/O buffer
1147 * @v meta Transfer metadata
1148 * @ret rc Return status code
1149 */
1150 static int dhcp_deliver ( struct dhcp_session *dhcp,
1151 struct io_buffer *iobuf,
1152 struct xfer_metadata *meta ) {
1153 struct net_device *netdev = dhcp->netdev;
1154 struct ll_protocol *ll_protocol = netdev->ll_protocol;
1155 struct sockaddr_in *peer;
1156 size_t data_len;
1157 struct dhcp_packet *dhcppkt;
1158 struct dhcphdr *dhcphdr;
1159 uint8_t msgtype = 0;
1160 struct in_addr server_id = { 0 };
1161 struct in_addr pseudo_id;
1162 int rc = 0;
1163
1164 /* Sanity checks */
1165 if ( ! meta->src ) {
1166 DBGC ( dhcp, "DHCP %p received packet without source port\n",
1167 dhcp );
1168 rc = -EINVAL;
1169 goto err_no_src;
1170 }
1171 peer = ( struct sockaddr_in * ) meta->src;
1172
1173 /* Create a DHCP packet containing the I/O buffer contents.
1174 * Whilst we could just use the original buffer in situ, that
1175 * would waste the unused space in the packet buffer, and also
1176 * waste a relatively scarce fully-aligned I/O buffer.
1177 */
1178 data_len = iob_len ( iobuf );
1179 dhcppkt = zalloc ( sizeof ( *dhcppkt ) + data_len );
1180 if ( ! dhcppkt ) {
1181 rc = -ENOMEM;
1182 goto err_alloc_dhcppkt;
1183 }
1184 dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
1185 memcpy ( dhcphdr, iobuf->data, data_len );
1186 dhcppkt_init ( dhcppkt, dhcphdr, data_len );
1187
1188 /* Identify message type */
1189 dhcppkt_fetch ( dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
1190 sizeof ( msgtype ) );
1191
1192 /* Identify server ID */
1193 dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
1194 &server_id, sizeof ( server_id ) );
1195
1196 /* Identify server pseudo-ID */
1197 pseudo_id = server_id;
1198 if ( ! pseudo_id.s_addr )
1199 pseudo_id = dhcppkt->dhcphdr->siaddr;
1200 if ( ! pseudo_id.s_addr )
1201 pseudo_id = peer->sin_addr;
1202
1203 /* Check for matching transaction ID */
1204 if ( dhcphdr->xid != dhcp->xid ) {
1205 DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction "
1206 "ID\n", dhcp, dhcp_msgtype_name ( msgtype ),
1207 inet_ntoa ( peer->sin_addr ),
1208 ntohs ( peer->sin_port ) );
1209 rc = -EINVAL;
1210 goto err_xid;
1211 };
1212
1213 /* Check for matching client hardware address */
1214 if ( memcmp ( dhcphdr->chaddr, netdev->ll_addr,
1215 ll_protocol->ll_addr_len ) != 0 ) {
1216 DBGC ( dhcp, "DHCP %p %s from %s:%d has bad chaddr %s\n",
1217 dhcp, dhcp_msgtype_name ( msgtype ),
1218 inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ),
1219 ll_protocol->ntoa ( dhcphdr->chaddr ) );
1220 rc = -EINVAL;
1221 goto err_chaddr;
1222 }
1223
1224 /* Handle packet based on current state */
1225 dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id, pseudo_id );
1226
1227 err_chaddr:
1228 err_xid:
1229 dhcppkt_put ( dhcppkt );
1230 err_alloc_dhcppkt:
1231 err_no_src:
1232 free_iob ( iobuf );
1233 return rc;
1234 }
1235
1236 /** DHCP data transfer interface operations */
1237 static struct interface_operation dhcp_xfer_operations[] = {
1238 INTF_OP ( xfer_deliver, struct dhcp_session *, dhcp_deliver ),
1239 };
1240
1241 /** DHCP data transfer interface descriptor */
1242 static struct interface_descriptor dhcp_xfer_desc =
1243 INTF_DESC ( struct dhcp_session, xfer, dhcp_xfer_operations );
1244
1245 /**
1246 * Handle DHCP retry timer expiry
1247 *
1248 * @v timer DHCP retry timer
1249 * @v fail Failure indicator
1250 */
1251 static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
1252 struct dhcp_session *dhcp =
1253 container_of ( timer, struct dhcp_session, timer );
1254
1255 /* If we have failed, terminate DHCP */
1256 if ( fail ) {
1257 dhcp_finished ( dhcp, -ETIMEDOUT );
1258 return;
1259 }
1260
1261 /* Handle timer expiry based on current state */
1262 dhcp->state->expired ( dhcp );
1263 }
1264
1265 /****************************************************************************
1266 *
1267 * Job control interface
1268 *
1269 */
1270
1271 /** DHCP job control interface operations */
1272 static struct interface_operation dhcp_job_op[] = {
1273 INTF_OP ( intf_close, struct dhcp_session *, dhcp_finished ),
1274 };
1275
1276 /** DHCP job control interface descriptor */
1277 static struct interface_descriptor dhcp_job_desc =
1278 INTF_DESC ( struct dhcp_session, job, dhcp_job_op );
1279
1280 /****************************************************************************
1281 *
1282 * Instantiators
1283 *
1284 */
1285
1286 /**
1287 * DHCP peer address for socket opening
1288 *
1289 * This is a dummy address; the only useful portion is the socket
1290 * family (so that we get a UDP connection). The DHCP client will set
1291 * the IP address and source port explicitly on each transmission.
1292 */
1293 static struct sockaddr dhcp_peer = {
1294 .sa_family = AF_INET,
1295 };
1296
1297 /**
1298 * Start DHCP state machine on a network device
1299 *
1300 * @v job Job control interface
1301 * @v netdev Network device
1302 * @ret rc Return status code
1303 *
1304 * Starts DHCP on the specified network device. If successful, the
1305 * DHCPACK (and ProxyDHCPACK, if applicable) will be registered as
1306 * option sources.
1307 */
1308 int start_dhcp ( struct interface *job, struct net_device *netdev ) {
1309 struct dhcp_session *dhcp;
1310 int rc;
1311
1312 /* Allocate and initialise structure */
1313 dhcp = zalloc ( sizeof ( *dhcp ) );
1314 if ( ! dhcp )
1315 return -ENOMEM;
1316 ref_init ( &dhcp->refcnt, dhcp_free );
1317 intf_init ( &dhcp->job, &dhcp_job_desc, &dhcp->refcnt );
1318 intf_init ( &dhcp->xfer, &dhcp_xfer_desc, &dhcp->refcnt );
1319 timer_init ( &dhcp->timer, dhcp_timer_expired, &dhcp->refcnt );
1320 dhcp->netdev = netdev_get ( netdev );
1321 dhcp->local.sin_family = AF_INET;
1322 dhcp->local.sin_port = htons ( BOOTPC_PORT );
1323 dhcp->xid = random();
1324
1325 /* Store DHCP transaction ID for fakedhcp code */
1326 dhcp_last_xid = dhcp->xid;
1327
1328 /* Instantiate child objects and attach to our interfaces */
1329 if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
1330 ( struct sockaddr * ) &dhcp->local ) ) != 0 )
1331 goto err;
1332
1333 /* Enter DHCPDISCOVER state */
1334 dhcp_set_state ( dhcp, &dhcp_state_discover );
1335
1336 /* Attach parent interface, mortalise self, and return */
1337 intf_plug_plug ( &dhcp->job, job );
1338 ref_put ( &dhcp->refcnt );
1339 return 0;
1340
1341 err:
1342 dhcp_finished ( dhcp, rc );
1343 ref_put ( &dhcp->refcnt );
1344 return rc;
1345 }
1346
1347 /**
1348 * Retrieve list of PXE boot servers for a given server type
1349 *
1350 * @v dhcp DHCP session
1351 * @v raw DHCP PXE boot server list
1352 * @v raw_len Length of DHCP PXE boot server list
1353 * @v ip IP address list to fill in
1354 *
1355 * The caller must ensure that the IP address list has sufficient
1356 * space.
1357 */
1358 static void pxebs_list ( struct dhcp_session *dhcp, void *raw,
1359 size_t raw_len, struct in_addr *ip ) {
1360 struct dhcp_pxe_boot_server *server = raw;
1361 size_t server_len;
1362 unsigned int i;
1363
1364 while ( raw_len ) {
1365 if ( raw_len < sizeof ( *server ) ) {
1366 DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
1367 dhcp );
1368 break;
1369 }
1370 server_len = offsetof ( typeof ( *server ),
1371 ip[ server->num_ip ] );
1372 if ( raw_len < server_len ) {
1373 DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
1374 dhcp );
1375 break;
1376 }
1377 if ( server->type == dhcp->pxe_type ) {
1378 for ( i = 0 ; i < server->num_ip ; i++ )
1379 *(ip++) = server->ip[i];
1380 }
1381 server = ( ( ( void * ) server ) + server_len );
1382 raw_len -= server_len;
1383 }
1384 }
1385
1386 /**
1387 * Start PXE Boot Server Discovery on a network device
1388 *
1389 * @v job Job control interface
1390 * @v netdev Network device
1391 * @v pxe_type PXE server type
1392 * @ret rc Return status code
1393 *
1394 * Starts PXE Boot Server Discovery on the specified network device.
1395 * If successful, the Boot Server ACK will be registered as an option
1396 * source.
1397 */
1398 int start_pxebs ( struct interface *job, struct net_device *netdev,
1399 unsigned int pxe_type ) {
1400 struct setting pxe_discovery_control_setting =
1401 { .tag = DHCP_PXE_DISCOVERY_CONTROL };
1402 struct setting pxe_boot_servers_setting =
1403 { .tag = DHCP_PXE_BOOT_SERVERS };
1404 struct setting pxe_boot_server_mcast_setting =
1405 { .tag = DHCP_PXE_BOOT_SERVER_MCAST };
1406 ssize_t pxebs_list_len;
1407 struct dhcp_session *dhcp;
1408 struct in_addr *ip;
1409 unsigned int pxe_discovery_control;
1410 int rc;
1411
1412 /* Get upper bound for PXE boot server IP address list */
1413 pxebs_list_len = fetch_raw_setting ( NULL, &pxe_boot_servers_setting,
1414 NULL, 0 );
1415 if ( pxebs_list_len < 0 )
1416 pxebs_list_len = 0;
1417
1418 /* Allocate and initialise structure */
1419 dhcp = zalloc ( sizeof ( *dhcp ) + sizeof ( *ip ) /* mcast */ +
1420 sizeof ( *ip ) /* bcast */ + pxebs_list_len +
1421 sizeof ( *ip ) /* terminator */ );
1422 if ( ! dhcp )
1423 return -ENOMEM;
1424 ref_init ( &dhcp->refcnt, dhcp_free );
1425 intf_init ( &dhcp->job, &dhcp_job_desc, &dhcp->refcnt );
1426 intf_init ( &dhcp->xfer, &dhcp_xfer_desc, &dhcp->refcnt );
1427 timer_init ( &dhcp->timer, dhcp_timer_expired, &dhcp->refcnt );
1428 dhcp->netdev = netdev_get ( netdev );
1429 dhcp->local.sin_family = AF_INET;
1430 fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting,
1431 &dhcp->local.sin_addr );
1432 dhcp->local.sin_port = htons ( BOOTPC_PORT );
1433 dhcp->pxe_type = cpu_to_le16 ( pxe_type );
1434
1435 /* Construct PXE boot server IP address lists */
1436 pxe_discovery_control =
1437 fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
1438 ip = ( ( ( void * ) dhcp ) + sizeof ( *dhcp ) );
1439 dhcp->pxe_attempt = ip;
1440 if ( ! ( pxe_discovery_control & PXEBS_NO_MULTICAST ) ) {
1441 fetch_ipv4_setting ( NULL, &pxe_boot_server_mcast_setting, ip);
1442 if ( ip->s_addr )
1443 ip++;
1444 }
1445 if ( ! ( pxe_discovery_control & PXEBS_NO_BROADCAST ) )
1446 (ip++)->s_addr = INADDR_BROADCAST;
1447 if ( pxe_discovery_control & PXEBS_NO_UNKNOWN_SERVERS )
1448 dhcp->pxe_accept = ip;
1449 if ( pxebs_list_len ) {
1450 uint8_t buf[pxebs_list_len];
1451
1452 fetch_raw_setting ( NULL, &pxe_boot_servers_setting,
1453 buf, sizeof ( buf ) );
1454 pxebs_list ( dhcp, buf, sizeof ( buf ), ip );
1455 }
1456 if ( ! dhcp->pxe_attempt->s_addr ) {
1457 DBGC ( dhcp, "DHCP %p has no PXE boot servers for type %04x\n",
1458 dhcp, pxe_type );
1459 rc = -EINVAL;
1460 goto err;
1461 }
1462
1463 /* Dump out PXE server lists */
1464 DBGC ( dhcp, "DHCP %p attempting", dhcp );
1465 for ( ip = dhcp->pxe_attempt ; ip->s_addr ; ip++ )
1466 DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
1467 DBGC ( dhcp, "\n" );
1468 if ( dhcp->pxe_accept ) {
1469 DBGC ( dhcp, "DHCP %p accepting", dhcp );
1470 for ( ip = dhcp->pxe_accept ; ip->s_addr ; ip++ )
1471 DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
1472 DBGC ( dhcp, "\n" );
1473 }
1474
1475 /* Instantiate child objects and attach to our interfaces */
1476 if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
1477 ( struct sockaddr * ) &dhcp->local ) ) != 0 )
1478 goto err;
1479
1480 /* Enter PXEBS state */
1481 dhcp_set_state ( dhcp, &dhcp_state_pxebs );
1482
1483 /* Attach parent interface, mortalise self, and return */
1484 intf_plug_plug ( &dhcp->job, job );
1485 ref_put ( &dhcp->refcnt );
1486 return 0;
1487
1488 err:
1489 dhcp_finished ( dhcp, rc );
1490 ref_put ( &dhcp->refcnt );
1491 return rc;
1492 }
1493
1494 /** DHCP network device configurator */
1495 struct net_device_configurator dhcp_configurator __net_device_configurator = {
1496 .name = "dhcp",
1497 .start = start_dhcp,
1498 };