Network API now allows for multiple network devices (although the
authorMichael Brown <mcb30@etherboot.org>
Mon, 24 Apr 2006 15:33:06 +0000 (15:33 +0000)
committerMichael Brown <mcb30@etherboot.org>
Mon, 24 Apr 2006 15:33:06 +0000 (15:33 +0000)
implementation allows for only one, and does so without compromising on
the efficiency of static allocation).

Link-layer protocols are cleanly separated from the device drivers.

Network-layer protocols are cleanly separated from individual network
devices.

Link-layer and network-layer protocols are cleanly separated from each
other.

src/drivers/net/pnic.c
src/include/gpxe/arp.h
src/include/gpxe/netdevice.h
src/include/gpxe/pkbuff.h
src/net/arp.c
src/net/ethernet.c
src/net/netdevice.c
src/proto/uip/uipopt.h

index 047bc22..243235a 100644 (file)
@@ -12,21 +12,21 @@ Bochs Pseudo NIC driver for Etherboot
  * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
  */
 
-/* to get some global routines like printf */
-#include "etherboot.h"
-/* to get the interface to the body of the program */
-#include "nic.h"
-/* to get the PCI support functions, if this is a PCI NIC */
-#include "pci.h"
-
-/* PNIC API */
-#include "pnic_api.h"
+#include <stdint.h>
+#include <io.h>
+#include <vsprintf.h>
+#include <errno.h>
+#include <gpxe/pci.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/pkbuff.h>
+#include <gpxe/netdevice.h>
 
-/* Function prototypes */
-static int pnic_api_check ( uint16_t api_version );
+#include "pnic_api.h"
 
-/* NIC specific static variables go here */
-uint8_t tx_buffer[ETH_FRAME_LEN] __shared;
+struct pnic {
+       unsigned short ioaddr;
+};
 
 /* 
  * Utility functions: issue a PNIC command, retrieve result.  Use
@@ -40,7 +40,7 @@ uint8_t tx_buffer[ETH_FRAME_LEN] __shared;
  * of data).
  */
 
-static uint16_t pnic_command_quiet ( struct nic *nic, uint16_t command,
+static uint16_t pnic_command_quiet ( struct pnic *pnic, uint16_t command,
                                     void *input, uint16_t input_length,
                                     void *output, uint16_t output_max_length,
                                     uint16_t *output_length ) {
@@ -50,18 +50,19 @@ static uint16_t pnic_command_quiet ( struct nic *nic, uint16_t command,
 
        if ( input != NULL ) {
                /* Write input length */
-               outw ( input_length, nic->ioaddr + PNIC_REG_LEN );
+               outw ( input_length, pnic->ioaddr + PNIC_REG_LEN );
                /* Write input data */
                for ( i = 0; i < input_length; i++ ) {
-                       outb( ((char*)input)[i], nic->ioaddr + PNIC_REG_DATA );
+                       outb( ((char*)input)[i],
+                             pnic->ioaddr + PNIC_REG_DATA );
                }
        }
        /* Write command */
-       outw ( command, nic->ioaddr + PNIC_REG_CMD );
+       outw ( command, pnic->ioaddr + PNIC_REG_CMD );
        /* Retrieve status */
-       status = inw ( nic->ioaddr + PNIC_REG_STAT );
+       status = inw ( pnic->ioaddr + PNIC_REG_STAT );
        /* Retrieve output length */
-       _output_length = inw ( nic->ioaddr + PNIC_REG_LEN );
+       _output_length = inw ( pnic->ioaddr + PNIC_REG_LEN );
        if ( output_length == NULL ) {
                if ( _output_length != output_max_length ) {
                        printf ( "pnic_command %#hx: wrong data length "
@@ -81,17 +82,17 @@ static uint16_t pnic_command_quiet ( struct nic *nic, uint16_t command,
                /* Retrieve output data */
                for ( i = 0; i < _output_length; i++ ) {
                        ((char*)output)[i] =
-                               inb ( nic->ioaddr + PNIC_REG_DATA );
+                               inb ( pnic->ioaddr + PNIC_REG_DATA );
                }
        }
        return status;
 }
 
-static uint16_t pnic_command ( struct nic *nic, uint16_t command,
+static uint16_t pnic_command ( struct pnic *pnic, uint16_t command,
                               void *input, uint16_t input_length,
                               void *output, uint16_t output_max_length,
                               uint16_t *output_length ) {
-       uint16_t status = pnic_command_quiet ( nic, command,
+       uint16_t status = pnic_command_quiet ( pnic, command,
                                               input, input_length,
                                               output, output_max_length,
                                               output_length );
@@ -110,135 +111,134 @@ static int pnic_api_check ( uint16_t api_version ) {
                         PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff );
        }
        if ( api_version < PNIC_API_VERSION ) {
-               printf ( "*** You may need to update your copy of Bochs ***\n" );
+               printf ( "** You may need to update your copy of Bochs **\n" );
        }
        return ( api_version == PNIC_API_VERSION );
 }
 
-/**************************************************************************
-CONNECT - connect adapter to the network
-***************************************************************************/
-static int pnic_connect ( struct nic *nic __unused ) {
-       /* Nothing to do */
-       return 1;
-}
-
 /**************************************************************************
 POLL - Wait for a frame
 ***************************************************************************/
-static int pnic_poll ( struct nic *nic, int retrieve ) {
+static void pnic_poll ( struct net_device *netdev ) {
+       struct pnic *pnic = netdev->priv;
+       struct pk_buff *pkb;
        uint16_t length;
        uint16_t qlen;
 
-       /* Check receive queue length to see if there's anything to
-        * get.  Necessary since once we've called PNIC_CMD_RECV we
-        * have to read out the packet, otherwise it's lost forever.
-        */
-       if ( pnic_command ( nic, PNIC_CMD_RECV_QLEN, NULL, 0,
-                           &qlen, sizeof(qlen), NULL )
-            != PNIC_STATUS_OK ) return ( 0 );
-       if ( qlen == 0 ) return ( 0 );
-
-       /* There is a packet ready.  Return 1 if we're only checking. */
-       if ( ! retrieve ) return ( 1 );
-
-       /* Retrieve the packet */
-       if ( pnic_command ( nic, PNIC_CMD_RECV, NULL, 0,
-                           nic->packet, ETH_FRAME_LEN, &length )
-            != PNIC_STATUS_OK ) return ( 0 );
-       nic->packetlen = length;
-       return ( 1 );
+       /* Fetch all available packets */
+       while ( 1 ) {
+               if ( pnic_command ( pnic, PNIC_CMD_RECV_QLEN, NULL, 0,
+                                   &qlen, sizeof ( qlen ), NULL )
+                    != PNIC_STATUS_OK )
+                       break;
+               if ( qlen == 0 )
+                       break;
+               pkb = alloc_pkb ( ETH_FRAME_LEN );
+               if ( ! pkb )
+                       break;
+               if ( pnic_command ( pnic, PNIC_CMD_RECV, NULL, 0,
+                                   pkb->data, ETH_FRAME_LEN, &length )
+                    != PNIC_STATUS_OK ) {
+                       free_pkb ( pkb );
+                       break;
+               }
+               pkb_put ( pkb, length );
+               netdev_rx ( netdev, pkb );
+       }
 }
 
 /**************************************************************************
 TRANSMIT - Transmit a frame
 ***************************************************************************/
-static void pnic_transmit ( struct nic *nic, const char *dest,
-                           unsigned int type, unsigned int size,
-                           const char *data ) {
-       unsigned int nstype = htons ( type );
-
-       if ( ( ETH_HLEN + size ) >= ETH_FRAME_LEN ) {
-               printf ( "pnic_transmit: packet too large\n" );
-               return;
-       }
+static int pnic_transmit ( struct net_device *netdev, struct pk_buff *pkb ) {
+       struct pnic *pnic = netdev->priv;
 
-       /* Assemble packet */
-       memcpy ( tx_buffer, dest, ETH_ALEN );
-       memcpy ( tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN );
-       memcpy ( tx_buffer + 2 * ETH_ALEN, &nstype, 2 );
-       memcpy ( tx_buffer + ETH_HLEN, data, size );
-
-       pnic_command ( nic, PNIC_CMD_XMIT, tx_buffer, ETH_HLEN + size,
+       pnic_command ( pnic, PNIC_CMD_XMIT, pkb, pkb_len ( pkb ),
                       NULL, 0, NULL );
-}
-
-/**************************************************************************
-DISABLE - Turn off ethernet interface
-***************************************************************************/
-static void pnic_disable ( struct nic *nic, struct pci_device *pci __unused ) {
-       nic_disable ( nic );
-       pnic_command ( nic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
+       free_pkb ( pkb );
+       return 0;
 }
 
 /**************************************************************************
 IRQ - Handle card interrupt status
 ***************************************************************************/
-static void pnic_irq ( struct nic *nic, irq_action_t action ) {
+#if 0
+static void pnic_irq ( struct net_device *netdev, irq_action_t action ) {
+       struct pnic *pnic = netdev->priv;
        uint8_t enabled;
 
        switch ( action ) {
        case DISABLE :
        case ENABLE :
                enabled = ( action == ENABLE ? 1 : 0 );
-               pnic_command ( nic, PNIC_CMD_MASK_IRQ,
-                              &enabled, sizeof(enabled), NULL, 0, NULL );
+               pnic_command ( pnic, PNIC_CMD_MASK_IRQ,
+                              &enabled, sizeof ( enabled ), NULL, 0, NULL );
                break;
        case FORCE :
-               pnic_command ( nic, PNIC_CMD_FORCE_IRQ,
+               pnic_command ( pnic, PNIC_CMD_FORCE_IRQ,
                               NULL, 0, NULL, 0, NULL );
                break;
        }
 }
+#endif
 
 /**************************************************************************
-NIC operations table
+DISABLE - Turn off ethernet interface
 ***************************************************************************/
-static struct nic_operations pnic_operations = {
-       .connect        = pnic_connect,
-       .poll           = pnic_poll,
-       .transmit       = pnic_transmit,
-       .irq            = pnic_irq,
-};
+static void pnic_remove ( struct pci_device *pci ) {
+       struct net_device *netdev = pci_get_drvdata ( pci );
+       struct pnic *pnic = netdev->priv;
+
+       unregister_netdev ( netdev );
+       pnic_command ( pnic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
+}
 
 /**************************************************************************
 PROBE - Look for an adapter, this routine's visible to the outside
 ***************************************************************************/
-static int pnic_probe ( struct nic *nic, struct pci_device *pci ) {
+static int pnic_probe ( struct pci_device *pci ) {
+       struct net_device *netdev;
+       struct pnic *pnic;
        uint16_t api_version;
        uint16_t status;
+       int rc;
 
-       /* Retrieve relevant information about PCI device */
-       pci_fill_nic ( nic, pci );
+       /* Allocate net device */
+       netdev = alloc_etherdev ( sizeof ( *pnic ) );
+       if ( ! netdev ) {
+               rc = -ENOMEM;
+               goto err;
+       }
+       pnic = netdev->priv;
+       pci_set_drvdata ( pci, netdev );
+       pnic->ioaddr = pci->ioaddr;
 
        /* API version check */
-       status = pnic_command_quietnic, PNIC_CMD_API_VER, NULL, 0,
-                                    &api_version,
-                                    sizeof(api_version), NULL );
+       status = pnic_command_quiet ( pnic, PNIC_CMD_API_VER, NULL, 0,
+                                     &api_version,
+                                     sizeof ( api_version ), NULL );
        if ( status != PNIC_STATUS_OK ) {
                printf ( "PNIC failed installation check, code %#hx\n",
                         status );
-               return 0;
+               rc = -EIO;
+               goto err;
        }
-       pnic_api_check(api_version);
+       pnic_api_check ( api_version );
 
        /* Get MAC address */
-       status = pnic_command ( nic, PNIC_CMD_READ_MAC, NULL, 0,
-                               nic->node_addr, ETH_ALEN, NULL );
+       status = pnic_command ( pnic, PNIC_CMD_READ_MAC, NULL, 0,
+                               netdev->ll_addr, ETH_ALEN, NULL );
+
+       /* Point to NIC specific routines */
+       netdev->poll     = pnic_poll;
+       netdev->transmit = pnic_transmit;
+
+       return 0;
 
-       /* point to NIC specific routines */
-       nic->nic_op     = &pnic_operations;
-       return 1;
+ err:
+       /* Free net device */
+       free_netdev ( netdev );
+       return rc;
 }
 
 static struct pci_id pnic_nics[] = {
@@ -246,7 +246,18 @@ static struct pci_id pnic_nics[] = {
 PCI_ROM ( 0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor" ),
 };
 
-PCI_DRIVER ( pnic_driver, pnic_nics, PCI_NO_CLASS );
+static struct pci_driver pnic_driver = {
+       .ids = pnic_nics,
+       .id_count = ( sizeof ( pnic_nics ) / sizeof ( pnic_nics[0] ) ),
+       .class = PCI_NO_CLASS,
+       //      .probe = pnic_probe,
+       //      .remove = pnic_remove,
+};
+
+// PCI_DRIVER ( pnic_driver );
+
+#include "dev.h"
+extern struct type_driver test_driver;
 
-DRIVER ( "PNIC", nic_driver, pci_driver, pnic_driver,
-        pnic_probe, pnic_disable );
+DRIVER ( "PNIC", test_driver, pci_driver, pnic_driver,
+        pnic_probe, pnic_remove );
index f6ec47a..a2db80f 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _ARP_H
-#define _ARP_H
+#ifndef _GPXE_ARP_H
+#define _GPXE_ARP_H
 
 /** @file
  *
@@ -7,17 +7,10 @@
  *
  */
 
-struct net_device;
-struct net_interface;
-struct pk_buff;
+struct net_header;
+struct ll_header;
 
-extern int arp_resolve ( struct net_device *netdev, struct pk_buff *pkb,
-                        const void **ll_addr );
+extern int arp_resolve ( const struct net_header *nethdr,
+                        struct ll_header *llhdr );
 
-extern int arp_process ( struct net_interface *arp_netif,
-                        struct pk_buff *pkb );
-
-extern int arp_add_generic_header ( struct net_interface *arp_netif,
-                                   struct pk_buff *pkb );
-
-#endif /* _ARP_H */
+#endif /* _GPXE_ARP_H */
index 93a9863..f7d7828 100644 (file)
-#ifndef _NETDEVICE_H
-#define _NETDEVICE_H
+#ifndef _GPXE_NETDEVICE_H
+#define _GPXE_NETDEVICE_H
 
 /** @file
  *
- * Network device and network interface
+ * Network device management
  *
  */
 
 #include <stdint.h>
-#include <gpxe/list.h>
+#include <gpxe/tables.h>
 
-struct net_device;
-struct net_interface;
 struct pk_buff;
+struct net_protocol;
+struct ll_protocol;
 
 /** Maximum length of a link-layer address */
-#define MAX_LLH_ADDR_LEN 6
+#define MAX_LL_ADDR_LEN 6
 
 /** Maximum length of a network-layer address */
 #define MAX_NET_ADDR_LEN 4
 
+/* Network-layer address may be required to hold a link-layer address
+ * (if NETADDR_FL_RAW is set
+ */
+#if MAX_NET_ADDR_LEN < MAX_LL_ADDR_LEN
+#undef MAX_NET_ADDR_LEN
+#define MAX_NET_ADDR_LEN MAX_LL_ADDR_LEN
+#endif
+
+/** A generic network-layer header */
+struct net_header {
+       /** Network-layer protocol */
+       struct net_protocol *net_protocol;
+       /** Destination address flags
+        *
+        * This is the bitwise OR of zero or more NETADDR_FL_XXX
+        * values.
+        */
+       int dest_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
+
+/** Address is a multicast address */
+#define NETADDR_FL_MULTICAST 0x02
+
+/** Address is a raw hardware address */
+#define NETADDR_FL_RAW 0x04
+
+/** A generic link-layer header */
+struct ll_header {
+       /** Link-layer protocol */
+       struct ll_protocol *ll_protocol;
+       /** Destination address flags
+        *
+        * This is the bitwise OR of zero or more NETADDR_FL_XXX
+        * values.
+        */
+       int dest_flags;
+       /** Link-layer destination address */
+       uint8_t dest_ll_addr[MAX_LL_ADDR_LEN];
+       /** Link-layer source address */
+       uint8_t source_ll_addr[MAX_LL_ADDR_LEN];
+       /** Network-layer protocol
+        *
+        *
+        * This is an ETH_P_XXX constant, in network-byte order
+        */
+       uint16_t net_proto;
+};
+
 /**
- * A network device
- *
- * This structure represents a piece of networking hardware.  It has
- * properties such as a link-layer address and methods for
- * transmitting and receiving raw packets.  It does not know anything
- * about network-layer protocols (e.g. IP) or their addresses; these
- * are handled by struct @c net_interface instead.
+ * A network-layer protocol
  *
- * Note that this structure must represent a generic network device,
- * not just an Ethernet device.
  */
-struct net_device {
-       /** Transmit packet
+struct net_protocol {
+       /**
+        * Perform network-layer routing
         *
-        * @v netdev    Network device
         * @v pkb       Packet buffer
+        * @ret source  Network-layer source address
+        * @ret dest    Network-layer destination address
         * @ret rc      Return status code
         *
-        * This method should cause the hardware to initiate
-        * transmission of the packet buffer.  The buffer may be
-        * reused immediately after the method returns, and so the
-        * method should either wait for packet transmission to
-        * complete, or take a copy of the buffer contents.
+        * This method should fill in the source and destination
+        * addresses 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).
         */
-       int ( * transmit ) ( struct net_device *netdev,
-                            struct pk_buff *pkb );
-       /** Poll for received packet
+       int ( * route ) ( const struct pk_buff *pkb,
+                         struct net_header *nethdr );
+       /**
+        * Handle received packets
         *
-        * @v netdev    Network device
-        * @v pkb       Packet buffer to contain received packet
+        * @v pkb       Packet buffer
         * @ret rc      Return status code
         *
-        * This method should cause the hardware to check for a
-        * received packet.  If no packet is available, the method
-        * should return -EAGAIN (i.e. this method is *always*
-        * considered to be a non-blocking read).  If a packet is
-        * available, the method should fill the packet buffer and
-        * return zero for success.
+        * If this method returns success, it has taken ownership of
+        * the packet buffer.
         */
-       int ( * poll ) ( struct net_device *netdev, struct pk_buff *pkb );
-       /** Build link-layer header
+       int ( * rx ) ( struct pk_buff *pkb );
+       /** Network-layer protocol
         *
-        * @v netdev    Network device
-        * @v pkb       Packet buffer
+        * This is an ETH_P_XXX constant, in network-byte order
+        */
+       uint16_t net_proto;
+       /** Network-layer address length */
+       uint8_t net_addr_len;
+};
+
+/**
+ * A link-layer protocol
+ *
+ */
+struct ll_protocol {
+       /**
+        * Perform link-layer routing
+        *
+        * @v nethdr    Generic network-layer header
+        * @ret llhdr   Generic link-layer header
         * @ret rc      Return status code
         *
-        * This method should fill in the link-layer header based on
-        * the metadata contained in @c pkb.
+        * This method should construct the generic link-layer header
+        * based on the generic network-layer header.
         *
         * If a link-layer header cannot be constructed (e.g. because
         * of a missing ARP cache entry), then this method should
         * return an error (after transmitting an ARP request, if
         * applicable).
         */
-       int ( * build_llh ) ( struct net_device *netdev, struct pk_buff *pkb );
-       /** Parse link-layer header
+       int ( * route ) ( const struct net_header *nethdr,
+                         struct ll_header *llhdr );
+       /**
+        * Fill media-specific link-layer header
         *
-        * @v netdev    Network device
+        * @v llhdr     Generic link-layer header
         * @v pkb       Packet buffer
-        * @ret rc      Return status code
         *
-        * This method should parse the link-layer header and fill in
-        * the metadata in @c pkb.
+        * This method should fill in the link-layer header in the
+        * packet buffer based on information in the generic
+        * link-layer header.
+        */
+       void ( * fill_llh ) ( const struct ll_header *llhdr,
+                             struct pk_buff *pkb );
+       /**
+        * Parse media-specific link-layer header
+        *
+        * @v pkb       Packet buffer
+        * @v llhdr     Generic link-layer header
+        *
+        * This method should fill in the generic link-layer header
+        * based on information in the link-layer header in the packet
+        * buffer.
         */
-       int ( * parse_llh ) ( struct net_device *netdev, struct pk_buff *pkb );
+       void ( * parse_llh ) ( const struct pk_buff *pkb,
+                              struct ll_header *llhdr );
+
        /** Link-layer protocol
         *
         * This is an ARPHRD_XXX constant, in network byte order.
         */
        uint16_t ll_proto;
-       /** Link-layer header length */
-       uint8_t ll_hlen;
        /** Link-layer address length */
        uint8_t ll_addr_len;
-       /** Link-layer address
-        *
-        * For Ethernet, this is the MAC address.
-        */
-       uint8_t ll_addr[MAX_LLH_ADDR_LEN];
-       /** Linked list of network devices */
-       struct list_head devices;
-       /** List of network interfaces */
-       struct list_head interfaces;
-       /** Driver private data */
-       void *priv;
+       /** Link-layer header length */
+       uint8_t ll_header_len;
 };
 
 /**
- * A network interface
- *
- * This structure represents a particular network layer protocol's
- * interface to a piece of network hardware (a struct @c net_device).
+ * A network-layer address assigned to a network device
  *
  */
-struct net_interface {
-       /** Underlying net device */
-       struct net_device *netdev;
-       /** Linked list of interfaces for this device */
-       struct list_head interfaces;
-       /** Network-layer protocol
-        *
-        * This is an ETH_P_XXX constant, in network byte order.
-        */
-       uint16_t net_proto;
-       /** Network-layer address length */
-       uint8_t net_addr_len;
+struct net_address {
+       /** Network-layer protocol */
+       struct net_protocol *net_protocol;
        /** Network-layer address */
        uint8_t net_addr[MAX_NET_ADDR_LEN];
-       /** Fill in packet metadata
+};
+
+/**
+ * A network device
+ *
+ * This structure represents a piece of networking hardware.  It has
+ * properties such as a link-layer address and methods for
+ * transmitting and receiving raw packets.
+ *
+ * Note that this structure must represent a generic network device,
+ * not just an Ethernet device.
+ */
+struct net_device {
+       /** Transmit packet
         *
-        * @v netif     Network interface
+        * @v netdev    Network device
         * @v pkb       Packet buffer
         * @ret rc      Return status code
         *
-        * This method should fill in the @c pkb metadata with enough
-        * information to enable net_device::build_llh to construct
-        * the link-layer header.
+        * This method should cause the hardware to initiate
+        * transmission of the packet buffer.
+        *
+        * If the method returns success, ownership of the packet
+        * buffer is transferred to the @c net_device, which must
+        * eventually call free_pkb() to release the buffer.
         */
-       int ( * add_llh_metadata ) ( struct net_interface *netif,
-                                    struct pk_buff *pkb );
-       /** Received packet processor
+       int ( * transmit ) ( struct net_device *netdev, struct pk_buff *pkb );
+       /** Poll for received packet
         *
-        * @v netif     Network interface
-        * @v pkb       Packet buffer
-        * @ret rc      Return status code
+        * @v netdev    Network device
         *
-        * This method is called for packets arriving on the
-        * associated network device that match this interface's
-        * network-layer protocol.
+        * This method should cause the hardware to check for received
+        * packets.  Any received packets should be delivered via
+        * netdev_rx().
+        */
+       void ( * poll ) ( struct net_device *netdev );
+
+       /** Link-layer protocol */
+       struct ll_protocol *ll_protocol;
+       /** Link-layer address
         *
-        * When this method is called, the link-layer header will
-        * already have been stripped from the packet.
+        * For Ethernet, this is the MAC address.
         */
-       int ( * rx_packet ) ( struct net_interface *netif,
-                             struct pk_buff *pkb );
+       uint8_t ll_addr[MAX_LL_ADDR_LEN];
+
+       /** Driver private data */
+       void *priv;
 };
 
+extern struct net_device static_single_netdev;
+
+/**
+ * Allocate network device
+ *
+ * @v priv_size                Size of private data area (net_device::priv)
+ * @ret netdev         Network device, or NULL
+ *
+ * Allocates space for a network device and its private data area.
+ *
+ * This macro allows for a very efficient implementation in the case
+ * of a single static network device; it neatly avoids dynamic
+ * allocation and can never return failure, meaning that the failure
+ * path will be optimised away.  However, driver writers should not
+ * rely on this feature; the drivers should be written to allow for
+ * multiple instances of network devices.
+ */
+#define alloc_netdev( priv_size ) ( {          \
+       static char priv_data[priv_size];       \
+       static_single_netdev.priv = priv_data;  \
+       &static_single_netdev; } )
+
 /**
- * Find interface for a specific protocol
+ * Register network device
  *
- * @v netdev   Network device
- * @v net_proto        Network-layer protocol, in network byte order
- * @ret netif  Network interface, or NULL if none found
+ * @v netdev           Network device
+ * @ret rc             Return status code
  *
+ * Adds the network device to the list of network devices.
  */
-static inline struct net_interface *
-netdev_find_netif ( const struct net_device *netdev, uint16_t net_proto ) {
-       struct net_interface *netif;
-
-       list_for_each_entry ( netif, &netdev->interfaces, interfaces ) {
-               if ( netif->net_proto == net_proto )
-                       return netif;
-       }
-       return NULL;
+static inline int
+register_netdev ( struct net_device *netdev __attribute__ (( unused )) ) {
+       return 0;
 }
 
-extern int register_netdevice ( struct net_device *netdev );
-extern void unregister_netdevice ( struct net_device *netdev );
-extern int netdev_send ( struct net_device *netdev, struct pk_buff *pkb );
-extern int netdev_poll ( struct net_device *netdev, struct pk_buff *pkb );
-extern int netif_send ( struct net_interface *netif, struct pk_buff *pkb );
-extern int netdev_rx_packet ( struct net_device *netdev, struct pk_buff *pkb );
-extern int net_poll ( struct pk_buff *pkb, struct net_device **netdev );
+/**
+ * Unregister network device
+ *
+ * @v netdev           Network device
+ *
+ * Removes the network device from the list of network devices.
+ */
+static inline void 
+unregister_netdev ( struct net_device *netdev __attribute__ (( unused )) ) {
+       /* Nothing to do */
+}
 
+/**
+ * Free network device
+ *
+ * @v netdev           Network device
+ */
+static inline void
+free_netdev ( struct net_device *netdev __attribute__ (( unused )) ) {
+       /* Nothing to do */
+}
 
+/**
+ * Register a link-layer protocol
+ *
+ * @v protocol         Link-layer protocol
+ */
+#define LL_PROTOCOL( protocol ) \
+       struct ll_protocol protocol __table ( ll_protocols, 00 )
 
-extern struct net_device static_single_netdev;
+/**
+ * Register a network-layer protocol
+ *
+ * @v protocol         Network-layer protocol
+ */
+#define NET_PROTOCOL( protocol ) \
+       struct net_protocol protocol __table ( net_protocols, 00 )
 
-/* Must be a macro because priv_data[] is of variable size */
-#define alloc_netdevice( priv_size ) ( {               \
-       static char priv_data[priv_size];               \
-       static_single_netdev.priv = priv_data;  \
-       &static_single_netdev; } )
+/**
+ * Register a network-layer address for the static single network device
+ *
+ * @v net_address      Network-layer address
+ */
+#define STATIC_SINGLE_NETDEV_ADDRESS( address ) \
+       struct net_address address __table ( sgl_netdev_addresses, 00 )
 
+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 );
 
-static inline void free_netdevice ( struct net_device *netdev __unused ) {
-       /* Do nothing */
-}
+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 /* _NETDEVICE_H */
+#endif /* _GPXE_NETDEVICE_H */
index f82f508..2e3980a 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _PKBUFF_H
-#define _PKBUFF_H
+#ifndef _GPXE_PKBUFF_H
+#define _GPXE_PKBUFF_H
 
 /** @file
  *
 
 #include <stdint.h>
 #include <assert.h>
+#include <gpxe/list.h>
+
+struct net_protocol;
+struct ll_protocol;
 
 /** A packet buffer
  *
@@ -27,38 +31,14 @@ struct pk_buff {
        /** End of the buffer */
         void *end;
 
-       /** The network-layer protocol
-        *
-        * This is the network-layer protocol expressed as an
-        * ETH_P_XXX constant, in network-byte order.
-        */
-       uint16_t net_proto;
-       /** Flags
-        *
-        * Filled in only on outgoing packets.  Value is the
-        * bitwise-OR of zero or more PKB_FL_XXX constants.
-        */
-       uint8_t flags;
-       /** Network-layer address length 
-        *
-        * Filled in only on outgoing packets.
-        */
-       uint8_t net_addr_len;
-       /** Network-layer address
-        *
-        * Filled in only on outgoing packets.
-        */
-       void *net_addr;
-};
-
-/** Packet is a broadcast packet */
-#define PKB_FL_BROADCAST 0x01
+       /** List of which this buffer is a member */
+       struct list_head list;
 
-/** Packet is a multicast packet */
-#define PKB_FL_MULTICAST 0x02
-
-/** Network-layer address is a raw hardware address */
-#define PKB_FL_RAW_NET_ADDR 0x04
+       /** The network-layer protocol */
+       struct net_protocol *net_protocol;
+       /** The link-layer protocol */
+       struct ll_protocol *ll_protocol;
+};
 
 /**
  * Add data to start of packet buffer
@@ -130,4 +110,7 @@ static inline size_t pkb_len ( struct pk_buff *pkb ) {
        return ( pkb->tail - pkb->data );
 }
 
-#endif /* _PKBUFF_H */
+extern struct pk_buff * alloc_pkb ( size_t len );
+extern void free_pkb ( struct pk_buff *pkb );
+
+#endif /* _GPXE_PKBUFF_H */
index bee7f57..7b8eede 100644 (file)
 /** An ARP cache entry */
 struct arp_entry {
        /** Network-layer protocol */
-       uint16_t net_proto;
+       struct net_protocol *net_protocol;
        /** Link-layer protocol */
-       uint16_t ll_proto;
+       struct ll_protocol *ll_protocol;
        /** Network-layer address */
        uint8_t net_addr[MAX_NET_ADDR_LEN];
        /** Link-layer address */
-       uint8_t ll_addr[MAX_LLH_ADDR_LEN];
+       uint8_t ll_addr[MAX_LL_ADDR_LEN];
 };
 
 /** Number of entries in the ARP cache
@@ -61,25 +61,28 @@ static struct arp_entry arp_table[NUM_ARP_ENTRIES];
 
 static unsigned int next_new_arp_entry = 0;
 
+struct net_protocol arp_protocol;
+
 /**
  * Find entry in the ARP cache
  *
- * @v ll_proto         Link-layer protocol
- * @v net_proto                Network-layer protocol
+ * @v ll_protocol      Link-layer protocol
+ * @v net_protocol     Network-layer protocol
  * @v net_addr         Network-layer address
- * @v net_addr_len     Network-layer address length
  * @ret arp            ARP cache entry, or NULL if not found
  *
  */
 static struct arp_entry *
-arp_find_entry ( uint16_t ll_proto, uint16_t net_proto, const void *net_addr,
-                size_t net_addr_len ) {
+arp_find_entry ( struct ll_protocol *ll_protocol,
+                struct net_protocol *net_protocol,
+                const void *net_addr ) {
        struct arp_entry *arp;
 
        for ( arp = arp_table ; arp < arp_table_end ; arp++ ) {
-               if ( ( arp->ll_proto == ll_proto ) &&
-                    ( arp->net_proto == net_proto ) &&
-                    ( memcmp ( arp->net_addr, net_addr, net_addr_len ) == 0 ))
+               if ( ( arp->ll_protocol == ll_protocol ) &&
+                    ( arp->net_protocol == net_protocol ) &&
+                    ( memcmp ( arp->net_addr, net_addr,
+                               net_protocol->net_addr_len ) == 0 ) )
                        return arp;
        }
        return NULL;
@@ -88,59 +91,64 @@ arp_find_entry ( uint16_t ll_proto, uint16_t net_proto, const void *net_addr,
 /**
  * Look up media-specific link-layer address in the ARP cache
  *
- * @v netdev           Network device
- * @v pkb              Packet buffer
- * @ret ll_addr                Pointer to link-layer address
+ * @v nethdr           Generic network-layer header
+ * @ret llhdr          Generic link-layer header
  * @ret rc             Return status code
  *
  * This function will use the ARP cache to look up the link-layer
- * address for the media corresponding to @c netdev and the
- * network-layer address as specified in the @c pkb metadata.
+ * address for the link-layer protocol specified in @c llhdr and the
+ * network-layer protocol and address as specified in @c nethdr.  If
+ * found, the destination link-layer address will be filled in in @c
+ * llhdr.
  *
  * If no address is found in the ARP cache, an ARP request will be
- * transmitted, -ENOENT will be returned, and the packet buffer
- * contents will be undefined.
+ * transmitted and -ENOENT will be returned.
  */
-int arp_resolve ( struct net_device *netdev, struct pk_buff *pkb,
-                 const void **ll_addr ) {
+int arp_resolve ( 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;
-       struct net_interface *netif;
+       struct pk_buff *pkb;
        struct arphdr *arphdr;
+       int rc;
 
        /* Look for existing entry in ARP table */
-       arp = arp_find_entry ( netdev->ll_proto, pkb->net_proto,
-                              pkb->net_addr, pkb->net_addr_len );
+       arp = arp_find_entry ( ll_protocol, net_protocol,
+                              nethdr->dest_net_addr );
        if ( arp ) {
-               *ll_addr = arp->ll_addr;
+               memcpy ( llhdr->dest_ll_addr, arp->ll_addr,
+                        sizeof ( llhdr->dest_ll_addr ) );
                return 0;
        }
 
-       /* Find interface for this protocol */
-       netif = netdev_find_netif ( netdev, pkb->net_proto );
-       if ( ! netif )
-               return -EAFNOSUPPORT;
+       /* Allocate ARP packet */
+       pkb = alloc_pkb ( sizeof ( *arphdr ) +
+                         2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) );
+       if ( ! pkb )
+               return -ENOMEM;
+       pkb->net_protocol = &arp_protocol;
 
        /* Build up ARP request */
-       pkb_empty ( pkb );
        arphdr = pkb_put ( pkb, sizeof ( *arphdr ) );
-       arphdr->ar_hrd = netdev->ll_proto;
-       arphdr->ar_hln = netdev->ll_addr_len;
-       arphdr->ar_pro = pkb->net_proto;
-       arphdr->ar_pln = pkb->net_addr_len;
+       arphdr->ar_hrd = ll_protocol->ll_proto;
+       arphdr->ar_hln = ll_protocol->ll_addr_len;
+       arphdr->ar_pro = net_protocol->net_proto;
+       arphdr->ar_pln = net_protocol->net_addr_len;
        arphdr->ar_op = htons ( ARPOP_REQUEST );
-       memcpy ( pkb_put ( pkb, netdev->ll_addr_len ),
-                netdev->ll_addr, netdev->ll_addr_len );
-       memcpy ( pkb_put ( pkb, netif->net_addr_len ),
-                netif->net_addr, netif->net_addr_len );
-       memset ( pkb_put ( pkb, netdev->ll_addr_len ),
-                0xff, netdev->ll_addr_len );
-       memcpy ( pkb_put ( pkb, netif->net_addr_len ),
-                pkb->net_addr, netif->net_addr_len );
-
-       /* Locate ARP interface and send ARP request */
-       netif = netdev_find_netif ( netdev, htons ( ETH_P_ARP ) );
-       assert ( netif != NULL );
-       netif_send ( netif, pkb );
+       memcpy ( pkb_put ( pkb, ll_protocol->ll_addr_len ),
+                llhdr->source_ll_addr, ll_protocol->ll_addr_len );
+       memcpy ( pkb_put ( pkb, net_protocol->net_addr_len ),
+                nethdr->source_net_addr, net_protocol->net_addr_len );
+       memset ( pkb_put ( pkb, ll_protocol->ll_addr_len ),
+                0, ll_protocol->ll_addr_len );
+       memcpy ( pkb_put ( pkb, net_protocol->net_addr_len ),
+                nethdr->dest_net_addr, net_protocol->net_addr_len );
+
+       /* Transmit ARP request */
+       if ( ( rc = net_transmit ( pkb ) ) != 0 ) {
+               free_pkb ( pkb );
+               return rc;
+       }
 
        return -ENOENT;
 }
@@ -148,7 +156,6 @@ int arp_resolve ( struct net_device *netdev, struct pk_buff *pkb,
 /**
  * Process incoming ARP packets
  *
- * @v arp_netif                Network interface for ARP packets
  * @v pkb              Packet buffer
  * @ret rc             Return status code
  *
@@ -158,80 +165,96 @@ int arp_resolve ( struct net_device *netdev, struct pk_buff *pkb,
  * avoiding the need for extraneous ARP requests; read the RFC for
  * details.
  */
-int arp_process ( struct net_interface *arp_netif, struct pk_buff *pkb ) {
+static int arp_rx ( struct pk_buff *pkb ) {
        struct arphdr *arphdr = pkb->data;
-       struct net_device *netdev = arp_netif->netdev;
-       struct net_interface *netif;
+       struct ll_protocol *ll_protocol;
+       struct net_protocol *net_protocol;
        struct arp_entry *arp;
+       struct net_device *netdev;
        int merge = 0;
 
-       /* Check for correct link-layer protocol and length */
-       if ( ( arphdr->ar_hrd != netdev->ll_proto ) ||
-            ( arphdr->ar_hln != netdev->ll_addr_len ) )
-               return 0;
+       /* Identify link-layer and network-layer protocols */
+       ll_protocol = pkb->ll_protocol;
+       net_protocol = net_find_protocol ( arphdr->ar_pro );
+       if ( ! net_protocol )
+               goto done;
 
-       /* See if we have an interface for this network-layer protocol */
-       netif = netdev_find_netif ( netdev, arphdr->ar_pro );
-       if ( ! netif )
-               return 0;
-       if ( arphdr->ar_pln != netif->net_addr_len )
-               return 0;
+       /* Sanity checks */
+       if ( ( arphdr->ar_hrd != ll_protocol->ll_proto ) ||
+            ( arphdr->ar_hln != ll_protocol->ll_addr_len ) ||
+            ( arphdr->ar_pln != net_protocol->net_addr_len ) )
+               goto done;
 
        /* See if we have an entry for this sender, and update it if so */
-       arp = arp_find_entry ( arphdr->ar_hrd, arphdr->ar_pro,
-                              arp_sender_pa ( arphdr ), arphdr->ar_pln );
+       arp = arp_find_entry ( ll_protocol, net_protocol,
+                              arp_sender_pa ( arphdr ) );
        if ( arp ) {
                memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
                         arphdr->ar_hln );
                merge = 1;
        }
 
-       /* See if we are the target protocol address */
-       if ( memcmp ( arp_target_pa ( arphdr ), netif->net_addr,
-                     arphdr->ar_pln ) != 0 )
-               return 0;
-
+       /* See if we own the target protocol address */
+       netdev = net_find_address ( net_protocol, arp_target_pa ( arphdr ) );
+       if ( ! netdev )
+               goto done;
+       
        /* Create new ARP table entry if necessary */
        if ( ! merge ) {
                arp = &arp_table[next_new_arp_entry++ % NUM_ARP_ENTRIES];
-               arp->ll_proto = arphdr->ar_hrd;
-               arp->net_proto = arphdr->ar_pro;
+               arp->ll_protocol = ll_protocol;
+               arp->net_protocol = net_protocol;
                memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
                         arphdr->ar_hln );
                memcpy ( arp->net_addr, arp_sender_pa ( arphdr ),
-                        arphdr->ar_pln );
+                        arphdr->ar_pln);
        }
 
        /* If it's not a request, there's nothing more to do */
        if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) )
-               return 0;
+               goto done;
 
        /* Change request to a reply, and send it */
        arphdr->ar_op = htons ( ARPOP_REPLY );
-       memcpy ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ),
+       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 );
-       memcpy ( arp_target_pa ( arphdr ), netif->net_addr, arphdr->ar_pln );
-       netif_send ( arp_netif, pkb );
+       if ( net_transmit ( pkb ) == 0 )
+               pkb = NULL;
 
+ done:
+       free_pkb ( pkb );
        return 0;
 }
 
 /**
- * Add media-independent link-layer header
+ * Perform ARP network-layer routing
  *
- * @v arp_netif                Network interface for ARP packets
- * @v pkb              Packet buffer
- * @ret rc             Return status code
+ * @v pkb      Packet buffer
+ * @ret source Network-layer source address
+ * @ret dest   Network-layer destination address
+ * @ret rc     Return status code
  */
-int arp_add_llh_metadata ( struct net_interface *arp_netif __unused,
-                          struct pk_buff *pkb ) {
+static int arp_route ( const struct pk_buff *pkb,
+                      struct net_header *nethdr ) {
        struct arphdr *arphdr = pkb->data;
 
-       pkb->net_proto = htons ( ETH_P_ARP );
-       pkb->flags = PKB_FL_RAW_NET_ADDR;
-       pkb->net_addr_len = arphdr->ar_hln;
-       pkb->net_addr = arp_target_ha ( arphdr );
+       memcpy ( nethdr->source_net_addr, arp_sender_ha ( arphdr ),
+                arphdr->ar_hln );
+       memcpy ( nethdr->dest_net_addr, arp_target_ha ( arphdr ),
+                arphdr->ar_hln );
+       nethdr->dest_flags = NETADDR_FL_RAW;
+       if ( arphdr->ar_op == htons ( ARPOP_REQUEST ) )
+               nethdr->dest_flags |= NETADDR_FL_BROADCAST;
        
        return 0;
 }
+
+/** ARP protocol */
+struct net_protocol arp_protocol = {
+       .net_proto = ETH_P_ARP,
+       .rx = arp_rx,
+       .route = arp_route,
+};
+
+NET_PROTOCOL ( arp_protocol );
index 078719b..59b469f 100644 (file)
 #include <string.h>
 #include <byteswap.h>
 #include <assert.h>
+#include <gpxe/if_arp.h>
 #include <gpxe/if_ether.h>
 #include <gpxe/netdevice.h>
 #include <gpxe/pkbuff.h>
 #include <gpxe/arp.h>
+#include <gpxe/ethernet.h>
 
 /** @file
  *
  */
 
 /** Ethernet broadcast MAC address */
-static uint8_t eth_broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
 
 /**
- * Build Ethernet link-layer header
+ * Perform Ethernet routing
  *
- * @v netdev   Network device
- * @v pkb      Packet buffer
+ * @v nethdr   Generic network-layer header
+ * @ret llhdr  Generic link-layer header
  * @ret rc     Return status code
  *
- * This constructs the Ethernet link-layer header (destination MAC,
- * source MAC, network-layer protocol) based on the metadata found in
- * @c pkb.
+ * Constructs the generic link-layer header based on the generic
+ * network-layer header, i.e. maps network-layer addresses (e.g. IPv4
+ * addresses) to MAC addresses.
  *
  * If the destination MAC address cannot be determined, an ARP request
- * is sent for the requested network-layer address instead.
+ * is sent for the requested network-layer address and -ENOENT is
+ * returned.
  */
-int eth_build_llh ( struct net_device *netdev, struct pk_buff *pkb ) {
-       struct ethhdr *ethhdr = pkb->data;
-       const void *eth_dest;
+static int eth_route ( const struct net_header *nethdr,
+                      struct ll_header *llhdr ) {
        int rc;
 
-       /* Do the easy bits */
-       ethhdr->h_protocol = pkb->net_proto;
-       memcpy ( ethhdr->h_source, netdev->ll_addr,
-                sizeof ( ethhdr->h_source ) );
-
        /* Work out the destination MAC address */
-       if ( pkb->flags & PKB_FL_RAW_NET_ADDR ) {
-               eth_dest = pkb->net_addr;
-       } else if ( pkb->flags & PKB_FL_BROADCAST ) {
-               eth_dest = eth_broadcast;
-       } else if ( pkb->flags & PKB_FL_MULTICAST ) {
+       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 ) {
+               memcpy ( llhdr->dest_ll_addr, eth_broadcast, ETH_ALEN );
+       } else if ( nethdr->dest_flags & NETADDR_FL_MULTICAST ) {
                /* IP multicast is a special case; there exists a
                 * direct mapping from IP address to MAC address
                 */
-               assert ( pkb->net_proto == htons ( ETH_P_IP ) );
-               ethhdr->h_dest[0] = 0x01;
-               ethhdr->h_dest[1] = 0x00;
-               ethhdr->h_dest[2] = 0x5e;
-               ethhdr->h_dest[3] = *( ( char * ) pkb->net_addr + 1 ) & 0x7f;
-               ethhdr->h_dest[4] = *( ( char * ) pkb->net_addr + 2 );
-               ethhdr->h_dest[5] = *( ( char * ) pkb->net_addr + 3 );
-               eth_dest = ethhdr->h_dest;
+               assert ( nethdr->net_protocol->net_proto == htons(ETH_P_IP) );
+               llhdr->dest_ll_addr[0] = 0x01;
+               llhdr->dest_ll_addr[1] = 0x00;
+               llhdr->dest_ll_addr[2] = 0x5e;
+               llhdr->dest_ll_addr[3] = nethdr->dest_net_addr[1] & 0x7f;
+               llhdr->dest_ll_addr[4] = nethdr->dest_net_addr[2];
+               llhdr->dest_ll_addr[5] = nethdr->dest_net_addr[3];
        } else {
                /* Otherwise, look up the address using ARP */
-               if ( ( rc = arp_resolve ( netdev, pkb, &eth_dest ) ) != 0 )
+               if ( ( rc = arp_resolve ( nethdr, llhdr ) ) != 0 )
                        return rc;
        }
 
-       /* Fill in destination MAC address */
-       memcpy ( ethhdr->h_dest, eth_dest, sizeof ( ethhdr->h_dest ) );
-
        return 0;
 }
 
+/**
+ * Fill in Ethernet link-layer header
+ *
+ * @v pkb      Packet buffer
+ * @v llhdr    Generic link-layer header
+ *
+ * Fills in the Ethernet link-layer header in the packet buffer based
+ * on information in the generic link-layer header.
+ */
+static void eth_fill_llh ( const struct ll_header *llhdr,
+                          struct pk_buff *pkb ) {
+       struct ethhdr *ethhdr = pkb->data;
+
+       memcpy ( ethhdr->h_dest, llhdr->dest_ll_addr, ETH_ALEN );
+       memcpy ( ethhdr->h_source, llhdr->source_ll_addr, ETH_ALEN );
+       ethhdr->h_protocol = llhdr->net_proto;
+}
+
 /**
  * Parse Ethernet link-layer header
  *
- * @v netdev   Network device
  * @v pkb      Packet buffer
- * @ret rc     Return status code
+ * @v llhdr    Generic link-layer header
  *
- * This parses the Ethernet link-layer header (destination MAC, source
- * MAC, network-layer protocol) and fills in the metadata in @c pkb.
+ * Fills in the generic link-layer header based on information in the
+ * Ethernet link-layer header in the packet buffer.
  */
-int eth_parse_llh ( struct net_device *netdev __unused, struct pk_buff *pkb ) {
+static void eth_parse_llh ( const struct pk_buff *pkb,
+                           struct ll_header *llhdr ) {
        struct ethhdr *ethhdr = pkb->data;
 
-       pkb->net_proto = ethhdr->h_protocol;
-       pkb->flags = PKB_FL_RAW_NET_ADDR;
-       pkb->net_addr_len = sizeof ( ethhdr->h_dest );
-       pkb->net_addr = ethhdr->h_dest;
+       memcpy ( llhdr->dest_ll_addr, ethhdr->h_dest, ETH_ALEN );
+       memcpy ( llhdr->source_ll_addr, ethhdr->h_source, ETH_ALEN );
+       llhdr->net_proto = ethhdr->h_protocol;
 
-       if ( memcmp ( ethhdr->h_dest, eth_broadcast,
-                     sizeof ( ethhdr->h_dest ) ) == 0 ) {
-               pkb->flags |= PKB_FL_BROADCAST;
+       if ( memcmp ( ethhdr->h_dest, eth_broadcast, ETH_ALEN ) == 0 ) {
+               llhdr->dest_flags = NETADDR_FL_BROADCAST;
        } else if ( ethhdr->h_dest[0] & 0x01 ) {
-               pkb->flags |= PKB_FL_MULTICAST;
+               llhdr->dest_flags = NETADDR_FL_MULTICAST;
+       } else {
+               llhdr->dest_flags = 0;
        }
-
-       return 0;
 }
+
+/** Ethernet protocol */
+struct ll_protocol ethernet_protocol = {
+       .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,
+};
+
+LL_PROTOCOL ( ethernet_protocol );
index d7ad308..3ff6657 100644 (file)
 
 #include <stdint.h>
 #include <byteswap.h>
+#include <string.h>
 #include <errno.h>
 #include <gpxe/if_ether.h>
 #include <gpxe/pkbuff.h>
+#include <gpxe/tables.h>
 #include <gpxe/netdevice.h>
 
 /** @file
  *
- * Network devices and network interfaces
+ * Network device management
  *
  */
 
-/** List of all registered network devices */
-static LIST_HEAD ( net_devices );
-
 /**
- * Register network device
+ * Static single instance of a network device
+ *
+ * The gPXE API is designed to accommodate multiple network devices.
+ * However, in the interests of code size, the implementation behind
+ * the API supports only a single instance of a network device.
  *
- * @v netdev   Network device
- * @ret rc     Return status code
+ * No code outside of netdevice.c should ever refer directly to @c
+ * static_single_netdev.
  *
- * Adds the network device to the list of network devices.
+ * Callers should always check the return status of alloc_netdev(),
+ * register_netdev() etc.  In the current implementation this code
+ * will be optimised out by the compiler, so there is no penalty.
  */
-int register_netdevice ( struct net_device *netdev ) {
-       list_add ( &netdev->devices, &net_devices );
-       return 0;
-}
+struct net_device static_single_netdev;
+
+/** Registered network-layer protocols */
+static struct net_protocol net_protocols[0] __table_start ( net_protocols );
+static struct net_protocol net_protocols_end[0] __table_end ( net_protocols );
+
+/** Network-layer addresses for @c static_single_netdev */
+static struct net_address static_single_netdev_addresses[0]
+       __table_start ( sgl_netdev_addresses );
+static struct net_address static_single_netdev_addresses_end[0]
+       __table_end ( sgl_netdev_addresses );
+
+/** Recevied packet queue */
+static LIST_HEAD ( rx_queue );
 
 /**
- * Unregister network device
+ * Identify network protocol
  *
- * @v netdev   Network device
+ * @v net_proto                Network-layer protocol, in network-byte order
+ * @ret net_protocol   Network-layer protocol, or NULL
  *
- * Removes the network device from the list of network devices.
+ * Identify a network-layer protocol from a protocol number, which
+ * must be an ETH_P_XXX constant in network-byte order.
  */
-void unregister_netdevice ( struct net_device *netdev ) {
-       list_del ( &netdev->devices );
+struct net_protocol * net_find_protocol ( uint16_t net_proto ) {
+       struct net_protocol *net_protocol;
+
+       for ( net_protocol = net_protocols ; net_protocol < net_protocols_end ;
+             net_protocol++ ) {
+               if ( net_protocol->net_proto == net_proto )
+                       return net_protocol;
+       }
+       return NULL;
 }
 
 /**
- * Transmit packet via network device
+ * Identify network device by network-layer address
  *
- * @v netdev   Network device
- * @v pkb      Packet buffer
- * @ret rc     Return status code
+ * @v net_protocol     Network-layer protocol
+ * @v net_addr         Network-layer address
+ * @ret netdev         Network device, or NULL
  *
- * Transmits the packet via the network device.  The @c pkb link-layer
- * metadata must already have been filled in, and space for the
- * link-layer header must already be present in the packet buffer.
+ * Searches through all network devices to find the device with the
+ * specified network-layer address.
+ *
+ * Note that even with a static single network device, this function
+ * can still return NULL.
  */
-int netdev_send ( struct net_device *netdev, struct pk_buff *pkb ) {
-       int rc;
-
-       if ( pkb->net_proto != ETH_P_RAW ) {
-               if ( ( rc = netdev->build_llh ( netdev, pkb ) ) != 0 )
-                       return rc;
+struct net_device * net_find_address ( struct net_protocol *net_protocol,
+                                      void *net_addr ) {
+       struct net_address *net_address;
+       struct net_device *netdev = &static_single_netdev;
+       
+       for ( net_address = static_single_netdev_addresses ;
+             net_address < static_single_netdev_addresses_end ;
+             net_address ++ ) {
+               if ( ( net_address->net_protocol == net_protocol ) &&
+                    ( memcmp ( net_address->net_addr, net_addr,
+                               net_protocol->net_addr_len ) == 0 ) )
+                       return netdev;
        }
-       return netdev->transmit ( netdev, pkb );
+       return NULL;
 }
 
 /**
- * Poll for packet on network device
+ * Transmit packet
  *
- * @v netdev   Network device
- * @v pkb      Packet buffer
- * @ret rc     Return status code
+ * @v pkb              Packet buffer
+ * @ret rc             Return status code
  *
- * Polls the network device for a packet.  If a packet is available,
- * it will be added to the packet buffer, and the link-layer metadata
- * fields in @c pkb will be filled in.
+ * Transmits the packet via the appropriate network device.  If this
+ * function returns success, it has taken ownership of the packet
+ * buffer.
  */
-int netdev_poll ( struct net_device *netdev, struct pk_buff *pkb ) {
+int net_transmit ( struct pk_buff *pkb ) {
+       struct net_protocol *net_protocol;
+       struct net_header nethdr;
+       struct ll_protocol *ll_protocol;
+       struct ll_header llhdr;
+       struct net_device *netdev;
        int rc;
 
-       if ( ( rc = netdev->poll ( netdev, pkb ) ) != 0 )
-               return rc;
-       return netdev->parse_llh ( netdev, pkb );
+       /* Perform network-layer routing */
+       net_protocol = pkb->net_protocol;
+       nethdr.net_protocol = net_protocol;
+       if ( ( rc = net_protocol->route ( pkb, &nethdr ) ) != 0 )
+               goto err;
+
+       /* Identify transmitting network device */
+       netdev = net_find_address ( net_protocol, nethdr.source_net_addr );
+       if ( ! netdev )
+               goto err;
+
+       /* 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;
+
+       /* 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;
+
+       return 0;
+
+ err:
+       free_pkb ( pkb );
+       return rc;
 }
 
 /**
- * Transmit packet via network interface
+ * Poll for packet on all network devices
  *
- * @v netif    Network interface
- * @v pkb      Packet buffer
- * @ret rc     Return status code
+ * @ret True           There are packets present in the receive queue
+ * @ret False          There are no packets present in the receive queue
  *
- * Transmits the packet via the network interface.  The packet must
- * start with a network-layer header (e.g. an IP header, for an IP
- * interface).  The packet contents are undefined on return.
+ * Polls all network devices for received packets.  Any received
+ * packets will be added to the RX packet queue via netdev_rx().
  */
-int netif_send ( struct net_interface *netif, struct pk_buff *pkb ) {
-       struct net_device *netdev = netif->netdev;
-       int rc;
+int net_poll ( void ) {
+       struct net_device *netdev = &static_single_netdev;
+
+       netdev->poll ( netdev );
 
-       if ( ( rc = netif->add_llh_metadata ( netif, pkb ) ) != 0 )
-               return rc;
-       pkb_push ( pkb, netdev->ll_hlen );
-       return netdev_send ( netdev, pkb );
+       return ( ! list_empty ( &rx_queue ) );
 }
 
 /**
- * Process received packet
+ * Add packet to receive queue
  *
- * @v netif    Network interface
- * @v pkb      Packet buffer
- * @ret rc     Return status code
+ * @v netdev           Network device
+ * @v pkb              Packet buffer
  *
- * Processes a packet received via netdev_poll().  The interface
- * corresponding to the network-layer protocol is identified, the
- * link-layer header is stripped from the packet and the packet is
- * passed to the net_interface::rx_packet() method.
+ * 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().
  */
-int netdev_rx_packet ( struct net_device *netdev, struct pk_buff *pkb ) {
-       struct net_interface *netif;
-
-       netif = netdev_find_netif ( netdev, pkb->net_proto );
-       if ( ! netif )
-               return -EAFNOSUPPORT;
-
-       pkb_pull ( pkb, netdev->ll_hlen );
-       return netif->rx_packet ( netif, pkb );
+void netdev_rx ( struct net_device *netdev, struct pk_buff *pkb ) {
+       pkb->ll_protocol = netdev->ll_protocol;
+       list_add_tail ( &pkb->list, &rx_queue );
 }
 
 /**
- * Poll for packet on all network devices
+ * Remove packet from receive queue
  *
- * @v pkb      Packet buffer
- * @ret netdev Network device
- * @ret rc     Return status code
+ * @ret pkb            Packet buffer, or NULL
  *
- * Polls all network devices for a packet.  If a packet is available
- * on any interface, @c netdev will be filled in and the packet will
- * be received as per netdev_poll().
+ * Removes the first packet from the RX queue and returns it.
+ * Ownership of the packet is transferred to the caller.
  */
-int net_poll ( struct pk_buff *pkb, struct net_device **netdev ) {
-       int rc;
+struct pk_buff * net_rx_dequeue ( void ) {
+       struct pk_buff *pkb;
 
-       list_for_each_entry ( (*netdev), &net_devices, devices ) {
-               if ( ( rc = netdev_poll ( *netdev, pkb ) ) == 0 )
-                       return rc;
+       list_for_each_entry ( pkb, &rx_queue, list ) {
+               list_del ( &pkb->list );
+               return pkb;
        }
+       return NULL;
+}
 
-       return -EAGAIN;
+void net_run ( void ) {
+       struct pk_buff *pkb;
+       struct ll_protocol *ll_protocol;
+       struct ll_header llhdr;
+       struct net_protocol *net_protocol;
+
+       while ( ( pkb = net_rx_dequeue () ) ) {
+
+               /* Parse link-layer header */
+               ll_protocol = pkb->ll_protocol;
+               ll_protocol->parse_llh ( pkb, &llhdr );
+
+               /* Identify network-layer protocol */
+               net_protocol = net_find_protocol ( llhdr.net_proto );
+               if ( ! net_protocol ) {
+                       DBG ( "Unknown network-layer protocol %02x\n",
+                             ntohs ( llhdr.net_proto ) );
+                       free_pkb ( pkb );
+                       continue;
+               }
+
+               /* Strip off link-layer header */
+               pkb_pull ( pkb, ll_protocol->ll_header_len );
+
+               /* Hand off to network layer */
+               if ( net_protocol->rx ( pkb ) != 0 ) {
+                       free_pkb ( pkb );
+                       continue;
+               }
+       }
 }
+
+
+
index 811284e..22a5990 100644 (file)
@@ -464,7 +464,7 @@ void uip_log(char *msg);
  *
  * \hideinitializer
  */
-#define UIP_LLH_LEN     14
+#define UIP_LLH_LEN     0
 
 
 /** @} */