|  | @@ -287,6 +287,8 @@ enum dhcp_session_state {
 | 
		
	
		
			
			| 287 | 287 |  	DHCP_STATE_REQUEST,
 | 
		
	
		
			
			| 288 | 288 |  	/** Sending ProxyDHCPREQUESTs, waiting for ProxyDHCPACK */
 | 
		
	
		
			
			| 289 | 289 |  	DHCP_STATE_PROXYREQUEST,
 | 
		
	
		
			
			|  | 290 | +	/** Sending BootServerDHCPREQUESTs, waiting for BootServerDHCPACK */
 | 
		
	
		
			
			|  | 291 | +	DHCP_STATE_BSREQUEST,
 | 
		
	
		
			
			| 290 | 292 |  };
 | 
		
	
		
			
			| 291 | 293 |  
 | 
		
	
		
			
			| 292 | 294 |  /**
 | 
		
	
	
		
			
			|  | @@ -300,6 +302,7 @@ static inline const char * dhcp_state_name ( enum dhcp_session_state state ) {
 | 
		
	
		
			
			| 300 | 302 |  	case DHCP_STATE_DISCOVER:	return "DHCPDISCOVER";
 | 
		
	
		
			
			| 301 | 303 |  	case DHCP_STATE_REQUEST:	return "DHCPREQUEST";
 | 
		
	
		
			
			| 302 | 304 |  	case DHCP_STATE_PROXYREQUEST:	return "ProxyDHCPREQUEST";
 | 
		
	
		
			
			|  | 305 | +	case DHCP_STATE_BSREQUEST:	return "BootServerREQUEST";
 | 
		
	
		
			
			| 303 | 306 |  	default:			return "<invalid>";
 | 
		
	
		
			
			| 304 | 307 |  	}
 | 
		
	
		
			
			| 305 | 308 |  }
 | 
		
	
	
		
			
			|  | @@ -326,6 +329,12 @@ struct dhcp_session {
 | 
		
	
		
			
			| 326 | 329 |  	struct dhcp_settings *dhcpoffer;
 | 
		
	
		
			
			| 327 | 330 |  	/** ProxyDHCPOFFER obtained during DHCPDISCOVER */
 | 
		
	
		
			
			| 328 | 331 |  	struct dhcp_settings *proxydhcpoffer;
 | 
		
	
		
			
			|  | 332 | +	/** DHCPACK obtained during DHCPREQUEST */
 | 
		
	
		
			
			|  | 333 | +	struct dhcp_settings *dhcpack;
 | 
		
	
		
			
			|  | 334 | +	/** ProxyDHCPACK obtained during ProxyDHCPREQUEST */
 | 
		
	
		
			
			|  | 335 | +	struct dhcp_settings *proxydhcpack;
 | 
		
	
		
			
			|  | 336 | +	/** BootServerDHCPACK obtained during BootServerDHCPREQUEST */
 | 
		
	
		
			
			|  | 337 | +	struct dhcp_settings *bsdhcpack;
 | 
		
	
		
			
			| 329 | 338 |  	/** Retransmission timer */
 | 
		
	
		
			
			| 330 | 339 |  	struct retry_timer timer;
 | 
		
	
		
			
			| 331 | 340 |  	/** Start time of the current state (in ticks) */
 | 
		
	
	
		
			
			|  | @@ -344,6 +353,9 @@ static void dhcp_free ( struct refcnt *refcnt ) {
 | 
		
	
		
			
			| 344 | 353 |  	netdev_put ( dhcp->netdev );
 | 
		
	
		
			
			| 345 | 354 |  	dhcpset_put ( dhcp->dhcpoffer );
 | 
		
	
		
			
			| 346 | 355 |  	dhcpset_put ( dhcp->proxydhcpoffer );
 | 
		
	
		
			
			|  | 356 | +	dhcpset_put ( dhcp->dhcpack );
 | 
		
	
		
			
			|  | 357 | +	dhcpset_put ( dhcp->proxydhcpack );
 | 
		
	
		
			
			|  | 358 | +	dhcpset_put ( dhcp->bsdhcpack );
 | 
		
	
		
			
			| 347 | 359 |  	free ( dhcp );
 | 
		
	
		
			
			| 348 | 360 |  }
 | 
		
	
		
			
			| 349 | 361 |  
 | 
		
	
	
		
			
			|  | @@ -555,6 +567,10 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
 | 
		
	
		
			
			| 555 | 567 |  		.sin_family = AF_INET,
 | 
		
	
		
			
			| 556 | 568 |  		.sin_port = htons ( PROXYDHCP_PORT ),
 | 
		
	
		
			
			| 557 | 569 |  	};
 | 
		
	
		
			
			|  | 570 | +	static struct sockaddr_in client = {
 | 
		
	
		
			
			|  | 571 | +		.sin_family = AF_INET,
 | 
		
	
		
			
			|  | 572 | +		.sin_port = htons ( BOOTPC_PORT ),
 | 
		
	
		
			
			|  | 573 | +	};
 | 
		
	
		
			
			| 558 | 574 |  	struct xfer_metadata meta = {
 | 
		
	
		
			
			| 559 | 575 |  		.netdev = dhcp->netdev,
 | 
		
	
		
			
			| 560 | 576 |  	};
 | 
		
	
	
		
			
			|  | @@ -584,6 +600,7 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
 | 
		
	
		
			
			| 584 | 600 |  		DBGC ( dhcp, "DHCP %p transmitting ProxyDHCPREQUEST\n", dhcp );
 | 
		
	
		
			
			| 585 | 601 |  		assert ( dhcp->dhcpoffer );
 | 
		
	
		
			
			| 586 | 602 |  		assert ( dhcp->proxydhcpoffer );
 | 
		
	
		
			
			|  | 603 | +		assert ( dhcp->dhcpack );
 | 
		
	
		
			
			| 587 | 604 |  		offer = &dhcp->proxydhcpoffer->dhcppkt;
 | 
		
	
		
			
			| 588 | 605 |  		ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
 | 
		
	
		
			
			| 589 | 606 |  		check_len = dhcppkt_fetch ( offer, DHCP_SERVER_IDENTIFIER,
 | 
		
	
	
		
			
			|  | @@ -594,6 +611,24 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
 | 
		
	
		
			
			| 594 | 611 |  		assert ( proxydhcp_server.sin_addr.s_addr != 0 );
 | 
		
	
		
			
			| 595 | 612 |  		assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) );
 | 
		
	
		
			
			| 596 | 613 |  		break;
 | 
		
	
		
			
			|  | 614 | +	case DHCP_STATE_BSREQUEST:
 | 
		
	
		
			
			|  | 615 | +		DBGC ( dhcp, "DHCP %p transmitting BootServerREQUEST\n",
 | 
		
	
		
			
			|  | 616 | +		       dhcp );
 | 
		
	
		
			
			|  | 617 | +		assert ( dhcp->dhcpoffer );
 | 
		
	
		
			
			|  | 618 | +		assert ( dhcp->proxydhcpoffer );
 | 
		
	
		
			
			|  | 619 | +		assert ( dhcp->dhcpack );
 | 
		
	
		
			
			|  | 620 | +		assert ( dhcp->proxydhcpack );
 | 
		
	
		
			
			|  | 621 | +		offer = &dhcp->proxydhcpoffer->dhcppkt;
 | 
		
	
		
			
			|  | 622 | +		ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
 | 
		
	
		
			
			|  | 623 | +		check_len = dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt,
 | 
		
	
		
			
			|  | 624 | +					    DHCP_PXE_BOOT_SERVER_MCAST,
 | 
		
	
		
			
			|  | 625 | +					    &proxydhcp_server.sin_addr,
 | 
		
	
		
			
			|  | 626 | +					    sizeof(proxydhcp_server.sin_addr));
 | 
		
	
		
			
			|  | 627 | +		meta.dest = ( struct sockaddr * ) &proxydhcp_server;
 | 
		
	
		
			
			|  | 628 | +		assert ( ciaddr.s_addr != 0 );
 | 
		
	
		
			
			|  | 629 | +		assert ( proxydhcp_server.sin_addr.s_addr != 0 );
 | 
		
	
		
			
			|  | 630 | +		assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) );
 | 
		
	
		
			
			|  | 631 | +		break;
 | 
		
	
		
			
			| 597 | 632 |  	default:
 | 
		
	
		
			
			| 598 | 633 |  		assert ( 0 );
 | 
		
	
		
			
			| 599 | 634 |  		break;
 | 
		
	
	
		
			
			|  | @@ -613,6 +648,12 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
 | 
		
	
		
			
			| 613 | 648 |  		goto done;
 | 
		
	
		
			
			| 614 | 649 |  	}
 | 
		
	
		
			
			| 615 | 650 |  
 | 
		
	
		
			
			|  | 651 | +	/* Explicitly specify source address, if available. */
 | 
		
	
		
			
			|  | 652 | +	if ( ciaddr.s_addr ) {
 | 
		
	
		
			
			|  | 653 | +		client.sin_addr = ciaddr;
 | 
		
	
		
			
			|  | 654 | +		meta.src = ( struct sockaddr * ) &client;
 | 
		
	
		
			
			|  | 655 | +	}
 | 
		
	
		
			
			|  | 656 | +
 | 
		
	
		
			
			| 616 | 657 |  	/* Transmit the packet */
 | 
		
	
		
			
			| 617 | 658 |  	iob_put ( iobuf, dhcppkt.len );
 | 
		
	
		
			
			| 618 | 659 |  	rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
 | 
		
	
	
		
			
			|  | @@ -650,6 +691,7 @@ static void dhcp_set_state ( struct dhcp_session *dhcp,
 | 
		
	
		
			
			| 650 | 691 |   * @v dhcp		DHCP session
 | 
		
	
		
			
			| 651 | 692 |   */
 | 
		
	
		
			
			| 652 | 693 |  static void dhcp_next_state ( struct dhcp_session *dhcp ) {
 | 
		
	
		
			
			|  | 694 | +	struct in_addr bs_mcast = { 0 };
 | 
		
	
		
			
			| 653 | 695 |  
 | 
		
	
		
			
			| 654 | 696 |  	switch ( dhcp->state ) {
 | 
		
	
		
			
			| 655 | 697 |  	case DHCP_STATE_DISCOVER:
 | 
		
	
	
		
			
			|  | @@ -662,6 +704,17 @@ static void dhcp_next_state ( struct dhcp_session *dhcp ) {
 | 
		
	
		
			
			| 662 | 704 |  		}
 | 
		
	
		
			
			| 663 | 705 |  		/* Fall through */
 | 
		
	
		
			
			| 664 | 706 |  	case DHCP_STATE_PROXYREQUEST:
 | 
		
	
		
			
			|  | 707 | +		if ( dhcp->proxydhcpack ) {
 | 
		
	
		
			
			|  | 708 | +			dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt,
 | 
		
	
		
			
			|  | 709 | +					DHCP_PXE_BOOT_SERVER_MCAST,
 | 
		
	
		
			
			|  | 710 | +					&bs_mcast, sizeof ( bs_mcast ) );
 | 
		
	
		
			
			|  | 711 | +			if ( bs_mcast.s_addr ) {
 | 
		
	
		
			
			|  | 712 | +				dhcp_set_state ( dhcp, DHCP_STATE_BSREQUEST );
 | 
		
	
		
			
			|  | 713 | +				break;
 | 
		
	
		
			
			|  | 714 | +			}
 | 
		
	
		
			
			|  | 715 | +		}
 | 
		
	
		
			
			|  | 716 | +		/* Fall through */
 | 
		
	
		
			
			|  | 717 | +	case DHCP_STATE_BSREQUEST:
 | 
		
	
		
			
			| 665 | 718 |  		dhcp_finished ( dhcp, 0 );
 | 
		
	
		
			
			| 666 | 719 |  		break;
 | 
		
	
		
			
			| 667 | 720 |  	default:
 | 
		
	
	
		
			
			|  | @@ -845,9 +898,13 @@ static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp,
 | 
		
	
		
			
			| 845 | 898 |  		return;
 | 
		
	
		
			
			| 846 | 899 |  	}
 | 
		
	
		
			
			| 847 | 900 |  
 | 
		
	
		
			
			|  | 901 | +	/* Record DHCPACK */
 | 
		
	
		
			
			|  | 902 | +	assert ( dhcp->dhcpack == NULL );
 | 
		
	
		
			
			|  | 903 | +	dhcp->dhcpack = dhcpset_get ( dhcpack );
 | 
		
	
		
			
			|  | 904 | +
 | 
		
	
		
			
			| 848 | 905 |  	/* Register settings */
 | 
		
	
		
			
			| 849 | 906 |  	parent = netdev_settings ( dhcp->netdev );
 | 
		
	
		
			
			| 850 |  | -	if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) !=0 )
 | 
		
	
		
			
			|  | 907 | +	if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) != 0 )
 | 
		
	
		
			
			| 851 | 908 |  		return;
 | 
		
	
		
			
			| 852 | 909 |  
 | 
		
	
		
			
			| 853 | 910 |  	/* Transition to next state */
 | 
		
	
	
		
			
			|  | @@ -885,6 +942,10 @@ static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
 | 
		
	
		
			
			| 885 | 942 |  	/* Rename settings */
 | 
		
	
		
			
			| 886 | 943 |  	proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME;
 | 
		
	
		
			
			| 887 | 944 |  
 | 
		
	
		
			
			|  | 945 | +	/* Record ProxyDHCPACK */
 | 
		
	
		
			
			|  | 946 | +	assert ( dhcp->proxydhcpack == NULL );
 | 
		
	
		
			
			|  | 947 | +	dhcp->proxydhcpack = dhcpset_get ( proxydhcpack );
 | 
		
	
		
			
			|  | 948 | +
 | 
		
	
		
			
			| 888 | 949 |  	/* Register settings */
 | 
		
	
		
			
			| 889 | 950 |  	if ( ( rc = dhcp_store_dhcpack ( dhcp, proxydhcpack, NULL ) ) != 0 )
 | 
		
	
		
			
			| 890 | 951 |  		return;
 | 
		
	
	
		
			
			|  | @@ -893,6 +954,31 @@ static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
 | 
		
	
		
			
			| 893 | 954 |  	dhcp_next_state ( dhcp );
 | 
		
	
		
			
			| 894 | 955 |  }
 | 
		
	
		
			
			| 895 | 956 |  
 | 
		
	
		
			
			|  | 957 | +/**
 | 
		
	
		
			
			|  | 958 | + * Handle received BootServerDHCPACK
 | 
		
	
		
			
			|  | 959 | + *
 | 
		
	
		
			
			|  | 960 | + * @v dhcp		DHCP session
 | 
		
	
		
			
			|  | 961 | + * @v bsdhcpack	Received BootServerDHCPACK
 | 
		
	
		
			
			|  | 962 | + */
 | 
		
	
		
			
			|  | 963 | +static void dhcp_rx_bsdhcpack ( struct dhcp_session *dhcp,
 | 
		
	
		
			
			|  | 964 | +				struct dhcp_settings *bsdhcpack ) {
 | 
		
	
		
			
			|  | 965 | +	int rc;
 | 
		
	
		
			
			|  | 966 | +
 | 
		
	
		
			
			|  | 967 | +	/* Rename settings */
 | 
		
	
		
			
			|  | 968 | +	bsdhcpack->settings.name = BSDHCP_SETTINGS_NAME;
 | 
		
	
		
			
			|  | 969 | +
 | 
		
	
		
			
			|  | 970 | +	/* Record ProxyDHCPACK */
 | 
		
	
		
			
			|  | 971 | +	assert ( dhcp->bsdhcpack == NULL );
 | 
		
	
		
			
			|  | 972 | +	dhcp->bsdhcpack = dhcpset_get ( bsdhcpack );
 | 
		
	
		
			
			|  | 973 | +
 | 
		
	
		
			
			|  | 974 | +	/* Register settings */
 | 
		
	
		
			
			|  | 975 | +	if ( ( rc = dhcp_store_dhcpack ( dhcp, bsdhcpack, NULL ) ) != 0 )
 | 
		
	
		
			
			|  | 976 | +		return;
 | 
		
	
		
			
			|  | 977 | +
 | 
		
	
		
			
			|  | 978 | +	/* Transition to next state */
 | 
		
	
		
			
			|  | 979 | +	dhcp_next_state ( dhcp );
 | 
		
	
		
			
			|  | 980 | +}
 | 
		
	
		
			
			|  | 981 | +
 | 
		
	
		
			
			| 896 | 982 |  /**
 | 
		
	
		
			
			| 897 | 983 |   * Receive new data
 | 
		
	
		
			
			| 898 | 984 |   *
 | 
		
	
	
		
			
			|  | @@ -969,6 +1055,11 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
 | 
		
	
		
			
			| 969 | 1055 |  		     ( src_port == htons ( PROXYDHCP_PORT ) ) )
 | 
		
	
		
			
			| 970 | 1056 |  			dhcp_rx_proxydhcpack ( dhcp, dhcpset );
 | 
		
	
		
			
			| 971 | 1057 |  		break;
 | 
		
	
		
			
			|  | 1058 | +	case DHCP_STATE_BSREQUEST:
 | 
		
	
		
			
			|  | 1059 | +		if ( ( msgtype == DHCPACK ) &&
 | 
		
	
		
			
			|  | 1060 | +		     ( src_port == htons ( PROXYDHCP_PORT ) ) )
 | 
		
	
		
			
			|  | 1061 | +			dhcp_rx_bsdhcpack ( dhcp, dhcpset );
 | 
		
	
		
			
			|  | 1062 | +		break;
 | 
		
	
		
			
			| 972 | 1063 |  	default:
 | 
		
	
		
			
			| 973 | 1064 |  		assert ( 0 );
 | 
		
	
		
			
			| 974 | 1065 |  		break;
 |