diff --git a/src/include/gpxe/dhcp.h b/src/include/gpxe/dhcp.h index a0a09412..c45004cf 100644 --- a/src/include/gpxe/dhcp.h +++ b/src/include/gpxe/dhcp.h @@ -17,6 +17,7 @@ struct net_device; struct job_interface; struct dhcp_options; struct dhcp_packet; +struct dhcp_pxe_boot_menu_item; /** BOOTP/DHCP server port */ #define BOOTPS_PORT 67 @@ -84,6 +85,12 @@ struct dhcp_packet; /** PXE boot server multicast address */ #define DHCP_PXE_BOOT_SERVER_MCAST DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 7 ) +/** PXE boot menu */ +#define DHCP_PXE_BOOT_MENU DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 9 ) + +/** PXE boot menu item */ +#define DHCP_PXE_BOOT_MENU_ITEM DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 71 ) + /** Requested IP address */ #define DHCP_REQUESTED_ADDRESS 50 @@ -492,8 +499,10 @@ extern int dhcp_create_packet ( struct dhcp_packet *dhcppkt, void *data, size_t max_len ); extern int dhcp_create_request ( struct dhcp_packet *dhcppkt, struct net_device *netdev, - struct in_addr ciaddr, - struct dhcp_packet *dhcpoffer, + unsigned int msgtype, struct in_addr ciaddr, + struct in_addr server, + struct in_addr requested_ip, + struct dhcp_pxe_boot_menu_item *menu_item, void *data, size_t max_len ); extern int start_dhcp ( struct job_interface *job, struct net_device *netdev ); diff --git a/src/net/fakedhcp.c b/src/net/fakedhcp.c index d9ae36cd..2fb08c69 100644 --- a/src/net/fakedhcp.c +++ b/src/net/fakedhcp.c @@ -108,11 +108,12 @@ static int copy_settings ( struct dhcp_packet *dest, int create_fakedhcpdiscover ( struct net_device *netdev, void *data, size_t max_len ) { struct dhcp_packet dhcppkt; - struct in_addr ciaddr = { 0 }; + struct in_addr dummy_addr = { 0 }; int rc; - if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, ciaddr, NULL, data, - max_len ) ) != 0 ) { + if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, DHCPDISCOVER, + dummy_addr, dummy_addr, dummy_addr, + NULL, data, max_len ) ) != 0 ) { DBG ( "Could not create DHCPDISCOVER: %s\n", strerror ( rc ) ); return rc; diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 90987c77..4955ea19 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -125,6 +125,21 @@ struct dhcp_client_uuid { #define DHCP_CLIENT_UUID_TYPE 0 +/** DHCP PXE boot menu item */ +struct dhcp_pxe_boot_menu_item { + /** "Type" + * + * This field actually identifies the specific boot server (or + * cluster of boot servers offering identical boot files). + */ + uint16_t type; + /** "Layer" + * + * Just don't ask. + */ + uint16_t layer; +} __attribute__ (( packed )); + /** * Name a DHCP packet type * @@ -448,27 +463,30 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt, * * @v dhcppkt DHCP packet structure to fill in * @v netdev Network device - * @v ciaddr Client IP address - * @v offer DHCP offer, if applicable + * @v msgtype DHCP message type + * @v ciaddr Client IP address, if applicable + * @v server Server identifier, if applicable + * @v requested_ip Requested address, if applicable + * @v menu_item PXE menu item, if applicable * @v data Buffer for DHCP packet * @v max_len Size of DHCP packet buffer * @ret rc Return status code */ int dhcp_create_request ( struct dhcp_packet *dhcppkt, - struct net_device *netdev, struct in_addr ciaddr, - struct dhcp_packet *offer, + struct net_device *netdev, unsigned int msgtype, + struct in_addr ciaddr, struct in_addr server, + struct in_addr requested_ip, + struct dhcp_pxe_boot_menu_item *menu_item, void *data, size_t max_len ) { struct device_description *desc = &netdev->dev->desc; struct dhcp_netdev_desc dhcp_desc; struct dhcp_client_id client_id; struct dhcp_client_uuid client_uuid; - unsigned int msgtype; size_t dhcp_features_len; size_t ll_addr_len; int rc; /* Create DHCP packet */ - msgtype = ( offer ? DHCPREQUEST : DHCPDISCOVER ); if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype, &dhcp_request_options, data, max_len ) ) != 0 ) { @@ -480,30 +498,23 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt, /* Set client IP address */ dhcppkt->dhcphdr->ciaddr = ciaddr; - /* Copy any required options from previous server repsonse */ - if ( offer ) { - struct in_addr server = { 0 }; - struct in_addr *ip = &offer->dhcphdr->yiaddr; - - /* Copy server identifier, if present */ - if ( ( dhcppkt_fetch ( offer, DHCP_SERVER_IDENTIFIER, &server, - sizeof ( server ) ) >= 0 ) && - ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER, - &server, - sizeof ( server ) ) ) != 0 ) ) { + /* Set server ID, if present */ + if ( server.s_addr && + ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER, + &server, sizeof ( server ) ) ) != 0 ) ) { DBG ( "DHCP could not set server ID: %s\n", strerror ( rc ) ); return rc; - } + } - /* Copy requested IP address, if present */ - if ( ( ip->s_addr != 0 ) && - ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS, - ip, sizeof ( *ip ) ) ) != 0 ) ) { - DBG ( "DHCP could not set requested address: %s\n", - strerror ( rc ) ); - return rc; - } + /* Set requested IP address, if present */ + if ( requested_ip.s_addr && + ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS, + &requested_ip, + sizeof ( requested_ip ) ) ) != 0 ) ) { + DBG ( "DHCP could not set requested address: %s\n", + strerror ( rc ) ); + return rc; } /* Add options to identify the feature list */ @@ -553,6 +564,16 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt, } } + /* Set PXE boot menu item, if present */ + if ( menu_item && menu_item->type && + ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM, + menu_item, + sizeof ( *menu_item ) ) ) != 0 ) ) { + DBG ( "DHCP could not set PXE menu item: %s\n", + strerror ( rc ) ); + return rc; + } + return 0; } @@ -563,11 +584,11 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt, * @ret rc Return status code */ static int dhcp_tx ( struct dhcp_session *dhcp ) { - static struct sockaddr_in proxydhcp_server = { + static struct sockaddr_in dest = { .sin_family = AF_INET, .sin_port = htons ( PROXYDHCP_PORT ), }; - static struct sockaddr_in client = { + static struct sockaddr_in src = { .sin_family = AF_INET, .sin_port = htons ( BOOTPC_PORT ), }; @@ -576,9 +597,11 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) { }; struct io_buffer *iobuf; struct dhcp_packet dhcppkt; - struct dhcp_packet *offer = NULL; struct in_addr ciaddr = { 0 }; - int check_len; + struct in_addr server = { 0 }; + struct in_addr requested_ip = { 0 }; + struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 }; + unsigned int msgtype; int rc; /* Start retry timer. Do this first so that failures to @@ -589,59 +612,82 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) { /* Determine packet contents based on current state */ switch ( dhcp->state ) { case DHCP_STATE_DISCOVER: - DBGC ( dhcp, "DHCP %p transmitting DHCPDISCOVER\n", dhcp ); + msgtype = DHCPDISCOVER; break; case DHCP_STATE_REQUEST: - DBGC ( dhcp, "DHCP %p transmitting DHCPREQUEST\n", dhcp ); assert ( dhcp->dhcpoffer ); - offer = &dhcp->dhcpoffer->dhcppkt; + msgtype = DHCPREQUEST; + dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt, + DHCP_SERVER_IDENTIFIER, &server, + sizeof ( server ) ); + requested_ip = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr; break; case DHCP_STATE_PROXYREQUEST: - DBGC ( dhcp, "DHCP %p transmitting ProxyDHCPREQUEST\n", dhcp ); assert ( dhcp->dhcpoffer ); assert ( dhcp->proxydhcpoffer ); assert ( dhcp->dhcpack ); - offer = &dhcp->proxydhcpoffer->dhcppkt; + msgtype = DHCPREQUEST; ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr; - check_len = dhcppkt_fetch ( offer, DHCP_SERVER_IDENTIFIER, - &proxydhcp_server.sin_addr, - sizeof(proxydhcp_server.sin_addr)); - meta.dest = ( struct sockaddr * ) &proxydhcp_server; - assert ( ciaddr.s_addr != 0 ); - assert ( proxydhcp_server.sin_addr.s_addr != 0 ); - assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) ); + dhcppkt_fetch ( &dhcp->proxydhcpoffer->dhcppkt, + DHCP_SERVER_IDENTIFIER, &dest.sin_addr, + sizeof ( dest.sin_addr ) ); + meta.dest = ( struct sockaddr * ) &dest; + server = dest.sin_addr; + assert ( dest.sin_addr.s_addr ); + assert ( ciaddr.s_addr ); break; case DHCP_STATE_BSREQUEST: - DBGC ( dhcp, "DHCP %p transmitting BootServerREQUEST\n", - dhcp ); assert ( dhcp->dhcpoffer ); assert ( dhcp->proxydhcpoffer ); assert ( dhcp->dhcpack ); assert ( dhcp->proxydhcpack ); - offer = &dhcp->proxydhcpoffer->dhcppkt; + msgtype = DHCPREQUEST; ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr; - check_len = dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt, - DHCP_PXE_BOOT_SERVER_MCAST, - &proxydhcp_server.sin_addr, - sizeof(proxydhcp_server.sin_addr)); - meta.dest = ( struct sockaddr * ) &proxydhcp_server; - assert ( ciaddr.s_addr != 0 ); - assert ( proxydhcp_server.sin_addr.s_addr != 0 ); - assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) ); + dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt, + DHCP_PXE_BOOT_SERVER_MCAST, + &dest.sin_addr, sizeof ( dest.sin_addr ) ); + meta.dest = ( struct sockaddr * ) &dest; + dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt, + DHCP_PXE_BOOT_MENU, &menu_item.type, + sizeof ( menu_item.type ) ); + assert ( dest.sin_addr.s_addr ); + assert ( menu_item.type ); + assert ( ciaddr.s_addr ); break; default: assert ( 0 ); - break; + return -EINVAL; } + DBGC ( dhcp, "DHCP %p %s", dhcp, dhcp_msgtype_name ( msgtype ) ); + if ( server.s_addr ) + DBGC ( dhcp, " to %s", inet_ntoa ( server ) ); + if ( meta.dest ) { + if ( dest.sin_addr.s_addr == server.s_addr ) { + DBGC ( dhcp, ":%d (unicast)", + ntohs ( dest.sin_port ) ); + } else { + DBGC ( dhcp, " via %s:%d", inet_ntoa ( dest.sin_addr ), + ntohs ( dest.sin_port ) ); + } + } else { + DBGC ( dhcp, " (broadcast)" ); + } + if ( requested_ip.s_addr ) + DBGC ( dhcp, " for %s", inet_ntoa ( requested_ip ) ); + if ( menu_item.type ) + DBGC ( dhcp, " for item %04x", ntohs ( menu_item.type ) ); + DBGC ( dhcp, "\n" ); + /* Allocate buffer for packet */ iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN ); if ( ! iobuf ) return -ENOMEM; /* Create DHCP packet in temporary buffer */ - if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, - ciaddr, offer, iobuf->data, + if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype, + ciaddr, server, requested_ip, + &menu_item, iobuf->data, iob_tailroom ( iobuf ) ) ) != 0 ) { DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n", dhcp, strerror ( rc ) ); @@ -650,8 +696,8 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) { /* Explicitly specify source address, if available. */ if ( ciaddr.s_addr ) { - client.sin_addr = ciaddr; - meta.src = ( struct sockaddr * ) &client; + src.sin_addr = ciaddr; + meta.src = ( struct sockaddr * ) &src; } /* Transmit the packet */ @@ -756,7 +802,7 @@ static void dhcp_store_dhcpoffer ( struct dhcp_session *dhcp, DBGC ( dhcp, "DHCP %p stored DHCPOFFER %p discarded\n", dhcp, *stored_dhcpoffer ); } - DBGC ( dhcp, "DHCP %p received DHCPOFFER %p stored\n", + DBGC ( dhcp, "DHCP %p DHCPOFFER %p stored\n", dhcp, dhcpoffer ); dhcpset_put ( *stored_dhcpoffer ); *stored_dhcpoffer = dhcpset_get ( dhcpoffer ); @@ -781,16 +827,17 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp, if ( dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER, &server_id, sizeof ( server_id ) ) != sizeof ( server_id ) ) { - DBGC ( dhcp, "DHCP %p received DHCPOFFER %p missing server " - "identifier\n", dhcp, dhcpoffer ); + DBGC ( dhcp, "DHCP %p DHCPOFFER %p missing server ID\n", + dhcp, dhcpoffer ); /* Could be a valid BOOTP offer; do not abort processing */ } /* If there is an IP address, it's a normal DHCPOFFER */ if ( dhcpoffer->dhcppkt.dhcphdr->yiaddr.s_addr != 0 ) { - DBGC ( dhcp, "DHCP %p received DHCPOFFER %p from %s has IP " - "address\n", + DBGC ( dhcp, "DHCP %p DHCPOFFER %p from %s", dhcp, dhcpoffer, inet_ntoa ( server_id ) ); + DBGC ( dhcp, " has IP %s\n", + inet_ntoa ( dhcpoffer->dhcppkt.dhcphdr->yiaddr ) ); dhcp_store_dhcpoffer ( dhcp, dhcpoffer, &dhcp->dhcpoffer ); } @@ -803,7 +850,7 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp, if ( ( server_id.s_addr != 0 ) && ( len >= ( int ) sizeof ( vci ) ) && ( strncmp ( "PXEClient", vci, sizeof ( vci ) ) == 0 ) ) { - DBGC ( dhcp, "DHCP %p received DHCPOFFER %p from %s is a " + DBGC ( dhcp, "DHCP %p DHCPOFFER %p from %s is a " "ProxyDHCPOFFER\n", dhcp, dhcpoffer, inet_ntoa ( server_id ) ); dhcp_store_dhcpoffer ( dhcp, dhcpoffer, @@ -992,7 +1039,7 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer, struct xfer_metadata *meta ) { struct dhcp_session *dhcp = container_of ( xfer, struct dhcp_session, xfer ); - struct sockaddr_tcpip *st_src; + struct sockaddr_in *sin_src; unsigned int src_port; struct dhcp_settings *dhcpset; struct dhcphdr *dhcphdr; @@ -1012,8 +1059,8 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer, rc = -EINVAL; goto err_no_src; } - st_src = ( struct sockaddr_tcpip * ) meta->src; - src_port = st_src->st_port; + sin_src = ( struct sockaddr_in * ) meta->src; + src_port = sin_src->sin_port; /* Convert packet into a DHCP settings block */ dhcpset = dhcpset_create ( iobuf->data, iob_len ( iobuf ) ); @@ -1027,12 +1074,13 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer, /* Identify message type */ dhcppkt_fetch ( &dhcpset->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype, sizeof ( msgtype ) ); - DBGC ( dhcp, "DHCP %p received %s %p from port %d\n", dhcp, - dhcp_msgtype_name ( msgtype ), dhcpset, ntohs ( src_port ) ); + DBGC ( dhcp, "DHCP %p %s %p from %s:%d\n", dhcp, + dhcp_msgtype_name ( msgtype ), dhcpset, + inet_ntoa ( sin_src->sin_addr ), ntohs ( src_port ) ); /* Check for matching transaction ID */ if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) { - DBGC ( dhcp, "DHCP %p received %s %p has bad transaction ID\n", + DBGC ( dhcp, "DHCP %p %s %p has bad transaction ID\n", dhcp, dhcp_msgtype_name ( msgtype ), dhcpset ); rc = -EINVAL; goto err_xid;