summaryrefslogtreecommitdiffstats
path: root/src/net/udp.c
diff options
context:
space:
mode:
authorMichael Brown2007-06-20 02:13:35 +0200
committerMichael Brown2007-06-20 02:13:35 +0200
commite381714c079b3f2e6b525216f34a5bba0fdb239f (patch)
tree4eb73b379a920c19221a9f5298eebfb50eca89de /src/net/udp.c
parentAdd iob_ensure_headroom() placeholder. (diff)
downloadipxe-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.c457
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,
+};