9f55c94f0e627288bd47dd289ea3cb56f64ea1aa
[people/andreif/gpxe.git] / src / net / udp.c
1 #include <stdint.h>
2 #include <string.h>
3 #include <assert.h>
4 #include <byteswap.h>
5 #include <errno.h>
6 #include <gpxe/tcpip.h>
7 #include <gpxe/pkbuff.h>
8 #include <gpxe/netdevice.h>
9 #include <gpxe/udp.h>
10
11 /** @file
12  *
13  * UDP protocol
14  */
15
16 struct tcpip_protocol udp_protocol;
17
18 /**
19  * List of registered UDP connections
20  */
21 static LIST_HEAD ( udp_conns );
22
23 /**
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
29  */
30 int udp_bind ( struct udp_connection *conn, uint16_t local_port ) {
31         struct udp_connection *existing;
32
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 }
40
41 /**
42  * Bind UDP connection to all local ports
43  *
44  * @v conn              UDP connection
45  *
46  * A promiscuous UDP connection will receive packets with any
47  * destination UDP port.  This is required in order to support the PXE
48  * UDP API.
49  *
50  * If the promiscuous connection is not the only UDP connection, the
51  * behaviour is undefined.
52  */
53 void udp_bind_promisc ( struct udp_connection *conn ) {
54         conn->local_port = 0;
55 }
56
57 /**
58  * Connect UDP connection to remote host and port
59  *
60  * @v conn              UDP connection
61  * @v peer              Destination socket address
62  *
63  * This function stores the socket address within the connection
64  */
65 void udp_connect ( struct udp_connection *conn, struct sockaddr_tcpip *peer ) {
66         memcpy ( &conn->peer, peer, sizeof ( conn->peer ) );
67 }
68
69 /**
70  * Connect UDP connection to all remote hosts and ports
71  *
72  * @v conn              UDP connection
73  *
74  * This undoes the effect of a call to udp_connect(), i.e. allows the
75  * connection to receive packets from all remote hosts and ports.
76  */
77 void udp_connect_promisc ( struct udp_connection *conn ) {
78         memset ( &conn->peer, 0, sizeof ( conn->peer ) );
79 }
80
81 /**
82  * Open a local port
83  *
84  * @v conn              UDP connection
85  * @v local_port        Local port, in network byte order, or zero
86  * @ret rc              Return status code
87  *
88  * Opens the UDP connection and binds to a local port.  If no local
89  * port is specified, the first available port will be used.
90  */
91 int udp_open ( struct udp_connection *conn, uint16_t local_port ) {
92         static uint16_t try_port = 1024;
93         int rc;
94
95         /* If no port specified, find the first available port */
96         if ( ! local_port ) {
97                 for ( ; try_port ; try_port++ ) {
98                         if ( try_port < 1024 )
99                                 continue;
100                         if ( udp_open ( conn, htons ( try_port ) ) == 0 )
101                                 return 0;
102                 }
103                 return -EADDRINUSE;
104         }
105
106         /* Attempt bind to local port */
107         if ( ( rc = udp_bind ( conn, local_port ) ) != 0 )
108                 return rc;
109
110         /* Add to UDP connection list */
111         list_add ( &conn->list, &udp_conns );
112         DBG ( "UDP opened %p on port %d\n", conn, ntohs ( local_port ) );
113
114         return 0;
115 }
116
117 /**
118  * Close a UDP connection
119  *
120  * @v conn              UDP connection
121  */
122 void udp_close ( struct udp_connection *conn ) {
123         list_del ( &conn->list );
124         DBG ( "UDP closed %p\n", conn );
125 }
126
127 /**
128  * User request to send data via a UDP connection
129  *
130  * @v conn              UDP connection
131  *
132  * This function allocates buffer space and invokes the function's senddata()
133  * callback. The callback may use the buffer space
134  */
135 int udp_senddata ( struct udp_connection *conn ) {
136         conn->tx_pkb = alloc_pkb ( UDP_MAX_TXPKB );
137         if ( conn->tx_pkb == NULL ) {
138                 DBG ( "UDP %p cannot allocate packet buffer of length %d\n",
139                       conn, UDP_MAX_TXPKB );
140                 return -ENOMEM;
141         }
142         pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN );
143         conn->udp_op->senddata ( conn, conn->tx_pkb->data, 
144                                  pkb_available ( conn->tx_pkb ) );
145         return 0;
146 }
147                 
148 /**
149  * Transmit data via a UDP connection to a specified address
150  *
151  * @v conn              UDP connection
152  * @v peer              Destination address
153  * @v data              Data to send
154  * @v len               Length of data
155  * @ret rc              Return status code
156  *
157  * This function fills up the UDP headers and sends the data.  It may
158  * be called only from within the context of an application's
159  * senddata() method; if the application wishes to send data it must
160  * call udp_senddata() and wait for its senddata() method to be
161  * called.
162  */
163 int udp_sendto ( struct udp_connection *conn, struct sockaddr_tcpip *peer,
164                  const void *data, size_t len ) {
165         struct udp_header *udphdr;
166
167         /* Avoid overflowing TX buffer */
168         if ( len > pkb_available ( conn->tx_pkb ) )
169                 len = pkb_available ( conn->tx_pkb );
170
171         /* Copy payload */
172         memmove ( pkb_put ( conn->tx_pkb, len ), data, len );
173
174         /*
175          * Add the UDP header
176          *
177          * Covert all 16- and 32- bit integers into network btye order before
178          * sending it over the network
179          */
180         udphdr = pkb_push ( conn->tx_pkb, sizeof ( *udphdr ) );
181         udphdr->dest_port = peer->st_port;
182         udphdr->source_port = conn->local_port;
183         udphdr->len = htons ( pkb_len ( conn->tx_pkb ) );
184         udphdr->chksum = 0;
185         udphdr->chksum = tcpip_chksum ( udphdr, sizeof ( *udphdr ) + len );
186
187         /* Dump debugging information */
188         DBG ( "UDP %p transmitting %p+%#zx len %#x src %d dest %d "
189               "chksum %#04x\n", conn, conn->tx_pkb->data,
190               pkb_len ( conn->tx_pkb ), ntohs ( udphdr->len ),
191               ntohs ( udphdr->source_port ), ntohs ( udphdr->dest_port ),
192               ntohs ( udphdr->chksum ) );
193
194         /* Send it to the next layer for processing */
195         return tcpip_tx ( conn->tx_pkb, &udp_protocol, peer );
196 }
197
198 /**
199  * Transmit data via a UDP connection
200  *
201  * @v conn              UDP connection
202  * @v data              Data to send
203  * @v len               Length of data
204  * @ret rc              Return status code
205  *
206  * This function fills up the UDP headers and sends the data.  It may
207  * be called only from within the context of an application's
208  * senddata() method; if the application wishes to send data it must
209  * call udp_senddata() and wait for its senddata() method to be
210  * called.
211  */
212 int udp_send ( struct udp_connection *conn, const void *data, size_t len ) {
213         return udp_sendto ( conn, &conn->peer, data, len );
214 }
215
216 /**
217  * Process a received packet
218  *
219  * @v pkb               Packet buffer
220  * @v st_src            Partially-filled source address
221  * @v st_dest           Partially-filled destination address
222  * @ret rc              Return status code
223  */
224 static int udp_rx ( struct pk_buff *pkb, struct sockaddr_tcpip *st_src,
225                     struct sockaddr_tcpip *st_dest ) {
226         struct udp_header *udphdr = pkb->data;
227         struct udp_connection *conn;
228         unsigned int ulen;
229         uint16_t chksum;
230
231         /* Sanity check */
232         if ( pkb_len ( pkb ) < sizeof ( *udphdr ) ) {
233                 DBG ( "UDP received underlength packet %p+%#zx\n",
234                       pkb->data, pkb_len ( pkb ) );
235                 return -EINVAL;
236         }
237
238         /* Dump debugging information */
239         DBG ( "UDP received %p+%#zx len %#x src %d dest %d chksum %#04x\n",
240               pkb->data, pkb_len ( pkb ), ntohs ( udphdr->len ),
241               ntohs ( udphdr->source_port ), ntohs ( udphdr->dest_port ),
242               ntohs ( udphdr->chksum ) );
243
244         /* Check length and trim any excess */
245         ulen = ntohs ( udphdr->len );
246         if ( ulen > pkb_len ( pkb ) ) {
247                 DBG ( "UDP received truncated packet %p+%#zx\n",
248                       pkb->data, pkb_len ( pkb ) );
249                 return -EINVAL;
250         }
251         pkb_unput ( pkb, ( pkb_len ( pkb ) - ulen ) );
252
253         /* Verify the checksum */
254 #warning "Don't we need to take the pseudo-header into account here?"
255 #if 0
256         chksum = tcpip_chksum ( pkb->data, pkb_len ( pkb ) );
257         if ( chksum != 0xffff ) {
258                 DBG ( "Bad checksum %#x\n", chksum );
259                 return -EINVAL;
260         }
261 #endif
262
263         /* Complete the socket addresses */
264         st_src->st_port = udphdr->source_port;
265         st_dest->st_port = udphdr->dest_port;
266
267         /* Demux the connection */
268         list_for_each_entry ( conn, &udp_conns, list ) {
269                 if ( conn->local_port &&
270                      ( conn->local_port != udphdr->dest_port ) ) {
271                         /* Bound to local port and local port doesn't match */
272                         continue;
273                 }
274                 if ( conn->peer.st_family &&
275                      ( memcmp ( &conn->peer, st_src,
276                                 sizeof ( conn->peer ) ) != 0 ) ) {
277                         /* Connected to remote port and remote port
278                          * doesn't match
279                          */
280                         continue;
281                 }
282                 
283                 /* Strip off the UDP header */
284                 pkb_pull ( pkb, sizeof ( *udphdr ) );
285
286                 DBG ( "UDP delivering to %p\n", conn );
287                 
288                 /* Call the application's callback */
289                 return conn->udp_op->newdata ( conn, pkb->data, pkb_len( pkb ),
290                                                st_src, st_dest );
291         }
292
293         DBG ( "No UDP connection listening on port %d\n",
294               ntohs ( udphdr->dest_port ) );
295         return 0;
296 }
297
298 struct tcpip_protocol udp_protocol  = {
299         .name = "UDP",
300         .rx = udp_rx,
301         .tcpip_proto = IP_UDP,
302         .csum_offset = 6,
303 };
304
305 TCPIP_PROTOCOL ( udp_protocol );