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
 
34
 
35
 FILE_LICENCE ( GPL2_OR_LATER );
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
 /** A PXE UDP connection */
49
 /** A PXE UDP connection */
38
 struct pxe_udp_connection {
50
 struct pxe_udp_connection {
39
 	/** Data transfer interface to UDP stack */
51
 	/** Data transfer interface to UDP stack */
40
 	struct interface xfer;
52
 	struct interface xfer;
41
 	/** Local address */
53
 	/** Local address */
42
 	struct sockaddr_in local;
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
 static int pxe_udp_deliver ( struct pxe_udp_connection *pxe_udp,
70
 static int pxe_udp_deliver ( struct pxe_udp_connection *pxe_udp,
59
 			     struct io_buffer *iobuf,
71
 			     struct io_buffer *iobuf,
60
 			     struct xfer_metadata *meta ) {
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
 	struct sockaddr_in *sin_src;
74
 	struct sockaddr_in *sin_src;
63
 	struct sockaddr_in *sin_dest;
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
 	assert ( meta );
79
 	assert ( meta );
85
 	sin_src = ( struct sockaddr_in * ) meta->src;
80
 	sin_src = ( struct sockaddr_in * ) meta->src;
86
 	assert ( sin_src );
81
 	assert ( sin_src );
87
 	assert ( sin_src->sin_family == AF_INET );
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
 	sin_dest = ( struct sockaddr_in * ) meta->dest;
83
 	sin_dest = ( struct sockaddr_in * ) meta->dest;
91
 	assert ( sin_dest );
84
 	assert ( sin_dest );
92
 	assert ( sin_dest->sin_family == AF_INET );
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
 	free_iob ( iobuf );
105
 	free_iob ( iobuf );
101
 	return rc;
106
 	return rc;
102
 }
107
 }
116
 	.local = {
121
 	.local = {
117
 		.sin_family = AF_INET,
122
 		.sin_family = AF_INET,
118
 	},
123
 	},
124
+	.list = LIST_HEAD_INIT ( pxe_udp.list ),
119
 };
125
 };
120
 
126
 
121
 /**
127
 /**
205
  */
211
  */
206
 static PXENV_EXIT_t
212
 static PXENV_EXIT_t
207
 pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
213
 pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
214
+	struct io_buffer *iobuf;
215
+	struct io_buffer *tmp;
216
+
208
 	DBG ( "PXENV_UDP_CLOSE\n" );
217
 	DBG ( "PXENV_UDP_CLOSE\n" );
209
 
218
 
210
 	/* Close UDP connection */
219
 	/* Close UDP connection */
211
 	intf_restart ( &pxe_udp.xfer, 0 );
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
 	pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
228
 	pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
214
 	return PXENV_EXIT_SUCCESS;
229
 	return PXENV_EXIT_SUCCESS;
215
 }
230
 }
365
 static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
380
 static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
366
 	struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip };
381
 	struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip };
367
 	struct in_addr dest_ip;
382
 	struct in_addr dest_ip;
383
+	struct io_buffer *iobuf;
384
+	struct pxe_udp_pseudo_header *pshdr;
368
 	uint16_t d_port_wanted = pxenv_udp_read->d_port;
385
 	uint16_t d_port_wanted = pxenv_udp_read->d_port;
369
 	uint16_t d_port;
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
 		/* No packet received */
397
 		/* No packet received */
376
 		DBG2 ( "PXENV_UDP_READ\n" );
398
 		DBG2 ( "PXENV_UDP_READ\n" );
377
-		pxe_udp.pxenv_udp_read = NULL;
378
 		goto no_packet;
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
 	DBG ( "PXENV_UDP_READ" );
409
 	DBG ( "PXENV_UDP_READ" );
383
 
410
 
384
 	/* Filter on destination address and/or port */
411
 	/* Filter on destination address and/or port */
386
 	     ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) {
413
 	     ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) {
387
 		DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) );
414
 		DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) );
388
 		DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) );
415
 		DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) );
389
-		goto no_packet;
416
+		goto drop;
390
 	}
417
 	}
391
 	if ( d_port_wanted && ( d_port_wanted != d_port ) ) {
418
 	if ( d_port_wanted && ( d_port_wanted != d_port ) ) {
392
 		DBG ( " wrong port %d", htons ( d_port ) );
419
 		DBG ( " wrong port %d", htons ( d_port ) );
393
 		DBG ( " (wanted %d)\n", htons ( d_port_wanted ) );
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
 	DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment,
439
 	DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment,
398
 	      pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size,
440
 	      pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size,
399
 	      inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) ));
441
 	      inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) ));
401
 	      inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ),
443
 	      inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ),
402
 	      ntohs ( pxenv_udp_read->d_port ) );
444
 	      ntohs ( pxenv_udp_read->d_port ) );
403
 
445
 
446
+	/* Free I/O buffer */
447
+	free_iob ( iobuf );
448
+
404
 	pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
449
 	pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
405
 	return PXENV_EXIT_SUCCESS;
450
 	return PXENV_EXIT_SUCCESS;
406
 
451
 
452
+ drop:
453
+	free_iob ( iobuf );
407
  no_packet:
454
  no_packet:
408
 	pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
455
 	pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
409
 	return PXENV_EXIT_FAILURE;
456
 	return PXENV_EXIT_FAILURE;

Loading…
Cancel
Save