|  | @@ -34,14 +34,26 @@
 | 
		
	
		
			
			| 34 | 34 |  
 | 
		
	
		
			
			| 35 | 35 |  FILE_LICENCE ( GPL2_OR_LATER );
 | 
		
	
		
			
			| 36 | 36 |  
 | 
		
	
		
			
			|  | 37 | +/** A PXE UDP pseudo-header */
 | 
		
	
		
			
			|  | 38 | +struct pxe_udp_pseudo_header {
 | 
		
	
		
			
			|  | 39 | +	/** Source IP address */
 | 
		
	
		
			
			|  | 40 | +	IP4_t src_ip;
 | 
		
	
		
			
			|  | 41 | +	/** Source port */
 | 
		
	
		
			
			|  | 42 | +	UDP_PORT_t s_port;
 | 
		
	
		
			
			|  | 43 | +	/** Destination IP address */
 | 
		
	
		
			
			|  | 44 | +	IP4_t dest_ip;
 | 
		
	
		
			
			|  | 45 | +	/** Destination port */
 | 
		
	
		
			
			|  | 46 | +	UDP_PORT_t d_port;
 | 
		
	
		
			
			|  | 47 | +} __attribute__ (( packed ));
 | 
		
	
		
			
			|  | 48 | +
 | 
		
	
		
			
			| 37 | 49 |  /** A PXE UDP connection */
 | 
		
	
		
			
			| 38 | 50 |  struct pxe_udp_connection {
 | 
		
	
		
			
			| 39 | 51 |  	/** Data transfer interface to UDP stack */
 | 
		
	
		
			
			| 40 | 52 |  	struct interface xfer;
 | 
		
	
		
			
			| 41 | 53 |  	/** Local address */
 | 
		
	
		
			
			| 42 | 54 |  	struct sockaddr_in local;
 | 
		
	
		
			
			| 43 |  | -	/** Current PXENV_UDP_READ parameter block */
 | 
		
	
		
			
			| 44 |  | -	struct s_PXENV_UDP_READ *pxenv_udp_read;
 | 
		
	
		
			
			|  | 55 | +	/** List of received packets */
 | 
		
	
		
			
			|  | 56 | +	struct list_head list;
 | 
		
	
		
			
			| 45 | 57 |  };
 | 
		
	
		
			
			| 46 | 58 |  
 | 
		
	
		
			
			| 47 | 59 |  /**
 | 
		
	
	
		
			
			|  | @@ -58,45 +70,38 @@ struct pxe_udp_connection {
 | 
		
	
		
			
			| 58 | 70 |  static int pxe_udp_deliver ( struct pxe_udp_connection *pxe_udp,
 | 
		
	
		
			
			| 59 | 71 |  			     struct io_buffer *iobuf,
 | 
		
	
		
			
			| 60 | 72 |  			     struct xfer_metadata *meta ) {
 | 
		
	
		
			
			| 61 |  | -	struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read;
 | 
		
	
		
			
			|  | 73 | +	struct pxe_udp_pseudo_header *pshdr;
 | 
		
	
		
			
			| 62 | 74 |  	struct sockaddr_in *sin_src;
 | 
		
	
		
			
			| 63 | 75 |  	struct sockaddr_in *sin_dest;
 | 
		
	
		
			
			| 64 |  | -	userptr_t buffer;
 | 
		
	
		
			
			| 65 |  | -	size_t len;
 | 
		
	
		
			
			| 66 |  | -	int rc = 0;
 | 
		
	
		
			
			| 67 |  | -
 | 
		
	
		
			
			| 68 |  | -	if ( ! pxenv_udp_read ) {
 | 
		
	
		
			
			| 69 |  | -		DBG ( "PXE discarded UDP packet\n" );
 | 
		
	
		
			
			| 70 |  | -		rc = -ENOBUFS;
 | 
		
	
		
			
			| 71 |  | -		goto done;
 | 
		
	
		
			
			| 72 |  | -	}
 | 
		
	
		
			
			| 73 |  | -
 | 
		
	
		
			
			| 74 |  | -	/* Copy packet to buffer and record length */
 | 
		
	
		
			
			| 75 |  | -	buffer = real_to_user ( pxenv_udp_read->buffer.segment,
 | 
		
	
		
			
			| 76 |  | -				pxenv_udp_read->buffer.offset );
 | 
		
	
		
			
			| 77 |  | -	len = iob_len ( iobuf );
 | 
		
	
		
			
			| 78 |  | -	if ( len > pxenv_udp_read->buffer_size )
 | 
		
	
		
			
			| 79 |  | -		len = pxenv_udp_read->buffer_size;
 | 
		
	
		
			
			| 80 |  | -	copy_to_user ( buffer, 0, iobuf->data, len );
 | 
		
	
		
			
			| 81 |  | -	pxenv_udp_read->buffer_size = len;
 | 
		
	
		
			
			|  | 76 | +	int rc;
 | 
		
	
		
			
			| 82 | 77 |  
 | 
		
	
		
			
			| 83 |  | -	/* Fill in source/dest information */
 | 
		
	
		
			
			|  | 78 | +	/* Extract metadata */
 | 
		
	
		
			
			| 84 | 79 |  	assert ( meta );
 | 
		
	
		
			
			| 85 | 80 |  	sin_src = ( struct sockaddr_in * ) meta->src;
 | 
		
	
		
			
			| 86 | 81 |  	assert ( sin_src );
 | 
		
	
		
			
			| 87 | 82 |  	assert ( sin_src->sin_family == AF_INET );
 | 
		
	
		
			
			| 88 |  | -	pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr;
 | 
		
	
		
			
			| 89 |  | -	pxenv_udp_read->s_port = sin_src->sin_port;
 | 
		
	
		
			
			| 90 | 83 |  	sin_dest = ( struct sockaddr_in * ) meta->dest;
 | 
		
	
		
			
			| 91 | 84 |  	assert ( sin_dest );
 | 
		
	
		
			
			| 92 | 85 |  	assert ( sin_dest->sin_family == AF_INET );
 | 
		
	
		
			
			| 93 |  | -	pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr;
 | 
		
	
		
			
			| 94 |  | -	pxenv_udp_read->d_port = sin_dest->sin_port;
 | 
		
	
		
			
			| 95 | 86 |  
 | 
		
	
		
			
			| 96 |  | -	/* Mark as received */
 | 
		
	
		
			
			| 97 |  | -	pxe_udp->pxenv_udp_read = NULL;
 | 
		
	
		
			
			|  | 87 | +	/* Construct pseudo-header */
 | 
		
	
		
			
			|  | 88 | +	if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *pshdr ) ) ) != 0 ) {
 | 
		
	
		
			
			|  | 89 | +		DBG ( "PXE could not prepend pseudo-header\n" );
 | 
		
	
		
			
			|  | 90 | +		rc = -ENOMEM;
 | 
		
	
		
			
			|  | 91 | +		goto drop;
 | 
		
	
		
			
			|  | 92 | +	}
 | 
		
	
		
			
			|  | 93 | +	pshdr = iob_push ( iobuf, sizeof ( *pshdr ) );
 | 
		
	
		
			
			|  | 94 | +	pshdr->src_ip = sin_src->sin_addr.s_addr;
 | 
		
	
		
			
			|  | 95 | +	pshdr->s_port = sin_src->sin_port;
 | 
		
	
		
			
			|  | 96 | +	pshdr->dest_ip = sin_dest->sin_addr.s_addr;
 | 
		
	
		
			
			|  | 97 | +	pshdr->d_port = sin_dest->sin_port;
 | 
		
	
		
			
			| 98 | 98 |  
 | 
		
	
		
			
			| 99 |  | - done:
 | 
		
	
		
			
			|  | 99 | +	/* Add to queue */
 | 
		
	
		
			
			|  | 100 | +	list_add_tail ( &iobuf->list, &pxe_udp->list );
 | 
		
	
		
			
			|  | 101 | +
 | 
		
	
		
			
			|  | 102 | +	return 0;
 | 
		
	
		
			
			|  | 103 | +
 | 
		
	
		
			
			|  | 104 | + drop:
 | 
		
	
		
			
			| 100 | 105 |  	free_iob ( iobuf );
 | 
		
	
		
			
			| 101 | 106 |  	return rc;
 | 
		
	
		
			
			| 102 | 107 |  }
 | 
		
	
	
		
			
			|  | @@ -116,6 +121,7 @@ static struct pxe_udp_connection pxe_udp = {
 | 
		
	
		
			
			| 116 | 121 |  	.local = {
 | 
		
	
		
			
			| 117 | 122 |  		.sin_family = AF_INET,
 | 
		
	
		
			
			| 118 | 123 |  	},
 | 
		
	
		
			
			|  | 124 | +	.list = LIST_HEAD_INIT ( pxe_udp.list ),
 | 
		
	
		
			
			| 119 | 125 |  };
 | 
		
	
		
			
			| 120 | 126 |  
 | 
		
	
		
			
			| 121 | 127 |  /**
 | 
		
	
	
		
			
			|  | @@ -205,11 +211,20 @@ static PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) {
 | 
		
	
		
			
			| 205 | 211 |   */
 | 
		
	
		
			
			| 206 | 212 |  static PXENV_EXIT_t
 | 
		
	
		
			
			| 207 | 213 |  pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
 | 
		
	
		
			
			|  | 214 | +	struct io_buffer *iobuf;
 | 
		
	
		
			
			|  | 215 | +	struct io_buffer *tmp;
 | 
		
	
		
			
			|  | 216 | +
 | 
		
	
		
			
			| 208 | 217 |  	DBG ( "PXENV_UDP_CLOSE\n" );
 | 
		
	
		
			
			| 209 | 218 |  
 | 
		
	
		
			
			| 210 | 219 |  	/* Close UDP connection */
 | 
		
	
		
			
			| 211 | 220 |  	intf_restart ( &pxe_udp.xfer, 0 );
 | 
		
	
		
			
			| 212 | 221 |  
 | 
		
	
		
			
			|  | 222 | +	/* Discard any received packets */
 | 
		
	
		
			
			|  | 223 | +	list_for_each_entry_safe ( iobuf, tmp, &pxe_udp.list, list ) {
 | 
		
	
		
			
			|  | 224 | +		list_del ( &iobuf->list );
 | 
		
	
		
			
			|  | 225 | +		free_iob ( iobuf );
 | 
		
	
		
			
			|  | 226 | +	}
 | 
		
	
		
			
			|  | 227 | +
 | 
		
	
		
			
			| 213 | 228 |  	pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			| 214 | 229 |  	return PXENV_EXIT_SUCCESS;
 | 
		
	
		
			
			| 215 | 230 |  }
 | 
		
	
	
		
			
			|  | @@ -365,20 +380,32 @@ pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
 | 
		
	
		
			
			| 365 | 380 |  static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
 | 
		
	
		
			
			| 366 | 381 |  	struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip };
 | 
		
	
		
			
			| 367 | 382 |  	struct in_addr dest_ip;
 | 
		
	
		
			
			|  | 383 | +	struct io_buffer *iobuf;
 | 
		
	
		
			
			|  | 384 | +	struct pxe_udp_pseudo_header *pshdr;
 | 
		
	
		
			
			| 368 | 385 |  	uint16_t d_port_wanted = pxenv_udp_read->d_port;
 | 
		
	
		
			
			| 369 | 386 |  	uint16_t d_port;
 | 
		
	
		
			
			|  | 387 | +	userptr_t buffer;
 | 
		
	
		
			
			|  | 388 | +	size_t len;
 | 
		
	
		
			
			|  | 389 | +
 | 
		
	
		
			
			|  | 390 | +	/* Try receiving a packet, if the queue is empty */
 | 
		
	
		
			
			|  | 391 | +	if ( list_empty ( &pxe_udp.list ) )
 | 
		
	
		
			
			|  | 392 | +		step();
 | 
		
	
		
			
			| 370 | 393 |  
 | 
		
	
		
			
			| 371 |  | -	/* Try receiving a packet */
 | 
		
	
		
			
			| 372 |  | -	pxe_udp.pxenv_udp_read = pxenv_udp_read;
 | 
		
	
		
			
			| 373 |  | -	step();
 | 
		
	
		
			
			| 374 |  | -	if ( pxe_udp.pxenv_udp_read ) {
 | 
		
	
		
			
			|  | 394 | +	/* Remove first packet from the queue */
 | 
		
	
		
			
			|  | 395 | +	iobuf = list_first_entry ( &pxe_udp.list, struct io_buffer, list );
 | 
		
	
		
			
			|  | 396 | +	if ( ! iobuf ) {
 | 
		
	
		
			
			| 375 | 397 |  		/* No packet received */
 | 
		
	
		
			
			| 376 | 398 |  		DBG2 ( "PXENV_UDP_READ\n" );
 | 
		
	
		
			
			| 377 |  | -		pxe_udp.pxenv_udp_read = NULL;
 | 
		
	
		
			
			| 378 | 399 |  		goto no_packet;
 | 
		
	
		
			
			| 379 | 400 |  	}
 | 
		
	
		
			
			| 380 |  | -	dest_ip.s_addr = pxenv_udp_read->dest_ip;
 | 
		
	
		
			
			| 381 |  | -	d_port = pxenv_udp_read->d_port;
 | 
		
	
		
			
			|  | 401 | +	list_del ( &iobuf->list );
 | 
		
	
		
			
			|  | 402 | +
 | 
		
	
		
			
			|  | 403 | +	/* Strip pseudo-header */
 | 
		
	
		
			
			|  | 404 | +	assert ( iob_len ( iobuf ) >= sizeof ( *pshdr ) );
 | 
		
	
		
			
			|  | 405 | +	pshdr = iobuf->data;
 | 
		
	
		
			
			|  | 406 | +	iob_pull ( iobuf, sizeof ( *pshdr ) );
 | 
		
	
		
			
			|  | 407 | +	dest_ip.s_addr = pshdr->dest_ip;
 | 
		
	
		
			
			|  | 408 | +	d_port = pshdr->d_port;
 | 
		
	
		
			
			| 382 | 409 |  	DBG ( "PXENV_UDP_READ" );
 | 
		
	
		
			
			| 383 | 410 |  
 | 
		
	
		
			
			| 384 | 411 |  	/* Filter on destination address and/or port */
 | 
		
	
	
		
			
			|  | @@ -386,14 +413,29 @@ static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
 | 
		
	
		
			
			| 386 | 413 |  	     ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) {
 | 
		
	
		
			
			| 387 | 414 |  		DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) );
 | 
		
	
		
			
			| 388 | 415 |  		DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) );
 | 
		
	
		
			
			| 389 |  | -		goto no_packet;
 | 
		
	
		
			
			|  | 416 | +		goto drop;
 | 
		
	
		
			
			| 390 | 417 |  	}
 | 
		
	
		
			
			| 391 | 418 |  	if ( d_port_wanted && ( d_port_wanted != d_port ) ) {
 | 
		
	
		
			
			| 392 | 419 |  		DBG ( " wrong port %d", htons ( d_port ) );
 | 
		
	
		
			
			| 393 | 420 |  		DBG ( " (wanted %d)\n", htons ( d_port_wanted ) );
 | 
		
	
		
			
			| 394 |  | -		goto no_packet;
 | 
		
	
		
			
			|  | 421 | +		goto drop;
 | 
		
	
		
			
			| 395 | 422 |  	}
 | 
		
	
		
			
			| 396 | 423 |  
 | 
		
	
		
			
			|  | 424 | +	/* Copy packet to buffer and record length */
 | 
		
	
		
			
			|  | 425 | +	buffer = real_to_user ( pxenv_udp_read->buffer.segment,
 | 
		
	
		
			
			|  | 426 | +				pxenv_udp_read->buffer.offset );
 | 
		
	
		
			
			|  | 427 | +	len = iob_len ( iobuf );
 | 
		
	
		
			
			|  | 428 | +	if ( len > pxenv_udp_read->buffer_size )
 | 
		
	
		
			
			|  | 429 | +		len = pxenv_udp_read->buffer_size;
 | 
		
	
		
			
			|  | 430 | +	copy_to_user ( buffer, 0, iobuf->data, len );
 | 
		
	
		
			
			|  | 431 | +	pxenv_udp_read->buffer_size = len;
 | 
		
	
		
			
			|  | 432 | +
 | 
		
	
		
			
			|  | 433 | +	/* Fill in source/dest information */
 | 
		
	
		
			
			|  | 434 | +	pxenv_udp_read->src_ip = pshdr->src_ip;
 | 
		
	
		
			
			|  | 435 | +	pxenv_udp_read->s_port = pshdr->s_port;
 | 
		
	
		
			
			|  | 436 | +	pxenv_udp_read->dest_ip = pshdr->dest_ip;
 | 
		
	
		
			
			|  | 437 | +	pxenv_udp_read->d_port = pshdr->d_port;
 | 
		
	
		
			
			|  | 438 | +
 | 
		
	
		
			
			| 397 | 439 |  	DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment,
 | 
		
	
		
			
			| 398 | 440 |  	      pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size,
 | 
		
	
		
			
			| 399 | 441 |  	      inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) ));
 | 
		
	
	
		
			
			|  | @@ -401,9 +443,14 @@ static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
 | 
		
	
		
			
			| 401 | 443 |  	      inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ),
 | 
		
	
		
			
			| 402 | 444 |  	      ntohs ( pxenv_udp_read->d_port ) );
 | 
		
	
		
			
			| 403 | 445 |  
 | 
		
	
		
			
			|  | 446 | +	/* Free I/O buffer */
 | 
		
	
		
			
			|  | 447 | +	free_iob ( iobuf );
 | 
		
	
		
			
			|  | 448 | +
 | 
		
	
		
			
			| 404 | 449 |  	pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
 | 
		
	
		
			
			| 405 | 450 |  	return PXENV_EXIT_SUCCESS;
 | 
		
	
		
			
			| 406 | 451 |  
 | 
		
	
		
			
			|  | 452 | + drop:
 | 
		
	
		
			
			|  | 453 | +	free_iob ( iobuf );
 | 
		
	
		
			
			| 407 | 454 |   no_packet:
 | 
		
	
		
			
			| 408 | 455 |  	pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
 | 
		
	
		
			
			| 409 | 456 |  	return PXENV_EXIT_FAILURE;
 |