2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
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.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL
);
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>
40 #include <ipxe/retry.h>
41 #include <ipxe/tcpip.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>
55 * Dynamic Host Configuration Protocol
60 static int dhcp_tx ( struct dhcp_session
*dhcp
);
63 * DHCP operation types
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
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
,
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
,
95 128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */
96 DHCP_EB_ENCAP
, DHCP_ISCSI_INITIATOR_IQN
),
100 /** DHCP server address setting */
101 const struct setting dhcp_server_setting
__setting ( SETTING_MISC
,
103 .name
= "dhcp-server",
104 .description
= "DHCP server",
105 .tag
= DHCP_SERVER_IDENTIFIER
,
106 .type
= &setting_type_ipv4
,
110 * Most recent DHCP transaction ID
112 * This is exposed for use by the fakedhcp code when reconstructing
113 * DHCP packets for PXE NBPs.
115 uint32_t dhcp_last_xid
;
118 * Name a DHCP packet type
120 * @v msgtype DHCP message type
121 * @ret string DHCP mesasge type name
123 static inline const char * dhcp_msgtype_name ( unsigned int 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>";
138 /****************************************************************************
146 /** DHCP session state operations */
147 struct dhcp_session_state
{
151 * Construct transmitted packet
153 * @v dhcp DHCP session
154 * @v dhcppkt DHCP packet
155 * @v peer Destination address
157 int ( * tx
) ( struct dhcp_session
*dhcp
, struct dhcp_packet
*dhcppkt
,
158 struct sockaddr_in
*peer
);
160 * Handle received packet
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
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
);
173 * Handle timer expiry
175 * @v dhcp DHCP session
177 void ( * expired
) ( struct dhcp_session
*dhcp
);
178 /** Transmitted message type */
180 /** Timeout parameters */
181 uint8_t min_timeout_sec
;
182 uint8_t max_timeout_sec
;
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
;
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
;
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) */
208 /** Offered IP address */
209 struct in_addr offer
;
211 struct in_addr server
;
212 /** DHCP offer priority */
215 /** ProxyDHCP protocol extensions should be ignored */
217 /** ProxyDHCP server */
218 struct in_addr proxy_server
;
219 /** ProxyDHCP offer */
220 struct dhcp_packet
*proxy_offer
;
221 /** ProxyDHCP offer priority */
224 /** PXE Boot Server 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
;
231 /** Retransmission timer */
232 struct retry_timer timer
;
233 /** Transmission counter */
235 /** Start time of the current state (in ticks) */
242 * @v refcnt Reference counter
244 static void dhcp_free ( struct refcnt
*refcnt
) {
245 struct dhcp_session
*dhcp
=
246 container_of ( refcnt
, struct dhcp_session
, refcnt
);
248 netdev_put ( dhcp
->netdev
);
249 dhcppkt_put ( dhcp
->proxy_offer
);
254 * Mark DHCP session as complete
256 * @v dhcp DHCP session
257 * @v rc Return status code
259 static void dhcp_finished ( struct dhcp_session
*dhcp
, int rc
) {
261 /* Stop retry timer */
262 stop_timer ( &dhcp
->timer
);
264 /* Shut down interfaces */
265 intf_shutdown ( &dhcp
->xfer
, rc
);
266 intf_shutdown ( &dhcp
->job
, rc
);
270 * Transition to new DHCP session state
272 * @v dhcp DHCP session
273 * @v state New session state
275 static void dhcp_set_state ( struct dhcp_session
*dhcp
,
276 struct dhcp_session_state
*state
) {
278 DBGC ( dhcp
, "DHCP %p entering %s state\n", dhcp
, state
->name
);
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
);
289 * Check if DHCP packet contains PXE options
291 * @v dhcppkt DHCP packet
292 * @ret has_pxeopts DHCP packet contains PXE options
294 * It is assumed that the packet is already known to contain option 60
295 * set to "PXEClient".
297 static int dhcp_has_pxeopts ( struct dhcp_packet
*dhcppkt
) {
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 ) )
304 /* Check for a PXE boot menu */
305 if ( dhcppkt_fetch ( dhcppkt
, DHCP_PXE_BOOT_MENU
, NULL
, 0 ) > 0 )
311 /****************************************************************************
318 * Construct transmitted packet for DHCP discovery
320 * @v dhcp DHCP session
321 * @v dhcppkt DHCP packet
322 * @v peer Destination address
324 static int dhcp_discovery_tx ( struct dhcp_session
*dhcp
,
325 struct dhcp_packet
*dhcppkt __unused
,
326 struct sockaddr_in
*peer
) {
328 DBGC ( dhcp
, "DHCP %p DHCPDISCOVER\n", dhcp
);
330 /* Set server address */
331 peer
->sin_addr
.s_addr
= INADDR_BROADCAST
;
332 peer
->sin_port
= htons ( BOOTPS_PORT
);
338 * Handle received packet during DHCP discovery
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
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
) {
353 char vci
[9]; /* "PXEClient" */
357 uint8_t no_pxedhcp
= 0;
358 unsigned long elapsed
;
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
) );
369 /* Identify offered IP address */
370 ip
= dhcppkt
->dhcphdr
->yiaddr
;
372 DBGC ( dhcp
, " for %s", inet_ntoa ( ip
) );
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
) {
381 ( dhcp_has_pxeopts ( dhcppkt
) ?
" pxe" : " proxy" ) );
384 /* Identify priority */
385 dhcppkt_fetch ( dhcppkt
, DHCP_EB_PRIORITY
, &priority
,
386 sizeof ( priority
) );
388 DBGC ( dhcp
, " pri %d", priority
);
390 /* Identify ignore-PXE flag */
391 dhcppkt_fetch ( dhcppkt
, DHCP_EB_NO_PXEDHCP
, &no_pxedhcp
,
392 sizeof ( no_pxedhcp
) );
394 DBGC ( dhcp
, " nopxe" );
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
) ) {
402 dhcp
->server
= server_id
;
403 dhcp
->priority
= priority
;
404 dhcp
->no_pxedhcp
= no_pxedhcp
;
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
;
416 /* We can exit the discovery state when we have a valid
417 * DHCPOFFER, and either:
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.
424 /* If we don't yet have a DHCPOFFER, do nothing */
425 if ( ! dhcp
->offer
.s_addr
)
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
) ) )
434 /* Transition to DHCPREQUEST */
435 dhcp_set_state ( dhcp
, &dhcp_state_request
);
439 * Handle timer expiry during DHCP discovery
441 * @v dhcp DHCP session
443 static void dhcp_discovery_expired ( struct dhcp_session
*dhcp
) {
444 unsigned long elapsed
= ( currticks() - dhcp
->start
);
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
*
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
);
462 /* Otherwise, retransmit current packet */
466 /** DHCP discovery state operations */
467 static struct dhcp_session_state dhcp_state_discover
= {
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
,
478 * Construct transmitted packet for DHCP request
480 * @v dhcp DHCP session
481 * @v dhcppkt DHCP packet
482 * @v peer Destination address
484 static int dhcp_request_tx ( struct dhcp_session
*dhcp
,
485 struct dhcp_packet
*dhcppkt
,
486 struct sockaddr_in
*peer
) {
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
) );
494 if ( ( rc
= dhcppkt_store ( dhcppkt
, DHCP_SERVER_IDENTIFIER
,
496 sizeof ( dhcp
->server
) ) ) != 0 )
499 /* Set requested IP address */
500 if ( ( rc
= dhcppkt_store ( dhcppkt
, DHCP_REQUESTED_ADDRESS
,
502 sizeof ( dhcp
->offer
) ) ) != 0 )
505 /* Set server address */
506 peer
->sin_addr
.s_addr
= INADDR_BROADCAST
;
507 peer
->sin_port
= htons ( BOOTPS_PORT
);
513 * Handle received packet during DHCP request
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
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
) {
528 struct settings
*parent
;
529 struct settings
*settings
;
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
) );
541 /* Identify leased IP address */
542 ip
= dhcppkt
->dhcphdr
->yiaddr
;
544 DBGC ( dhcp
, " for %s", inet_ntoa ( ip
) );
547 /* Filter out unacceptable responses */
548 if ( peer
->sin_port
!= htons ( BOOTPS_PORT
) )
550 if ( msgtype
/* BOOTP */ && ( msgtype
!= DHCPACK
) )
552 if ( server_id
.s_addr
!= dhcp
->server
.s_addr
)
554 if ( ip
.s_addr
!= dhcp
->offer
.s_addr
)
557 /* Record assigned address */
558 dhcp
->local
.sin_addr
= ip
;
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
);
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
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
);
588 /* PXE options not present; use a ProxyDHCPREQUEST */
589 dhcp_set_state ( dhcp
, &dhcp_state_proxy
);
595 dhcp_finished ( dhcp
, 0 );
599 * Handle timer expiry during DHCP discovery
601 * @v dhcp DHCP session
603 static void dhcp_request_expired ( struct dhcp_session
*dhcp
) {
605 /* Retransmit current packet */
609 /** DHCP request state operations */
610 static struct dhcp_session_state dhcp_state_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
,
621 * Construct transmitted packet for ProxyDHCP request
623 * @v dhcp DHCP session
624 * @v dhcppkt DHCP packet
625 * @v peer Destination address
627 static int dhcp_proxy_tx ( struct dhcp_session
*dhcp
,
628 struct dhcp_packet
*dhcppkt
,
629 struct sockaddr_in
*peer
) {
632 DBGC ( dhcp
, "DHCP %p ProxyDHCP REQUEST to %s\n", dhcp
,
633 inet_ntoa ( dhcp
->proxy_server
) );
636 if ( ( rc
= dhcppkt_store ( dhcppkt
, DHCP_SERVER_IDENTIFIER
,
638 sizeof ( dhcp
->proxy_server
) ) ) != 0 )
641 /* Set server address */
642 peer
->sin_addr
= dhcp
->proxy_server
;
643 peer
->sin_port
= htons ( PXE_PORT
);
649 * Handle received packet during ProxyDHCP request
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
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
;
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
) );
674 if ( dhcp_has_pxeopts ( dhcppkt
) )
675 DBGC ( dhcp
, " pxe" );
678 /* Filter out unacceptable responses */
679 if ( peer
->sin_port
!= ntohs ( PXE_PORT
) )
681 if ( ( msgtype
!= DHCPOFFER
) && ( msgtype
!= DHCPACK
) )
683 if ( ( pseudo_id
.s_addr
!= dhcp
->proxy_server
.s_addr
) )
685 if ( ! dhcp_has_pxeopts ( dhcppkt
) )
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
);
698 dhcp_finished ( dhcp
, 0 );
702 * Handle timer expiry during ProxyDHCP request
704 * @v dhcp DHCP session
706 static void dhcp_proxy_expired ( struct dhcp_session
*dhcp
) {
707 unsigned long elapsed
= ( currticks() - dhcp
->start
);
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 );
715 /* Retransmit current packet */
719 /** ProxyDHCP request state operations */
720 static struct dhcp_session_state dhcp_state_proxy
= {
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
,
731 * Construct transmitted packet for PXE Boot Server Discovery
733 * @v dhcp DHCP session
734 * @v dhcppkt DHCP packet
735 * @v peer Destination address
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 };
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
) );
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
) );
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 )
762 * Check to see if PXE Boot Server address is acceptable
764 * @v dhcp DHCP session
765 * @v bs Boot Server address
766 * @ret accept Boot Server is acceptable
768 static int dhcp_pxebs_accept ( struct dhcp_session
*dhcp
,
769 struct in_addr bs
) {
770 struct in_addr
*accept
;
772 /* Accept if we have no acceptance filter */
773 if ( ! dhcp
->pxe_accept
)
776 /* Scan through acceptance list */
777 for ( accept
= dhcp
->pxe_accept
; accept
->s_addr
; accept
++ ) {
778 if ( accept
->s_addr
== bs
.s_addr
)
782 DBGC ( dhcp
, "DHCP %p rejecting server %s\n",
783 dhcp
, inet_ntoa ( bs
) );
788 * Handle received packet during PXE Boot Server Discovery
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
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 };
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
) );
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
) );
821 /* Filter out unacceptable responses */
822 if ( ( peer
->sin_port
!= htons ( BOOTPS_PORT
) ) &&
823 ( peer
->sin_port
!= htons ( PXE_PORT
) ) )
825 if ( msgtype
!= DHCPACK
)
827 if ( menu_item
.type
!= dhcp
->pxe_type
)
829 if ( ! dhcp_pxebs_accept ( dhcp
, pseudo_id
) )
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
);
842 dhcp_finished ( dhcp
, 0 );
846 * Handle timer expiry during PXE Boot Server Discovery
848 * @v dhcp DHCP session
850 static void dhcp_pxebs_expired ( struct dhcp_session
*dhcp
) {
851 unsigned long elapsed
= ( currticks() - dhcp
->start
);
853 /* Give up waiting before we reach the failure point, and fail
854 * over to the next server in the attempt list
856 if ( elapsed
> PXEBS_MAX_TIMEOUT_SEC
* TICKS_PER_SEC
) {
858 if ( dhcp
->pxe_attempt
->s_addr
) {
859 dhcp_set_state ( dhcp
, &dhcp_state_pxebs
);
862 dhcp_finished ( dhcp
, -ETIMEDOUT
);
867 /* Retransmit current packet */
871 /** PXE Boot Server Discovery state operations */
872 static struct dhcp_session_state dhcp_state_pxebs
= {
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
,
882 /****************************************************************************
884 * Packet construction
889 * Create a DHCP packet
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
901 * Creates a DHCP packet in the specified buffer, and initialise a
902 * DHCP packet structure.
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
;
912 if ( max_len
< ( sizeof ( *dhcphdr
) + options_len
) )
915 /* Initialise DHCP packet content */
916 memset ( dhcphdr
, 0, max_len
);
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
);
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.
930 if ( netdev
->ll_protocol
->flags
& LL_NAME_ONLY
)
931 dhcphdr
->flags
|= htons ( BOOTP_FL_BROADCAST
);
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.
937 if ( ipv4_has_any_addr ( netdev
) )
938 dhcphdr
->flags
|= htons ( BOOTP_FL_BROADCAST
);
940 /* Initialise DHCP packet structure */
941 memset ( dhcppkt
, 0, sizeof ( *dhcppkt
) );
942 dhcppkt_init ( dhcppkt
, data
, max_len
);
944 /* Set DHCP_MESSAGE_TYPE option */
945 if ( ( rc
= dhcppkt_store ( dhcppkt
, DHCP_MESSAGE_TYPE
,
946 &msgtype
, sizeof ( msgtype
) ) ) != 0 )
953 * Create DHCP request packet
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
964 * Creates a DHCP request packet in the specified buffer, and
965 * initialise a DHCP packet structure.
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
;
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",
988 goto err_create_packet
;
991 /* Set client IP address */
992 dhcppkt
->dhcphdr
->ciaddr
= ciaddr
;
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",
1001 goto err_store_features
;
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",
1011 goto err_store_busid
;
1014 /* Add DHCP client identifier. Required for Infiniband, and
1015 * doesn't hurt other link layers.
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",
1025 goto err_store_client_id
;
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.
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
,
1039 sizeof ( client_uuid
) ) ) != 0 ) {
1040 DBG ( "DHCP could not set client UUID: %s\n",
1042 goto err_store_client_uuid
;
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",
1053 goto err_store_user_class
;
1057 err_store_user_class
:
1058 free ( user_class
);
1059 err_store_client_uuid
:
1060 err_store_client_id
:
1067 /****************************************************************************
1069 * Data transfer interface
1074 * Transmit DHCP request
1076 * @v dhcp DHCP session
1077 * @ret rc Return status code
1079 static int dhcp_tx ( struct dhcp_session
*dhcp
) {
1080 static struct sockaddr_in peer
= {
1081 .sin_family
= AF_INET
,
1083 struct xfer_metadata meta
= {
1084 .netdev
= dhcp
->netdev
,
1085 .src
= ( struct sockaddr
* ) &dhcp
->local
,
1086 .dest
= ( struct sockaddr
* ) &peer
,
1088 struct io_buffer
*iobuf
;
1089 uint8_t msgtype
= dhcp
->state
->tx_msgtype
;
1090 struct dhcp_packet dhcppkt
;
1093 /* Start retry timer. Do this first so that failures to
1094 * transmit will be retried.
1096 start_timer ( &dhcp
->timer
);
1098 /* Allocate buffer for packet */
1099 iobuf
= xfer_alloc_iob ( &dhcp
->xfer
, DHCP_MIN_LEN
);
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
,
1107 iob_tailroom ( iobuf
) ) ) != 0 ) {
1108 DBGC ( dhcp
, "DHCP %p could not construct DHCP request: %s\n",
1109 dhcp
, strerror ( rc
) );
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.
1117 dhcppkt
.dhcphdr
->secs
= htons ( ( ++(dhcp
->count
) << 2 ) |
1118 ( dhcp
->offer
.s_addr ?
0x02 : 0 ) |
1119 ( dhcp
->proxy_offer ?
0x01 : 0 ) );
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
) );
1128 /* Transmit the packet */
1129 iob_put ( iobuf
, dhcppkt_len ( &dhcppkt
) );
1130 if ( ( rc
= xfer_deliver ( &dhcp
->xfer
, iob_disown ( iobuf
),
1132 DBGC ( dhcp
, "DHCP %p could not transmit UDP packet: %s\n",
1133 dhcp
, strerror ( rc
) );
1145 * @v dhcp DHCP session
1146 * @v iobuf I/O buffer
1147 * @v meta Transfer metadata
1148 * @ret rc Return status code
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
;
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
;
1165 if ( ! meta
->src
) {
1166 DBGC ( dhcp
, "DHCP %p received packet without source port\n",
1171 peer
= ( struct sockaddr_in
* ) meta
->src
;
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.
1178 data_len
= iob_len ( iobuf
);
1179 dhcppkt
= zalloc ( sizeof ( *dhcppkt
) + data_len
);
1182 goto err_alloc_dhcppkt
;
1184 dhcphdr
= ( ( ( void * ) dhcppkt
) + sizeof ( *dhcppkt
) );
1185 memcpy ( dhcphdr
, iobuf
->data
, data_len
);
1186 dhcppkt_init ( dhcppkt
, dhcphdr
, data_len
);
1188 /* Identify message type */
1189 dhcppkt_fetch ( dhcppkt
, DHCP_MESSAGE_TYPE
, &msgtype
,
1190 sizeof ( msgtype
) );
1192 /* Identify server ID */
1193 dhcppkt_fetch ( dhcppkt
, DHCP_SERVER_IDENTIFIER
,
1194 &server_id
, sizeof ( server_id
) );
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
;
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
) );
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
) );
1224 /* Handle packet based on current state */
1225 dhcp
->state
->rx ( dhcp
, dhcppkt
, peer
, msgtype
, server_id
, pseudo_id
);
1229 dhcppkt_put ( dhcppkt
);
1236 /** DHCP data transfer interface operations */
1237 static struct interface_operation dhcp_xfer_operations
[] = {
1238 INTF_OP ( xfer_deliver
, struct dhcp_session
*, dhcp_deliver
),
1241 /** DHCP data transfer interface descriptor */
1242 static struct interface_descriptor dhcp_xfer_desc
=
1243 INTF_DESC ( struct dhcp_session
, xfer
, dhcp_xfer_operations
);
1246 * Handle DHCP retry timer expiry
1248 * @v timer DHCP retry timer
1249 * @v fail Failure indicator
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
);
1255 /* If we have failed, terminate DHCP */
1257 dhcp_finished ( dhcp
, -ETIMEDOUT
);
1261 /* Handle timer expiry based on current state */
1262 dhcp
->state
->expired ( dhcp
);
1265 /****************************************************************************
1267 * Job control interface
1271 /** DHCP job control interface operations */
1272 static struct interface_operation dhcp_job_op
[] = {
1273 INTF_OP ( intf_close
, struct dhcp_session
*, dhcp_finished
),
1276 /** DHCP job control interface descriptor */
1277 static struct interface_descriptor dhcp_job_desc
=
1278 INTF_DESC ( struct dhcp_session
, job
, dhcp_job_op
);
1280 /****************************************************************************
1287 * DHCP peer address for socket opening
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.
1293 static struct sockaddr dhcp_peer
= {
1294 .sa_family
= AF_INET
,
1298 * Start DHCP state machine on a network device
1300 * @v job Job control interface
1301 * @v netdev Network device
1302 * @ret rc Return status code
1304 * Starts DHCP on the specified network device. If successful, the
1305 * DHCPACK (and ProxyDHCPACK, if applicable) will be registered as
1308 int start_dhcp ( struct interface
*job
, struct net_device
*netdev
) {
1309 struct dhcp_session
*dhcp
;
1312 /* Allocate and initialise structure */
1313 dhcp
= zalloc ( sizeof ( *dhcp
) );
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();
1325 /* Store DHCP transaction ID for fakedhcp code */
1326 dhcp_last_xid
= dhcp
->xid
;
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 )
1333 /* Enter DHCPDISCOVER state */
1334 dhcp_set_state ( dhcp
, &dhcp_state_discover
);
1336 /* Attach parent interface, mortalise self, and return */
1337 intf_plug_plug ( &dhcp
->job
, job
);
1338 ref_put ( &dhcp
->refcnt
);
1342 dhcp_finished ( dhcp
, rc
);
1343 ref_put ( &dhcp
->refcnt
);
1348 * Retrieve list of PXE boot servers for a given server type
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
1355 * The caller must ensure that the IP address list has sufficient
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
;
1365 if ( raw_len
< sizeof ( *server
) ) {
1366 DBGC ( dhcp
, "DHCP %p malformed PXE server list\n",
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",
1377 if ( server
->type
== dhcp
->pxe_type
) {
1378 for ( i
= 0 ; i
< server
->num_ip
; i
++ )
1379 *(ip
++) = server
->ip
[i
];
1381 server
= ( ( ( void * ) server
) + server_len
);
1382 raw_len
-= server_len
;
1387 * Start PXE Boot Server Discovery on a network device
1389 * @v job Job control interface
1390 * @v netdev Network device
1391 * @v pxe_type PXE server type
1392 * @ret rc Return status code
1394 * Starts PXE Boot Server Discovery on the specified network device.
1395 * If successful, the Boot Server ACK will be registered as an option
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
;
1409 unsigned int pxe_discovery_control
;
1412 /* Get upper bound for PXE boot server IP address list */
1413 pxebs_list_len
= fetch_raw_setting ( NULL
, &pxe_boot_servers_setting
,
1415 if ( pxebs_list_len
< 0 )
1418 /* Allocate and initialise structure */
1419 dhcp
= zalloc ( sizeof ( *dhcp
) + sizeof ( *ip
) /* mcast */ +
1420 sizeof ( *ip
) /* bcast */ + pxebs_list_len
+
1421 sizeof ( *ip
) /* terminator */ );
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
);
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
);
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
];
1452 fetch_raw_setting ( NULL
, &pxe_boot_servers_setting
,
1453 buf
, sizeof ( buf
) );
1454 pxebs_list ( dhcp
, buf
, sizeof ( buf
), ip
);
1456 if ( ! dhcp
->pxe_attempt
->s_addr
) {
1457 DBGC ( dhcp
, "DHCP %p has no PXE boot servers for type %04x\n",
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" );
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 )
1480 /* Enter PXEBS state */
1481 dhcp_set_state ( dhcp
, &dhcp_state_pxebs
);
1483 /* Attach parent interface, mortalise self, and return */
1484 intf_plug_plug ( &dhcp
->job
, job
);
1485 ref_put ( &dhcp
->refcnt
);
1489 dhcp_finished ( dhcp
, rc
);
1490 ref_put ( &dhcp
->refcnt
);
1494 /** DHCP network device configurator */
1495 struct net_device_configurator dhcp_configurator __net_device_configurator
= {
1497 .start
= start_dhcp
,