Browse Source

[pxe] Maintain a queue for received PXE UDP packets

Some devices return multiple packets in a single poll.  Handle such
devices gracefully by enqueueing received PXE UDP packets (along with
a pseudo-header to hold the IPv4 addresses and port numbers) and
dequeueing them on subsequent calls to PXENV_UDP_READ.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
95bc563f0c
1 changed files with 85 additions and 38 deletions
  1. 85
    38
      src/arch/i386/interface/pxe/pxe_udp.c

+ 85
- 38
src/arch/i386/interface/pxe/pxe_udp.c View File

@@ -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;

Loading…
Cancel
Save