diff --git a/src/drivers/net/pnic.c b/src/drivers/net/pnic.c index 047bc229..243235a4 100644 --- a/src/drivers/net/pnic.c +++ b/src/drivers/net/pnic.c @@ -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" +#include +#include +#include +#include +#include +#include +#include +#include +#include -/* PNIC API */ #include "pnic_api.h" -/* Function prototypes */ -static int pnic_api_check ( uint16_t api_version ); - -/* 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 ); +static int pnic_transmit ( struct net_device *netdev, struct pk_buff *pkb ) { + struct pnic *pnic = netdev->priv; - if ( ( ETH_HLEN + size ) >= ETH_FRAME_LEN ) { - printf ( "pnic_transmit: packet too large\n" ); - return; - } - - /* 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_quiet( nic, 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 */ - nic->nic_op = &pnic_operations; - return 1; + /* Point to NIC specific routines */ + netdev->poll = pnic_poll; + netdev->transmit = pnic_transmit; + + return 0; + + 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, +}; -DRIVER ( "PNIC", nic_driver, pci_driver, pnic_driver, - pnic_probe, pnic_disable ); +// PCI_DRIVER ( pnic_driver ); + +#include "dev.h" +extern struct type_driver test_driver; + +DRIVER ( "PNIC", test_driver, pci_driver, pnic_driver, + pnic_probe, pnic_remove ); diff --git a/src/include/gpxe/arp.h b/src/include/gpxe/arp.h index f6ec47af..a2db80f1 100644 --- a/src/include/gpxe/arp.h +++ b/src/include/gpxe/arp.h @@ -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 */ diff --git a/src/include/gpxe/netdevice.h b/src/include/gpxe/netdevice.h index 93a98634..f7d78288 100644 --- a/src/include/gpxe/netdevice.h +++ b/src/include/gpxe/netdevice.h @@ -1,33 +1,197 @@ -#ifndef _NETDEVICE_H -#define _NETDEVICE_H +#ifndef _GPXE_NETDEVICE_H +#define _GPXE_NETDEVICE_H /** @file * - * Network device and network interface + * Network device management * */ #include -#include +#include -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-layer protocol + * + */ +struct net_protocol { + /** + * Perform network-layer routing + * + * @v pkb Packet buffer + * @ret source Network-layer source address + * @ret dest Network-layer destination address + * @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. + * + * 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 ( * route ) ( const struct pk_buff *pkb, + struct net_header *nethdr ); + /** + * Handle received packets + * + * @v pkb Packet buffer + * @ret rc Return status code + * + * If this method returns success, it has taken ownership of + * the packet buffer. + */ + int ( * rx ) ( struct pk_buff *pkb ); + /** 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; +}; + +/** + * 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 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 ( * route ) ( const struct net_header *nethdr, + struct ll_header *llhdr ); + /** + * Fill media-specific link-layer header + * + * @v llhdr Generic link-layer header + * @v pkb Packet buffer + * + * 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. + */ + 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 address length */ + uint8_t ll_addr_len; + /** Link-layer header length */ + uint8_t ll_header_len; +}; + +/** + * A network-layer address assigned to a network device + * + */ +struct net_address { + /** Network-layer protocol */ + struct net_protocol *net_protocol; + /** Network-layer address */ + uint8_t net_addr[MAX_NET_ADDR_LEN]; +}; + /** * 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. + * transmitting and receiving raw packets. * * Note that this structure must represent a generic network device, * not just an Ethernet device. @@ -40,164 +204,123 @@ struct net_device { * @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. + * 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 ( * transmit ) ( struct net_device *netdev, - struct pk_buff *pkb ); + int ( * transmit ) ( struct net_device *netdev, struct pk_buff *pkb ); /** Poll for received packet * * @v netdev Network device - * @v pkb Packet buffer to contain received packet - * @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. + * This method should cause the hardware to check for received + * packets. Any received packets should be delivered via + * netdev_rx(). */ - int ( * poll ) ( struct net_device *netdev, struct pk_buff *pkb ); - /** Build link-layer header - * - * @v netdev Network device - * @v pkb Packet buffer - * @ret rc Return status code - * - * This method should fill in the link-layer header based on - * the metadata contained in @c pkb. - * - * 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 - * - * @v netdev Network device - * @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. - */ - int ( * parse_llh ) ( struct net_device *netdev, struct pk_buff *pkb ); - /** 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; + void ( * poll ) ( struct net_device *netdev ); + + /** Link-layer protocol */ + struct ll_protocol *ll_protocol; /** 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; + uint8_t ll_addr[MAX_LL_ADDR_LEN]; + /** Driver private data */ void *priv; }; -/** - * A network interface - * - * This structure represents a particular network layer protocol's - * interface to a piece of network hardware (a struct @c net_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; - /** Network-layer address */ - uint8_t net_addr[MAX_NET_ADDR_LEN]; - /** Fill in packet metadata - * - * @v netif Network interface - * @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. - */ - int ( * add_llh_metadata ) ( struct net_interface *netif, - struct pk_buff *pkb ); - /** Received packet processor - * - * @v netif Network interface - * @v pkb Packet buffer - * @ret rc Return status code - * - * This method is called for packets arriving on the - * associated network device that match this interface's - * network-layer protocol. - * - * When this method is called, the link-layer header will - * already have been stripped from the packet. - */ - int ( * rx_packet ) ( struct net_interface *netif, - struct pk_buff *pkb ); -}; - -/** - * Find interface for a specific protocol - * - * @v netdev Network device - * @v net_proto Network-layer protocol, in network byte order - * @ret netif Network interface, or NULL if none found - * - */ -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; -} - -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 ); - - - extern struct net_device static_single_netdev; -/* Must be a macro because priv_data[] is of variable size */ -#define alloc_netdevice( priv_size ) ( { \ - static char priv_data[priv_size]; \ +/** + * 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; } ) - -static inline void free_netdevice ( struct net_device *netdev __unused ) { - /* Do nothing */ +/** + * Register network device + * + * @v netdev Network device + * @ret rc Return status code + * + * Adds the network device to the list of network devices. + */ +static inline int +register_netdev ( struct net_device *netdev __attribute__ (( unused )) ) { + return 0; } -#endif /* _NETDEVICE_H */ +/** + * 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 ) + +/** + * Register a network-layer protocol + * + * @v protocol Network-layer protocol + */ +#define NET_PROTOCOL( protocol ) \ + struct net_protocol protocol __table ( net_protocols, 00 ) + +/** + * 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 ); + +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 */ diff --git a/src/include/gpxe/pkbuff.h b/src/include/gpxe/pkbuff.h index f82f508a..2e3980a8 100644 --- a/src/include/gpxe/pkbuff.h +++ b/src/include/gpxe/pkbuff.h @@ -1,5 +1,5 @@ -#ifndef _PKBUFF_H -#define _PKBUFF_H +#ifndef _GPXE_PKBUFF_H +#define _GPXE_PKBUFF_H /** @file * @@ -12,6 +12,10 @@ #include #include +#include + +struct net_protocol; +struct ll_protocol; /** A packet buffer * @@ -27,39 +31,15 @@ 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; + /** List of which this buffer is a member */ + struct list_head list; + + /** The network-layer protocol */ + struct net_protocol *net_protocol; + /** The link-layer protocol */ + struct ll_protocol *ll_protocol; }; -/** Packet is a broadcast packet */ -#define PKB_FL_BROADCAST 0x01 - -/** 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 - /** * 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 */ diff --git a/src/net/arp.c b/src/net/arp.c index bee7f577..7b8eede6 100644 --- a/src/net/arp.c +++ b/src/net/arp.c @@ -39,13 +39,13 @@ /** 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 ); + 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 ); - /* Locate ARP interface and send ARP request */ - netif = netdev_find_netif ( netdev, htons ( ETH_P_ARP ) ); - assert ( netif != NULL ); - netif_send ( netif, pkb ); + /* 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 ); diff --git a/src/net/ethernet.c b/src/net/ethernet.c index 078719b3..59b469f1 100644 --- a/src/net/ethernet.c +++ b/src/net/ethernet.c @@ -20,10 +20,12 @@ #include #include #include +#include #include #include #include #include +#include /** @file * @@ -32,85 +34,104 @@ */ /** 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, ð_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 ); 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 #include +#include #include #include #include +#include #include /** @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 * - * @v netdev Network device - * @ret rc Return status code + * 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. * - * Adds the network device to the list of network devices. + * No code outside of netdevice.c should ever refer directly to @c + * static_single_netdev. + * + * 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; -/** - * Transmit packet via network device - * - * @v netdev Network device - * @v pkb Packet buffer - * @ret rc Return status code - * - * 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. - */ -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; + for ( net_protocol = net_protocols ; net_protocol < net_protocols_end ; + net_protocol++ ) { + if ( net_protocol->net_proto == net_proto ) + return net_protocol; } - return netdev->transmit ( netdev, pkb ); + return NULL; } /** - * Poll for packet on 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 * - * 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. + * 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_poll ( struct net_device *netdev, struct pk_buff *pkb ) { +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 NULL; +} + +/** + * 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 ) { + 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; -/** - * Transmit packet via network interface - * - * @v netif Network interface - * @v pkb Packet buffer - * @ret rc Return status code - * - * 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. - */ -int netif_send ( struct net_interface *netif, struct pk_buff *pkb ) { - struct net_device *netdev = netif->netdev; - int rc; + /* Identify transmitting network device */ + netdev = net_find_address ( net_protocol, nethdr.source_net_addr ); + if ( ! netdev ) + goto err; - if ( ( rc = netif->add_llh_metadata ( netif, pkb ) ) != 0 ) - return rc; - pkb_push ( pkb, netdev->ll_hlen ); - return netdev_send ( netdev, pkb ); -} + /* 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; -/** - * Process received packet - * - * @v netif Network interface - * @v pkb Packet buffer - * @ret rc Return status code - * - * 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. - */ -int netdev_rx_packet ( struct net_device *netdev, struct pk_buff *pkb ) { - struct net_interface *netif; + /* Prepend link-layer header */ + pkb_push ( pkb, ll_protocol->ll_header_len ); + ll_protocol->fill_llh ( &llhdr, pkb ); - netif = netdev_find_netif ( netdev, pkb->net_proto ); - if ( ! netif ) - return -EAFNOSUPPORT; + /* Transmit packet */ + if ( ( rc = netdev->transmit ( netdev, pkb ) ) != 0 ) + goto err; - pkb_pull ( pkb, netdev->ll_hlen ); - return netif->rx_packet ( netif, pkb ); + return 0; + + err: + free_pkb ( pkb ); + return rc; } /** * Poll for packet on all network devices * - * @v pkb Packet buffer - * @ret netdev Network device - * @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 * - * 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(). + * Polls all network devices for received packets. Any received + * packets will be added to the RX packet queue via netdev_rx(). */ -int net_poll ( struct pk_buff *pkb, struct net_device **netdev ) { - int rc; +int net_poll ( void ) { + struct net_device *netdev = &static_single_netdev; - list_for_each_entry ( (*netdev), &net_devices, devices ) { - if ( ( rc = netdev_poll ( *netdev, pkb ) ) == 0 ) - return rc; - } + netdev->poll ( netdev ); - return -EAGAIN; + 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 + * + * @ret pkb Packet buffer, or NULL + * + * Removes the first packet from the RX queue and returns it. + * Ownership of the packet is transferred to the caller. + */ +struct pk_buff * net_rx_dequeue ( void ) { + struct pk_buff *pkb; + + list_for_each_entry ( pkb, &rx_queue, list ) { + list_del ( &pkb->list ); + return pkb; + } + return NULL; +} + +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; + } + } +} + + + diff --git a/src/proto/uip/uipopt.h b/src/proto/uip/uipopt.h index 811284e1..22a5990c 100644 --- a/src/proto/uip/uipopt.h +++ b/src/proto/uip/uipopt.h @@ -464,7 +464,7 @@ void uip_log(char *msg); * * \hideinitializer */ -#define UIP_LLH_LEN 14 +#define UIP_LLH_LEN 0 /** @} */