Network layer now works as a proof of concept
authorMichael Brown <mcb30@etherboot.org>
Fri, 28 Apr 2006 14:13:50 +0000 (14:13 +0000)
committerMichael Brown <mcb30@etherboot.org>
Fri, 28 Apr 2006 14:13:50 +0000 (14:13 +0000)
src/include/gpxe/arp.h
src/include/gpxe/netdevice.h
src/net/arp.c
src/net/ethernet.c
src/net/ipv4.c
src/net/netdevice.c

index a2db80f..27591d3 100644 (file)
@@ -7,10 +7,12 @@
  *
  */
 
+struct net_device;
 struct net_header;
 struct ll_header;
 
-extern int arp_resolve ( const struct net_header *nethdr,
+extern int arp_resolve ( struct net_device *netdev,
+                        const struct net_header *nethdr,
                         struct ll_header *llhdr );
 
 #endif /* _GPXE_ARP_H */
index f7d7828..6db95bb 100644 (file)
 #include <gpxe/tables.h>
 
 struct pk_buff;
+struct net_device;
 struct net_protocol;
 struct ll_protocol;
 
 /** Maximum length of a link-layer address */
 #define MAX_LL_ADDR_LEN 6
 
+/** Maximum length of a link-layer header */
+#define MAX_LL_HEADER_LEN 16
+
 /** Maximum length of a network-layer address */
 #define MAX_NET_ADDR_LEN 4
 
@@ -32,37 +36,37 @@ struct ll_protocol;
 struct net_header {
        /** Network-layer protocol */
        struct net_protocol *net_protocol;
-       /** Destination address flags
+       /** Flags
         *
-        * This is the bitwise OR of zero or more NETADDR_FL_XXX
+        * This is the bitwise OR of zero or more PKT_FL_XXX
         * values.
         */
-       int dest_flags;
+       int flags;
        /** Network-layer destination address */
        uint8_t dest_net_addr[MAX_NET_ADDR_LEN];
        /** Network-layer source address */
        uint8_t source_net_addr[MAX_NET_ADDR_LEN];
 };
 
-/** Address is a broadcast address */
-#define NETADDR_FL_BROADCAST 0x01
+/** Packet is a broadcast packet */
+#define PKT_FL_BROADCAST 0x01
 
-/** Address is a multicast address */
-#define NETADDR_FL_MULTICAST 0x02
+/** Packet is a multicast packet */
+#define PKT_FL_MULTICAST 0x02
 
-/** Address is a raw hardware address */
-#define NETADDR_FL_RAW 0x04
+/** Addresses are raw hardware addresses */
+#define PKT_FL_RAW_ADDR 0x04
 
 /** A generic link-layer header */
 struct ll_header {
        /** Link-layer protocol */
        struct ll_protocol *ll_protocol;
-       /** Destination address flags
+       /** Flags
         *
-        * This is the bitwise OR of zero or more NETADDR_FL_XXX
+        * This is the bitwise OR of zero or more PKT_FL_XXX
         * values.
         */
-       int dest_flags;
+       int flags;
        /** Link-layer destination address */
        uint8_t dest_ll_addr[MAX_LL_ADDR_LEN];
        /** Link-layer source address */
@@ -80,21 +84,21 @@ struct ll_header {
  *
  */
 struct net_protocol {
+       /** Protocol name */
+       const char *name;
        /**
         * Perform network-layer routing
         *
         * @v pkb       Packet buffer
-        * @ret source  Network-layer source address
-        * @ret dest    Network-layer destination address
+        * @v nethdr    Generic network-layer header
         * @ret rc      Return status code
         *
-        * This method should fill in the source and destination
-        * addresses with enough information to allow the link layer
-        * to route the packet.
+        * This method should fill in the network header with enough
+        * information to allow the link layer to route the packet.
         *
         * For example, in the case of IPv4, this method should fill
-        * in @c source with the IP addresses of the local adapter and
-        * @c dest with the next hop destination (e.g. the gateway).
+        * in the IP addresses of the local adapter and the next hop
+        * destination (e.g. the gateway).
         */
        int ( * route ) ( const struct pk_buff *pkb,
                          struct net_header *nethdr );
@@ -108,6 +112,19 @@ struct net_protocol {
         * the packet buffer.
         */
        int ( * rx ) ( struct pk_buff *pkb );
+       /**
+        * Transcribe network-layer address
+        *
+        * @v net_addr  Network-layer address
+        * @ret string  Human-readable transcription of address
+        *
+        * This method should convert the network-layer address into a
+        * human-readable format (e.g. dotted quad notation for IPv4).
+        *
+        * The buffer used to hold the transcription is statically
+        * allocated.
+        */
+       const char * ( *ntoa ) ( const void * net_addr );
        /** Network-layer protocol
         *
         * This is an ETH_P_XXX constant, in network-byte order
@@ -122,9 +139,12 @@ struct net_protocol {
  *
  */
 struct ll_protocol {
+       /** Protocol name */
+       const char *name;
        /**
         * Perform link-layer routing
         *
+        * @v netdev    Network device
         * @v nethdr    Generic network-layer header
         * @ret llhdr   Generic link-layer header
         * @ret rc      Return status code
@@ -137,7 +157,8 @@ struct ll_protocol {
         * return an error (after transmitting an ARP request, if
         * applicable).
         */
-       int ( * route ) ( const struct net_header *nethdr,
+       int ( * route ) ( struct net_device *netdev,
+                         const struct net_header *nethdr,
                          struct ll_header *llhdr );
        /**
         * Fill media-specific link-layer header
@@ -164,6 +185,19 @@ struct ll_protocol {
        void ( * parse_llh ) ( const struct pk_buff *pkb,
                               struct ll_header *llhdr );
 
+       /**
+        * Transcribe link-layer address
+        *
+        * @v ll_addr   Link-layer address
+        * @ret string  Human-readable transcription of address
+        *
+        * This method should convert the link-layer address into a
+        * human-readable format.
+        *
+        * The buffer used to hold the transcription is statically
+        * allocated.
+        */
+       const char * ( *ntoa ) ( const void * ll_addr );
        /** Link-layer protocol
         *
         * This is an ARPHRD_XXX constant, in network byte order.
@@ -290,13 +324,30 @@ free_netdev ( struct net_device *netdev __attribute__ (( unused )) ) {
        /* Nothing to do */
 }
 
+/**
+ * Transmit raw packet via network device
+ *
+ * @v netdev           Network device
+ * @v pkb              Packet buffer
+ * @ret rc             Return status code
+ *
+ * Transmits the packet via the specified network device.  The
+ * link-layer header must already have been filled in.  If this
+ * function returns success, it has taken ownership of the packet
+ * buffer.
+ */
+static inline int netdev_transmit ( struct net_device *netdev,
+                                   struct pk_buff *pkb ) {
+       return netdev->transmit ( netdev, pkb );
+}
+
 /**
  * Register a link-layer protocol
  *
  * @v protocol         Link-layer protocol
  */
 #define LL_PROTOCOL( protocol ) \
-       struct ll_protocol protocol __table ( ll_protocols, 00 )
+       struct ll_protocol protocol __table ( ll_protocols, 01 )
 
 /**
  * Register a network-layer protocol
@@ -304,7 +355,7 @@ free_netdev ( struct net_device *netdev __attribute__ (( unused )) ) {
  * @v protocol         Network-layer protocol
  */
 #define NET_PROTOCOL( protocol ) \
-       struct net_protocol protocol __table ( net_protocols, 00 )
+       struct net_protocol protocol __table ( net_protocols, 01 )
 
 /**
  * Register a network-layer address for the static single network device
@@ -312,15 +363,17 @@ free_netdev ( struct net_device *netdev __attribute__ (( unused )) ) {
  * @v net_address      Network-layer address
  */
 #define STATIC_SINGLE_NETDEV_ADDRESS( address ) \
-       struct net_address address __table ( sgl_netdev_addresses, 00 )
+       struct net_address address __table ( sgl_netdev_addresses, 01 )
 
-extern struct net_protocol *net_find_protocol ( uint16_t net_proto );
-extern struct net_device * net_find_address ( struct net_protocol *net_proto,
-                                             void *net_addr );
+extern void netdev_rx ( struct net_device *netdev, struct pk_buff *pkb );
+
+extern struct net_protocol *find_net_protocol ( uint16_t net_proto );
+extern struct net_device *
+find_netdev_by_net_addr ( struct net_protocol *net_protocol, void *net_addr );
 
+extern int net_transmit_via ( struct pk_buff *pkb, struct net_device *netdev );
 extern int net_transmit ( struct pk_buff *pkb );
 extern int net_poll ( void );
-extern void netdev_rx ( struct net_device *netdev, struct pk_buff *pkb );
 extern struct pk_buff * net_rx_dequeue ( void );
 
 #endif /* _GPXE_NETDEVICE_H */
index a375eec..3720acf 100644 (file)
@@ -91,6 +91,7 @@ arp_find_entry ( struct ll_protocol *ll_protocol,
 /**
  * Look up media-specific link-layer address in the ARP cache
  *
+ * @v netdev           Network device
  * @v nethdr           Generic network-layer header
  * @ret llhdr          Generic link-layer header
  * @ret rc             Return status code
@@ -102,9 +103,11 @@ arp_find_entry ( struct ll_protocol *ll_protocol,
  * llhdr.
  *
  * If no address is found in the ARP cache, an ARP request will be
- * transmitted and -ENOENT will be returned.
+ * transmitted on the specified network device and -ENOENT will be
+ * returned.
  */
-int arp_resolve ( const struct net_header *nethdr, struct ll_header *llhdr ) {
+int arp_resolve ( struct net_device *netdev, const struct net_header *nethdr,
+                 struct ll_header *llhdr ) {
        struct net_protocol *net_protocol = nethdr->net_protocol;
        struct ll_protocol *ll_protocol = llhdr->ll_protocol;
        const struct arp_entry *arp;
@@ -116,17 +119,23 @@ int arp_resolve ( const struct net_header *nethdr, struct ll_header *llhdr ) {
        arp = arp_find_entry ( ll_protocol, net_protocol,
                               nethdr->dest_net_addr );
        if ( arp ) {
+               DBG ( "ARP cache hit: %s %s => %s %s\n",
+                     net_protocol->name, net_protocol->ntoa ( arp->net_addr ),
+                     ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) );
                memcpy ( llhdr->dest_ll_addr, arp->ll_addr,
                         sizeof ( llhdr->dest_ll_addr ) );
                return 0;
        }
+       DBG ( "ARP cache miss: %s %s\n", net_protocol->name,
+             net_protocol->ntoa ( nethdr->dest_net_addr ) );
 
        /* Allocate ARP packet */
-       pkb = alloc_pkb ( sizeof ( *arphdr ) +
+       pkb = alloc_pkb ( MAX_LL_HEADER_LEN + sizeof ( *arphdr ) +
                          2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) );
        if ( ! pkb )
                return -ENOMEM;
        pkb->net_protocol = &arp_protocol;
+       pkb_reserve ( pkb, MAX_LL_HEADER_LEN );
 
        /* Build up ARP request */
        arphdr = pkb_put ( pkb, sizeof ( *arphdr ) );
@@ -145,7 +154,7 @@ int arp_resolve ( const struct net_header *nethdr, struct ll_header *llhdr ) {
                 nethdr->dest_net_addr, net_protocol->net_addr_len );
 
        /* Transmit ARP request */
-       if ( ( rc = net_transmit ( pkb ) ) != 0 ) {
+       if ( ( rc = net_transmit_via ( pkb, netdev ) ) != 0 ) {
                free_pkb ( pkb );
                return rc;
        }
@@ -175,7 +184,7 @@ static int arp_rx ( struct pk_buff *pkb ) {
 
        /* Identify link-layer and network-layer protocols */
        ll_protocol = pkb->ll_protocol;
-       net_protocol = net_find_protocol ( arphdr->ar_pro );
+       net_protocol = find_net_protocol ( arphdr->ar_pro );
        if ( ! net_protocol )
                goto done;
 
@@ -192,10 +201,14 @@ static int arp_rx ( struct pk_buff *pkb ) {
                memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
                         arphdr->ar_hln );
                merge = 1;
+               DBG ( "ARP cache update: %s %s => %s %s\n",
+                     net_protocol->name, net_protocol->ntoa ( arp->net_addr ),
+                     ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) );
        }
 
        /* See if we own the target protocol address */
-       netdev = net_find_address ( net_protocol, arp_target_pa ( arphdr ) );
+       netdev = find_netdev_by_net_addr ( net_protocol,
+                                          arp_target_pa ( arphdr ) );
        if ( ! netdev )
                goto done;
        
@@ -208,6 +221,9 @@ static int arp_rx ( struct pk_buff *pkb ) {
                         arphdr->ar_hln );
                memcpy ( arp->net_addr, arp_sender_pa ( arphdr ),
                         arphdr->ar_pln);
+               DBG ( "ARP cache add: %s %s => %s %s\n",
+                     net_protocol->name, net_protocol->ntoa ( arp->net_addr ),
+                     ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) );
        }
 
        /* If it's not a request, there's nothing more to do */
@@ -215,11 +231,14 @@ static int arp_rx ( struct pk_buff *pkb ) {
                goto done;
 
        /* Change request to a reply, and send it */
+       DBG ( "ARP reply: %s %s => %s %s\n", net_protocol->name,
+             net_protocol->ntoa ( arp_target_pa ( arphdr ) ),
+             ll_protocol->name, ll_protocol->ntoa ( netdev->ll_addr ) );
        arphdr->ar_op = htons ( ARPOP_REPLY );
        memswap ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ),
                 arphdr->ar_hln + arphdr->ar_pln );
        memcpy ( arp_target_ha ( arphdr ), netdev->ll_addr, arphdr->ar_hln );
-       if ( net_transmit ( pkb ) == 0 )
+       if ( net_transmit_via ( pkb, netdev ) == 0 )
                pkb = NULL;
 
  done:
@@ -243,18 +262,33 @@ static int arp_route ( const struct pk_buff *pkb,
                 arphdr->ar_hln );
        memcpy ( nethdr->dest_net_addr, arp_target_ha ( arphdr ),
                 arphdr->ar_hln );
-       nethdr->dest_flags = NETADDR_FL_RAW;
+       nethdr->flags = PKT_FL_RAW_ADDR;
        if ( arphdr->ar_op == htons ( ARPOP_REQUEST ) )
-               nethdr->dest_flags |= NETADDR_FL_BROADCAST;
+               nethdr->flags |= PKT_FL_BROADCAST;
        
        return 0;
 }
 
+/**
+ * Transcribe ARP address
+ *
+ * @v net_addr ARP address
+ * @ret string "<ARP>"
+ *
+ * This operation is meaningless for the ARP protocol.
+ */
+static const char *
+arp_ntoa ( const void *net_addr __attribute__ (( unused )) ) {
+       return "<ARP>";
+}
+
 /** ARP protocol */
 struct net_protocol arp_protocol = {
+       .name = "ARP",
        .net_proto = htons ( ETH_P_ARP ),
        .rx = arp_rx,
        .route = arp_route,
+       .ntoa = arp_ntoa,
 };
 
 NET_PROTOCOL ( arp_protocol );
index 59b469f..cf0ddd7 100644 (file)
@@ -51,16 +51,21 @@ static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
  * is sent for the requested network-layer address and -ENOENT is
  * returned.
  */
-static int eth_route ( const struct net_header *nethdr,
+static int eth_route ( struct net_device *netdev,
+                      const struct net_header *nethdr,
                       struct ll_header *llhdr ) {
        int rc;
 
+       /* Fill in the easy bits */
+       llhdr->net_proto = nethdr->net_protocol->net_proto;
+       memcpy ( llhdr->source_ll_addr, netdev->ll_addr, ETH_ALEN );
+
        /* Work out the destination MAC address */
-       if ( nethdr->dest_flags & NETADDR_FL_RAW ) {
-               memcpy ( llhdr->dest_ll_addr, nethdr->dest_net_addr, ETH_ALEN);
-       } else if ( nethdr->dest_flags & NETADDR_FL_BROADCAST ) {
+       if ( nethdr->flags & PKT_FL_BROADCAST ) {
                memcpy ( llhdr->dest_ll_addr, eth_broadcast, ETH_ALEN );
-       } else if ( nethdr->dest_flags & NETADDR_FL_MULTICAST ) {
+       } else if ( nethdr->flags & PKT_FL_RAW_ADDR ) {
+               memcpy ( llhdr->dest_ll_addr, nethdr->dest_net_addr, ETH_ALEN);
+       } else if ( nethdr->flags & PKT_FL_MULTICAST ) {
                /* IP multicast is a special case; there exists a
                 * direct mapping from IP address to MAC address
                 */
@@ -73,7 +78,7 @@ static int eth_route ( const struct net_header *nethdr,
                llhdr->dest_ll_addr[5] = nethdr->dest_net_addr[3];
        } else {
                /* Otherwise, look up the address using ARP */
-               if ( ( rc = arp_resolve ( nethdr, llhdr ) ) != 0 )
+               if ( ( rc = arp_resolve ( netdev, nethdr, llhdr ) ) != 0 )
                        return rc;
        }
 
@@ -116,22 +121,40 @@ static void eth_parse_llh ( const struct pk_buff *pkb,
        llhdr->net_proto = ethhdr->h_protocol;
 
        if ( memcmp ( ethhdr->h_dest, eth_broadcast, ETH_ALEN ) == 0 ) {
-               llhdr->dest_flags = NETADDR_FL_BROADCAST;
+               llhdr->flags = PKT_FL_BROADCAST;
        } else if ( ethhdr->h_dest[0] & 0x01 ) {
-               llhdr->dest_flags = NETADDR_FL_MULTICAST;
+               llhdr->flags = PKT_FL_MULTICAST;
        } else {
-               llhdr->dest_flags = 0;
+               llhdr->flags = 0;
        }
 }
 
+/**
+ * Transcribe Ethernet address
+ *
+ * @v ll_addr  Link-layer address
+ * @ret string Link-layer address in human-readable format
+ */
+static const char * eth_ntoa ( const void *ll_addr ) {
+       static char buf[18]; /* "00:00:00:00:00:00" */
+       uint8_t *eth_addr = ll_addr;
+
+       sprintf ( buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+                 eth_addr[0], eth_addr[1], eth_addr[2],
+                 eth_addr[3], eth_addr[4], eth_addr[5] );
+       return buf;
+}
+
 /** Ethernet protocol */
 struct ll_protocol ethernet_protocol = {
+       .name = "Ethernet",
        .ll_proto = htons ( ARPHRD_ETHER ),
        .ll_addr_len = ETH_ALEN,
        .ll_header_len = ETH_HLEN,
        .route = eth_route,
        .fill_llh = eth_fill_llh,
        .parse_llh = eth_parse_llh,
+       .ntoa = eth_ntoa,
 };
 
 LL_PROTOCOL ( ethernet_protocol );
index 4200d23..2fc5067 100644 (file)
@@ -1,6 +1,7 @@
 #include <string.h>
 #include <stdint.h>
 #include <byteswap.h>
+#include <vsprintf.h>
 #include <gpxe/in.h>
 
 
@@ -153,7 +154,8 @@ static int ipv4_rx ( struct pk_buff *pkb ) {
        /* Transfer to uIP buffer.  Horrendously space-inefficient,
         * but will do as a proof-of-concept for now.
         */
-       memcpy ( uip_buf, pkb->data, pkb_len ( pkb ) );
+       uip_len = pkb_len ( pkb );
+       memcpy ( uip_buf, pkb->data, uip_len );
 
        /* Hand to uIP for processing */
        uip_input ();
@@ -198,22 +200,40 @@ static int ipv4_route ( const struct pk_buff *pkb,
        }
 
        /* Set broadcast and multicast flags as applicable */
-       nethdr->dest_flags = 0;
+       nethdr->flags = 0;
        if ( dest->s_addr == htonl ( INADDR_BROADCAST ) ) {
-               nethdr->dest_flags = NETADDR_FL_BROADCAST;
+               nethdr->flags = PKT_FL_BROADCAST;
        } else if ( IN_MULTICAST ( dest->s_addr ) ) {
-               nethdr->dest_flags = NETADDR_FL_MULTICAST;
+               nethdr->flags = PKT_FL_MULTICAST;
        }
 
        return 0;
 }
 
+/**
+ * Transcribe IP address
+ *
+ * @v net_addr IP address
+ * @ret string IP address in dotted-quad notation
+ *
+ */
+static const char * ipv4_ntoa ( const void *net_addr ) {
+       static char buf[16]; /* "xxx.xxx.xxx.xxx" */
+       uint8_t *ip_addr = net_addr;
+
+       sprintf ( buf, "%d.%d.%d.%d", ip_addr[0], ip_addr[1], ip_addr[2],
+                 ip_addr[3] );
+       return buf;
+}
+
 /** IPv4 protocol */
 struct net_protocol ipv4_protocol = {
-       .net_proto = ETH_P_IP,
+       .name = "IP",
+       .net_proto = htons ( ETH_P_IP ),
        .net_addr_len = sizeof ( struct in_addr ),
        .rx = ipv4_rx,
        .route = ipv4_route,
+       .ntoa = ipv4_ntoa,
 };
 
 NET_PROTOCOL ( ipv4_protocol );
@@ -221,6 +241,18 @@ NET_PROTOCOL ( ipv4_protocol );
 /** IPv4 address for the static single net device */
 struct net_address static_single_ipv4_address = {
        .net_protocol = &ipv4_protocol,
+
+#warning "Remove this static-IP hack"
+       .net_addr = { 0x0a, 0xfe, 0xfe, 0x01 },
 };
 
 STATIC_SINGLE_NETDEV_ADDRESS ( static_single_ipv4_address );
+
+#warning "Remove this static-IP hack"
+static struct ipv4_route routing_table[NUM_ROUTES] = {
+       { { htonl ( 0x0afefe00 ) }, { htonl ( 0xfffffffc ) },
+         { htonl ( 0x00000000 ) }, { htonl ( 0x0afefe01 ) } },
+       { { htonl ( 0x00000000 ) }, { htonl ( 0x00000000 ) },
+         { htonl ( 0x0afefe02 ) }, { htonl ( 0x0afefe01 ) } },
+};
+
index 3ff6657..59861d0 100644 (file)
@@ -60,6 +60,22 @@ static struct net_address static_single_netdev_addresses_end[0]
 /** Recevied packet queue */
 static LIST_HEAD ( rx_queue );
 
+/**
+ * Add packet to receive queue
+ *
+ * @v netdev           Network device
+ * @v pkb              Packet buffer
+ *
+ * The packet is added to the RX queue.  Ownership of the packet is
+ * transferred to the RX queue; the caller must not touch the packet
+ * buffer after calling netdev_rx().
+ */
+void netdev_rx ( struct net_device *netdev, struct pk_buff *pkb ) {
+       DBG ( "Packet received\n" );
+       pkb->ll_protocol = netdev->ll_protocol;
+       list_add_tail ( &pkb->list, &rx_queue );
+}
+
 /**
  * Identify network protocol
  *
@@ -69,7 +85,7 @@ static LIST_HEAD ( rx_queue );
  * Identify a network-layer protocol from a protocol number, which
  * must be an ETH_P_XXX constant in network-byte order.
  */
-struct net_protocol * net_find_protocol ( uint16_t net_proto ) {
+struct net_protocol * find_net_protocol ( uint16_t net_proto ) {
        struct net_protocol *net_protocol;
 
        for ( net_protocol = net_protocols ; net_protocol < net_protocols_end ;
@@ -93,8 +109,9 @@ struct net_protocol * net_find_protocol ( uint16_t net_proto ) {
  * Note that even with a static single network device, this function
  * can still return NULL.
  */
-struct net_device * net_find_address ( struct net_protocol *net_protocol,
-                                      void *net_addr ) {
+struct net_device *
+find_netdev_by_net_addr ( struct net_protocol *net_protocol,
+                         void *net_addr ) {
        struct net_address *net_address;
        struct net_device *netdev = &static_single_netdev;
        
@@ -106,60 +123,89 @@ struct net_device * net_find_address ( struct net_protocol *net_protocol,
                                net_protocol->net_addr_len ) == 0 ) )
                        return netdev;
        }
+
        return NULL;
 }
 
 /**
- * Transmit packet
+ * Transmit packet via a network device
  *
  * @v pkb              Packet buffer
+ * @v netdev           Network device, or NULL
  * @ret rc             Return status code
  *
- * Transmits the packet via the appropriate network device.  If this
+ * Transmits the packet via the specified network device.  The packet
+ * must begin with a network-layer header, and the @c net_protocol
+ * field must have been filled in.  If @c netdev is NULL, the network
+ * device is identified via the packet contents, if possible.  If this
  * function returns success, it has taken ownership of the packet
  * buffer.
  */
-int net_transmit ( struct pk_buff *pkb ) {
+int net_transmit_via ( struct pk_buff *pkb, struct net_device *netdev ) {
        struct net_protocol *net_protocol;
        struct net_header nethdr;
        struct ll_protocol *ll_protocol;
        struct ll_header llhdr;
-       struct net_device *netdev;
        int rc;
 
        /* Perform network-layer routing */
        net_protocol = pkb->net_protocol;
        nethdr.net_protocol = net_protocol;
-       if ( ( rc = net_protocol->route ( pkb, &nethdr ) ) != 0 )
-               goto err;
+       if ( ( rc = net_protocol->route ( pkb, &nethdr ) ) != 0 ) {
+               DBG ( "Could not route to %s address %s\n",
+                     net_protocol->name,
+                     net_protocol->ntoa ( nethdr.dest_net_addr ) );
+               return rc;
+       }
 
-       /* Identify transmitting network device */
-       netdev = net_find_address ( net_protocol, nethdr.source_net_addr );
-       if ( ! netdev )
-               goto err;
+       /* Identify transmitting network device, if not specified */
+       if ( ! netdev ) {
+               netdev = find_netdev_by_net_addr ( net_protocol,
+                                                  nethdr.source_net_addr );
+               if ( ! netdev ) {
+                       DBG ( "No network device for %s address %s\n",
+                             net_protocol->name,
+                             net_protocol->ntoa ( nethdr.source_net_addr ) );
+                       return -EHOSTUNREACH;
+               }
+       }
 
        /* Perform link-layer routing */
        ll_protocol = netdev->ll_protocol;
        llhdr.ll_protocol = ll_protocol;
-       llhdr.net_proto = net_protocol->net_proto;
-       memcpy ( llhdr.source_ll_addr, netdev->ll_addr,
-                ll_protocol->ll_addr_len);
-       if ( ( rc = ll_protocol->route ( &nethdr, &llhdr ) ) != 0 )
-               goto err;
+       if ( ( rc = ll_protocol->route ( netdev, &nethdr, &llhdr ) ) != 0 ) {
+               DBG ( "No link-layer route to %s address %s\n",
+                     net_protocol->name,
+                     net_protocol->ntoa ( nethdr.dest_net_addr ) );
+               return rc;
+       }
 
        /* Prepend link-layer header */
        pkb_push ( pkb, ll_protocol->ll_header_len );
        ll_protocol->fill_llh ( &llhdr, pkb );
 
        /* Transmit packet */
-       if ( ( rc = netdev->transmit ( netdev, pkb ) ) != 0 )
-               goto err;
-
+       if ( ( rc = netdev->transmit ( netdev, pkb ) ) != 0 ) {
+               DBG ( "Device failed to transmit packet\n" );
+               return rc;
+       }
+       
+       DBG ( "Packet transmitted\n" );
        return 0;
+}
 
- err:
-       free_pkb ( pkb );
-       return rc;
+/**
+ * Transmit packet
+ *
+ * @v pkb              Packet buffer
+ * @ret rc             Return status code
+ *
+ * Transmits the packet via the appropriate network device.  If this
+ * function returns success, it has taken ownership of the packet
+ * buffer.
+ */
+int net_transmit ( struct pk_buff *pkb ) {
+       return net_transmit_via ( pkb, NULL );
 }
 
 /**
@@ -174,26 +220,12 @@ int net_transmit ( struct pk_buff *pkb ) {
 int net_poll ( void ) {
        struct net_device *netdev = &static_single_netdev;
 
+       DBG ( "Polling network\n" );
        netdev->poll ( netdev );
 
        return ( ! list_empty ( &rx_queue ) );
 }
 
-/**
- * Add packet to receive queue
- *
- * @v netdev           Network device
- * @v pkb              Packet buffer
- *
- * The packet is added to the RX queue.  Ownership of the packet is
- * transferred to the RX queue; the caller must not touch the packet
- * buffer after calling netdev_rx().
- */
-void netdev_rx ( struct net_device *netdev, struct pk_buff *pkb ) {
-       pkb->ll_protocol = netdev->ll_protocol;
-       list_add_tail ( &pkb->list, &rx_queue );
-}
-
 /**
  * Remove packet from receive queue
  *
@@ -225,22 +257,26 @@ void net_run ( void ) {
                ll_protocol->parse_llh ( pkb, &llhdr );
 
                /* Identify network-layer protocol */
-               net_protocol = net_find_protocol ( llhdr.net_proto );
+               net_protocol = find_net_protocol ( llhdr.net_proto );
                if ( ! net_protocol ) {
-                       DBG ( "Unknown network-layer protocol %02x\n",
+                       DBG ( "Unknown network-layer protocol %x\n",
                              ntohs ( llhdr.net_proto ) );
                        free_pkb ( pkb );
                        continue;
                }
+               pkb->net_protocol = net_protocol;
 
                /* Strip off link-layer header */
                pkb_pull ( pkb, ll_protocol->ll_header_len );
 
                /* Hand off to network layer */
                if ( net_protocol->rx ( pkb ) != 0 ) {
+                       DBG ( "Network-layer protocol refused packet\n" );
                        free_pkb ( pkb );
                        continue;
                }
+
+               DBG ( "Processed received packet\n" );
        }
 }