00001 #include <stdint.h>
00002 #include <stdlib.h>
00003 #include <string.h>
00004 #include <assert.h>
00005 #include <byteswap.h>
00006 #include <errno.h>
00007 #include <gpxe/tcpip.h>
00008 #include <gpxe/iobuf.h>
00009 #include <gpxe/xfer.h>
00010 #include <gpxe/open.h>
00011 #include <gpxe/uri.h>
00012 #include <gpxe/udp.h>
00013
00014
00015
00016
00017
00018
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020
00021
00022
00023
00024
00025 struct udp_connection {
00026
00027 struct refcnt refcnt;
00028
00029 struct list_head list;
00030
00031
00032 struct xfer_interface xfer;
00033
00034
00035 struct sockaddr_tcpip local;
00036
00037 struct sockaddr_tcpip peer;
00038 };
00039
00040
00041
00042
00043 static LIST_HEAD ( udp_conns );
00044
00045
00046 static struct xfer_interface_operations udp_xfer_operations;
00047 struct tcpip_protocol udp_protocol;
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058 static int udp_bind ( struct udp_connection *udp ) {
00059 struct udp_connection *existing;
00060 static uint16_t try_port = 1023;
00061
00062
00063 if ( ! udp->local.st_port ) {
00064 while ( try_port ) {
00065 try_port++;
00066 if ( try_port < 1024 )
00067 continue;
00068 udp->local.st_port = htons ( try_port );
00069 if ( udp_bind ( udp ) == 0 )
00070 return 0;
00071 }
00072 return -EADDRINUSE;
00073 }
00074
00075
00076 list_for_each_entry ( existing, &udp_conns, list ) {
00077 if ( existing->local.st_port == udp->local.st_port ) {
00078 DBGC ( udp, "UDP %p could not bind: port %d in use\n",
00079 udp, ntohs ( udp->local.st_port ) );
00080 return -EADDRINUSE;
00081 }
00082 }
00083
00084
00085 DBGC ( udp, "UDP %p bound to port %d\n",
00086 udp, ntohs ( udp->local.st_port ) );
00087
00088 return 0;
00089 }
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100 static int udp_open_common ( struct xfer_interface *xfer,
00101 struct sockaddr *peer, struct sockaddr *local,
00102 int promisc ) {
00103 struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
00104 struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
00105 struct udp_connection *udp;
00106 int rc;
00107
00108
00109 udp = zalloc ( sizeof ( *udp ) );
00110 if ( ! udp )
00111 return -ENOMEM;
00112 DBGC ( udp, "UDP %p allocated\n", udp );
00113 xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt );
00114 if ( st_peer )
00115 memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) );
00116 if ( st_local )
00117 memcpy ( &udp->local, st_local, sizeof ( udp->local ) );
00118
00119
00120 if ( ! promisc ) {
00121 if ( ( rc = udp_bind ( udp ) ) != 0 )
00122 goto err;
00123 }
00124
00125
00126
00127
00128 xfer_plug_plug ( &udp->xfer, xfer );
00129 list_add ( &udp->list, &udp_conns );
00130 return 0;
00131
00132 err:
00133 ref_put ( &udp->refcnt );
00134 return rc;
00135 }
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145 int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
00146 struct sockaddr *local ) {
00147 return udp_open_common ( xfer, peer, local, 0 );
00148 }
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159 int udp_open_promisc ( struct xfer_interface *xfer ) {
00160 return udp_open_common ( xfer, NULL, NULL, 1 );
00161 }
00162
00163
00164
00165
00166
00167
00168
00169 static void udp_close ( struct udp_connection *udp, int rc ) {
00170
00171
00172 xfer_nullify ( &udp->xfer );
00173 xfer_close ( &udp->xfer, rc );
00174
00175
00176 list_del ( &udp->list );
00177 ref_put ( &udp->refcnt );
00178
00179 DBGC ( udp, "UDP %p closed\n", udp );
00180 }
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192 static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
00193 struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
00194 struct net_device *netdev ) {
00195 struct udp_header *udphdr;
00196 size_t len;
00197 int rc;
00198
00199
00200 if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) {
00201 free_iob ( iobuf );
00202 return rc;
00203 }
00204
00205
00206 if ( ! src )
00207 src = &udp->local;
00208 if ( ! dest )
00209 dest = &udp->peer;
00210
00211
00212 udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
00213 len = iob_len ( iobuf );
00214 udphdr->dest = dest->st_port;
00215 udphdr->src = src->st_port;
00216 udphdr->len = htons ( len );
00217 udphdr->chksum = 0;
00218 udphdr->chksum = tcpip_chksum ( udphdr, len );
00219
00220
00221 DBGC ( udp, "UDP %p TX %d->%d len %d\n", udp,
00222 ntohs ( udphdr->src ), ntohs ( udphdr->dest ),
00223 ntohs ( udphdr->len ) );
00224
00225
00226 if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev,
00227 &udphdr->chksum ) ) != 0 ) {
00228 DBGC ( udp, "UDP %p could not transmit packet: %s\n",
00229 udp, strerror ( rc ) );
00230 return rc;
00231 }
00232
00233 return 0;
00234 }
00235
00236
00237
00238
00239
00240
00241
00242 static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) {
00243 static const struct sockaddr_tcpip empty_sockaddr = { .pad = { 0, } };
00244 struct udp_connection *udp;
00245
00246 list_for_each_entry ( udp, &udp_conns, list ) {
00247 if ( ( ( udp->local.st_family == local->st_family ) ||
00248 ( udp->local.st_family == 0 ) ) &&
00249 ( ( udp->local.st_port == local->st_port ) ||
00250 ( udp->local.st_port == 0 ) ) &&
00251 ( ( memcmp ( udp->local.pad, local->pad,
00252 sizeof ( udp->local.pad ) ) == 0 ) ||
00253 ( memcmp ( udp->local.pad, empty_sockaddr.pad,
00254 sizeof ( udp->local.pad ) ) == 0 ) ) ) {
00255 return udp;
00256 }
00257 }
00258 return NULL;
00259 }
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270 static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
00271 struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
00272 struct udp_header *udphdr = iobuf->data;
00273 struct udp_connection *udp;
00274 struct xfer_metadata meta;
00275 size_t ulen;
00276 unsigned int csum;
00277 int rc = 0;
00278
00279
00280 if ( iob_len ( iobuf ) < sizeof ( *udphdr ) ) {
00281 DBG ( "UDP packet too short at %zd bytes (min %zd bytes)\n",
00282 iob_len ( iobuf ), sizeof ( *udphdr ) );
00283
00284 rc = -EINVAL;
00285 goto done;
00286 }
00287 ulen = ntohs ( udphdr->len );
00288 if ( ulen < sizeof ( *udphdr ) ) {
00289 DBG ( "UDP length too short at %zd bytes "
00290 "(header is %zd bytes)\n", ulen, sizeof ( *udphdr ) );
00291 rc = -EINVAL;
00292 goto done;
00293 }
00294 if ( ulen > iob_len ( iobuf ) ) {
00295 DBG ( "UDP length too long at %zd bytes (packet is %zd "
00296 "bytes)\n", ulen, iob_len ( iobuf ) );
00297 rc = -EINVAL;
00298 goto done;
00299 }
00300 if ( udphdr->chksum ) {
00301 csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, ulen );
00302 if ( csum != 0 ) {
00303 DBG ( "UDP checksum incorrect (is %04x including "
00304 "checksum field, should be 0000)\n", csum );
00305 rc = -EINVAL;
00306 goto done;
00307 }
00308 }
00309
00310
00311 st_src->st_port = udphdr->src;
00312 st_dest->st_port = udphdr->dest;
00313 udp = udp_demux ( st_dest );
00314 iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) );
00315 iob_pull ( iobuf, sizeof ( *udphdr ) );
00316
00317
00318 DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
00319 ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
00320
00321
00322 if ( ! udp ) {
00323 DBG ( "No UDP connection listening on port %d\n",
00324 ntohs ( udphdr->dest ) );
00325 rc = -ENOTCONN;
00326 goto done;
00327 }
00328
00329
00330 memset ( &meta, 0, sizeof ( meta ) );
00331 meta.src = ( struct sockaddr * ) st_src;
00332 meta.dest = ( struct sockaddr * ) st_dest;
00333 rc = xfer_deliver_iob_meta ( &udp->xfer, iob_disown ( iobuf ), &meta );
00334
00335 done:
00336 free_iob ( iobuf );
00337 return rc;
00338 }
00339
00340 struct tcpip_protocol udp_protocol __tcpip_protocol = {
00341 .name = "UDP",
00342 .rx = udp_rx,
00343 .tcpip_proto = IP_UDP,
00344 };
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359 static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) {
00360 struct udp_connection *udp =
00361 container_of ( xfer, struct udp_connection, xfer );
00362
00363
00364 udp_close ( udp, rc );
00365 }
00366
00367
00368
00369
00370
00371
00372
00373
00374 static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer,
00375 size_t len ) {
00376 struct udp_connection *udp =
00377 container_of ( xfer, struct udp_connection, xfer );
00378 struct io_buffer *iobuf;
00379
00380 iobuf = alloc_iob ( UDP_MAX_HLEN + len );
00381 if ( ! iobuf ) {
00382 DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n",
00383 udp, len );
00384 return NULL;
00385 }
00386 iob_reserve ( iobuf, UDP_MAX_HLEN );
00387 return iobuf;
00388 }
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398 static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
00399 struct io_buffer *iobuf,
00400 struct xfer_metadata *meta ) {
00401 struct udp_connection *udp =
00402 container_of ( xfer, struct udp_connection, xfer );
00403
00404
00405 udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ),
00406 ( ( struct sockaddr_tcpip * ) meta->dest ), meta->netdev );
00407
00408 return 0;
00409 }
00410
00411
00412 static struct xfer_interface_operations udp_xfer_operations = {
00413 .close = udp_xfer_close,
00414 .vredirect = ignore_xfer_vredirect,
00415 .window = unlimited_xfer_window,
00416 .alloc_iob = udp_alloc_iob,
00417 .deliver_iob = udp_xfer_deliver_iob,
00418 .deliver_raw = xfer_deliver_as_iob,
00419 };
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429 struct socket_opener udp_socket_opener __socket_opener = {
00430 .semantics = UDP_SOCK_DGRAM,
00431 .family = AF_INET,
00432 .open = udp_open,
00433 };
00434
00435
00436 int udp_sock_dgram = UDP_SOCK_DGRAM;
00437
00438
00439
00440
00441
00442
00443
00444
00445 static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
00446 struct sockaddr_tcpip peer;
00447
00448
00449 if ( ! uri->host )
00450 return -EINVAL;
00451
00452 memset ( &peer, 0, sizeof ( peer ) );
00453 peer.st_port = htons ( uri_port ( uri, 0 ) );
00454 return xfer_open_named_socket ( xfer, SOCK_DGRAM,
00455 ( struct sockaddr * ) &peer,
00456 uri->host, NULL );
00457 }
00458
00459
00460 struct uri_opener udp_uri_opener __uri_opener = {
00461 .scheme = "udp",
00462 .open = udp_open_uri,
00463 };