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