diff options
author | Michael Brown | 2006-04-24 17:33:06 +0200 |
---|---|---|
committer | Michael Brown | 2006-04-24 17:33:06 +0200 |
commit | fdc2ee79db376b03710f9255720ce45c79022768 (patch) | |
tree | f1d8ea36b34b2490a17e40698d1c4e6741230932 /src/net/netdevice.c | |
parent | Obviate uip_init(); our bss is zeroed at startup already. (diff) | |
download | ipxe-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.c | 257 |
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; + } + } } + + + |