|
@@ -1,11 +1,14 @@
|
1
|
1
|
#include <stdint.h>
|
|
2
|
+#include <stdlib.h>
|
2
|
3
|
#include <string.h>
|
3
|
4
|
#include <assert.h>
|
4
|
5
|
#include <byteswap.h>
|
5
|
6
|
#include <errno.h>
|
6
|
7
|
#include <gpxe/tcpip.h>
|
7
|
8
|
#include <gpxe/iobuf.h>
|
8
|
|
-#include <gpxe/netdevice.h>
|
|
9
|
+#include <gpxe/xfer.h>
|
|
10
|
+#include <gpxe/open.h>
|
|
11
|
+#include <gpxe/uri.h>
|
9
|
12
|
#include <gpxe/udp.h>
|
10
|
13
|
|
11
|
14
|
/** @file
|
|
@@ -13,233 +16,234 @@
|
13
|
16
|
* UDP protocol
|
14
|
17
|
*/
|
15
|
18
|
|
16
|
|
-struct tcpip_protocol udp_protocol;
|
17
|
|
-
|
18
|
19
|
/**
|
19
|
|
- * List of registered UDP connections
|
|
20
|
+ * A UDP connection
|
|
21
|
+ *
|
20
|
22
|
*/
|
21
|
|
-static LIST_HEAD ( udp_conns );
|
|
23
|
+struct udp_connection {
|
|
24
|
+ /** Reference counter */
|
|
25
|
+ struct refcnt refcnt;
|
|
26
|
+ /** List of UDP connections */
|
|
27
|
+ struct list_head list;
|
|
28
|
+
|
|
29
|
+ /** Data transfer interface */
|
|
30
|
+ struct xfer_interface xfer;
|
|
31
|
+
|
|
32
|
+ /** Remote socket address */
|
|
33
|
+ struct sockaddr_tcpip peer;
|
|
34
|
+ /** Local port on which the connection receives packets */
|
|
35
|
+ unsigned int local_port;
|
|
36
|
+};
|
22
|
37
|
|
23
|
38
|
/**
|
24
|
|
- * Bind UDP connection to local port
|
25
|
|
- *
|
26
|
|
- * @v conn UDP connection
|
27
|
|
- * @v local_port Local port, in network byte order
|
28
|
|
- * @ret rc Return status code
|
|
39
|
+ * List of registered UDP connections
|
29
|
40
|
*/
|
30
|
|
-int udp_bind ( struct udp_connection *conn, uint16_t local_port ) {
|
31
|
|
- struct udp_connection *existing;
|
|
41
|
+static LIST_HEAD ( udp_conns );
|
32
|
42
|
|
33
|
|
- list_for_each_entry ( existing, &udp_conns, list ) {
|
34
|
|
- if ( existing->local_port == local_port )
|
35
|
|
- return -EADDRINUSE;
|
36
|
|
- }
|
37
|
|
- conn->local_port = local_port;
|
38
|
|
- return 0;
|
39
|
|
-}
|
|
43
|
+/* Forward declatations */
|
|
44
|
+static struct xfer_interface_operations udp_xfer_operations;
|
|
45
|
+struct tcpip_protocol udp_protocol;
|
40
|
46
|
|
41
|
47
|
/**
|
42
|
|
- * Open a local port
|
|
48
|
+ * Bind UDP connection to local port
|
43
|
49
|
*
|
44
|
|
- * @v conn UDP connection
|
45
|
|
- * @v local_port Local port, in network byte order, or zero
|
|
50
|
+ * @v udp UDP connection
|
|
51
|
+ * @v port Local port, in network byte order, or zero
|
46
|
52
|
* @ret rc Return status code
|
47
|
53
|
*
|
48
|
54
|
* Opens the UDP connection and binds to a local port. If no local
|
49
|
55
|
* port is specified, the first available port will be used.
|
50
|
56
|
*/
|
51
|
|
-int udp_open ( struct udp_connection *conn, uint16_t local_port ) {
|
|
57
|
+static int udp_bind ( struct udp_connection *udp, unsigned int port ) {
|
|
58
|
+ struct udp_connection *existing;
|
52
|
59
|
static uint16_t try_port = 1024;
|
53
|
|
- int rc;
|
54
|
60
|
|
55
|
61
|
/* If no port specified, find the first available port */
|
56
|
|
- if ( ! local_port ) {
|
|
62
|
+ if ( ! port ) {
|
57
|
63
|
for ( ; try_port ; try_port++ ) {
|
58
|
64
|
if ( try_port < 1024 )
|
59
|
65
|
continue;
|
60
|
|
- if ( udp_open ( conn, htons ( try_port ) ) == 0 )
|
|
66
|
+ if ( udp_bind ( udp, htons ( try_port ) ) == 0 )
|
61
|
67
|
return 0;
|
62
|
68
|
}
|
63
|
69
|
return -EADDRINUSE;
|
64
|
70
|
}
|
65
|
71
|
|
66
|
72
|
/* Attempt bind to local port */
|
67
|
|
- if ( ( rc = udp_bind ( conn, local_port ) ) != 0 ) {
|
68
|
|
- DBGC ( conn, "UDP %p could not bind to local port %d: %s\n",
|
69
|
|
- conn, local_port, strerror ( rc ) );
|
70
|
|
- return rc;
|
|
73
|
+ list_for_each_entry ( existing, &udp_conns, list ) {
|
|
74
|
+ if ( existing->local_port == port ) {
|
|
75
|
+ DBGC ( udp, "UDP %p could not bind: port %d in use\n",
|
|
76
|
+ udp, ntohs ( port ) );
|
|
77
|
+ return -EADDRINUSE;
|
|
78
|
+ }
|
71
|
79
|
}
|
|
80
|
+ udp->local_port = port;
|
72
|
81
|
|
73
|
82
|
/* Add to UDP connection list */
|
74
|
|
- list_add ( &conn->list, &udp_conns );
|
75
|
|
- DBGC ( conn, "UDP %p opened on port %d\n", conn,
|
76
|
|
- ntohs ( local_port ) );
|
|
83
|
+ DBGC ( udp, "UDP %p bound to port %d\n", udp, ntohs ( port ) );
|
77
|
84
|
|
78
|
85
|
return 0;
|
79
|
86
|
}
|
80
|
87
|
|
81
|
88
|
/**
|
82
|
|
- * Close a UDP connection
|
|
89
|
+ * Open a UDP connection
|
83
|
90
|
*
|
84
|
|
- * @v conn UDP connection
|
|
91
|
+ * @v xfer Data transfer interface
|
|
92
|
+ * @v peer Peer socket address
|
|
93
|
+ * @v local Local socket address, or NULL
|
|
94
|
+ * @v promisc Socket is promiscuous
|
|
95
|
+ * @ret rc Return status code
|
85
|
96
|
*/
|
86
|
|
-void udp_close ( struct udp_connection *conn ) {
|
87
|
|
- list_del ( &conn->list );
|
88
|
|
- DBGC ( conn, "UDP %p closed\n", conn );
|
|
97
|
+static int udp_open_common ( struct xfer_interface *xfer,
|
|
98
|
+ struct sockaddr *peer, struct sockaddr *local,
|
|
99
|
+ int promisc ) {
|
|
100
|
+ struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
|
|
101
|
+ struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
|
|
102
|
+ struct udp_connection *udp;
|
|
103
|
+ unsigned int bind_port;
|
|
104
|
+ int rc;
|
|
105
|
+
|
|
106
|
+ /* Allocate and initialise structure */
|
|
107
|
+ udp = malloc ( sizeof ( *udp ) );
|
|
108
|
+ if ( ! udp )
|
|
109
|
+ return -ENOMEM;
|
|
110
|
+ DBGC ( udp, "UDP %p allocated\n", udp );
|
|
111
|
+ memset ( udp, 0, sizeof ( *udp ) );
|
|
112
|
+ xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt );
|
|
113
|
+ memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) );
|
|
114
|
+
|
|
115
|
+ /* Bind to local port */
|
|
116
|
+ if ( ! promisc ) {
|
|
117
|
+ bind_port = ( st_local ? st_local->st_port : 0 );
|
|
118
|
+ if ( ( rc = udp_bind ( udp, bind_port ) ) != 0 )
|
|
119
|
+ goto err;
|
|
120
|
+ }
|
|
121
|
+
|
|
122
|
+ /* Attach parent interface, transfer reference to connection
|
|
123
|
+ * list and return
|
|
124
|
+ */
|
|
125
|
+ xfer_plug_plug ( &udp->xfer, xfer );
|
|
126
|
+ list_add ( &udp->list, &udp_conns );
|
|
127
|
+ return 0;
|
|
128
|
+
|
|
129
|
+ err:
|
|
130
|
+ ref_put ( &udp->refcnt );
|
|
131
|
+ return rc;
|
89
|
132
|
}
|
90
|
133
|
|
91
|
134
|
/**
|
92
|
|
- * Allocate I/O buffer for UDP
|
|
135
|
+ * Open a UDP connection
|
93
|
136
|
*
|
94
|
|
- * @v conn UDP connection
|
95
|
|
- * @ret iobuf I/O buffer, or NULL
|
|
137
|
+ * @v xfer Data transfer interface
|
|
138
|
+ * @v peer Peer socket address
|
|
139
|
+ * @v local Local socket address, or NULL
|
|
140
|
+ * @ret rc Return status code
|
96
|
141
|
*/
|
97
|
|
-static struct io_buffer * udp_alloc_iob ( struct udp_connection *conn ) {
|
98
|
|
- struct io_buffer *iobuf;
|
99
|
|
-
|
100
|
|
- iobuf = alloc_iob ( UDP_MAX_TXIOB );
|
101
|
|
- if ( ! iobuf ) {
|
102
|
|
- DBGC ( conn, "UDP %p cannot allocate buffer of length %d\n",
|
103
|
|
- conn, UDP_MAX_TXIOB );
|
104
|
|
- return NULL;
|
105
|
|
- }
|
106
|
|
- iob_reserve ( iobuf, UDP_MAX_HLEN );
|
107
|
|
- return iobuf;
|
|
142
|
+int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
|
|
143
|
+ struct sockaddr *local ) {
|
|
144
|
+ return udp_open_common ( xfer, peer, local, 0 );
|
108
|
145
|
}
|
109
|
146
|
|
110
|
147
|
/**
|
111
|
|
- * User request to send data via a UDP connection
|
|
148
|
+ * Open a promiscuous UDP connection
|
112
|
149
|
*
|
113
|
|
- * @v conn UDP connection
|
|
150
|
+ * @v xfer Data transfer interface
|
|
151
|
+ * @ret rc Return status code
|
114
|
152
|
*
|
115
|
|
- * This function allocates buffer space and invokes the function's
|
116
|
|
- * senddata() callback. The callback may use the buffer space as
|
117
|
|
- * temporary storage space.
|
|
153
|
+ * Promiscuous UDP connections are required in order to support the
|
|
154
|
+ * PXE API.
|
118
|
155
|
*/
|
119
|
|
-int udp_senddata ( struct udp_connection *conn ) {
|
120
|
|
- int rc;
|
|
156
|
+int udp_open_promisc ( struct xfer_interface *xfer ) {
|
|
157
|
+ return udp_open_common ( xfer, NULL, NULL, 1 );
|
|
158
|
+}
|
121
|
159
|
|
122
|
|
- conn->tx_iob = udp_alloc_iob ( conn );
|
123
|
|
- if ( ! conn->tx_iob )
|
124
|
|
- return -ENOMEM;
|
|
160
|
+/**
|
|
161
|
+ * Close a UDP connection
|
|
162
|
+ *
|
|
163
|
+ * @v udp UDP connection
|
|
164
|
+ * @v rc Reason for close
|
|
165
|
+ */
|
|
166
|
+static void udp_close ( struct udp_connection *udp, int rc ) {
|
125
|
167
|
|
126
|
|
- rc = conn->udp_op->senddata ( conn, conn->tx_iob->data,
|
127
|
|
- iob_tailroom ( conn->tx_iob ) );
|
128
|
|
- if ( rc != 0 ) {
|
129
|
|
- DBGC ( conn, "UDP %p application could not send packet: %s\n",
|
130
|
|
- conn, strerror ( rc ) );
|
131
|
|
- }
|
|
168
|
+ /* Close data transfer interface */
|
|
169
|
+ xfer_nullify ( &udp->xfer );
|
|
170
|
+ xfer_close ( &udp->xfer, rc );
|
132
|
171
|
|
133
|
|
- if ( conn->tx_iob ) {
|
134
|
|
- free_iob ( conn->tx_iob );
|
135
|
|
- conn->tx_iob = NULL;
|
136
|
|
- }
|
|
172
|
+ /* Remove from list of connections and drop list's reference */
|
|
173
|
+ list_del ( &udp->list );
|
|
174
|
+ ref_put ( &udp->refcnt );
|
137
|
175
|
|
138
|
|
- return rc;
|
|
176
|
+ DBGC ( udp, "UDP %p closed\n", udp );
|
139
|
177
|
}
|
140
|
|
-
|
|
178
|
+
|
141
|
179
|
/**
|
142
|
180
|
* Transmit data via a UDP connection to a specified address
|
143
|
181
|
*
|
144
|
|
- * @v conn UDP connection
|
145
|
|
- * @v peer Destination address
|
146
|
|
- * @v netdev Network device to use if no route found, or NULL
|
147
|
|
- * @v data Data to send
|
148
|
|
- * @v len Length of data
|
|
182
|
+ * @v udp UDP connection
|
|
183
|
+ * @v iobuf I/O buffer
|
|
184
|
+ * @v src_port Source port, or 0 to use default
|
|
185
|
+ * @v dest Destination address, or NULL to use default
|
149
|
186
|
* @ret rc Return status code
|
150
|
187
|
*/
|
151
|
|
-int udp_sendto_via ( struct udp_connection *conn, struct sockaddr_tcpip *peer,
|
152
|
|
- struct net_device *netdev, const void *data,
|
153
|
|
- size_t len ) {
|
|
188
|
+static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
|
|
189
|
+ unsigned int src_port, struct sockaddr_tcpip *dest ) {
|
154
|
190
|
struct udp_header *udphdr;
|
155
|
|
- struct io_buffer *iobuf;
|
|
191
|
+ struct net_device *netdev = NULL;
|
|
192
|
+ size_t len;
|
156
|
193
|
int rc;
|
157
|
194
|
|
158
|
|
- /* Use precreated I/O buffer if one is available */
|
159
|
|
- if ( conn->tx_iob ) {
|
160
|
|
- iobuf = conn->tx_iob;
|
161
|
|
- conn->tx_iob = NULL;
|
162
|
|
- } else {
|
163
|
|
- iobuf = udp_alloc_iob ( conn );
|
164
|
|
- if ( ! iobuf )
|
165
|
|
- return -ENOMEM;
|
166
|
|
- }
|
|
195
|
+#warning "netdev?"
|
167
|
196
|
|
168
|
|
- /* Avoid overflowing TX buffer */
|
169
|
|
- if ( len > iob_tailroom ( iobuf ) )
|
170
|
|
- len = iob_tailroom ( iobuf );
|
|
197
|
+ /* Check we can accommodate the header */
|
|
198
|
+ if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) {
|
|
199
|
+ free_iob ( iobuf );
|
|
200
|
+ return rc;
|
|
201
|
+ }
|
171
|
202
|
|
172
|
|
- /* Copy payload */
|
173
|
|
- memmove ( iob_put ( iobuf, len ), data, len );
|
|
203
|
+ /* Fill in default values if not explicitly provided */
|
|
204
|
+ if ( ! src_port )
|
|
205
|
+ src_port = udp->local_port;
|
|
206
|
+ if ( ! dest )
|
|
207
|
+ dest = &udp->peer;
|
174
|
208
|
|
175
|
|
- /*
|
176
|
|
- * Add the UDP header
|
177
|
|
- *
|
178
|
|
- * Covert all 16- and 32- bit integers into network btye order before
|
179
|
|
- * sending it over the network
|
180
|
|
- */
|
|
209
|
+ /* Add the UDP header */
|
181
|
210
|
udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
|
182
|
|
- udphdr->dest_port = peer->st_port;
|
183
|
|
- udphdr->source_port = conn->local_port;
|
184
|
|
- udphdr->len = htons ( iob_len ( iobuf ) );
|
|
211
|
+ len = iob_len ( iobuf );
|
|
212
|
+ udphdr->dest = dest->st_port;
|
|
213
|
+ udphdr->src = src_port;
|
|
214
|
+ udphdr->len = htons ( len );
|
185
|
215
|
udphdr->chksum = 0;
|
186
|
|
- udphdr->chksum = tcpip_chksum ( udphdr, sizeof ( *udphdr ) + len );
|
|
216
|
+ udphdr->chksum = tcpip_chksum ( udphdr, len );
|
187
|
217
|
|
188
|
218
|
/* Dump debugging information */
|
189
|
|
- DBGC ( conn, "UDP %p TX %d->%d len %zd\n", conn,
|
190
|
|
- ntohs ( udphdr->source_port ), ntohs ( udphdr->dest_port ),
|
|
219
|
+ DBGC ( udp, "UDP %p TX %d->%d len %zd\n", udp,
|
|
220
|
+ ntohs ( udphdr->src ), ntohs ( udphdr->dest ),
|
191
|
221
|
ntohs ( udphdr->len ) );
|
192
|
222
|
|
193
|
223
|
/* Send it to the next layer for processing */
|
194
|
|
- if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, peer, netdev,
|
|
224
|
+ if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, dest, netdev,
|
195
|
225
|
&udphdr->chksum ) ) != 0 ) {
|
196
|
|
- DBGC ( conn, "UDP %p could not transmit packet: %s\n",
|
197
|
|
- conn, strerror ( rc ) );
|
|
226
|
+ DBGC ( udp, "UDP %p could not transmit packet: %s\n",
|
|
227
|
+ udp, strerror ( rc ) );
|
198
|
228
|
return rc;
|
199
|
229
|
}
|
200
|
230
|
|
201
|
231
|
return 0;
|
202
|
232
|
}
|
203
|
233
|
|
204
|
|
-/**
|
205
|
|
- * Transmit data via a UDP connection to a specified address
|
206
|
|
- *
|
207
|
|
- * @v conn UDP connection
|
208
|
|
- * @v peer Destination address
|
209
|
|
- * @v data Data to send
|
210
|
|
- * @v len Length of data
|
211
|
|
- * @ret rc Return status code
|
212
|
|
- */
|
213
|
|
-int udp_sendto ( struct udp_connection *conn, struct sockaddr_tcpip *peer,
|
214
|
|
- const void *data, size_t len ) {
|
215
|
|
- return udp_sendto_via ( conn, peer, NULL, data, len );
|
216
|
|
-}
|
217
|
|
-
|
218
|
|
-/**
|
219
|
|
- * Transmit data via a UDP connection
|
220
|
|
- *
|
221
|
|
- * @v conn UDP connection
|
222
|
|
- * @v data Data to send
|
223
|
|
- * @v len Length of data
|
224
|
|
- * @ret rc Return status code
|
225
|
|
- */
|
226
|
|
-int udp_send ( struct udp_connection *conn, const void *data, size_t len ) {
|
227
|
|
- return udp_sendto ( conn, &conn->peer, data, len );
|
228
|
|
-}
|
229
|
|
-
|
230
|
234
|
/**
|
231
|
235
|
* Identify UDP connection by local port number
|
232
|
236
|
*
|
233
|
237
|
* @v local_port Local port (in network-endian order)
|
234
|
|
- * @ret conn TCP connection, or NULL
|
|
238
|
+ * @ret udp UDP connection, or NULL
|
235
|
239
|
*/
|
236
|
|
-static struct udp_connection * udp_demux ( uint16_t local_port ) {
|
237
|
|
- struct udp_connection *conn;
|
|
240
|
+static struct udp_connection * udp_demux ( unsigned int local_port ) {
|
|
241
|
+ struct udp_connection *udp;
|
238
|
242
|
|
239
|
|
- list_for_each_entry ( conn, &udp_conns, list ) {
|
240
|
|
- if ( ( conn->local_port == local_port ) ||
|
241
|
|
- ( conn->local_port == 0 ) ) {
|
242
|
|
- return conn;
|
|
243
|
+ list_for_each_entry ( udp, &udp_conns, list ) {
|
|
244
|
+ if ( ( udp->local_port == local_port ) ||
|
|
245
|
+ ( udp->local_port == 0 ) ) {
|
|
246
|
+ return udp;
|
243
|
247
|
}
|
244
|
248
|
}
|
245
|
249
|
return NULL;
|
|
@@ -257,9 +261,10 @@ static struct udp_connection * udp_demux ( uint16_t local_port ) {
|
257
|
261
|
static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
|
258
|
262
|
struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
|
259
|
263
|
struct udp_header *udphdr = iobuf->data;
|
260
|
|
- struct udp_connection *conn;
|
|
264
|
+ struct udp_connection *udp;
|
|
265
|
+ struct xfer_metadata meta;
|
261
|
266
|
size_t ulen;
|
262
|
|
- uint16_t csum;
|
|
267
|
+ unsigned int csum;
|
263
|
268
|
int rc = 0;
|
264
|
269
|
|
265
|
270
|
/* Sanity check packet */
|
|
@@ -294,37 +299,30 @@ static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
|
294
|
299
|
}
|
295
|
300
|
|
296
|
301
|
/* Parse parameters from header and strip header */
|
297
|
|
- st_src->st_port = udphdr->source_port;
|
298
|
|
- st_dest->st_port = udphdr->dest_port;
|
299
|
|
- conn = udp_demux ( udphdr->dest_port );
|
|
302
|
+ st_src->st_port = udphdr->src;
|
|
303
|
+ st_dest->st_port = udphdr->dest;
|
|
304
|
+ udp = udp_demux ( udphdr->dest );
|
300
|
305
|
iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) );
|
301
|
306
|
iob_pull ( iobuf, sizeof ( *udphdr ) );
|
302
|
307
|
|
303
|
308
|
/* Dump debugging information */
|
304
|
|
- DBGC ( conn, "UDP %p RX %d<-%d len %zd\n", conn,
|
305
|
|
- ntohs ( udphdr->dest_port ), ntohs ( udphdr->source_port ),
|
306
|
|
- ulen );
|
|
309
|
+ DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
|
|
310
|
+ ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
|
307
|
311
|
|
308
|
312
|
/* Ignore if no matching connection found */
|
309
|
|
- if ( ! conn ) {
|
|
313
|
+ if ( ! udp ) {
|
310
|
314
|
DBG ( "No UDP connection listening on port %d\n",
|
311
|
|
- ntohs ( udphdr->dest_port ) );
|
|
315
|
+ ntohs ( udphdr->dest ) );
|
312
|
316
|
rc = -ENOTCONN;
|
313
|
317
|
goto done;
|
314
|
318
|
}
|
315
|
319
|
|
316
|
320
|
/* Pass data to application */
|
317
|
|
- if ( conn->udp_op->newdata ) {
|
318
|
|
- rc = conn->udp_op->newdata ( conn, iobuf->data, iob_len ( iobuf ),
|
319
|
|
- st_src, st_dest );
|
320
|
|
- if ( rc != 0 ) {
|
321
|
|
- DBGC ( conn, "UDP %p application rejected packet: %s\n",
|
322
|
|
- conn, strerror ( rc ) );
|
323
|
|
- }
|
324
|
|
- } else {
|
325
|
|
- DBGC ( conn, "UDP %p application has no newdata handler for " \
|
326
|
|
- "incoming packet\n", conn );
|
327
|
|
- }
|
|
321
|
+ memset ( &meta, 0, sizeof ( meta ) );
|
|
322
|
+ meta.src = ( struct sockaddr * ) st_src;
|
|
323
|
+ meta.dest = ( struct sockaddr * ) st_dest;
|
|
324
|
+ rc = xfer_deliver_iob_meta ( &udp->xfer, iobuf, &meta );
|
|
325
|
+ iobuf = NULL;
|
328
|
326
|
|
329
|
327
|
done:
|
330
|
328
|
free_iob ( iobuf );
|
|
@@ -336,3 +334,130 @@ struct tcpip_protocol udp_protocol __tcpip_protocol = {
|
336
|
334
|
.rx = udp_rx,
|
337
|
335
|
.tcpip_proto = IP_UDP,
|
338
|
336
|
};
|
|
337
|
+
|
|
338
|
+/***************************************************************************
|
|
339
|
+ *
|
|
340
|
+ * Data transfer interface
|
|
341
|
+ *
|
|
342
|
+ ***************************************************************************
|
|
343
|
+ */
|
|
344
|
+
|
|
345
|
+/**
|
|
346
|
+ * Close interface
|
|
347
|
+ *
|
|
348
|
+ * @v xfer Data transfer interface
|
|
349
|
+ * @v rc Reason for close
|
|
350
|
+ */
|
|
351
|
+static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) {
|
|
352
|
+ struct udp_connection *udp =
|
|
353
|
+ container_of ( xfer, struct udp_connection, xfer );
|
|
354
|
+
|
|
355
|
+ /* Close connection */
|
|
356
|
+ udp_close ( udp, rc );
|
|
357
|
+}
|
|
358
|
+
|
|
359
|
+/**
|
|
360
|
+ * Allocate I/O buffer for UDP
|
|
361
|
+ *
|
|
362
|
+ * @v xfer Data transfer interface
|
|
363
|
+ * @v len Payload size
|
|
364
|
+ * @ret iobuf I/O buffer, or NULL
|
|
365
|
+ */
|
|
366
|
+static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer,
|
|
367
|
+ size_t len ) {
|
|
368
|
+ struct udp_connection *udp =
|
|
369
|
+ container_of ( xfer, struct udp_connection, xfer );
|
|
370
|
+ struct io_buffer *iobuf;
|
|
371
|
+
|
|
372
|
+ iobuf = alloc_iob ( UDP_MAX_HLEN + len );
|
|
373
|
+ if ( ! iobuf ) {
|
|
374
|
+ DBGC ( udp, "UDP %p cannot allocate buffer of length %d\n",
|
|
375
|
+ udp, len );
|
|
376
|
+ return NULL;
|
|
377
|
+ }
|
|
378
|
+ iob_reserve ( iobuf, UDP_MAX_HLEN );
|
|
379
|
+ return iobuf;
|
|
380
|
+}
|
|
381
|
+
|
|
382
|
+/**
|
|
383
|
+ * Deliver datagram as I/O buffer
|
|
384
|
+ *
|
|
385
|
+ * @v xfer Data transfer interface
|
|
386
|
+ * @v iobuf Datagram I/O buffer
|
|
387
|
+ * @v meta Data transfer metadata, or NULL
|
|
388
|
+ * @ret rc Return status code
|
|
389
|
+ */
|
|
390
|
+static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
|
|
391
|
+ struct io_buffer *iobuf,
|
|
392
|
+ struct xfer_metadata *meta ) {
|
|
393
|
+ struct udp_connection *udp =
|
|
394
|
+ container_of ( xfer, struct udp_connection, xfer );
|
|
395
|
+ struct sockaddr_tcpip *src;
|
|
396
|
+ struct sockaddr_tcpip *dest = NULL;
|
|
397
|
+ unsigned int src_port = 0;
|
|
398
|
+
|
|
399
|
+ /* Apply xfer metadata */
|
|
400
|
+ if ( meta ) {
|
|
401
|
+ src = ( struct sockaddr_tcpip * ) meta->src;
|
|
402
|
+ if ( src )
|
|
403
|
+ src_port = src->st_port;
|
|
404
|
+ dest = ( struct sockaddr_tcpip * ) meta->dest;
|
|
405
|
+ }
|
|
406
|
+
|
|
407
|
+ /* Transmit data, if possible */
|
|
408
|
+ udp_tx ( udp, iobuf, src_port, dest );
|
|
409
|
+
|
|
410
|
+ return 0;
|
|
411
|
+}
|
|
412
|
+
|
|
413
|
+/** UDP data transfer interface operations */
|
|
414
|
+static struct xfer_interface_operations udp_xfer_operations = {
|
|
415
|
+ .close = udp_xfer_close,
|
|
416
|
+ .vredirect = ignore_xfer_vredirect,
|
|
417
|
+ .request = ignore_xfer_request,
|
|
418
|
+ .seek = ignore_xfer_seek,
|
|
419
|
+ .alloc_iob = udp_alloc_iob,
|
|
420
|
+ .deliver_iob = udp_xfer_deliver_iob,
|
|
421
|
+ .deliver_raw = xfer_deliver_as_iob,
|
|
422
|
+};
|
|
423
|
+
|
|
424
|
+/***************************************************************************
|
|
425
|
+ *
|
|
426
|
+ * Openers
|
|
427
|
+ *
|
|
428
|
+ ***************************************************************************
|
|
429
|
+ */
|
|
430
|
+
|
|
431
|
+/** UDP socket opener */
|
|
432
|
+struct socket_opener udp_socket_opener __socket_opener = {
|
|
433
|
+ .semantics = SOCK_DGRAM,
|
|
434
|
+ .family = AF_INET,
|
|
435
|
+ .open = udp_open,
|
|
436
|
+};
|
|
437
|
+
|
|
438
|
+/**
|
|
439
|
+ * Open UDP URI
|
|
440
|
+ *
|
|
441
|
+ * @v xfer Data transfer interface
|
|
442
|
+ * @v uri URI
|
|
443
|
+ * @ret rc Return status code
|
|
444
|
+ */
|
|
445
|
+static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
|
|
446
|
+ struct sockaddr_tcpip peer;
|
|
447
|
+
|
|
448
|
+ /* Sanity check */
|
|
449
|
+ if ( ! uri->host )
|
|
450
|
+ return -EINVAL;
|
|
451
|
+
|
|
452
|
+ memset ( &peer, 0, sizeof ( peer ) );
|
|
453
|
+ peer.st_port = htons ( uri_port ( uri, 0 ) );
|
|
454
|
+ return xfer_open_named_socket ( xfer, SOCK_DGRAM,
|
|
455
|
+ ( struct sockaddr * ) &peer,
|
|
456
|
+ uri->host, NULL );
|
|
457
|
+}
|
|
458
|
+
|
|
459
|
+/** UDP URI opener */
|
|
460
|
+struct uri_opener udp_uri_opener __uri_opener = {
|
|
461
|
+ .scheme = "udp",
|
|
462
|
+ .open = udp_open_uri,
|
|
463
|
+};
|