diff options
author | Michael Brown | 2007-06-20 02:13:35 +0200 |
---|---|---|
committer | Michael Brown | 2007-06-20 02:13:35 +0200 |
commit | e381714c079b3f2e6b525216f34a5bba0fdb239f (patch) | |
tree | 4eb73b379a920c19221a9f5298eebfb50eca89de /src/net/udp.c | |
parent | Add iob_ensure_headroom() placeholder. (diff) | |
download | ipxe-e381714c079b3f2e6b525216f34a5bba0fdb239f.tar.gz ipxe-e381714c079b3f2e6b525216f34a5bba0fdb239f.tar.xz ipxe-e381714c079b3f2e6b525216f34a5bba0fdb239f.zip |
Partial migration of UDP to data-xfer interface. (Will not link at
present; DHCP is broken).
Diffstat (limited to 'src/net/udp.c')
-rw-r--r-- | src/net/udp.c | 457 |
1 files changed, 291 insertions, 166 deletions
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 <stdint.h> +#include <stdlib.h> #include <string.h> #include <assert.h> #include <byteswap.h> #include <errno.h> #include <gpxe/tcpip.h> #include <gpxe/iobuf.h> -#include <gpxe/netdevice.h> +#include <gpxe/xfer.h> +#include <gpxe/open.h> +#include <gpxe/uri.h> #include <gpxe/udp.h> /** @file @@ -13,188 +16,215 @@ * UDP protocol */ -struct tcpip_protocol udp_protocol; - /** - * List of registered UDP connections + * A UDP connection + * */ -static LIST_HEAD ( udp_conns ); +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; +}; /** - * Bind UDP connection to local port - * - * @v conn UDP connection - * @v local_port Local port, in network byte order - * @ret rc Return status code + * List of registered UDP connections */ -int udp_bind ( struct udp_connection *conn, uint16_t local_port ) { - struct udp_connection *existing; +static LIST_HEAD ( udp_conns ); - list_for_each_entry ( existing, &udp_conns, list ) { - if ( existing->local_port == local_port ) - return -EADDRINUSE; - } - conn->local_port = local_port; - return 0; -} +/* Forward declatations */ +static struct xfer_interface_operations udp_xfer_operations; +struct tcpip_protocol udp_protocol; /** - * Open a local port + * Bind UDP connection to 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; } /** - * Close a UDP connection + * Open a UDP connection * - * @v conn 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 */ -void udp_close ( struct udp_connection *conn ) { - list_del ( &conn->list ); - DBGC ( conn, "UDP %p closed\n", conn ); +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; } /** - * Allocate I/O buffer for UDP + * Open a UDP connection * - * @v conn UDP connection - * @ret iobuf I/O buffer, or NULL + * @v xfer Data transfer interface + * @v peer Peer socket address + * @v local Local socket address, or NULL + * @ret rc Return status code */ -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; +int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer, + struct sockaddr *local ) { + return udp_open_common ( xfer, peer, local, 0 ); } /** - * User request to send data via a UDP connection + * Open a promiscuous UDP connection * - * @v conn UDP connection + * @v xfer Data transfer interface + * @ret rc Return status code * - * This function allocates buffer space and invokes the function's - * senddata() callback. The callback may use the buffer space as - * temporary storage space. + * Promiscuous UDP connections are required in order to support the + * PXE API. */ -int udp_senddata ( struct udp_connection *conn ) { - int rc; +int udp_open_promisc ( struct xfer_interface *xfer ) { + return udp_open_common ( xfer, NULL, NULL, 1 ); +} - conn->tx_iob = udp_alloc_iob ( conn ); - if ( ! conn->tx_iob ) - return -ENOMEM; +/** + * Close a UDP connection + * + * @v udp UDP connection + * @v rc Reason for close + */ +static void udp_close ( struct udp_connection *udp, int rc ) { - 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 ) ); - } + /* Close data transfer interface */ + xfer_nullify ( &udp->xfer ); + xfer_close ( &udp->xfer, rc ); - if ( conn->tx_iob ) { - free_iob ( conn->tx_iob ); - conn->tx_iob = NULL; - } + /* Remove from list of connections and drop list's reference */ + list_del ( &udp->list ); + ref_put ( &udp->refcnt ); - return rc; + DBGC ( udp, "UDP %p closed\n", udp ); } - + /** * 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?" - /* Avoid overflowing TX buffer */ - if ( len > iob_tailroom ( iobuf ) ) - len = iob_tailroom ( iobuf ); + /* Check we can accommodate the header */ + if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) { + free_iob ( iobuf ); + return rc; + } - /* Copy payload */ - memmove ( iob_put ( iobuf, len ), data, len ); + /* Fill in default values if not explicitly provided */ + if ( ! src_port ) + src_port = udp->local_port; + if ( ! dest ) + dest = &udp->peer; - /* - * 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; } @@ -202,44 +232,18 @@ int udp_sendto_via ( struct udp_connection *conn, struct sockaddr_tcpip *peer, } /** - * 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, +}; |