2006-06-25 07:13:17 +02:00
|
|
|
#include <stdint.h>
|
2007-06-20 02:13:35 +02:00
|
|
|
#include <stdlib.h>
|
2006-06-25 07:13:17 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <byteswap.h>
|
|
|
|
#include <errno.h>
|
2010-04-19 21:16:01 +02:00
|
|
|
#include <ipxe/tcpip.h>
|
|
|
|
#include <ipxe/iobuf.h>
|
|
|
|
#include <ipxe/xfer.h>
|
|
|
|
#include <ipxe/open.h>
|
|
|
|
#include <ipxe/uri.h>
|
2010-11-19 16:45:41 +01:00
|
|
|
#include <ipxe/netdevice.h>
|
2010-04-19 21:16:01 +02:00
|
|
|
#include <ipxe/udp.h>
|
2006-06-25 07:13:17 +02:00
|
|
|
|
|
|
|
/** @file
|
|
|
|
*
|
|
|
|
* UDP protocol
|
|
|
|
*/
|
|
|
|
|
2009-05-01 16:41:06 +02:00
|
|
|
FILE_LICENCE ( GPL2_OR_LATER );
|
|
|
|
|
2006-07-19 18:25:23 +02:00
|
|
|
/**
|
2007-06-20 02:13:35 +02:00
|
|
|
* A UDP connection
|
|
|
|
*
|
2006-07-19 18:25:23 +02:00
|
|
|
*/
|
2007-06-20 02:13:35 +02:00
|
|
|
struct udp_connection {
|
|
|
|
/** Reference counter */
|
|
|
|
struct refcnt refcnt;
|
|
|
|
/** List of UDP connections */
|
|
|
|
struct list_head list;
|
|
|
|
|
|
|
|
/** Data transfer interface */
|
2010-06-16 02:31:29 +02:00
|
|
|
struct interface xfer;
|
2007-06-20 02:13:35 +02:00
|
|
|
|
2008-06-09 23:56:48 +02:00
|
|
|
/** Local socket address */
|
|
|
|
struct sockaddr_tcpip local;
|
2007-06-20 02:13:35 +02:00
|
|
|
/** Remote socket address */
|
|
|
|
struct sockaddr_tcpip peer;
|
|
|
|
};
|
2006-07-19 18:25:23 +02:00
|
|
|
|
|
|
|
/**
|
2007-06-20 02:13:35 +02:00
|
|
|
* List of registered UDP connections
|
2006-07-19 18:25:23 +02:00
|
|
|
*/
|
2007-06-20 02:13:35 +02:00
|
|
|
static LIST_HEAD ( udp_conns );
|
2006-06-28 09:46:28 +02:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
/* Forward declatations */
|
2010-06-16 02:31:29 +02:00
|
|
|
static struct interface_descriptor udp_xfer_desc;
|
2010-08-19 23:39:17 +02:00
|
|
|
struct tcpip_protocol udp_protocol __tcpip_protocol;
|
2006-06-28 09:46:28 +02:00
|
|
|
|
2006-06-25 07:13:17 +02:00
|
|
|
/**
|
2007-06-20 02:13:35 +02:00
|
|
|
* Bind UDP connection to local port
|
2006-08-02 02:02:21 +02:00
|
|
|
*
|
2007-06-20 02:13:35 +02:00
|
|
|
* @v udp UDP connection
|
2006-08-02 02:02:21 +02:00
|
|
|
* @ret rc Return status code
|
2006-06-25 07:13:17 +02:00
|
|
|
*
|
2008-06-09 23:56:48 +02:00
|
|
|
* Opens the UDP connection and binds to the specified local port. If
|
|
|
|
* no local port is specified, the first available port will be used.
|
2006-06-25 07:13:17 +02:00
|
|
|
*/
|
2008-06-09 23:56:48 +02:00
|
|
|
static int udp_bind ( struct udp_connection *udp ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
struct udp_connection *existing;
|
2008-07-10 05:21:10 +02:00
|
|
|
static uint16_t try_port = 1023;
|
2006-08-02 02:02:21 +02:00
|
|
|
|
|
|
|
/* If no port specified, find the first available port */
|
2008-06-09 23:56:48 +02:00
|
|
|
if ( ! udp->local.st_port ) {
|
2008-07-10 05:21:10 +02:00
|
|
|
while ( try_port ) {
|
|
|
|
try_port++;
|
2006-08-02 02:02:21 +02:00
|
|
|
if ( try_port < 1024 )
|
|
|
|
continue;
|
2008-06-09 23:56:48 +02:00
|
|
|
udp->local.st_port = htons ( try_port );
|
|
|
|
if ( udp_bind ( udp ) == 0 )
|
2006-08-02 02:02:21 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -EADDRINUSE;
|
2006-06-28 09:46:28 +02:00
|
|
|
}
|
2006-08-02 02:02:21 +02:00
|
|
|
|
|
|
|
/* Attempt bind to local port */
|
2007-06-20 02:13:35 +02:00
|
|
|
list_for_each_entry ( existing, &udp_conns, list ) {
|
2008-06-09 23:56:48 +02:00
|
|
|
if ( existing->local.st_port == udp->local.st_port ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
DBGC ( udp, "UDP %p could not bind: port %d in use\n",
|
2008-06-09 23:56:48 +02:00
|
|
|
udp, ntohs ( udp->local.st_port ) );
|
2007-06-20 02:13:35 +02:00
|
|
|
return -EADDRINUSE;
|
|
|
|
}
|
2007-01-16 04:19:40 +01:00
|
|
|
}
|
2006-08-02 02:02:21 +02:00
|
|
|
|
|
|
|
/* Add to UDP connection list */
|
2008-06-09 23:56:48 +02:00
|
|
|
DBGC ( udp, "UDP %p bound to port %d\n",
|
|
|
|
udp, ntohs ( udp->local.st_port ) );
|
2006-08-02 02:02:21 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2007-06-20 02:13:35 +02:00
|
|
|
* Open a UDP connection
|
2006-08-02 02:02:21 +02:00
|
|
|
*
|
2007-06-20 02:13:35 +02:00
|
|
|
* @v xfer Data transfer interface
|
2007-11-28 12:56:14 +01:00
|
|
|
* @v peer Peer socket address, or NULL
|
2007-06-20 02:13:35 +02:00
|
|
|
* @v local Local socket address, or NULL
|
|
|
|
* @v promisc Socket is promiscuous
|
|
|
|
* @ret rc Return status code
|
2006-08-02 02:02:21 +02:00
|
|
|
*/
|
2010-06-16 02:31:29 +02:00
|
|
|
static int udp_open_common ( struct interface *xfer,
|
2007-06-20 02:13:35 +02:00
|
|
|
struct sockaddr *peer, struct sockaddr *local,
|
|
|
|
int promisc ) {
|
|
|
|
struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
|
|
|
|
struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
|
|
|
|
struct udp_connection *udp;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Allocate and initialise structure */
|
2007-07-06 21:08:41 +02:00
|
|
|
udp = zalloc ( sizeof ( *udp ) );
|
2007-06-20 02:13:35 +02:00
|
|
|
if ( ! udp )
|
|
|
|
return -ENOMEM;
|
|
|
|
DBGC ( udp, "UDP %p allocated\n", udp );
|
2010-06-15 18:33:23 +02:00
|
|
|
ref_init ( &udp->refcnt, NULL );
|
2010-06-16 02:31:29 +02:00
|
|
|
intf_init ( &udp->xfer, &udp_xfer_desc, &udp->refcnt );
|
2007-11-28 12:56:14 +01:00
|
|
|
if ( st_peer )
|
|
|
|
memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) );
|
2008-06-09 23:56:48 +02:00
|
|
|
if ( st_local )
|
|
|
|
memcpy ( &udp->local, st_local, sizeof ( udp->local ) );
|
2007-06-20 02:13:35 +02:00
|
|
|
|
|
|
|
/* Bind to local port */
|
|
|
|
if ( ! promisc ) {
|
2008-06-09 23:56:48 +02:00
|
|
|
if ( ( rc = udp_bind ( udp ) ) != 0 )
|
2007-06-20 02:13:35 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Attach parent interface, transfer reference to connection
|
|
|
|
* list and return
|
|
|
|
*/
|
2010-06-16 02:31:29 +02:00
|
|
|
intf_plug_plug ( &udp->xfer, xfer );
|
2007-06-20 02:13:35 +02:00
|
|
|
list_add ( &udp->list, &udp_conns );
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
ref_put ( &udp->refcnt );
|
|
|
|
return rc;
|
2006-06-25 07:13:17 +02:00
|
|
|
}
|
|
|
|
|
2007-01-10 03:46:39 +01:00
|
|
|
/**
|
2007-06-20 02:13:35 +02:00
|
|
|
* Open a UDP connection
|
2007-01-10 03:46:39 +01:00
|
|
|
*
|
2007-06-20 02:13:35 +02:00
|
|
|
* @v xfer Data transfer interface
|
|
|
|
* @v peer Peer socket address
|
|
|
|
* @v local Local socket address, or NULL
|
|
|
|
* @ret rc Return status code
|
2007-01-10 03:46:39 +01:00
|
|
|
*/
|
2010-06-16 02:31:29 +02:00
|
|
|
int udp_open ( struct interface *xfer, struct sockaddr *peer,
|
2007-06-27 15:48:31 +02:00
|
|
|
struct sockaddr *local ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
return udp_open_common ( xfer, peer, local, 0 );
|
2007-01-10 03:46:39 +01:00
|
|
|
}
|
|
|
|
|
2006-06-25 07:13:17 +02:00
|
|
|
/**
|
2007-06-20 02:13:35 +02:00
|
|
|
* Open a promiscuous UDP connection
|
2006-07-19 18:25:23 +02:00
|
|
|
*
|
2007-06-20 02:13:35 +02:00
|
|
|
* @v xfer Data transfer interface
|
|
|
|
* @ret rc Return status code
|
2006-07-19 18:25:23 +02:00
|
|
|
*
|
2007-06-20 02:13:35 +02:00
|
|
|
* Promiscuous UDP connections are required in order to support the
|
|
|
|
* PXE API.
|
2006-07-19 18:25:23 +02:00
|
|
|
*/
|
2010-06-16 02:31:29 +02:00
|
|
|
int udp_open_promisc ( struct interface *xfer ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
return udp_open_common ( xfer, NULL, NULL, 1 );
|
|
|
|
}
|
2006-08-09 03:24:32 +02:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
/**
|
|
|
|
* Close a UDP connection
|
|
|
|
*
|
|
|
|
* @v udp UDP connection
|
|
|
|
* @v rc Reason for close
|
|
|
|
*/
|
|
|
|
static void udp_close ( struct udp_connection *udp, int rc ) {
|
2007-01-10 03:46:39 +01:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
/* Close data transfer interface */
|
2010-06-16 02:31:29 +02:00
|
|
|
intf_shutdown ( &udp->xfer, rc );
|
2007-01-10 03:46:39 +01:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
/* Remove from list of connections and drop list's reference */
|
|
|
|
list_del ( &udp->list );
|
|
|
|
ref_put ( &udp->refcnt );
|
2007-01-10 03:46:39 +01:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
DBGC ( udp, "UDP %p closed\n", udp );
|
2006-07-19 18:25:23 +02:00
|
|
|
}
|
2007-06-20 02:13:35 +02:00
|
|
|
|
2006-07-19 18:25:23 +02:00
|
|
|
/**
|
2006-07-19 22:38:49 +02:00
|
|
|
* Transmit data via a UDP connection to a specified address
|
2006-06-25 07:13:17 +02:00
|
|
|
*
|
2007-06-20 02:13:35 +02:00
|
|
|
* @v udp UDP connection
|
|
|
|
* @v iobuf I/O buffer
|
2009-01-21 04:40:39 +01:00
|
|
|
* @v src Source address, or NULL to use default
|
2007-06-20 02:13:35 +02:00
|
|
|
* @v dest Destination address, or NULL to use default
|
2007-06-28 00:20:36 +02:00
|
|
|
* @v netdev Network device, or NULL to use default
|
2006-08-02 02:02:21 +02:00
|
|
|
* @ret rc Return status code
|
2006-06-25 07:13:17 +02:00
|
|
|
*/
|
2007-06-20 02:13:35 +02:00
|
|
|
static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
|
2009-01-21 04:40:39 +01:00
|
|
|
struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
|
2007-06-28 00:20:36 +02:00
|
|
|
struct net_device *netdev ) {
|
2006-08-02 02:02:21 +02:00
|
|
|
struct udp_header *udphdr;
|
2007-06-20 02:13:35 +02:00
|
|
|
size_t len;
|
2007-01-16 04:19:40 +01:00
|
|
|
int rc;
|
2006-08-09 03:24:32 +02:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
/* Check we can accommodate the header */
|
2010-11-19 16:45:41 +01:00
|
|
|
if ( ( rc = iob_ensure_headroom ( iobuf,
|
|
|
|
MAX_LL_NET_HEADER_LEN ) ) != 0 ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
free_iob ( iobuf );
|
|
|
|
return rc;
|
|
|
|
}
|
2006-06-28 09:46:28 +02:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
/* Fill in default values if not explicitly provided */
|
2009-01-21 04:40:39 +01:00
|
|
|
if ( ! src )
|
|
|
|
src = &udp->local;
|
2007-06-20 02:13:35 +02:00
|
|
|
if ( ! dest )
|
|
|
|
dest = &udp->peer;
|
2006-06-25 07:13:17 +02:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
/* Add the UDP header */
|
2007-05-19 20:39:40 +02:00
|
|
|
udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
|
2007-06-20 02:13:35 +02:00
|
|
|
len = iob_len ( iobuf );
|
|
|
|
udphdr->dest = dest->st_port;
|
2009-01-21 04:40:39 +01:00
|
|
|
udphdr->src = src->st_port;
|
2007-06-20 02:13:35 +02:00
|
|
|
udphdr->len = htons ( len );
|
2006-07-20 01:38:05 +02:00
|
|
|
udphdr->chksum = 0;
|
2007-06-20 02:13:35 +02:00
|
|
|
udphdr->chksum = tcpip_chksum ( udphdr, len );
|
2006-06-28 09:46:28 +02:00
|
|
|
|
2006-08-02 02:02:21 +02:00
|
|
|
/* Dump debugging information */
|
2007-12-07 01:11:43 +01:00
|
|
|
DBGC ( udp, "UDP %p TX %d->%d len %d\n", udp,
|
2007-06-20 02:13:35 +02:00
|
|
|
ntohs ( udphdr->src ), ntohs ( udphdr->dest ),
|
2007-01-03 22:13:11 +01:00
|
|
|
ntohs ( udphdr->len ) );
|
2006-06-28 09:46:28 +02:00
|
|
|
|
|
|
|
/* Send it to the next layer for processing */
|
2009-01-21 04:40:39 +01:00
|
|
|
if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev,
|
2007-01-16 04:19:40 +01:00
|
|
|
&udphdr->chksum ) ) != 0 ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
DBGC ( udp, "UDP %p could not transmit packet: %s\n",
|
|
|
|
udp, strerror ( rc ) );
|
2007-01-16 04:19:40 +01:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2007-01-10 03:31:38 +01:00
|
|
|
}
|
|
|
|
|
2007-01-03 22:13:11 +01:00
|
|
|
/**
|
2008-06-09 23:56:48 +02:00
|
|
|
* Identify UDP connection by local address
|
2007-01-03 22:13:11 +01:00
|
|
|
*
|
2008-06-09 23:56:48 +02:00
|
|
|
* @v local Local address
|
2007-06-20 02:13:35 +02:00
|
|
|
* @ret udp UDP connection, or NULL
|
2007-01-03 22:13:11 +01:00
|
|
|
*/
|
2008-06-09 23:56:48 +02:00
|
|
|
static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) {
|
2009-03-10 18:50:01 +01:00
|
|
|
static const struct sockaddr_tcpip empty_sockaddr = { .pad = { 0, } };
|
2007-06-20 02:13:35 +02:00
|
|
|
struct udp_connection *udp;
|
2007-01-03 22:13:11 +01:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
list_for_each_entry ( udp, &udp_conns, list ) {
|
2008-06-09 23:56:48 +02:00
|
|
|
if ( ( ( udp->local.st_family == local->st_family ) ||
|
|
|
|
( udp->local.st_family == 0 ) ) &&
|
|
|
|
( ( udp->local.st_port == local->st_port ) ||
|
|
|
|
( udp->local.st_port == 0 ) ) &&
|
|
|
|
( ( memcmp ( udp->local.pad, local->pad,
|
|
|
|
sizeof ( udp->local.pad ) ) == 0 ) ||
|
|
|
|
( memcmp ( udp->local.pad, empty_sockaddr.pad,
|
|
|
|
sizeof ( udp->local.pad ) ) == 0 ) ) ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
return udp;
|
2007-01-03 22:13:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-06-25 07:13:17 +02:00
|
|
|
/**
|
|
|
|
* Process a received packet
|
|
|
|
*
|
2007-05-19 20:39:40 +02:00
|
|
|
* @v iobuf I/O buffer
|
2006-08-02 02:02:21 +02:00
|
|
|
* @v st_src Partially-filled source address
|
|
|
|
* @v st_dest Partially-filled destination address
|
2007-01-03 21:48:52 +01:00
|
|
|
* @v pshdr_csum Pseudo-header checksum
|
2006-08-02 02:02:21 +02:00
|
|
|
* @ret rc Return status code
|
2006-06-25 07:13:17 +02:00
|
|
|
*/
|
2007-05-19 20:39:40 +02:00
|
|
|
static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
|
2007-01-03 21:48:52 +01:00
|
|
|
struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
|
2007-05-19 20:39:40 +02:00
|
|
|
struct udp_header *udphdr = iobuf->data;
|
2007-06-20 02:13:35 +02:00
|
|
|
struct udp_connection *udp;
|
|
|
|
struct xfer_metadata meta;
|
2007-01-03 22:13:11 +01:00
|
|
|
size_t ulen;
|
2007-06-20 02:13:35 +02:00
|
|
|
unsigned int csum;
|
2007-04-09 19:34:10 +02:00
|
|
|
int rc = 0;
|
2006-06-28 09:46:28 +02:00
|
|
|
|
2007-01-03 22:13:11 +01:00
|
|
|
/* Sanity check packet */
|
2007-05-19 20:39:40 +02:00
|
|
|
if ( iob_len ( iobuf ) < sizeof ( *udphdr ) ) {
|
2007-12-07 01:11:43 +01:00
|
|
|
DBG ( "UDP packet too short at %zd bytes (min %zd bytes)\n",
|
2007-05-19 20:39:40 +02:00
|
|
|
iob_len ( iobuf ), sizeof ( *udphdr ) );
|
2007-01-03 22:13:11 +01:00
|
|
|
|
2006-08-09 03:24:32 +02:00
|
|
|
rc = -EINVAL;
|
|
|
|
goto done;
|
2006-06-28 09:46:28 +02:00
|
|
|
}
|
|
|
|
ulen = ntohs ( udphdr->len );
|
2007-01-03 22:13:11 +01:00
|
|
|
if ( ulen < sizeof ( *udphdr ) ) {
|
2007-12-07 01:11:43 +01:00
|
|
|
DBG ( "UDP length too short at %zd bytes "
|
|
|
|
"(header is %zd bytes)\n", ulen, sizeof ( *udphdr ) );
|
2007-01-03 22:13:11 +01:00
|
|
|
rc = -EINVAL;
|
|
|
|
goto done;
|
|
|
|
}
|
2007-05-19 20:39:40 +02:00
|
|
|
if ( ulen > iob_len ( iobuf ) ) {
|
2007-12-07 01:11:43 +01:00
|
|
|
DBG ( "UDP length too long at %zd bytes (packet is %zd "
|
|
|
|
"bytes)\n", ulen, iob_len ( iobuf ) );
|
2006-08-09 03:24:32 +02:00
|
|
|
rc = -EINVAL;
|
|
|
|
goto done;
|
2006-06-28 09:46:28 +02:00
|
|
|
}
|
2007-01-04 06:17:28 +01:00
|
|
|
if ( udphdr->chksum ) {
|
2007-05-19 20:39:40 +02:00
|
|
|
csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, ulen );
|
2007-01-04 06:17:28 +01:00
|
|
|
if ( csum != 0 ) {
|
|
|
|
DBG ( "UDP checksum incorrect (is %04x including "
|
|
|
|
"checksum field, should be 0000)\n", csum );
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto done;
|
|
|
|
}
|
2006-06-28 09:46:28 +02:00
|
|
|
}
|
|
|
|
|
2007-01-03 22:13:11 +01:00
|
|
|
/* Parse parameters from header and strip header */
|
2007-06-20 02:13:35 +02:00
|
|
|
st_src->st_port = udphdr->src;
|
|
|
|
st_dest->st_port = udphdr->dest;
|
2008-06-09 23:56:48 +02:00
|
|
|
udp = udp_demux ( st_dest );
|
2007-05-19 20:39:40 +02:00
|
|
|
iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) );
|
|
|
|
iob_pull ( iobuf, sizeof ( *udphdr ) );
|
2006-06-28 09:46:28 +02:00
|
|
|
|
2007-01-03 22:13:11 +01:00
|
|
|
/* Dump debugging information */
|
2007-06-20 02:13:35 +02:00
|
|
|
DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
|
|
|
|
ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
|
2006-06-28 09:46:28 +02:00
|
|
|
|
2007-01-03 22:13:11 +01:00
|
|
|
/* Ignore if no matching connection found */
|
2007-06-20 02:13:35 +02:00
|
|
|
if ( ! udp ) {
|
2007-01-03 22:13:11 +01:00
|
|
|
DBG ( "No UDP connection listening on port %d\n",
|
2007-06-20 02:13:35 +02:00
|
|
|
ntohs ( udphdr->dest ) );
|
2007-01-03 22:13:11 +01:00
|
|
|
rc = -ENOTCONN;
|
2006-08-09 03:24:32 +02:00
|
|
|
goto done;
|
2006-08-02 02:02:21 +02:00
|
|
|
}
|
2006-06-28 09:46:28 +02:00
|
|
|
|
2007-01-03 22:13:11 +01:00
|
|
|
/* Pass data to application */
|
2007-06-20 02:13:35 +02:00
|
|
|
memset ( &meta, 0, sizeof ( meta ) );
|
|
|
|
meta.src = ( struct sockaddr * ) st_src;
|
|
|
|
meta.dest = ( struct sockaddr * ) st_dest;
|
2010-06-16 02:31:29 +02:00
|
|
|
rc = xfer_deliver ( &udp->xfer, iob_disown ( iobuf ), &meta );
|
2006-08-09 03:24:32 +02:00
|
|
|
|
|
|
|
done:
|
2007-05-19 20:39:40 +02:00
|
|
|
free_iob ( iobuf );
|
2006-08-09 03:24:32 +02:00
|
|
|
return rc;
|
2006-06-25 07:13:17 +02:00
|
|
|
}
|
|
|
|
|
2006-08-09 06:42:14 +02:00
|
|
|
struct tcpip_protocol udp_protocol __tcpip_protocol = {
|
2006-06-28 09:46:28 +02:00
|
|
|
.name = "UDP",
|
|
|
|
.rx = udp_rx,
|
2006-08-02 02:02:21 +02:00
|
|
|
.tcpip_proto = IP_UDP,
|
2006-06-25 07:13:17 +02:00
|
|
|
};
|
2007-06-20 02:13:35 +02:00
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Data transfer interface
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate I/O buffer for UDP
|
|
|
|
*
|
2010-06-16 02:31:29 +02:00
|
|
|
* @v udp UDP connection
|
2007-06-20 02:13:35 +02:00
|
|
|
* @v len Payload size
|
|
|
|
* @ret iobuf I/O buffer, or NULL
|
|
|
|
*/
|
2010-06-16 02:31:29 +02:00
|
|
|
static struct io_buffer * udp_xfer_alloc_iob ( struct udp_connection *udp,
|
|
|
|
size_t len ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
struct io_buffer *iobuf;
|
|
|
|
|
2010-11-19 16:45:41 +01:00
|
|
|
iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len );
|
2007-06-20 02:13:35 +02:00
|
|
|
if ( ! iobuf ) {
|
2007-12-07 01:11:43 +01:00
|
|
|
DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n",
|
2007-06-20 02:13:35 +02:00
|
|
|
udp, len );
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-11-19 16:45:41 +01:00
|
|
|
iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN );
|
2007-06-20 02:13:35 +02:00
|
|
|
return iobuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deliver datagram as I/O buffer
|
|
|
|
*
|
2010-06-16 02:31:29 +02:00
|
|
|
* @v udp UDP connection
|
2007-06-20 02:13:35 +02:00
|
|
|
* @v iobuf Datagram I/O buffer
|
2009-02-15 09:41:46 +01:00
|
|
|
* @v meta Data transfer metadata
|
2007-06-20 02:13:35 +02:00
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
2010-06-16 02:31:29 +02:00
|
|
|
static int udp_xfer_deliver ( struct udp_connection *udp,
|
|
|
|
struct io_buffer *iobuf,
|
|
|
|
struct xfer_metadata *meta ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
|
|
|
|
/* Transmit data, if possible */
|
2012-02-20 20:29:49 +01:00
|
|
|
return udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ),
|
|
|
|
( ( struct sockaddr_tcpip * ) meta->dest ),
|
|
|
|
meta->netdev );
|
2007-06-20 02:13:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** UDP data transfer interface operations */
|
2010-06-16 02:31:29 +02:00
|
|
|
static struct interface_operation udp_xfer_operations[] = {
|
|
|
|
INTF_OP ( xfer_deliver, struct udp_connection *, udp_xfer_deliver ),
|
|
|
|
INTF_OP ( xfer_alloc_iob, struct udp_connection *, udp_xfer_alloc_iob ),
|
|
|
|
INTF_OP ( intf_close, struct udp_connection *, udp_close ),
|
2007-06-20 02:13:35 +02:00
|
|
|
};
|
|
|
|
|
2010-06-16 02:31:29 +02:00
|
|
|
/** UDP data transfer interface descriptor */
|
|
|
|
static struct interface_descriptor udp_xfer_desc =
|
|
|
|
INTF_DESC ( struct udp_connection, xfer, udp_xfer_operations );
|
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Openers
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** UDP socket opener */
|
|
|
|
struct socket_opener udp_socket_opener __socket_opener = {
|
2008-11-19 20:33:05 +01:00
|
|
|
.semantics = UDP_SOCK_DGRAM,
|
2007-06-20 02:13:35 +02:00
|
|
|
.family = AF_INET,
|
|
|
|
.open = udp_open,
|
|
|
|
};
|
|
|
|
|
2008-11-19 20:33:05 +01:00
|
|
|
/** Linkage hack */
|
|
|
|
int udp_sock_dgram = UDP_SOCK_DGRAM;
|
2007-06-28 19:06:25 +02:00
|
|
|
|
2007-06-20 02:13:35 +02:00
|
|
|
/**
|
|
|
|
* Open UDP URI
|
|
|
|
*
|
|
|
|
* @v xfer Data transfer interface
|
|
|
|
* @v uri URI
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
2010-06-16 02:31:29 +02:00
|
|
|
static int udp_open_uri ( struct interface *xfer, struct uri *uri ) {
|
2007-06-20 02:13:35 +02:00
|
|
|
struct sockaddr_tcpip peer;
|
|
|
|
|
|
|
|
/* Sanity check */
|
|
|
|
if ( ! uri->host )
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memset ( &peer, 0, sizeof ( peer ) );
|
|
|
|
peer.st_port = htons ( uri_port ( uri, 0 ) );
|
|
|
|
return xfer_open_named_socket ( xfer, SOCK_DGRAM,
|
|
|
|
( struct sockaddr * ) &peer,
|
|
|
|
uri->host, NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
/** UDP URI opener */
|
|
|
|
struct uri_opener udp_uri_opener __uri_opener = {
|
|
|
|
.scheme = "udp",
|
|
|
|
.open = udp_open_uri,
|
|
|
|
};
|