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