From 53f78346bf65c7080da1c750c7590cc343d55726 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 Apr 2006 15:38:53 +0000 Subject: [PATCH] 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. --- src/include/gpxe/ethernet.h | 28 +++++ src/net/ipv4.c | 226 ++++++++++++++++++++++++++++++++++++ src/net/pkbuff.c | 65 +++++++++++ src/proto/ip.c | 137 ---------------------- 4 files changed, 319 insertions(+), 137 deletions(-) create mode 100644 src/include/gpxe/ethernet.h create mode 100644 src/net/ipv4.c create mode 100644 src/net/pkbuff.c delete mode 100644 src/proto/ip.c diff --git a/src/include/gpxe/ethernet.h b/src/include/gpxe/ethernet.h new file mode 100644 index 00000000..e62eb09b --- /dev/null +++ b/src/include/gpxe/ethernet.h @@ -0,0 +1,28 @@ +#ifndef _GPXE_ETHERNET_H +#define _GPXE_ETHERNET_H + +/** @file + * + * Ethernet protocol + * + */ + +#include +#include + +extern struct ll_protocol ethernet_protocol; + +/** + * Allocate Ethernet device + * + * @v priv_size Size of driver private data + * @ret netdev Network device, or NULL + */ +#define alloc_etherdev( priv_size ) ( { \ + struct net_device *netdev; \ + netdev = alloc_netdev ( priv_size ); \ + if ( netdev ) \ + netdev->ll_protocol = ðernet_protocol; \ + netdev; } ) + +#endif /* _GPXE_ETHERNET_H */ diff --git a/src/net/ipv4.c b/src/net/ipv4.c new file mode 100644 index 00000000..4200d234 --- /dev/null +++ b/src/net/ipv4.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include + + +#include + + +#include +#include +#include +#include "../proto/uip/uip.h" + +/** @file + * + * IPv4 protocol + * + * The gPXE IP stack is currently implemented on top of the uIP + * protocol stack. This file provides wrappers around uIP so that + * higher-level protocol implementations do not need to talk directly + * to uIP (which has a somewhat baroque API). + * + */ + +/** An IPv4 routing table entry */ +struct ipv4_route { + /** Network address */ + struct in_addr network; + /** Subnet mask */ + struct in_addr netmask; + /** Gateway address */ + struct in_addr gateway; + /** Gateway device */ + struct in_addr gatewaydev; +}; + +enum { + STATIC_SINGLE_NETDEV_ROUTE = 0, + DEFAULT_ROUTE, + NUM_ROUTES +}; + +/** IPv4 routing table */ +static struct ipv4_route routing_table[NUM_ROUTES]; + +#define routing_table_end ( routing_table + NUM_ROUTES ) + +#if 0 +/** + * Set IP address + * + */ +void set_ipaddr ( struct in_addr address ) { + union { + struct in_addr address; + uint16_t uip_address[2]; + } u; + + u.address = address; + uip_sethostaddr ( u.uip_address ); +} + +/** + * Set netmask + * + */ +void set_netmask ( struct in_addr address ) { + union { + struct in_addr address; + uint16_t uip_address[2]; + } u; + + u.address = address; + uip_setnetmask ( u.uip_address ); +} + +/** + * Set default gateway + * + */ +void set_gateway ( struct in_addr address ) { + union { + struct in_addr address; + uint16_t uip_address[2]; + } u; + + u.address = address; + uip_setdraddr ( u.uip_address ); +} + +/** + * Run the TCP/IP stack + * + * Call this function in a loop in order to allow TCP/IP processing to + * take place. This call takes the stack through a single iteration; + * it will typically be used in a loop such as + * + * @code + * + * struct tcp_connection *my_connection; + * ... + * tcp_connect ( my_connection ); + * while ( ! my_connection->finished ) { + * run_tcpip(); + * } + * + * @endcode + * + * where @c my_connection->finished is set by one of the connection's + * #tcp_operations methods to indicate completion. + */ +void run_tcpip ( void ) { + void *data; + size_t len; + uint16_t type; + int i; + + if ( netdev_poll ( 1, &data, &len ) ) { + /* We have data */ + memcpy ( uip_buf, data, len ); + uip_len = len; + type = ntohs ( *( ( uint16_t * ) ( uip_buf + 12 ) ) ); + if ( type == UIP_ETHTYPE_ARP ) { + uip_arp_arpin(); + } else { + uip_arp_ipin(); + uip_input(); + } + if ( uip_len > 0 ) + uip_transmit(); + } else { + for ( i = 0 ; i < UIP_CONNS ; i++ ) { + uip_periodic ( i ); + if ( uip_len > 0 ) + uip_transmit(); + } + } +} +#endif + +/** + * Process incoming IP packets + * + * @v pkb Packet buffer + * @ret rc Return status code + * + * This handles IP packets by handing them off to the uIP protocol + * stack. + */ +static int ipv4_rx ( struct pk_buff *pkb ) { + + /* Transfer to uIP buffer. Horrendously space-inefficient, + * but will do as a proof-of-concept for now. + */ + memcpy ( uip_buf, pkb->data, pkb_len ( pkb ) ); + + /* Hand to uIP for processing */ + uip_input (); + if ( uip_len > 0 ) { + pkb_empty ( pkb ); + pkb_put ( pkb, uip_len ); + memcpy ( pkb->data, uip_buf, uip_len ); + if ( net_transmit ( pkb ) != 0 ) + free_pkb ( pkb ); + } else { + free_pkb ( pkb ); + } + return 0; +} + +/** + * Perform IP layer routing + * + * @v pkb Packet buffer + * @ret source Network-layer source address + * @ret dest Network-layer destination address + * @ret rc Return status code + */ +static int ipv4_route ( const struct pk_buff *pkb, + struct net_header *nethdr ) { + struct iphdr *iphdr = pkb->data; + struct in_addr *source = ( struct in_addr * ) nethdr->source_net_addr; + struct in_addr *dest = ( struct in_addr * ) nethdr->dest_net_addr; + struct ipv4_route *route; + + /* Route IP packet according to routing table */ + source->s_addr = INADDR_NONE; + dest->s_addr = iphdr->dest.s_addr; + for ( route = routing_table ; route < routing_table_end ; route++ ) { + if ( ( dest->s_addr & route->netmask.s_addr ) + == route->network.s_addr ) { + source->s_addr = route->gatewaydev.s_addr; + if ( route->gateway.s_addr ) + dest->s_addr = route->gateway.s_addr; + break; + } + } + + /* Set broadcast and multicast flags as applicable */ + nethdr->dest_flags = 0; + if ( dest->s_addr == htonl ( INADDR_BROADCAST ) ) { + nethdr->dest_flags = NETADDR_FL_BROADCAST; + } else if ( IN_MULTICAST ( dest->s_addr ) ) { + nethdr->dest_flags = NETADDR_FL_MULTICAST; + } + + return 0; +} + +/** IPv4 protocol */ +struct net_protocol ipv4_protocol = { + .net_proto = ETH_P_IP, + .net_addr_len = sizeof ( struct in_addr ), + .rx = ipv4_rx, + .route = ipv4_route, +}; + +NET_PROTOCOL ( ipv4_protocol ); + +/** IPv4 address for the static single net device */ +struct net_address static_single_ipv4_address = { + .net_protocol = &ipv4_protocol, +}; + +STATIC_SINGLE_NETDEV_ADDRESS ( static_single_ipv4_address ); diff --git a/src/net/pkbuff.c b/src/net/pkbuff.c new file mode 100644 index 00000000..ec09ab1b --- /dev/null +++ b/src/net/pkbuff.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +/** @file + * + * Packet buffers + * + */ + +/** + * Allocate packet buffer + * + * @v len Required length of buffer + * @ret pkb Packet buffer, or NULL if none available + * + * The packet buffer will be aligned as per gmalloc(). + */ +struct pk_buff * alloc_pkb ( size_t len ) { + struct pk_buff *pkb = NULL; + void *data; + + /* Align buffer length */ + len = ( len + __alignof__ ( *pkb ) - 1 ) & ~ __alignof__ ( *pkb ); + + /* Allocate memory for buffer plus descriptor */ + data = gmalloc ( len + sizeof ( *pkb ) ); + if ( ! data ) + return NULL; + + pkb = ( struct pk_buff * ) ( data + len ); + pkb->head = pkb->data = pkb->tail = data; + pkb->end = pkb; + return pkb; +} + +/** + * Free packet buffer + * + * @v pkb Packet buffer + */ +void free_pkb ( struct pk_buff *pkb ) { + if ( pkb ) { + gfree ( pkb->head, + ( pkb->end - pkb->head ) + sizeof ( *pkb ) ); + } +} diff --git a/src/proto/ip.c b/src/proto/ip.c deleted file mode 100644 index a0f36d74..00000000 --- a/src/proto/ip.c +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include -#include -#include -#include -#include "uip/uip.h" -#include "uip/uip_arp.h" - -/** @file - * - * IP protocol - * - * The gPXE IP stack is currently implemented on top of the uIP - * protocol stack. This file provides wrappers around uIP so that - * higher-level protocol implementations do not need to talk directly - * to uIP (which has a somewhat baroque API). - * - */ - -/** - * Set IP address - * - */ -void set_ipaddr ( struct in_addr address ) { - union { - struct in_addr address; - uint16_t uip_address[2]; - } u; - - u.address = address; - uip_sethostaddr ( u.uip_address ); -} - -/** - * Set netmask - * - */ -void set_netmask ( struct in_addr address ) { - union { - struct in_addr address; - uint16_t uip_address[2]; - } u; - - u.address = address; - uip_setnetmask ( u.uip_address ); -} - -/** - * Set default gateway - * - */ -void set_gateway ( struct in_addr address ) { - union { - struct in_addr address; - uint16_t uip_address[2]; - } u; - - u.address = address; - uip_setdraddr ( u.uip_address ); -} - -/** - * Initialise TCP/IP stack - * - */ -void init_tcpip ( void ) { - uip_init(); - uip_arp_init(); -} - -#define UIP_HLEN ( 40 + UIP_LLH_LEN ) - -/** - * Transmit TCP data - * - * This is a wrapper around netdev_transmit(). It gathers up the - * packet produced by uIP, and then passes it to netdev_transmit() as - * a single buffer. - */ -static void uip_transmit ( void ) { - uip_arp_out(); - if ( uip_len > UIP_HLEN ) { - memcpy ( uip_buf + UIP_HLEN, ( void * ) uip_appdata, - uip_len - UIP_HLEN ); - } - netdev_transmit ( uip_buf, uip_len ); - uip_len = 0; -} - -/** - * Run the TCP/IP stack - * - * Call this function in a loop in order to allow TCP/IP processing to - * take place. This call takes the stack through a single iteration; - * it will typically be used in a loop such as - * - * @code - * - * struct tcp_connection *my_connection; - * ... - * tcp_connect ( my_connection ); - * while ( ! my_connection->finished ) { - * run_tcpip(); - * } - * - * @endcode - * - * where @c my_connection->finished is set by one of the connection's - * #tcp_operations methods to indicate completion. - */ -void run_tcpip ( void ) { - void *data; - size_t len; - uint16_t type; - int i; - - if ( netdev_poll ( 1, &data, &len ) ) { - /* We have data */ - memcpy ( uip_buf, data, len ); - uip_len = len; - type = ntohs ( *( ( uint16_t * ) ( uip_buf + 12 ) ) ); - if ( type == UIP_ETHTYPE_ARP ) { - uip_arp_arpin(); - } else { - uip_arp_ipin(); - uip_input(); - } - if ( uip_len > 0 ) - uip_transmit(); - } else { - for ( i = 0 ; i < UIP_CONNS ; i++ ) { - uip_periodic ( i ); - if ( uip_len > 0 ) - uip_transmit(); - } - } -}