|
|
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.