updated UDP interface
authorNikhil Chandru Rao <nikhilcrao@users.sourceforge.net>
Sun, 25 Jun 2006 05:13:17 +0000 (05:13 +0000)
committerNikhil Chandru Rao <nikhilcrao@users.sourceforge.net>
Sun, 25 Jun 2006 05:13:17 +0000 (05:13 +0000)
src/include/gpxe/udp.h
src/net/udp.c [new file with mode: 0644]

index d47b9d2..ed45c18 100644 (file)
 
 #include <stddef.h>
 #include <gpxe/in.h>
+#include <gpxe/pkbuff.h>
+#include <gpxe/if_ether.h>
+
+/**
+ * UDP constants
+ */
+
+#define UDP_HLEN       8
+#define UDP_MAX_HLEN   72
+#define UDP_MAX_TXPKB  ETH_MAX_MTU
+#define UDP_MIN_TXPKB  ETH_ZLEN
+
+typedef uint16_t port_t;
+
+/**
+ * A UDP header
+ */
+struct udp_header {
+        port_t source_port;
+        port_t dest_port;
+        uint16_t len;
+        uint16_t chksum;
+};
 
 struct udp_connection;
 
@@ -35,15 +58,42 @@ struct udp_operations {
  *
  */
 struct udp_connection {
-       /** Address of the remote end of the connection */
-       struct sockaddr_in sin;
-       /** Operations table for this connection */
-       struct udp_operations *udp_op;
+       /** Address of the remote end of the connection */
+        struct sockaddr sin;
+        /** Local port on which the connection receives packets */
+        port_t local_port;
+        /** Transmit buffer */
+        struct pk_buff *tx_pkb;
+        /** List of registered connections */
+        struct list_head list;
+        /** Operations table for this connection */
+        struct udp_operations *udp_op;
 };
 
-extern void udp_connect ( struct udp_connection *conn );
-extern void udp_send ( struct udp_connection *conn, const void *data,
-                      size_t len );
+/**
+ * List of registered UDP connections
+ */
+static LIST_HEAD ( udp_conns );
+
+/**
+ * Functions provided to the application layer
+ */
+
+extern void udp_init ( struct udp_connection *conn, struct udp_operations *udp_op );
+extern int udp_open ( struct udp_connection *conn, uint16_t local_port );
+
+extern void udp_connect ( struct udp_connection *conn, struct sockaddr *peer );
 extern void udp_close ( 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 *peer, const void *data, size_t len );
+
+static inline void * udp_buffer ( struct udp_connection *conn ) {
+       return conn->tx_pkb->data;
+}
+
+static inline int udp_buflen ( struct udp_connection *conn ) {
+       return pkb_len ( conn->tx_pkb );
+}
+
 #endif /* _GPXE_UDP_H */
diff --git a/src/net/udp.c b/src/net/udp.c
new file mode 100644 (file)
index 0000000..9a6898d
--- /dev/null
@@ -0,0 +1,225 @@
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <latch.h>
+#include <errno.h>
+#include <gpxe/in.h>
+#include <gpxe/ip.h>
+#include <gpxe/udp.h>
+#include <gpxe/init.h>
+#include <gpxe/pkbuff.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/interface.h>
+
+/** @file
+ *
+ * UDP protocol
+ */
+
+void copy_sockaddr ( struct sockaddr *source, struct sockaddr *dest );
+
+/**
+ * Open a UDP connection
+ *
+ * @v conn      UDP connection
+ * @v peer      Destination socket address
+ *
+ * This function stores the socket address within the connection
+ */
+void udp_connect ( struct udp_connection *conn, struct sockaddr *peer ) {
+        copy_sockaddr ( peer, &conn->sin );
+        /**
+         * Not sure if this should add the connection to udp_conns; If it does, uncomment the following code
+         */
+//     list_add ( &conn->list, &udp_conns );
+}
+
+/**
+ * Initialize a UDP connection
+ *
+ * @v conn      UDP connection
+ * @v udp_op   UDP operations
+ */
+void udp_init ( struct udp_connection *conn, struct udp_operations *udp_op ) {
+        conn->local_port = 0;
+        conn->tx_pkb = NULL;
+        if ( udp_op != NULL ) {
+                conn->udp_op = udp_op;
+        }
+}
+
+/**
+ * Allocate space to the UDP buffer
+ *
+ * @v conn      UDP connection
+ * @v len       Length to allocate
+ * @ret rc      Status
+ *
+ * Allocate "len" amount of space in the transmit buffer
+ */
+int udp_buf_alloc ( struct udp_connection *conn, size_t len ) {
+        if ( conn->tx_pkb != NULL ) {
+                free_pkb ( conn->tx_pkb );
+                conn->tx_pkb = NULL;
+        }
+        conn->tx_pkb = alloc_pkb ( len < UDP_MIN_TXPKB ? UDP_MIN_TXPKB : len );
+        return !conn ? -ENOMEM : 0;
+}
+
+/**
+ * Send data via a UDP connection
+ *
+ * @v conn      UDP connection
+ * @v data      Data to send
+ * @v len       Length of data
+ *
+ * This function fills up the UDP headers and sends the data. Discover the network protocol to
+ * use through the sa_family field in the destination socket address.
+ */
+int udp_send ( struct udp_connection *conn, const void *data, size_t len ) {
+               struct udp_header *udphdr;              /* UDP header */
+        struct sockaddr *sock = &conn->sin;     /* Destination sockaddr */
+        int rc;
+
+        /* Copy data into packet buffer if necessary */
+        if ( data != conn->tx_pkb->data ) {
+                /* Allocate a buffer */
+                if ( ( rc = udp_buf_alloc ( conn, len + UDP_MAX_HLEN ) ) != 0 ) {
+                        DBG ( "Error allocating buffer" );
+                        return rc;
+                }
+
+                /* Reserve space for the headers and copy contents */
+                pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN );
+                memcpy ( pkb_put ( conn->tx_pkb, len ), data, len );
+        }
+
+        /* Add the UDP header */
+        udphdr = pkb_push ( conn->tx_pkb, UDP_HLEN );
+        if ( sock->sa_family == AF_INET )
+                udphdr->dest_port = sock->sin.sin_port;
+        else
+                udphdr->dest_port = sock->sin6.sin6_port;
+        udphdr->source_port = conn->local_port;
+        udphdr->len = htons ( pkb_len ( conn->tx_pkb ) );
+        udphdr->chksum = calc_chksum ( udphdr, UDP_HLEN );
+
+        /* Print UDP header for debugging */
+        DBG ( "UDP header at %#x + %d\n", udphdr, UDP_HDR_LEN  );
+        DBG ( "\tSource Port = %d\n", udphdr->source_port );
+        DBG ( "\tDestination Port = %d\n", udphdr->dest_port );
+        DBG ( "\tLength = %d\n", udphdr->len );
+        DBG ( "\tChecksum = %d\n", udphdr->chksum );
+        DBG ( "\tChecksum located at %#x\n", &udphdr->chksum );
+
+       return trans_tx ( conn->tx_pkb, IP_UDP, sock );
+}
+
+/**
+ * Send data to a specified address
+ *
+ * @v conn      UDP connection
+ * @v peer      Destination address
+ * @v data      Data to send
+ * @v len       Length of data
+ */
+int udp_sendto ( struct udp_connection *conn, struct sockaddr *peer,
+                        const void *data, size_t len ) {
+        struct sockaddr tempsock;
+        copy_sockaddr ( &conn->sin, &tempsock );
+        copy_sockaddr ( peer, &conn->sin );
+        int rc = udp_send ( conn, data, len );
+        copy_sockaddr ( &tempsock, &conn->sin );
+        return rc;
+}
+
+/**
+ * Close a UDP connection
+ *
+ * @v conn      UDP connection
+ */
+void udp_close ( struct udp_connection *conn ) {
+        list_del ( &conn->list );
+}
+
+/**
+ * Open a local port
+ *
+ * @v conn             UDP connection
+ * @v local_port       Local port on which to open connection
+ *
+ * This does not support the 0 port option correctly yet
+ */
+int udp_open ( struct udp_connection *conn, uint16_t local_port ) {
+        struct udp_connection *connr;
+        uint16_t min_port = 0xffff;
+        list_for_each_entry ( connr, &udp_conns, list ) {
+                if ( connr->local_port == local_port ) {
+                        return -EISCONN; /* CHECK THE ERROR NUMBER */
+                }
+                if ( min_port > connr->local_port ) {
+                        min_port = connr->local_port;
+                }
+        }
+        conn->local_port = local_port == 0 ? min_port > 1024 ? 1024 : min_port + 1 : local_port; // FAULTY!!!
+        list_add ( &conn->list, &udp_conns );
+        return 0;
+}
+
+/**
+ * Process a received packet
+ *
+ * @v pkb               Packet buffer
+ * @v src_net_addr      Source network address
+ * @v dest_net_addr     Destination network address
+ */
+void udp_rx ( struct pk_buff *pkb, struct in_addr *src_net_addr __unused,
+                        struct in_addr *dest_net_addr __unused ) {
+        struct udp_header *udphdr = pkb->data;
+        struct udp_connection *conn;
+
+        /* todo: Compute and verify checksum */
+
+        /* Print UDP header for debugging */
+        DBG ( "UDP header at %#x + %d\n", udphdr, UDP_HDR_LEN  );
+        DBG ( "\tSource Port = %d\n", udphdr->source_port );
+        DBG ( "\tDestination Port = %d\n", udphdr->dest_port );
+        DBG ( "\tLength = %d\n", udphdr->len );
+        DBG ( "\tChecksum = %d\n", udphdr->chksum );
+        DBG ( "\tChecksum located at %#x\n", &udphdr->chksum );
+
+        /* Demux the connection */
+        list_for_each_entry ( conn, &udp_conns, list ) {
+                if ( conn->local_port == udphdr->dest_port ) {
+                        goto conn;
+                }
+        }
+        return;
+
+        conn:
+        /** Strip off the UDP header */
+        pkb_pull ( pkb, UDP_HLEN );
+
+        /** Allocate max possible buffer space to the tx buffer */
+        conn->tx_pkb = alloc_pkb ( UDP_MAX_TXPKB );
+        pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN );
+
+        /** Call the application's callback */
+        conn->udp_op->newdata ( conn, pkb->data, ntohs ( udphdr->len ) - UDP_HLEN  );
+}
+
+struct trans_protocol udp_protocol  = {
+        .name = "UDP",
+        .rx = udp_rx,
+        .trans_proto = IP_UDP,
+};
+
+TRANS_PROTOCOL ( udp_protocol );
+
+/**
+ * Internal functions
+ */
+void copy_sockaddr ( struct sockaddr *source, struct sockaddr *dest ) {
+        memcpy ( dest, source, sizeof ( struct sockaddr ) );
+}