From e381714c079b3f2e6b525216f34a5bba0fdb239f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 20 Jun 2007 01:13:35 +0100 Subject: [PATCH] Partial migration of UDP to data-xfer interface. (Will not link at present; DHCP is broken). --- src/include/gpxe/udp.h | 147 +---------- src/interface/pxe/pxe_udp.c | 37 +-- src/net/udp.c | 483 +++++++++++++++++++++++------------- 3 files changed, 323 insertions(+), 344 deletions(-) diff --git a/src/include/gpxe/udp.h b/src/include/gpxe/udp.h index 0ca77b4d..cb0e44eb 100644 --- a/src/include/gpxe/udp.h +++ b/src/include/gpxe/udp.h @@ -14,7 +14,8 @@ #include #include -struct net_device; +struct xfer_interface; +struct sockaddr; /** * UDP constants @@ -24,147 +25,23 @@ struct net_device; #define UDP_MAX_TXIOB ETH_MAX_MTU #define UDP_MIN_TXIOB ETH_ZLEN -typedef uint16_t port_t; - /** * A UDP header */ struct udp_header { - port_t source_port; - port_t dest_port; + /** Source port */ + uint16_t src; + /** Destination port */ + uint16_t dest; + /** Length */ uint16_t len; + /** Checksum */ uint16_t chksum; }; -struct udp_connection; - -/** - * UDP operations - * - */ -struct udp_operations { - - /** - * Transmit data - * - * @v conn UDP connection - * @v buf Temporary data buffer - * @v len Length of temporary data buffer - * @ret rc Return status code - * - * The application may use the temporary data buffer to - * construct the data to be sent. Note that merely filling - * the buffer will do nothing; the application must call - * udp_send() in order to actually transmit the data. Use of - * the buffer is not compulsory; the application may call - * udp_send() on any block of data. - */ - int ( * senddata ) ( struct udp_connection *conn, void *buf, - size_t len ); - /** - * New data received - * - * @v conn UDP connection - * @v data Data - * @v len Length of data - * @v st_src Source address - * @v st_dest Destination address - * @ret rc Return status code - */ - int ( * newdata ) ( struct udp_connection *conn, void *data, - size_t len, struct sockaddr_tcpip *st_src, - struct sockaddr_tcpip *st_dest ); -}; - -/** - * A UDP connection - * - */ -struct udp_connection { - /** Address of the remote end of the connection */ - struct sockaddr_tcpip peer; - /** Local port on which the connection receives packets */ - port_t local_port; - /** Transmit buffer */ - struct io_buffer *tx_iob; - /** List of registered connections */ - struct list_head list; - /** Operations table for this connection */ - struct udp_operations *udp_op; -}; - -/* - * Functions provided to the application layer - */ - -/** - * Bind UDP connection to all local ports - * - * @v conn UDP connection - * - * A promiscuous UDP connection will receive packets with any - * destination UDP port. This is required in order to support the PXE - * UDP API. - * - * If the promiscuous connection is not the only UDP connection, the - * behaviour is undefined. - */ -static inline void udp_bind_promisc ( struct udp_connection *conn ) { - conn->local_port = 0; -} - -/** - * Connect UDP connection to remote host and port - * - * @v conn UDP connection - * @v peer Destination socket address - * - * This function sets the default address for transmitted packets, - * i.e. the address used when udp_send() is called rather than - * udp_sendto(). - */ -static inline void udp_connect ( struct udp_connection *conn, - struct sockaddr_tcpip *peer ) { - memcpy ( &conn->peer, peer, sizeof ( conn->peer ) ); -} - -/** - * Connect UDP connection to remote port - * - * @v conn UDP connection - * @v port Destination port - * - * This function sets only the port part of the default address for - * transmitted packets. - */ -static inline void udp_connect_port ( struct udp_connection *conn, - uint16_t port ) { - conn->peer.st_port = port; -} - -/** - * Get default address for transmitted packets - * - * @v conn UDP connection - * @ret peer Default destination socket address - */ -static inline struct sockaddr_tcpip * -udp_peer ( struct udp_connection *conn ) { - return &conn->peer; -} - -extern int udp_bind ( struct udp_connection *conn, uint16_t local_port ); -extern int udp_open ( struct udp_connection *conn, uint16_t local_port ); -extern void udp_close ( struct udp_connection *conn ); - -extern int udp_senddata ( struct udp_connection *conn ); -extern int udp_send ( struct udp_connection *conn, - const void *data, size_t len ); -extern int udp_sendto ( struct udp_connection *conn, - struct sockaddr_tcpip *peer, - const void *data, size_t len ); -int udp_sendto_via ( struct udp_connection *conn, struct sockaddr_tcpip *peer, - struct net_device *netdev, const void *data, - size_t len ); +extern int udp_open_promisc ( struct xfer_interface *xfer ); +extern int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer, + struct sockaddr *local ); #endif /* _GPXE_UDP_H */ + diff --git a/src/interface/pxe/pxe_udp.c b/src/interface/pxe/pxe_udp.c index be2bc130..d85028e4 100644 --- a/src/interface/pxe/pxe_udp.c +++ b/src/interface/pxe/pxe_udp.c @@ -34,8 +34,6 @@ struct pxe_udp_connection { /** Data transfer interface to UDP stack */ struct xfer_interface xfer; - /** "Connection is open" flag */ - int open; /** Local address */ struct sockaddr_in local; /** Current PXENV_UDP_READ parameter block */ @@ -169,21 +167,19 @@ static struct pxe_udp_connection pxe_udp = { * */ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) { + int rc; DBG ( "PXENV_UDP_OPEN" ); - /* Check connection is not already open */ - if ( pxe_udp.open ) { - pxenv_udp_open->Status = PXENV_STATUS_UDP_OPEN; - return PXENV_EXIT_FAILURE; - } - /* Record source IP address */ pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip; /* Open promiscuous UDP connection */ - udp_open_promisc ( &pxe_udp.xfer ); - pxe_udp.open = 1; + xfer_close ( &pxe_udp.xfer, 0 ); + if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) { + pxenv_udp_open->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } pxenv_udp_open->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; @@ -213,15 +209,8 @@ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) { PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) { DBG ( "PXENV_UDP_CLOSE" ); - /* Check connection is open */ - if ( ! pxe_udp.open ) { - pxenv_udp_close->Status = PXENV_STATUS_UDP_CLOSED; - return PXENV_EXIT_SUCCESS; /* Well, it *is* closed */ - } - /* Close UDP connection */ - udp_close_promisc ( &pxe_udp.xfer ); - pxe_udp.open = 0; + xfer_close ( &pxe_udp.xfer, 0 ); pxenv_udp_close->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; @@ -281,12 +270,6 @@ PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) { DBG ( "PXENV_UDP_WRITE" ); - /* Check connection is open */ - if ( ! pxe_udp.open ) { - pxenv_udp_write->Status = PXENV_STATUS_UDP_CLOSED; - return PXENV_EXIT_FAILURE; - } - /* Construct destination socket address */ memset ( &dest, 0, sizeof ( dest ) ); dest.sin_family = AF_INET; @@ -383,12 +366,6 @@ PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { DBG ( "PXENV_UDP_READ" ); - /* Check connection is open */ - if ( ! pxe_udp.open ) { - pxenv_udp_read->Status = PXENV_STATUS_UDP_CLOSED; - return PXENV_EXIT_FAILURE; - } - /* Try receiving a packet */ pxe_udp.pxenv_udp_read = pxenv_udp_read; step(); diff --git a/src/net/udp.c b/src/net/udp.c index 3c30284b..ca0c816f 100644 --- a/src/net/udp.c +++ b/src/net/udp.c @@ -1,11 +1,14 @@ #include +#include #include #include #include #include #include #include -#include +#include +#include +#include #include /** @file @@ -13,233 +16,234 @@ * UDP protocol */ -struct tcpip_protocol udp_protocol; +/** + * A UDP connection + * + */ +struct udp_connection { + /** Reference counter */ + struct refcnt refcnt; + /** List of UDP connections */ + struct list_head list; + + /** Data transfer interface */ + struct xfer_interface xfer; + + /** Remote socket address */ + struct sockaddr_tcpip peer; + /** Local port on which the connection receives packets */ + unsigned int local_port; +}; /** * List of registered UDP connections */ static LIST_HEAD ( udp_conns ); +/* Forward declatations */ +static struct xfer_interface_operations udp_xfer_operations; +struct tcpip_protocol udp_protocol; + /** * Bind UDP connection to local port * - * @v conn UDP connection - * @v local_port Local port, in network byte order - * @ret rc Return status code - */ -int udp_bind ( struct udp_connection *conn, uint16_t local_port ) { - struct udp_connection *existing; - - list_for_each_entry ( existing, &udp_conns, list ) { - if ( existing->local_port == local_port ) - return -EADDRINUSE; - } - conn->local_port = local_port; - return 0; -} - -/** - * Open a local port - * - * @v conn UDP connection - * @v local_port Local port, in network byte order, or zero + * @v udp UDP connection + * @v port Local port, in network byte order, or zero * @ret rc Return status code * * Opens the UDP connection and binds to a local port. If no local * port is specified, the first available port will be used. */ -int udp_open ( struct udp_connection *conn, uint16_t local_port ) { +static int udp_bind ( struct udp_connection *udp, unsigned int port ) { + struct udp_connection *existing; static uint16_t try_port = 1024; - int rc; /* If no port specified, find the first available port */ - if ( ! local_port ) { + if ( ! port ) { for ( ; try_port ; try_port++ ) { if ( try_port < 1024 ) continue; - if ( udp_open ( conn, htons ( try_port ) ) == 0 ) + if ( udp_bind ( udp, htons ( try_port ) ) == 0 ) return 0; } return -EADDRINUSE; } /* Attempt bind to local port */ - if ( ( rc = udp_bind ( conn, local_port ) ) != 0 ) { - DBGC ( conn, "UDP %p could not bind to local port %d: %s\n", - conn, local_port, strerror ( rc ) ); - return rc; + list_for_each_entry ( existing, &udp_conns, list ) { + if ( existing->local_port == port ) { + DBGC ( udp, "UDP %p could not bind: port %d in use\n", + udp, ntohs ( port ) ); + return -EADDRINUSE; + } } + udp->local_port = port; /* Add to UDP connection list */ - list_add ( &conn->list, &udp_conns ); - DBGC ( conn, "UDP %p opened on port %d\n", conn, - ntohs ( local_port ) ); + DBGC ( udp, "UDP %p bound to port %d\n", udp, ntohs ( port ) ); return 0; } +/** + * Open a UDP connection + * + * @v xfer Data transfer interface + * @v peer Peer socket address + * @v local Local socket address, or NULL + * @v promisc Socket is promiscuous + * @ret rc Return status code + */ +static int udp_open_common ( struct xfer_interface *xfer, + 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; + unsigned int bind_port; + int rc; + + /* Allocate and initialise structure */ + udp = malloc ( sizeof ( *udp ) ); + if ( ! udp ) + return -ENOMEM; + DBGC ( udp, "UDP %p allocated\n", udp ); + memset ( udp, 0, sizeof ( *udp ) ); + xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt ); + memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) ); + + /* Bind to local port */ + if ( ! promisc ) { + bind_port = ( st_local ? st_local->st_port : 0 ); + if ( ( rc = udp_bind ( udp, bind_port ) ) != 0 ) + goto err; + } + + /* Attach parent interface, transfer reference to connection + * list and return + */ + xfer_plug_plug ( &udp->xfer, xfer ); + list_add ( &udp->list, &udp_conns ); + return 0; + + err: + ref_put ( &udp->refcnt ); + return rc; +} + +/** + * Open a UDP connection + * + * @v xfer Data transfer interface + * @v peer Peer socket address + * @v local Local socket address, or NULL + * @ret rc Return status code + */ +int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer, + struct sockaddr *local ) { + return udp_open_common ( xfer, peer, local, 0 ); +} + +/** + * Open a promiscuous UDP connection + * + * @v xfer Data transfer interface + * @ret rc Return status code + * + * Promiscuous UDP connections are required in order to support the + * PXE API. + */ +int udp_open_promisc ( struct xfer_interface *xfer ) { + return udp_open_common ( xfer, NULL, NULL, 1 ); +} + /** * Close a UDP connection * - * @v conn UDP connection + * @v udp UDP connection + * @v rc Reason for close */ -void udp_close ( struct udp_connection *conn ) { - list_del ( &conn->list ); - DBGC ( conn, "UDP %p closed\n", conn ); +static void udp_close ( struct udp_connection *udp, int rc ) { + + /* Close data transfer interface */ + xfer_nullify ( &udp->xfer ); + xfer_close ( &udp->xfer, rc ); + + /* Remove from list of connections and drop list's reference */ + list_del ( &udp->list ); + ref_put ( &udp->refcnt ); + + DBGC ( udp, "UDP %p closed\n", udp ); } -/** - * Allocate I/O buffer for UDP - * - * @v conn UDP connection - * @ret iobuf I/O buffer, or NULL - */ -static struct io_buffer * udp_alloc_iob ( struct udp_connection *conn ) { - struct io_buffer *iobuf; - - iobuf = alloc_iob ( UDP_MAX_TXIOB ); - if ( ! iobuf ) { - DBGC ( conn, "UDP %p cannot allocate buffer of length %d\n", - conn, UDP_MAX_TXIOB ); - return NULL; - } - iob_reserve ( iobuf, UDP_MAX_HLEN ); - return iobuf; -} - -/** - * User request to send data via a UDP connection - * - * @v conn UDP connection - * - * This function allocates buffer space and invokes the function's - * senddata() callback. The callback may use the buffer space as - * temporary storage space. - */ -int udp_senddata ( struct udp_connection *conn ) { - int rc; - - conn->tx_iob = udp_alloc_iob ( conn ); - if ( ! conn->tx_iob ) - return -ENOMEM; - - rc = conn->udp_op->senddata ( conn, conn->tx_iob->data, - iob_tailroom ( conn->tx_iob ) ); - if ( rc != 0 ) { - DBGC ( conn, "UDP %p application could not send packet: %s\n", - conn, strerror ( rc ) ); - } - - if ( conn->tx_iob ) { - free_iob ( conn->tx_iob ); - conn->tx_iob = NULL; - } - - return rc; -} - /** * Transmit data via a UDP connection to a specified address * - * @v conn UDP connection - * @v peer Destination address - * @v netdev Network device to use if no route found, or NULL - * @v data Data to send - * @v len Length of data + * @v udp UDP connection + * @v iobuf I/O buffer + * @v src_port Source port, or 0 to use default + * @v dest Destination address, or NULL to use default * @ret rc Return status code */ -int udp_sendto_via ( struct udp_connection *conn, struct sockaddr_tcpip *peer, - struct net_device *netdev, const void *data, - size_t len ) { +static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf, + unsigned int src_port, struct sockaddr_tcpip *dest ) { struct udp_header *udphdr; - struct io_buffer *iobuf; + struct net_device *netdev = NULL; + size_t len; int rc; - /* Use precreated I/O buffer if one is available */ - if ( conn->tx_iob ) { - iobuf = conn->tx_iob; - conn->tx_iob = NULL; - } else { - iobuf = udp_alloc_iob ( conn ); - if ( ! iobuf ) - return -ENOMEM; +#warning "netdev?" + + /* Check we can accommodate the header */ + if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) { + free_iob ( iobuf ); + return rc; } - /* Avoid overflowing TX buffer */ - if ( len > iob_tailroom ( iobuf ) ) - len = iob_tailroom ( iobuf ); + /* Fill in default values if not explicitly provided */ + if ( ! src_port ) + src_port = udp->local_port; + if ( ! dest ) + dest = &udp->peer; - /* Copy payload */ - memmove ( iob_put ( iobuf, len ), data, len ); - - /* - * Add the UDP header - * - * Covert all 16- and 32- bit integers into network btye order before - * sending it over the network - */ + /* Add the UDP header */ udphdr = iob_push ( iobuf, sizeof ( *udphdr ) ); - udphdr->dest_port = peer->st_port; - udphdr->source_port = conn->local_port; - udphdr->len = htons ( iob_len ( iobuf ) ); + len = iob_len ( iobuf ); + udphdr->dest = dest->st_port; + udphdr->src = src_port; + udphdr->len = htons ( len ); udphdr->chksum = 0; - udphdr->chksum = tcpip_chksum ( udphdr, sizeof ( *udphdr ) + len ); + udphdr->chksum = tcpip_chksum ( udphdr, len ); /* Dump debugging information */ - DBGC ( conn, "UDP %p TX %d->%d len %zd\n", conn, - ntohs ( udphdr->source_port ), ntohs ( udphdr->dest_port ), + DBGC ( udp, "UDP %p TX %d->%d len %zd\n", udp, + ntohs ( udphdr->src ), ntohs ( udphdr->dest ), ntohs ( udphdr->len ) ); /* Send it to the next layer for processing */ - if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, peer, netdev, + if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, dest, netdev, &udphdr->chksum ) ) != 0 ) { - DBGC ( conn, "UDP %p could not transmit packet: %s\n", - conn, strerror ( rc ) ); + DBGC ( udp, "UDP %p could not transmit packet: %s\n", + udp, strerror ( rc ) ); return rc; } return 0; } -/** - * Transmit data via a UDP connection to a specified address - * - * @v conn UDP connection - * @v peer Destination address - * @v data Data to send - * @v len Length of data - * @ret rc Return status code - */ -int udp_sendto ( struct udp_connection *conn, struct sockaddr_tcpip *peer, - const void *data, size_t len ) { - return udp_sendto_via ( conn, peer, NULL, data, len ); -} - -/** - * Transmit data via a UDP connection - * - * @v conn UDP connection - * @v data Data to send - * @v len Length of data - * @ret rc Return status code - */ -int udp_send ( struct udp_connection *conn, const void *data, size_t len ) { - return udp_sendto ( conn, &conn->peer, data, len ); -} - /** * Identify UDP connection by local port number * * @v local_port Local port (in network-endian order) - * @ret conn TCP connection, or NULL + * @ret udp UDP connection, or NULL */ -static struct udp_connection * udp_demux ( uint16_t local_port ) { - struct udp_connection *conn; +static struct udp_connection * udp_demux ( unsigned int local_port ) { + struct udp_connection *udp; - list_for_each_entry ( conn, &udp_conns, list ) { - if ( ( conn->local_port == local_port ) || - ( conn->local_port == 0 ) ) { - return conn; + list_for_each_entry ( udp, &udp_conns, list ) { + if ( ( udp->local_port == local_port ) || + ( udp->local_port == 0 ) ) { + return udp; } } return NULL; @@ -257,9 +261,10 @@ static struct udp_connection * udp_demux ( uint16_t local_port ) { static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) { struct udp_header *udphdr = iobuf->data; - struct udp_connection *conn; + struct udp_connection *udp; + struct xfer_metadata meta; size_t ulen; - uint16_t csum; + unsigned int csum; int rc = 0; /* Sanity check packet */ @@ -294,37 +299,30 @@ static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, } /* Parse parameters from header and strip header */ - st_src->st_port = udphdr->source_port; - st_dest->st_port = udphdr->dest_port; - conn = udp_demux ( udphdr->dest_port ); + st_src->st_port = udphdr->src; + st_dest->st_port = udphdr->dest; + udp = udp_demux ( udphdr->dest ); iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) ); iob_pull ( iobuf, sizeof ( *udphdr ) ); /* Dump debugging information */ - DBGC ( conn, "UDP %p RX %d<-%d len %zd\n", conn, - ntohs ( udphdr->dest_port ), ntohs ( udphdr->source_port ), - ulen ); + DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp, + ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen ); /* Ignore if no matching connection found */ - if ( ! conn ) { + if ( ! udp ) { DBG ( "No UDP connection listening on port %d\n", - ntohs ( udphdr->dest_port ) ); + ntohs ( udphdr->dest ) ); rc = -ENOTCONN; goto done; } /* Pass data to application */ - if ( conn->udp_op->newdata ) { - rc = conn->udp_op->newdata ( conn, iobuf->data, iob_len ( iobuf ), - st_src, st_dest ); - if ( rc != 0 ) { - DBGC ( conn, "UDP %p application rejected packet: %s\n", - conn, strerror ( rc ) ); - } - } else { - DBGC ( conn, "UDP %p application has no newdata handler for " \ - "incoming packet\n", conn ); - } + memset ( &meta, 0, sizeof ( meta ) ); + meta.src = ( struct sockaddr * ) st_src; + meta.dest = ( struct sockaddr * ) st_dest; + rc = xfer_deliver_iob_meta ( &udp->xfer, iobuf, &meta ); + iobuf = NULL; done: free_iob ( iobuf ); @@ -336,3 +334,130 @@ struct tcpip_protocol udp_protocol __tcpip_protocol = { .rx = udp_rx, .tcpip_proto = IP_UDP, }; + +/*************************************************************************** + * + * Data transfer interface + * + *************************************************************************** + */ + +/** + * Close interface + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct udp_connection *udp = + container_of ( xfer, struct udp_connection, xfer ); + + /* Close connection */ + udp_close ( udp, rc ); +} + +/** + * Allocate I/O buffer for UDP + * + * @v xfer Data transfer interface + * @v len Payload size + * @ret iobuf I/O buffer, or NULL + */ +static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer, + size_t len ) { + struct udp_connection *udp = + container_of ( xfer, struct udp_connection, xfer ); + struct io_buffer *iobuf; + + iobuf = alloc_iob ( UDP_MAX_HLEN + len ); + if ( ! iobuf ) { + DBGC ( udp, "UDP %p cannot allocate buffer of length %d\n", + udp, len ); + return NULL; + } + iob_reserve ( iobuf, UDP_MAX_HLEN ); + return iobuf; +} + +/** + * Deliver datagram as I/O buffer + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata, or NULL + * @ret rc Return status code + */ +static int udp_xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct udp_connection *udp = + container_of ( xfer, struct udp_connection, xfer ); + struct sockaddr_tcpip *src; + struct sockaddr_tcpip *dest = NULL; + unsigned int src_port = 0; + + /* Apply xfer metadata */ + if ( meta ) { + src = ( struct sockaddr_tcpip * ) meta->src; + if ( src ) + src_port = src->st_port; + dest = ( struct sockaddr_tcpip * ) meta->dest; + } + + /* Transmit data, if possible */ + udp_tx ( udp, iobuf, src_port, dest ); + + return 0; +} + +/** UDP data transfer interface operations */ +static struct xfer_interface_operations udp_xfer_operations = { + .close = udp_xfer_close, + .vredirect = ignore_xfer_vredirect, + .request = ignore_xfer_request, + .seek = ignore_xfer_seek, + .alloc_iob = udp_alloc_iob, + .deliver_iob = udp_xfer_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/*************************************************************************** + * + * Openers + * + *************************************************************************** + */ + +/** UDP socket opener */ +struct socket_opener udp_socket_opener __socket_opener = { + .semantics = SOCK_DGRAM, + .family = AF_INET, + .open = udp_open, +}; + +/** + * Open UDP URI + * + * @v xfer Data transfer interface + * @v uri URI + * @ret rc Return status code + */ +static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) { + 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, +};