summaryrefslogtreecommitdiffstats
path: root/src/net/netdevice.c
diff options
context:
space:
mode:
authorMichael Brown2006-04-24 17:33:06 +0200
committerMichael Brown2006-04-24 17:33:06 +0200
commitfdc2ee79db376b03710f9255720ce45c79022768 (patch)
treef1d8ea36b34b2490a17e40698d1c4e6741230932 /src/net/netdevice.c
parentObviate uip_init(); our bss is zeroed at startup already. (diff)
downloadipxe-fdc2ee79db376b03710f9255720ce45c79022768.tar.gz
ipxe-fdc2ee79db376b03710f9255720ce45c79022768.tar.xz
ipxe-fdc2ee79db376b03710f9255720ce45c79022768.zip
Network API now allows for multiple network devices (although the
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.
Diffstat (limited to 'src/net/netdevice.c')
-rw-r--r--src/net/netdevice.c257
1 files changed, 171 insertions, 86 deletions
diff --git a/src/net/netdevice.c b/src/net/netdevice.c
index d7ad3080..3ff6657c 100644
--- a/src/net/netdevice.c
+++ b/src/net/netdevice.c
@@ -18,146 +18,231 @@
#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;
+ }
+ }
}
+
+
+