|  | @@ -4,9 +4,12 @@
 | 
		
	
		
			
			| 4 | 4 |   *
 | 
		
	
		
			
			| 5 | 5 |   */
 | 
		
	
		
			
			| 6 | 6 |  
 | 
		
	
		
			
			| 7 |  | -#include "pxe.h"
 | 
		
	
		
			
			| 8 |  | -#include "io.h"
 | 
		
	
		
			
			| 9 |  | -#include "string.h"
 | 
		
	
		
			
			|  | 7 | +#include <string.h>
 | 
		
	
		
			
			|  | 8 | +#include <byteswap.h>
 | 
		
	
		
			
			|  | 9 | +#include <gpxe/udp.h>
 | 
		
	
		
			
			|  | 10 | +#include <gpxe/uaccess.h>
 | 
		
	
		
			
			|  | 11 | +#include <gpxe/process.h>
 | 
		
	
		
			
			|  | 12 | +#include <pxe.h>
 | 
		
	
		
			
			| 10 | 13 |  
 | 
		
	
		
			
			| 11 | 14 |  /*
 | 
		
	
		
			
			| 12 | 15 |   * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
 | 
		
	
	
		
			
			|  | @@ -26,14 +29,116 @@
 | 
		
	
		
			
			| 26 | 29 |   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
		
	
		
			
			| 27 | 30 |   */
 | 
		
	
		
			
			| 28 | 31 |  
 | 
		
	
		
			
			|  | 32 | +/** A PXE UDP connection */
 | 
		
	
		
			
			|  | 33 | +struct pxe_udp_connection {
 | 
		
	
		
			
			|  | 34 | +	/** Etherboot UDP connection */
 | 
		
	
		
			
			|  | 35 | +	struct udp_connection udp;
 | 
		
	
		
			
			|  | 36 | +	/** "Connection is open" flag */
 | 
		
	
		
			
			|  | 37 | +	int open;
 | 
		
	
		
			
			|  | 38 | +	/** Current pxenv_udp_read() operation, if any */
 | 
		
	
		
			
			|  | 39 | +	struct s_PXENV_UDP_READ *pxenv_udp_read;
 | 
		
	
		
			
			|  | 40 | +	/** Current pxenv_udp_write() operation, if any */
 | 
		
	
		
			
			|  | 41 | +	struct s_PXENV_UDP_WRITE *pxenv_udp_write;
 | 
		
	
		
			
			|  | 42 | +};
 | 
		
	
		
			
			|  | 43 | +
 | 
		
	
		
			
			|  | 44 | +static inline struct pxe_udp_connection *
 | 
		
	
		
			
			|  | 45 | +udp_to_pxe ( struct udp_connection *conn ) {
 | 
		
	
		
			
			|  | 46 | +	return container_of ( conn, struct pxe_udp_connection, udp );
 | 
		
	
		
			
			|  | 47 | +}
 | 
		
	
		
			
			|  | 48 | +
 | 
		
	
		
			
			|  | 49 | +/**
 | 
		
	
		
			
			|  | 50 | + * Send PXE UDP data
 | 
		
	
		
			
			|  | 51 | + *
 | 
		
	
		
			
			|  | 52 | + * @v conn			UDP connection
 | 
		
	
		
			
			|  | 53 | + * @v data			Temporary data buffer
 | 
		
	
		
			
			|  | 54 | + * @v len			Size of temporary data buffer
 | 
		
	
		
			
			|  | 55 | + *
 | 
		
	
		
			
			|  | 56 | + * Sends the packet belonging to the current pxenv_udp_write()
 | 
		
	
		
			
			|  | 57 | + * operation.
 | 
		
	
		
			
			|  | 58 | + */
 | 
		
	
		
			
			|  | 59 | +static void pxe_udp_senddata ( struct udp_connection *conn, void *data,
 | 
		
	
		
			
			|  | 60 | +			       size_t len ) {
 | 
		
	
		
			
			|  | 61 | +	struct pxe_udp_connection *pxe_udp = udp_to_pxe ( conn );
 | 
		
	
		
			
			|  | 62 | +	struct s_PXENV_UDP_WRITE *pxenv_udp_write = pxe_udp->pxenv_udp_write;
 | 
		
	
		
			
			|  | 63 | +	userptr_t buffer;
 | 
		
	
		
			
			|  | 64 | +
 | 
		
	
		
			
			|  | 65 | +	/* Transmit packet */
 | 
		
	
		
			
			|  | 66 | +	buffer = real_to_user ( pxenv_udp_write->buffer.segment,
 | 
		
	
		
			
			|  | 67 | +				pxenv_udp_write->buffer.offset );
 | 
		
	
		
			
			|  | 68 | +	if ( len > pxenv_udp_write->buffer_size )
 | 
		
	
		
			
			|  | 69 | +		len = pxenv_udp_write->buffer_size;
 | 
		
	
		
			
			|  | 70 | +	copy_from_user ( data, buffer, 0, len );
 | 
		
	
		
			
			|  | 71 | +	udp_send ( conn, data, len );
 | 
		
	
		
			
			|  | 72 | +}
 | 
		
	
		
			
			|  | 73 | +
 | 
		
	
		
			
			|  | 74 | +/**
 | 
		
	
		
			
			|  | 75 | + * Receive PXE UDP data
 | 
		
	
		
			
			|  | 76 | + *
 | 
		
	
		
			
			|  | 77 | + * @v conn			UDP connection
 | 
		
	
		
			
			|  | 78 | + * @v data			Received data
 | 
		
	
		
			
			|  | 79 | + * @v len			Length of received data
 | 
		
	
		
			
			|  | 80 | + * @v st_src			Source address
 | 
		
	
		
			
			|  | 81 | + * @v st_dest			Destination address
 | 
		
	
		
			
			|  | 82 | + *
 | 
		
	
		
			
			|  | 83 | + * Receives a packet as part of the current pxenv_udp_read()
 | 
		
	
		
			
			|  | 84 | + * operation.
 | 
		
	
		
			
			|  | 85 | + */
 | 
		
	
		
			
			|  | 86 | +static int pxe_udp_newdata ( struct udp_connection *conn, void *data,
 | 
		
	
		
			
			|  | 87 | +			     size_t len, struct sockaddr_tcpip *st_src,
 | 
		
	
		
			
			|  | 88 | +			     struct sockaddr_tcpip *st_dest ) {
 | 
		
	
		
			
			|  | 89 | +	struct pxe_udp_connection *pxe_udp = udp_to_pxe ( conn );
 | 
		
	
		
			
			|  | 90 | +	struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read;
 | 
		
	
		
			
			|  | 91 | +	struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src );
 | 
		
	
		
			
			|  | 92 | +	struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
 | 
		
	
		
			
			|  | 93 | +	userptr_t buffer;
 | 
		
	
		
			
			|  | 94 | +
 | 
		
	
		
			
			|  | 95 | +	if ( ! pxenv_udp_read ) {
 | 
		
	
		
			
			|  | 96 | +		DBG ( "PXE discarded UDP packet\n" );
 | 
		
	
		
			
			|  | 97 | +		return -ENOBUFS;
 | 
		
	
		
			
			|  | 98 | +	}
 | 
		
	
		
			
			|  | 99 | +
 | 
		
	
		
			
			|  | 100 | +	/* Copy packet to buffer and record length */
 | 
		
	
		
			
			|  | 101 | +	buffer = real_to_user ( pxenv_udp_read->buffer.segment,
 | 
		
	
		
			
			|  | 102 | +				pxenv_udp_read->buffer.offset );
 | 
		
	
		
			
			|  | 103 | +	if ( len > pxenv_udp_read->buffer_size )
 | 
		
	
		
			
			|  | 104 | +		len = pxenv_udp_read->buffer_size;
 | 
		
	
		
			
			|  | 105 | +	copy_to_user ( buffer, 0, data, len );
 | 
		
	
		
			
			|  | 106 | +	pxenv_udp_read->buffer_size = len;
 | 
		
	
		
			
			|  | 107 | +
 | 
		
	
		
			
			|  | 108 | +	/* Fill in source/dest information */
 | 
		
	
		
			
			|  | 109 | +	assert ( sin_src->sin_family == AF_INET );
 | 
		
	
		
			
			|  | 110 | +	pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr;
 | 
		
	
		
			
			|  | 111 | +	pxenv_udp_read->s_port = sin_src->sin_port;
 | 
		
	
		
			
			|  | 112 | +	assert ( sin_dest->sin_family == AF_INET );
 | 
		
	
		
			
			|  | 113 | +	pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr;
 | 
		
	
		
			
			|  | 114 | +	pxenv_udp_read->d_port = sin_dest->sin_port;
 | 
		
	
		
			
			|  | 115 | +
 | 
		
	
		
			
			|  | 116 | +	/* Mark as received */
 | 
		
	
		
			
			|  | 117 | +	pxe_udp->pxenv_udp_read = NULL;
 | 
		
	
		
			
			|  | 118 | +
 | 
		
	
		
			
			|  | 119 | +	return 0;
 | 
		
	
		
			
			|  | 120 | +}
 | 
		
	
		
			
			|  | 121 | +
 | 
		
	
		
			
			|  | 122 | +/** PXE UDP operations */
 | 
		
	
		
			
			|  | 123 | +static struct udp_operations pxe_udp_operations = {
 | 
		
	
		
			
			|  | 124 | +	.senddata = pxe_udp_senddata,
 | 
		
	
		
			
			|  | 125 | +	.newdata = pxe_udp_newdata,
 | 
		
	
		
			
			|  | 126 | +};
 | 
		
	
		
			
			|  | 127 | +
 | 
		
	
		
			
			|  | 128 | +/** The PXE UDP connection */
 | 
		
	
		
			
			|  | 129 | +static struct pxe_udp_connection pxe_udp = {
 | 
		
	
		
			
			|  | 130 | +	.udp.udp_op = &pxe_udp_operations,
 | 
		
	
		
			
			|  | 131 | +};
 | 
		
	
		
			
			|  | 132 | +
 | 
		
	
		
			
			| 29 | 133 |  /**
 | 
		
	
		
			
			| 30 | 134 |   * UDP OPEN
 | 
		
	
		
			
			| 31 | 135 |   *
 | 
		
	
		
			
			| 32 |  | - * @v udp_open				Pointer to a struct s_PXENV_UDP_OPEN
 | 
		
	
		
			
			|  | 136 | + * @v pxenv_udp_open			Pointer to a struct s_PXENV_UDP_OPEN
 | 
		
	
		
			
			| 33 | 137 |   * @v s_PXENV_UDP_OPEN::src_ip		IP address of this station, or 0.0.0.0
 | 
		
	
		
			
			| 34 | 138 |   * @ret #PXENV_EXIT_SUCCESS		Always
 | 
		
	
		
			
			| 35 | 139 |   * @ret s_PXENV_UDP_OPEN::Status	PXE status code
 | 
		
	
		
			
			| 36 |  | - * @err #PXENV_STATUS_UNDI_INVALID_STATE NIC could not be initialised
 | 
		
	
		
			
			|  | 140 | + * @err #PXENV_STATUS_UDP_OPEN		UDP connection already open
 | 
		
	
		
			
			|  | 141 | + * @err #PXENV_STATUS_OUT_OF_RESOURCES	Could not open connection
 | 
		
	
		
			
			| 37 | 142 |   *
 | 
		
	
		
			
			| 38 | 143 |   * Prepares the PXE stack for communication using pxenv_udp_write()
 | 
		
	
		
			
			| 39 | 144 |   * and pxenv_udp_read().
 | 
		
	
	
		
			
			|  | @@ -45,10 +150,13 @@
 | 
		
	
		
			
			| 45 | 150 |   * s_PXENV_UDP_OPEN::src_ip is 0.0.0.0, the local station's IP address
 | 
		
	
		
			
			| 46 | 151 |   * will remain unchanged.)
 | 
		
	
		
			
			| 47 | 152 |   *
 | 
		
	
		
			
			| 48 |  | - * You can only have one open UDP connection at a time.  You cannot
 | 
		
	
		
			
			| 49 |  | - * have a UDP connection open at the same time as a TFTP connection.
 | 
		
	
		
			
			| 50 |  | - * (This is not strictly true for Etherboot; see the relevant @ref
 | 
		
	
		
			
			| 51 |  | - * pxe_note_udp "implementation note" for more details.)
 | 
		
	
		
			
			|  | 153 | + * You can only have one open UDP connection at a time.  This is not a
 | 
		
	
		
			
			|  | 154 | + * meaningful restriction, since pxenv_udp_write() and
 | 
		
	
		
			
			|  | 155 | + * pxenv_udp_read() allow you to specify arbitrary local and remote
 | 
		
	
		
			
			|  | 156 | + * ports and an arbitrary remote address for each packet.  According
 | 
		
	
		
			
			|  | 157 | + * to the PXE specifiation, you cannot have a UDP connection open at
 | 
		
	
		
			
			|  | 158 | + * the same time as a TFTP connection; this restriction does not apply
 | 
		
	
		
			
			|  | 159 | + * to Etherboot.
 | 
		
	
		
			
			| 52 | 160 |   *
 | 
		
	
		
			
			| 53 | 161 |   * On x86, you must set the s_PXE::StatusCallout field to a nonzero
 | 
		
	
		
			
			| 54 | 162 |   * value before calling this function in protected mode.  You cannot
 | 
		
	
	
		
			
			|  | @@ -60,38 +168,52 @@
 | 
		
	
		
			
			| 60 | 168 |   * for this UDP connection, or retained for all future communication.
 | 
		
	
		
			
			| 61 | 169 |   * The latter seems more consistent with typical PXE stack behaviour.
 | 
		
	
		
			
			| 62 | 170 |   *
 | 
		
	
		
			
			|  | 171 | + * @note Etherboot currently ignores the s_PXENV_UDP_OPEN::src_ip
 | 
		
	
		
			
			|  | 172 | + * parameter.
 | 
		
	
		
			
			|  | 173 | + *
 | 
		
	
		
			
			| 63 | 174 |   */
 | 
		
	
		
			
			| 64 |  | -PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) {
 | 
		
	
		
			
			|  | 175 | +PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) {
 | 
		
	
		
			
			|  | 176 | +	struct in_addr new_ip = { .s_addr = pxenv_udp_open->src_ip };
 | 
		
	
		
			
			|  | 177 | +
 | 
		
	
		
			
			| 65 | 178 |  	DBG ( "PXENV_UDP_OPEN" );
 | 
		
	
		
			
			| 66 |  | -	ENSURE_READY ( udp_open );
 | 
		
	
		
			
			| 67 | 179 |  
 | 
		
	
		
			
			| 68 |  | -	if ( udp_open->src_ip &&
 | 
		
	
		
			
			| 69 |  | -	     udp_open->src_ip != arptable[ARP_CLIENT].ipaddr.s_addr ) {
 | 
		
	
		
			
			| 70 |  | -		/* Overwrite our IP address */
 | 
		
	
		
			
			| 71 |  | -		DBG ( " with new IP %@", udp_open->src_ip );
 | 
		
	
		
			
			| 72 |  | -		arptable[ARP_CLIENT].ipaddr.s_addr = udp_open->src_ip;
 | 
		
	
		
			
			|  | 180 | +	/* Check connection is not already open */
 | 
		
	
		
			
			|  | 181 | +	if ( pxe_udp.open ) {
 | 
		
	
		
			
			|  | 182 | +		pxenv_udp_open->Status = PXENV_STATUS_UDP_OPEN;
 | 
		
	
		
			
			|  | 183 | +		return PXENV_EXIT_FAILURE;
 | 
		
	
		
			
			|  | 184 | +	}
 | 
		
	
		
			
			|  | 185 | +
 | 
		
	
		
			
			|  | 186 | +	/* Set IP address if specified */
 | 
		
	
		
			
			|  | 187 | +	if ( new_ip.s_addr ) {
 | 
		
	
		
			
			|  | 188 | +		/* FIXME: actually do something here */
 | 
		
	
		
			
			|  | 189 | +		DBG ( " with new IP address %s", inet_ntoa ( new_ip ) );
 | 
		
	
		
			
			|  | 190 | +	}
 | 
		
	
		
			
			|  | 191 | +
 | 
		
	
		
			
			|  | 192 | +	/* Open UDP connection */
 | 
		
	
		
			
			|  | 193 | +	if ( udp_open ( &pxe_udp.udp, 0 ) != 0 ) {
 | 
		
	
		
			
			|  | 194 | +		pxenv_udp_open->Status = PXENV_STATUS_OUT_OF_RESOURCES;
 | 
		
	
		
			
			|  | 195 | +		return PXENV_EXIT_FAILURE;
 | 
		
	
		
			
			| 73 | 196 |  	}
 | 
		
	
		
			
			|  | 197 | +	pxe_udp.open = 1;
 | 
		
	
		
			
			| 74 | 198 |  
 | 
		
	
		
			
			| 75 |  | -	udp_open->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			|  | 199 | +	pxenv_udp_open->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			| 76 | 200 |  	return PXENV_EXIT_SUCCESS;
 | 
		
	
		
			
			| 77 | 201 |  }
 | 
		
	
		
			
			| 78 | 202 |  
 | 
		
	
		
			
			| 79 | 203 |  /**
 | 
		
	
		
			
			| 80 | 204 |   * UDP CLOSE
 | 
		
	
		
			
			| 81 | 205 |   *
 | 
		
	
		
			
			| 82 |  | - * @v udp_close				Pointer to a struct s_PXENV_UDP_CLOSE
 | 
		
	
		
			
			|  | 206 | + * @v pxenv_udp_close			Pointer to a struct s_PXENV_UDP_CLOSE
 | 
		
	
		
			
			| 83 | 207 |   * @ret #PXENV_EXIT_SUCCESS		Always
 | 
		
	
		
			
			| 84 | 208 |   * @ret s_PXENV_UDP_CLOSE::Status	PXE status code
 | 
		
	
		
			
			| 85 | 209 |   * @err None				-
 | 
		
	
		
			
			| 86 | 210 |   *
 | 
		
	
		
			
			| 87 |  | - * Closes a UDP "connection" opened with pxenv_udp_open().
 | 
		
	
		
			
			|  | 211 | + * Closes a UDP connection opened with pxenv_udp_open().
 | 
		
	
		
			
			| 88 | 212 |   *
 | 
		
	
		
			
			| 89 | 213 |   * You can only have one open UDP connection at a time.  You cannot
 | 
		
	
		
			
			| 90 | 214 |   * have a UDP connection open at the same time as a TFTP connection.
 | 
		
	
		
			
			| 91 | 215 |   * You cannot use pxenv_udp_close() to close a TFTP connection; use
 | 
		
	
		
			
			| 92 |  | - * pxenv_tftp_close() instead.  (This is not strictly true for
 | 
		
	
		
			
			| 93 |  | - * Etherboot; see the relevant @ref pxe_note_udp "implementation note"
 | 
		
	
		
			
			| 94 |  | - * for more details.)
 | 
		
	
		
			
			|  | 216 | + * pxenv_tftp_close() instead.
 | 
		
	
		
			
			| 95 | 217 |   *
 | 
		
	
		
			
			| 96 | 218 |   * On x86, you must set the s_PXE::StatusCallout field to a nonzero
 | 
		
	
		
			
			| 97 | 219 |   * value before calling this function in protected mode.  You cannot
 | 
		
	
	
		
			
			|  | @@ -99,16 +221,27 @@ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) {
 | 
		
	
		
			
			| 99 | 221 |   * @ref pxe_x86_pmode16 "implementation note" for more details.)
 | 
		
	
		
			
			| 100 | 222 |   *
 | 
		
	
		
			
			| 101 | 223 |   */
 | 
		
	
		
			
			| 102 |  | -PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
 | 
		
	
		
			
			|  | 224 | +PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
 | 
		
	
		
			
			| 103 | 225 |  	DBG ( "PXENV_UDP_CLOSE" );
 | 
		
	
		
			
			| 104 |  | -	udp_close->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			|  | 226 | +
 | 
		
	
		
			
			|  | 227 | +	/* Check connection is open */
 | 
		
	
		
			
			|  | 228 | +	if ( ! pxe_udp.open ) {
 | 
		
	
		
			
			|  | 229 | +		pxenv_udp_close->Status = PXENV_STATUS_UDP_CLOSED;
 | 
		
	
		
			
			|  | 230 | +		return PXENV_EXIT_SUCCESS; /* Well, it *is* closed */
 | 
		
	
		
			
			|  | 231 | +	}
 | 
		
	
		
			
			|  | 232 | +
 | 
		
	
		
			
			|  | 233 | +	/* Close UDP connection */
 | 
		
	
		
			
			|  | 234 | +	udp_close ( &pxe_udp.udp );
 | 
		
	
		
			
			|  | 235 | +	pxe_udp.open = 0;
 | 
		
	
		
			
			|  | 236 | +
 | 
		
	
		
			
			|  | 237 | +	pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			| 105 | 238 |  	return PXENV_EXIT_SUCCESS;
 | 
		
	
		
			
			| 106 | 239 |  }
 | 
		
	
		
			
			| 107 | 240 |  
 | 
		
	
		
			
			| 108 | 241 |  /**
 | 
		
	
		
			
			| 109 | 242 |   * UDP WRITE
 | 
		
	
		
			
			| 110 | 243 |   *
 | 
		
	
		
			
			| 111 |  | - * @v udp_write				Pointer to a struct s_PXENV_UDP_WRITE
 | 
		
	
		
			
			|  | 244 | + * @v pxenv_udp_write			Pointer to a struct s_PXENV_UDP_WRITE
 | 
		
	
		
			
			| 112 | 245 |   * @v s_PXENV_UDP_WRITE::ip		Destination IP address
 | 
		
	
		
			
			| 113 | 246 |   * @v s_PXENV_UDP_WRITE::gw		Relay agent IP address, or 0.0.0.0
 | 
		
	
		
			
			| 114 | 247 |   * @v s_PXENV_UDP_WRITE::src_port	Source UDP port, or 0
 | 
		
	
	
		
			
			|  | @@ -118,9 +251,8 @@ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
 | 
		
	
		
			
			| 118 | 251 |   * @ret #PXENV_EXIT_SUCCESS		Packet was transmitted successfully
 | 
		
	
		
			
			| 119 | 252 |   * @ret #PXENV_EXIT_FAILURE		Packet could not be transmitted
 | 
		
	
		
			
			| 120 | 253 |   * @ret s_PXENV_UDP_WRITE::Status	PXE status code
 | 
		
	
		
			
			| 121 |  | - * @err #PXENV_STATUS_UNDI_INVALID_STATE NIC could not be initialised
 | 
		
	
		
			
			| 122 |  | - * @err #PXENV_STATUS_OUT_OF_RESOURCES	Packet was too large to transmit
 | 
		
	
		
			
			| 123 |  | - * @err other				Any error from pxenv_undi_transmit()
 | 
		
	
		
			
			|  | 254 | + * @err #PXENV_STATUS_UDP_CLOSED	UDP connection is not open
 | 
		
	
		
			
			|  | 255 | + * @err #PXENV_STATUS_UNDI_TRANSMIT_ERROR Could not transmit packet
 | 
		
	
		
			
			| 124 | 256 |   *
 | 
		
	
		
			
			| 125 | 257 |   * Transmits a single UDP packet.  A valid IP and UDP header will be
 | 
		
	
		
			
			| 126 | 258 |   * prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer
 | 
		
	
	
		
			
			|  | @@ -136,116 +268,64 @@ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
 | 
		
	
		
			
			| 136 | 268 |   * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used.
 | 
		
	
		
			
			| 137 | 269 |   *
 | 
		
	
		
			
			| 138 | 270 |   * You must have opened a UDP connection with pxenv_udp_open() before
 | 
		
	
		
			
			| 139 |  | - * calling pxenv_udp_write().  (This is not strictly true for
 | 
		
	
		
			
			| 140 |  | - * Etherboot; see the relevant @ref pxe_note_udp "implementation note"
 | 
		
	
		
			
			| 141 |  | - * for more details.)
 | 
		
	
		
			
			|  | 271 | + * calling pxenv_udp_write().
 | 
		
	
		
			
			| 142 | 272 |   *
 | 
		
	
		
			
			| 143 | 273 |   * On x86, you must set the s_PXE::StatusCallout field to a nonzero
 | 
		
	
		
			
			| 144 | 274 |   * value before calling this function in protected mode.  You cannot
 | 
		
	
		
			
			| 145 | 275 |   * call this function with a 32-bit stack segment.  (See the relevant
 | 
		
	
		
			
			| 146 | 276 |   * @ref pxe_x86_pmode16 "implementation note" for more details.)
 | 
		
	
		
			
			| 147 | 277 |   *
 | 
		
	
		
			
			|  | 278 | + * @note Etherboot currently ignores the s_PXENV_UDP_WRITE::gw
 | 
		
	
		
			
			|  | 279 | + * parameter.
 | 
		
	
		
			
			|  | 280 | + *
 | 
		
	
		
			
			| 148 | 281 |   */
 | 
		
	
		
			
			| 149 |  | -PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *udp_write ) {
 | 
		
	
		
			
			| 150 |  | -	uint16_t src_port;
 | 
		
	
		
			
			| 151 |  | -	uint16_t dst_port;
 | 
		
	
		
			
			| 152 |  | -	struct udppacket *packet = (struct udppacket *)nic.packet;
 | 
		
	
		
			
			| 153 |  | -	int packet_size;
 | 
		
	
		
			
			|  | 282 | +PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
 | 
		
	
		
			
			|  | 283 | +	union {
 | 
		
	
		
			
			|  | 284 | +		struct sockaddr_in sin;
 | 
		
	
		
			
			|  | 285 | +		struct sockaddr_tcpip st;
 | 
		
	
		
			
			|  | 286 | +	} dest;
 | 
		
	
		
			
			| 154 | 287 |  
 | 
		
	
		
			
			| 155 | 288 |  	DBG ( "PXENV_UDP_WRITE" );
 | 
		
	
		
			
			| 156 |  | -	ENSURE_READY ( udp_write );
 | 
		
	
		
			
			| 157 |  | -
 | 
		
	
		
			
			| 158 |  | -	/* PXE spec says source port is 2069 if not specified */
 | 
		
	
		
			
			| 159 |  | -	src_port = ntohs(udp_write->src_port);
 | 
		
	
		
			
			| 160 |  | -	if ( src_port == 0 ) src_port = 2069;
 | 
		
	
		
			
			| 161 |  | -	dst_port = ntohs(udp_write->dst_port);
 | 
		
	
		
			
			| 162 |  | -	DBG ( " %d->%@:%d (%d)", src_port, udp_write->ip, dst_port,
 | 
		
	
		
			
			| 163 |  | -	      udp_write->buffer_size );
 | 
		
	
		
			
			| 164 |  | -	
 | 
		
	
		
			
			|  | 289 | +
 | 
		
	
		
			
			|  | 290 | +	/* Check connection is open */
 | 
		
	
		
			
			|  | 291 | +	if ( ! pxe_udp.open ) {
 | 
		
	
		
			
			|  | 292 | +		pxenv_udp_write->Status = PXENV_STATUS_UDP_CLOSED;
 | 
		
	
		
			
			|  | 293 | +		return PXENV_EXIT_FAILURE;
 | 
		
	
		
			
			|  | 294 | +	}
 | 
		
	
		
			
			|  | 295 | +
 | 
		
	
		
			
			|  | 296 | +	/* Construct destination socket address */
 | 
		
	
		
			
			|  | 297 | +	memset ( &dest, 0, sizeof ( dest ) );
 | 
		
	
		
			
			|  | 298 | +	dest.sin.sin_family = AF_INET;
 | 
		
	
		
			
			|  | 299 | +	dest.sin.sin_addr.s_addr = pxenv_udp_write->ip;
 | 
		
	
		
			
			|  | 300 | +	dest.sin.sin_port = pxenv_udp_write->dst_port;
 | 
		
	
		
			
			|  | 301 | +
 | 
		
	
		
			
			|  | 302 | +	/* Set local (source) port.  PXE spec says source port is 2069
 | 
		
	
		
			
			|  | 303 | +	 * if not specified.  Really, this ought to be set at UDP open
 | 
		
	
		
			
			|  | 304 | +	 * time but hey, we didn't design this API.
 | 
		
	
		
			
			|  | 305 | +	 */
 | 
		
	
		
			
			|  | 306 | +	if ( ! pxenv_udp_write->src_port )
 | 
		
	
		
			
			|  | 307 | +		pxenv_udp_write->src_port = htons ( 2069 );
 | 
		
	
		
			
			|  | 308 | +	udp_bind ( &pxe_udp.udp, pxenv_udp_write->src_port );
 | 
		
	
		
			
			|  | 309 | +
 | 
		
	
		
			
			| 165 | 310 |  	/* FIXME: we ignore the gateway specified, since we're
 | 
		
	
		
			
			| 166 | 311 |  	 * confident of being able to do our own routing.  We should
 | 
		
	
		
			
			| 167 | 312 |  	 * probably allow for multiple gateways.
 | 
		
	
		
			
			| 168 | 313 |  	 */
 | 
		
	
		
			
			| 169 | 314 |  	
 | 
		
	
		
			
			| 170 |  | -	/* Copy payload to packet buffer */
 | 
		
	
		
			
			| 171 |  | -	packet_size = ( (void*)&packet->payload - (void*)packet )
 | 
		
	
		
			
			| 172 |  | -		+ udp_write->buffer_size;
 | 
		
	
		
			
			| 173 |  | -	if ( packet_size > ETH_FRAME_LEN ) {
 | 
		
	
		
			
			| 174 |  | -		udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
 | 
		
	
		
			
			| 175 |  | -		return PXENV_EXIT_FAILURE;
 | 
		
	
		
			
			| 176 |  | -	}
 | 
		
	
		
			
			| 177 |  | -	memcpy ( &packet->payload, SEGOFF16_TO_PTR(udp_write->buffer),
 | 
		
	
		
			
			| 178 |  | -		 udp_write->buffer_size );
 | 
		
	
		
			
			| 179 |  | -
 | 
		
	
		
			
			| 180 | 315 |  	/* Transmit packet */
 | 
		
	
		
			
			| 181 |  | -	if ( ! udp_transmit ( udp_write->ip, src_port, dst_port,
 | 
		
	
		
			
			| 182 |  | -			      packet_size, packet ) ) {
 | 
		
	
		
			
			| 183 |  | -		udp_write->Status = errno;
 | 
		
	
		
			
			|  | 316 | +	if ( udp_senddata ( &pxe_udp.udp ) != 0 ) {
 | 
		
	
		
			
			|  | 317 | +		pxenv_udp_write->Status = PXENV_STATUS_UNDI_TRANSMIT_ERROR;
 | 
		
	
		
			
			| 184 | 318 |  		return PXENV_EXIT_FAILURE;
 | 
		
	
		
			
			| 185 | 319 |  	}
 | 
		
	
		
			
			| 186 | 320 |  
 | 
		
	
		
			
			| 187 |  | -	udp_write->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			|  | 321 | +	pxenv_udp_write->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			| 188 | 322 |  	return PXENV_EXIT_SUCCESS;
 | 
		
	
		
			
			| 189 | 323 |  }
 | 
		
	
		
			
			| 190 | 324 |  
 | 
		
	
		
			
			| 191 |  | -/* Utility function for pxenv_udp_read() */
 | 
		
	
		
			
			| 192 |  | -static int await_pxe_udp ( int ival __unused, void *ptr,
 | 
		
	
		
			
			| 193 |  | -			   unsigned short ptype __unused,
 | 
		
	
		
			
			| 194 |  | -			   struct iphdr *ip, struct udphdr *udp,
 | 
		
	
		
			
			| 195 |  | -			   struct tcphdr *tcp __unused ) {
 | 
		
	
		
			
			| 196 |  | -	struct s_PXENV_UDP_READ *udp_read = (struct s_PXENV_UDP_READ*)ptr;
 | 
		
	
		
			
			| 197 |  | -	uint16_t d_port;
 | 
		
	
		
			
			| 198 |  | -	size_t size;
 | 
		
	
		
			
			| 199 |  | -
 | 
		
	
		
			
			| 200 |  | -	/* Ignore non-UDP packets */
 | 
		
	
		
			
			| 201 |  | -	if ( !udp ) {
 | 
		
	
		
			
			| 202 |  | -		DBG ( " non-UDP" );
 | 
		
	
		
			
			| 203 |  | -		return 0;
 | 
		
	
		
			
			| 204 |  | -	}
 | 
		
	
		
			
			| 205 |  | -	
 | 
		
	
		
			
			| 206 |  | -	/* Check dest_ip */
 | 
		
	
		
			
			| 207 |  | -	if ( udp_read->dest_ip && ( udp_read->dest_ip != ip->dest.s_addr ) ) {
 | 
		
	
		
			
			| 208 |  | -		DBG ( " wrong dest IP (got %@, wanted %@)",
 | 
		
	
		
			
			| 209 |  | -		      ip->dest.s_addr, udp_read->dest_ip );
 | 
		
	
		
			
			| 210 |  | -		return 0;
 | 
		
	
		
			
			| 211 |  | -	}
 | 
		
	
		
			
			| 212 |  | -
 | 
		
	
		
			
			| 213 |  | -	/* Check dest_port */
 | 
		
	
		
			
			| 214 |  | -	d_port = ntohs ( udp_read->d_port );
 | 
		
	
		
			
			| 215 |  | -	if ( d_port && ( d_port != ntohs(udp->dest) ) ) {
 | 
		
	
		
			
			| 216 |  | -		DBG ( " wrong dest port (got %d, wanted %d)",
 | 
		
	
		
			
			| 217 |  | -		      ntohs(udp->dest), d_port );
 | 
		
	
		
			
			| 218 |  | -		return 0;
 | 
		
	
		
			
			| 219 |  | -	}
 | 
		
	
		
			
			| 220 |  | -
 | 
		
	
		
			
			| 221 |  | -	/* Copy packet to buffer and fill in information */
 | 
		
	
		
			
			| 222 |  | -	udp_read->src_ip = ip->src.s_addr;
 | 
		
	
		
			
			| 223 |  | -	udp_read->s_port = udp->src; /* Both in network order */
 | 
		
	
		
			
			| 224 |  | -	size = ntohs(udp->len) - sizeof(*udp);
 | 
		
	
		
			
			| 225 |  | -	/* Workaround: NTLDR expects us to fill these in, even though
 | 
		
	
		
			
			| 226 |  | -	 * PXESPEC clearly defines them as input parameters.
 | 
		
	
		
			
			| 227 |  | -	 */
 | 
		
	
		
			
			| 228 |  | -	udp_read->dest_ip = ip->dest.s_addr;
 | 
		
	
		
			
			| 229 |  | -	udp_read->d_port = udp->dest;
 | 
		
	
		
			
			| 230 |  | -	DBG ( " %@:%d->%@:%d (%d)",
 | 
		
	
		
			
			| 231 |  | -	      udp_read->src_ip, ntohs(udp_read->s_port),
 | 
		
	
		
			
			| 232 |  | -	      udp_read->dest_ip, ntohs(udp_read->d_port), size );
 | 
		
	
		
			
			| 233 |  | -	if ( udp_read->buffer_size < size ) {
 | 
		
	
		
			
			| 234 |  | -		/* PXESPEC: what error code should we actually return? */
 | 
		
	
		
			
			| 235 |  | -		DBG ( " buffer too small (%d)", udp_read->buffer_size );
 | 
		
	
		
			
			| 236 |  | -		udp_read->Status = PXENV_STATUS_OUT_OF_RESOURCES;
 | 
		
	
		
			
			| 237 |  | -		return 0;
 | 
		
	
		
			
			| 238 |  | -	}
 | 
		
	
		
			
			| 239 |  | -	memcpy ( SEGOFF16_TO_PTR ( udp_read->buffer ), &udp->payload, size );
 | 
		
	
		
			
			| 240 |  | -	udp_read->buffer_size = size;
 | 
		
	
		
			
			| 241 |  | -
 | 
		
	
		
			
			| 242 |  | -	return 1;
 | 
		
	
		
			
			| 243 |  | -}
 | 
		
	
		
			
			| 244 |  | -
 | 
		
	
		
			
			| 245 | 325 |  /**
 | 
		
	
		
			
			| 246 | 326 |   * UDP READ
 | 
		
	
		
			
			| 247 | 327 |   *
 | 
		
	
		
			
			| 248 |  | - * @v udp_read				Pointer to a struct s_PXENV_UDP_READ
 | 
		
	
		
			
			|  | 328 | + * @v pxenv_udp_read			Pointer to a struct s_PXENV_UDP_READ
 | 
		
	
		
			
			| 249 | 329 |   * @v s_PXENV_UDP_READ::dest_ip		Destination IP address, or 0.0.0.0
 | 
		
	
		
			
			| 250 | 330 |   * @v s_PXENV_UDP_READ::d_port		Destination UDP port, or 0
 | 
		
	
		
			
			| 251 | 331 |   * @v s_PXENV_UDP_READ::buffer_size	Size of the UDP payload buffer
 | 
		
	
	
		
			
			|  | @@ -258,8 +338,7 @@ static int await_pxe_udp ( int ival __unused, void *ptr,
 | 
		
	
		
			
			| 258 | 338 |   * @ret s_PXENV_UDP_READ::s_port	Source UDP port
 | 
		
	
		
			
			| 259 | 339 |   * @ret s_PXENV_UDP_READ::d_port	Destination UDP port
 | 
		
	
		
			
			| 260 | 340 |   * @ret s_PXENV_UDP_READ::buffer_size	Length of UDP payload
 | 
		
	
		
			
			| 261 |  | - * @err #PXENV_STATUS_UNDI_INVALID_STATE NIC could not be initialised
 | 
		
	
		
			
			| 262 |  | - * @err #PXENV_STATUS_OUT_OF_RESOURCES	Buffer was too small for payload
 | 
		
	
		
			
			|  | 341 | + * @err #PXENV_STATUS_UDP_CLOSED	UDP connection is not open
 | 
		
	
		
			
			| 263 | 342 |   * @err #PXENV_STATUS_FAILURE		No packet was ready to read
 | 
		
	
		
			
			| 264 | 343 |   *
 | 
		
	
		
			
			| 265 | 344 |   * Receive a single UDP packet.  This is a non-blocking call; if no
 | 
		
	
	
		
			
			|  | @@ -273,9 +352,7 @@ static int await_pxe_udp ( int ival __unused, void *ptr,
 | 
		
	
		
			
			| 273 | 352 |   * port will be accepted and may be returned to the caller.
 | 
		
	
		
			
			| 274 | 353 |   *
 | 
		
	
		
			
			| 275 | 354 |   * You must have opened a UDP connection with pxenv_udp_open() before
 | 
		
	
		
			
			| 276 |  | - * calling pxenv_udp_read().  (This is not strictly true for
 | 
		
	
		
			
			| 277 |  | - * Etherboot; see the relevant @ref pxe_note_udp "implementation note"
 | 
		
	
		
			
			| 278 |  | - * for more details.)
 | 
		
	
		
			
			|  | 355 | + * calling pxenv_udp_read().
 | 
		
	
		
			
			| 279 | 356 |   *
 | 
		
	
		
			
			| 280 | 357 |   * On x86, you must set the s_PXE::StatusCallout field to a nonzero
 | 
		
	
		
			
			| 281 | 358 |   * value before calling this function in protected mode.  You cannot
 | 
		
	
	
		
			
			|  | @@ -288,45 +365,40 @@ static int await_pxe_udp ( int ival __unused, void *ptr,
 | 
		
	
		
			
			| 288 | 365 |   * expects us to do so, and will fail if we don't.
 | 
		
	
		
			
			| 289 | 366 |   *
 | 
		
	
		
			
			| 290 | 367 |   */
 | 
		
	
		
			
			| 291 |  | -PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *udp_read ) {
 | 
		
	
		
			
			|  | 368 | +PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
 | 
		
	
		
			
			|  | 369 | +	struct in_addr dest_ip = { .s_addr = pxenv_udp_read->dest_ip };
 | 
		
	
		
			
			|  | 370 | +	uint16_t d_port = pxenv_udp_read->d_port;
 | 
		
	
		
			
			|  | 371 | +
 | 
		
	
		
			
			| 292 | 372 |  	DBG ( "PXENV_UDP_READ" );
 | 
		
	
		
			
			| 293 |  | -	ENSURE_READY ( udp_read );
 | 
		
	
		
			
			| 294 | 373 |  
 | 
		
	
		
			
			| 295 |  | -	/* Use await_reply with a timeout of zero */
 | 
		
	
		
			
			| 296 |  | -	/* Allow await_reply to change Status if necessary */
 | 
		
	
		
			
			| 297 |  | -	udp_read->Status = PXENV_STATUS_FAILURE;
 | 
		
	
		
			
			| 298 |  | -	if ( ! await_reply ( await_pxe_udp, 0, udp_read, 0 ) ) {
 | 
		
	
		
			
			|  | 374 | +	/* Check connection is open */
 | 
		
	
		
			
			|  | 375 | +	if ( ! pxe_udp.open ) {
 | 
		
	
		
			
			|  | 376 | +		pxenv_udp_read->Status = PXENV_STATUS_UDP_CLOSED;
 | 
		
	
		
			
			| 299 | 377 |  		return PXENV_EXIT_FAILURE;
 | 
		
	
		
			
			| 300 | 378 |  	}
 | 
		
	
		
			
			| 301 | 379 |  
 | 
		
	
		
			
			| 302 |  | -	udp_read->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			|  | 380 | +	/* Bind promiscuously; we will do our own filtering */
 | 
		
	
		
			
			|  | 381 | +	udp_bind_promisc ( &pxe_udp.udp );
 | 
		
	
		
			
			|  | 382 | +
 | 
		
	
		
			
			|  | 383 | +	/* Try receiving a packet */
 | 
		
	
		
			
			|  | 384 | +	pxe_udp.pxenv_udp_read = pxenv_udp_read;
 | 
		
	
		
			
			|  | 385 | +	step();
 | 
		
	
		
			
			|  | 386 | +	if ( pxe_udp.pxenv_udp_read ) {
 | 
		
	
		
			
			|  | 387 | +		/* No packet received */
 | 
		
	
		
			
			|  | 388 | +		pxe_udp.pxenv_udp_read = NULL;
 | 
		
	
		
			
			|  | 389 | +		goto no_packet;
 | 
		
	
		
			
			|  | 390 | +	}
 | 
		
	
		
			
			|  | 391 | +
 | 
		
	
		
			
			|  | 392 | +	/* Filter on destination address and/or port */
 | 
		
	
		
			
			|  | 393 | +	if ( dest_ip.s_addr && ( dest_ip.s_addr != pxenv_udp_read->dest_ip ) )
 | 
		
	
		
			
			|  | 394 | +		goto no_packet;
 | 
		
	
		
			
			|  | 395 | +	if ( d_port && ( d_port != pxenv_udp_read->d_port ) )
 | 
		
	
		
			
			|  | 396 | +		goto no_packet;
 | 
		
	
		
			
			|  | 397 | +
 | 
		
	
		
			
			|  | 398 | +	pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			| 303 | 399 |  	return PXENV_EXIT_SUCCESS;
 | 
		
	
		
			
			| 304 |  | -}
 | 
		
	
		
			
			| 305 | 400 |  
 | 
		
	
		
			
			| 306 |  | -/** @page pxe_notes Etherboot PXE implementation notes
 | 
		
	
		
			
			| 307 |  | -
 | 
		
	
		
			
			| 308 |  | -@section pxe_note_udp The connectionless nature of UDP
 | 
		
	
		
			
			| 309 |  | -
 | 
		
	
		
			
			| 310 |  | -The PXE specification states that it is possible to have only one open
 | 
		
	
		
			
			| 311 |  | -UDP or TFTP connection at any one time.  Etherboot does not
 | 
		
	
		
			
			| 312 |  | -rigourously enforce this restriction, on the UNIX principle that the
 | 
		
	
		
			
			| 313 |  | -code should not prevent the user from doing stupid things, because
 | 
		
	
		
			
			| 314 |  | -that would also prevent the user from doing clever things.  Since UDP
 | 
		
	
		
			
			| 315 |  | -is a connectionless protocol, it is perfectly possible to have
 | 
		
	
		
			
			| 316 |  | -multiple concurrent UDP "connections" open, provided that you take the
 | 
		
	
		
			
			| 317 |  | -multiplicity of connections into account when calling
 | 
		
	
		
			
			| 318 |  | -pxenv_udp_read().  Similarly, there is no technical reason that
 | 
		
	
		
			
			| 319 |  | -prevents you from calling pxenv_udp_write() in the middle of a TFTP
 | 
		
	
		
			
			| 320 |  | -download.
 | 
		
	
		
			
			| 321 |  | -
 | 
		
	
		
			
			| 322 |  | -Etherboot will therefore never return error codes indicating "a
 | 
		
	
		
			
			| 323 |  | -connection is already open", such as #PXENV_STATUS_UDP_OPEN.  If you
 | 
		
	
		
			
			| 324 |  | -want to have multiple concurrent connections, go for it (but don't
 | 
		
	
		
			
			| 325 |  | -expect your perfectly sensible code to work with any other PXE stack).
 | 
		
	
		
			
			| 326 |  | -
 | 
		
	
		
			
			| 327 |  | -Since Etherboot treats UDP as the connectionless protocol that it
 | 
		
	
		
			
			| 328 |  | -really is, pxenv_udp_close() is actually a no-op, and there is no need
 | 
		
	
		
			
			| 329 |  | -to call pxenv_udp_open() before using pxenv_udp_write() or
 | 
		
	
		
			
			| 330 |  | -pxenv_udp_read().
 | 
		
	
		
			
			| 331 |  | -
 | 
		
	
		
			
			| 332 |  | -*/
 | 
		
	
		
			
			|  | 401 | + no_packet:
 | 
		
	
		
			
			|  | 402 | +	pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
 | 
		
	
		
			
			|  | 403 | +	return PXENV_EXIT_FAILURE;
 | 
		
	
		
			
			|  | 404 | +}
 |