|
@@ -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
|
+}
|