4a82e976140d875848b418c1cb0a6a8e8264a278
[people/xl0/gpxe.git] / src / net / udp.c
1 #include <stdint.h>
2 #include <string.h>
3 #include <assert.h>
4 #include <byteswap.h>
5 #include <latch.h>
6 #include <errno.h>
7 #include <gpxe/in.h>
8 #include <gpxe/ip.h>
9 #include <gpxe/ip6.h>
10 #include <gpxe/udp.h>
11 #include <gpxe/init.h>
12 #include <gpxe/pkbuff.h>
13 #include <gpxe/netdevice.h>
14 #include <gpxe/tcpip_if.h>
15
16 /** @file
17  *
18  * UDP protocol
19  */
20
21 static inline void copy_sockaddr ( struct sockaddr *source, struct sockaddr *dest ) {
22         memcpy ( dest, source, sizeof ( *dest ) );
23 }
24
25 static inline uint16_t dest_port ( struct sockaddr *sock, uint16_t *dest ) {
26         switch ( sock->sa_family ) {
27         case AF_INET:
28                 dest = &sock->sin.sin_port;
29                 break;
30         case AF_INET6:
31                 dest = &sock->sin6.sin6_port;
32                 break;
33         default:
34                 DBG ( "Network family %d not supported\n", sock->sa_family );
35                 return -EAFNOSUPPORT;
36         }
37         return 0;
38 }
39
40 /**
41  * Dump the UDP header
42  *
43  * @v udphdr    UDP header
44  */
45 void udp_dump ( struct udp_header *udphdr ) {
46
47         /* Print UDP header for debugging */
48         DBG ( "UDP header at %#x + %d\n", udphdr, sizeof ( *udphdr ) );
49         DBG ( "\tSource Port = %d\n", ntohs ( udphdr->source_port ) );
50         DBG ( "\tDestination Port = %d\n", ntohs ( udphdr->dest_port ) );
51         DBG ( "\tLength = %d\n", ntohs ( udphdr->len ) );
52         DBG ( "\tChecksum = %d\n", ntohs ( udphdr->chksum ) );
53         DBG ( "\tChecksum located at %#x\n", &udphdr->chksum );
54 }
55
56 /**
57  * Open a UDP connection
58  *
59  * @v conn      UDP connection
60  * @v peer      Destination socket address
61  *
62  * This function stores the socket address within the connection
63  */
64 void udp_connect ( struct udp_connection *conn, struct sockaddr *peer ) {
65         copy_sockaddr ( peer, &conn->sin );
66
67         /* Not sure if this should add the connection to udp_conns; If it does,
68          * uncomment the following code
69          */
70 //      list_add ( &conn->list, &udp_conns );
71 }
72
73 /**
74  * Initialize a UDP connection
75  *
76  * @v conn      UDP connection
77  * @v udp_op    UDP operations
78  */
79 void udp_init ( struct udp_connection *conn, struct udp_operations *udp_op ) {
80         conn->local_port = 0;
81         conn->tx_pkb = NULL;
82         if ( udp_op != NULL ) {
83                 conn->udp_op = udp_op;
84         }
85 }
86
87 /**
88  * Allocate space to the UDP buffer
89  *
90  * @v conn      UDP connection
91  * @v len       Length to allocate
92  * @ret rc      Status
93  *
94  * Allocate "len" amount of space in the transmit buffer
95  */
96 int udp_buf_alloc ( struct udp_connection *conn, size_t len ) {
97         if ( conn->tx_pkb != NULL ) {
98                 free_pkb ( conn->tx_pkb );
99                 conn->tx_pkb = NULL;
100         }
101         conn->tx_pkb = alloc_pkb ( len < UDP_MIN_TXPKB ? UDP_MIN_TXPKB : len );
102         return !conn ? -ENOMEM : 0;
103 }
104
105 /**
106  * Send data via a UDP connection
107  *
108  * @v conn      UDP connection
109  * @v data      Data to send
110  * @v len       Length of data
111  *
112  * This function fills up the UDP headers and sends the data. Discover the
113  * network protocol through the sa_family field in the destination socket
114  * address.
115  */
116 int udp_send ( struct udp_connection *conn, const void *data, size_t len ) {
117         struct udp_header *udphdr;              /* UDP header */
118         struct sockaddr *sock = &conn->sin;     /* Destination sockaddr */
119         uint16_t *dest;
120         int rc;
121
122         /* Copy data into packet buffer, if necessary */
123         if ( data != conn->tx_pkb->data ) {
124                 /* Allocate space for data and lower layer headers */
125                 if ( ( rc = udp_buf_alloc ( conn, len + UDP_MAX_HLEN ) ) != 0 ) {
126                         DBG ( "Error allocating buffer" );
127                         return rc;
128                 }
129
130                 /* Reserve space for the headers and copy contents */
131                 pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN );
132                 memcpy ( pkb_put ( conn->tx_pkb, len ), data, len );
133         }
134
135         /*
136          * Add the UDP header
137          *
138          * Covert all 16- and 32- bit integers into network btye order before
139          * sending it over the network
140          */
141         udphdr = pkb_push ( conn->tx_pkb, sizeof ( *udphdr ) );
142         if ( (rc = dest_port ( sock, dest ) ) != 0 ) {
143                 return rc;
144         }
145         udphdr->dest_port = htons ( *dest );
146         udphdr->source_port = htons ( conn->local_port );
147         udphdr->len = htons ( pkb_len ( conn->tx_pkb ) );
148         udphdr->chksum = htons ( calc_chksum ( udphdr, sizeof ( *udphdr ) ) );
149
150         udp_dump ( udphdr );
151
152         /* Send it to the next layer for processing */
153         return trans_tx ( conn->tx_pkb, &udp_protocol, sock );
154 }
155
156 /**
157  * Send data to a specified address
158  *
159  * @v conn      UDP connection
160  * @v peer      Destination address
161  * @v data      Data to send
162  * @v len       Length of data
163  */
164 int udp_sendto ( struct udp_connection *conn, struct sockaddr *peer,
165                  const void *data, size_t len ) {
166         struct sockaddr tempsock;
167         copy_sockaddr ( &conn->sin, &tempsock );
168         copy_sockaddr ( peer, &conn->sin );
169         int rc = udp_send ( conn, data, len );
170         copy_sockaddr ( &tempsock, &conn->sin );
171         return rc;
172 }
173
174 /**
175  * Close a UDP connection
176  *
177  * @v conn      UDP connection
178  */
179 void udp_close ( struct udp_connection *conn ) {
180         list_del ( &conn->list );
181 }
182
183 /**
184  * Open a local port
185  *
186  * @v conn              UDP connection
187  * @v local_port        Local port on which to open connection
188  *
189  * This does not support the 0 port option correctly yet
190  */
191 int udp_open ( struct udp_connection *conn, uint16_t local_port ) {
192         struct udp_connection *connr;
193         uint16_t min_port = 0xffff;
194
195         /* Iterate through udp_conns to see if local_port is available */
196         list_for_each_entry ( connr, &udp_conns, list ) {
197                 if ( connr->local_port == local_port ) {
198                         return -EISCONN;
199                 }
200                 if ( min_port > connr->local_port ) {
201                         min_port = connr->local_port;
202                 }
203         }
204         /* This code is buggy. I will update it soon :) */
205         conn->local_port = local_port == 0 ? min_port > 1024 ? 1024 :
206                                                 min_port + 1 : local_port;
207
208         /* Add the connection to the list of listening connections */
209         list_add ( &conn->list, &udp_conns );
210         return 0;
211 }
212
213 /**
214  * Process a received packet
215  *
216  * @v pkb              Packet buffer
217  * @v src_net_addr      Source network address
218  * @v dest_net_addr     Destination network address
219  */
220 void udp_rx ( struct pk_buff *pkb, struct in_addr *src_net_addr __unused,
221                         struct in_addr *dest_net_addr __unused ) {
222         struct udp_header *udphdr = pkb->data;
223         struct udp_connection *conn;
224         uint16_t ulen;
225         uint16_t chksum;
226
227         udp_dump ( udphdr );
228
229         /* Validate the packet and the UDP length */
230         if ( pkb_len ( pkb ) < sizeof ( *udphdr ) ) {
231                 DBG ( "UDP packet too short (%d bytes)\n",
232                       pkb_len ( pkb ) );
233                 return;
234         }
235
236         ulen = ntohs ( udphdr->len );
237         if ( ulen != pkb_len ( pkb ) ) {
238                 DBG ( "Inconsistent UDP packet length (%d bytes)\n",
239                       pkb_len ( pkb ) );
240                 return;
241         }
242
243         /* Verify the checksum */
244         chksum = calc_chksum ( pkb->data, pkb_len ( pkb ) );
245         if ( chksum != 0xffff ) {
246                 DBG ( "Bad checksum %d\n", chksum );
247                 return;
248         }
249
250         /* Todo: Check if it is a broadcast or multicast address */
251
252         /* Demux the connection */
253         list_for_each_entry ( conn, &udp_conns, list ) {
254                 if ( conn->local_port == ntohs ( udphdr->dest_port ) ) {
255                         goto conn;
256                 }
257         }
258         return;
259
260         conn:
261         /** Strip off the UDP header */
262         pkb_pull ( pkb, sizeof ( *udphdr ) );
263
264         /** Allocate max possible buffer space to the tx buffer */
265         conn->tx_pkb = alloc_pkb ( UDP_MAX_TXPKB );
266         pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN );
267
268         /** Call the application's callback */
269         conn->udp_op->newdata ( conn, pkb->data, ulen - sizeof ( *udphdr ) );
270 }
271
272 struct tcpip_protocol udp_protocol  = {
273         .name = "UDP",
274         .rx = udp_rx,
275         .trans_proto = IP_UDP,
276         .csum_offset = 6,
277 };
278
279 TCPIP_PROTOCOL ( udp_protocol );