Partial migration of UDP to data-xfer interface. (Will not link at
authorMichael Brown <mcb30@etherboot.org>
Wed, 20 Jun 2007 00:13:35 +0000 (01:13 +0100)
committerMichael Brown <mcb30@etherboot.org>
Wed, 20 Jun 2007 00:13:35 +0000 (01:13 +0100)
present; DHCP is broken).

src/include/gpxe/udp.h
src/interface/pxe/pxe_udp.c
src/net/udp.c

index 0ca77b4..cb0e44e 100644 (file)
@@ -14,7 +14,8 @@
 #include <gpxe/tcpip.h>
 #include <gpxe/if_ether.h>
 
-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 */
+
index be2bc13..d85028e 100644 (file)
@@ -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();
index 3c30284..ca0c816 100644 (file)
@@ -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
  * 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;
        }
 
        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,
+};