2006-04-19 12:07:46 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
|
|
|
|
*
|
|
|
|
* 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 <stdint.h>
|
|
|
|
#include <byteswap.h>
|
2006-04-24 15:33:06 +00:00
|
|
|
#include <string.h>
|
2006-04-19 12:07:46 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <gpxe/if_ether.h>
|
|
|
|
#include <gpxe/pkbuff.h>
|
2006-04-24 15:33:06 +00:00
|
|
|
#include <gpxe/tables.h>
|
2006-04-29 17:17:43 +00:00
|
|
|
#include <gpxe/process.h>
|
2006-04-30 01:08:52 +00:00
|
|
|
#include <gpxe/init.h>
|
2006-04-19 12:07:46 +00:00
|
|
|
#include <gpxe/netdevice.h>
|
|
|
|
|
|
|
|
/** @file
|
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* Network device management
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2006-04-24 15:33:06 +00:00
|
|
|
* 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.
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* No code outside of netdevice.c should ever refer directly to @c
|
|
|
|
* static_single_netdev.
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* 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.
|
2006-04-19 12:07:46 +00:00
|
|
|
*/
|
2006-04-24 15:33:06 +00:00
|
|
|
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 );
|
2006-04-19 12:07:46 +00:00
|
|
|
|
2006-04-28 14:13:50 +00:00
|
|
|
/**
|
|
|
|
* Add packet to receive queue
|
|
|
|
*
|
|
|
|
* @v netdev Network device
|
|
|
|
* @v pkb Packet buffer
|
|
|
|
*
|
2006-04-29 17:17:43 +00:00
|
|
|
* The packet is added to the RX queue. This function takes ownership
|
|
|
|
* of the packet buffer.
|
2006-04-28 14:13:50 +00:00
|
|
|
*/
|
|
|
|
void netdev_rx ( struct net_device *netdev, struct pk_buff *pkb ) {
|
|
|
|
DBG ( "Packet received\n" );
|
|
|
|
pkb->ll_protocol = netdev->ll_protocol;
|
|
|
|
list_add_tail ( &pkb->list, &rx_queue );
|
|
|
|
}
|
|
|
|
|
2006-04-19 12:07:46 +00:00
|
|
|
/**
|
2006-04-24 15:33:06 +00:00
|
|
|
* Identify network protocol
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* @v net_proto Network-layer protocol, in network-byte order
|
|
|
|
* @ret net_protocol Network-layer protocol, or NULL
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* Identify a network-layer protocol from a protocol number, which
|
|
|
|
* must be an ETH_P_XXX constant in network-byte order.
|
2006-04-19 12:07:46 +00:00
|
|
|
*/
|
2006-04-28 14:13:50 +00:00
|
|
|
struct net_protocol * find_net_protocol ( uint16_t net_proto ) {
|
2006-04-24 15:33:06 +00:00
|
|
|
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;
|
2006-04-19 12:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-04-24 15:33:06 +00:00
|
|
|
* Identify network device by network-layer address
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* @v net_protocol Network-layer protocol
|
|
|
|
* @v net_addr Network-layer address
|
|
|
|
* @ret netdev Network device, or NULL
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* 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.
|
2006-04-19 12:07:46 +00:00
|
|
|
*/
|
2006-04-28 14:13:50 +00:00
|
|
|
struct net_device *
|
|
|
|
find_netdev_by_net_addr ( struct net_protocol *net_protocol,
|
|
|
|
void *net_addr ) {
|
2006-04-24 15:33:06 +00:00
|
|
|
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;
|
2006-04-19 12:07:46 +00:00
|
|
|
}
|
2006-04-28 14:13:50 +00:00
|
|
|
|
2006-04-24 15:33:06 +00:00
|
|
|
return NULL;
|
2006-04-19 12:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-04-28 14:13:50 +00:00
|
|
|
* Transmit packet via a network device
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* @v pkb Packet buffer
|
2006-04-28 14:13:50 +00:00
|
|
|
* @v netdev Network device, or NULL
|
2006-04-24 15:33:06 +00:00
|
|
|
* @ret rc Return status code
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-28 14:13:50 +00:00
|
|
|
* Transmits the packet via the specified network device. The packet
|
|
|
|
* must begin with a network-layer header, and the @c net_protocol
|
|
|
|
* field must have been filled in. If @c netdev is NULL, the network
|
2006-04-29 17:17:43 +00:00
|
|
|
* device is identified via the packet contents, if possible. This
|
|
|
|
* function takes ownership of the packet buffer.
|
2006-04-19 12:07:46 +00:00
|
|
|
*/
|
2006-04-28 14:13:50 +00:00
|
|
|
int net_transmit_via ( struct pk_buff *pkb, struct net_device *netdev ) {
|
2006-04-24 15:33:06 +00:00
|
|
|
struct net_protocol *net_protocol;
|
|
|
|
struct net_header nethdr;
|
|
|
|
struct ll_protocol *ll_protocol;
|
|
|
|
struct ll_header llhdr;
|
2006-04-19 12:07:46 +00:00
|
|
|
int rc;
|
|
|
|
|
2006-04-24 15:33:06 +00:00
|
|
|
/* Perform network-layer routing */
|
|
|
|
net_protocol = pkb->net_protocol;
|
|
|
|
nethdr.net_protocol = net_protocol;
|
2006-04-28 14:13:50 +00:00
|
|
|
if ( ( rc = net_protocol->route ( pkb, &nethdr ) ) != 0 ) {
|
|
|
|
DBG ( "Could not route to %s address %s\n",
|
|
|
|
net_protocol->name,
|
|
|
|
net_protocol->ntoa ( nethdr.dest_net_addr ) );
|
2006-04-29 17:17:43 +00:00
|
|
|
free_pkb ( pkb );
|
2006-04-28 14:13:50 +00:00
|
|
|
return rc;
|
|
|
|
}
|
2006-04-24 15:33:06 +00:00
|
|
|
|
2006-04-28 14:13:50 +00:00
|
|
|
/* Identify transmitting network device, if not specified */
|
|
|
|
if ( ! netdev ) {
|
|
|
|
netdev = find_netdev_by_net_addr ( net_protocol,
|
|
|
|
nethdr.source_net_addr );
|
|
|
|
if ( ! netdev ) {
|
|
|
|
DBG ( "No network device for %s address %s\n",
|
|
|
|
net_protocol->name,
|
|
|
|
net_protocol->ntoa ( nethdr.source_net_addr ) );
|
2006-04-29 17:17:43 +00:00
|
|
|
free_pkb ( pkb );
|
2006-04-28 14:13:50 +00:00
|
|
|
return -EHOSTUNREACH;
|
|
|
|
}
|
|
|
|
}
|
2006-04-24 15:33:06 +00:00
|
|
|
|
|
|
|
/* Perform link-layer routing */
|
|
|
|
ll_protocol = netdev->ll_protocol;
|
|
|
|
llhdr.ll_protocol = ll_protocol;
|
2006-04-28 14:13:50 +00:00
|
|
|
if ( ( rc = ll_protocol->route ( netdev, &nethdr, &llhdr ) ) != 0 ) {
|
|
|
|
DBG ( "No link-layer route to %s address %s\n",
|
|
|
|
net_protocol->name,
|
|
|
|
net_protocol->ntoa ( nethdr.dest_net_addr ) );
|
2006-04-29 17:17:43 +00:00
|
|
|
free_pkb ( pkb );
|
2006-04-28 14:13:50 +00:00
|
|
|
return rc;
|
|
|
|
}
|
2006-04-24 15:33:06 +00:00
|
|
|
|
|
|
|
/* Prepend link-layer header */
|
|
|
|
pkb_push ( pkb, ll_protocol->ll_header_len );
|
|
|
|
ll_protocol->fill_llh ( &llhdr, pkb );
|
|
|
|
|
2006-04-29 17:17:43 +00:00
|
|
|
/* Hand off packet to network device */
|
2006-04-28 14:13:50 +00:00
|
|
|
if ( ( rc = netdev->transmit ( netdev, pkb ) ) != 0 ) {
|
|
|
|
DBG ( "Device failed to transmit packet\n" );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG ( "Packet transmitted\n" );
|
2006-04-24 15:33:06 +00:00
|
|
|
return 0;
|
2006-04-28 14:13:50 +00:00
|
|
|
}
|
2006-04-24 15:33:06 +00:00
|
|
|
|
2006-04-28 14:13:50 +00:00
|
|
|
/**
|
|
|
|
* Transmit packet
|
|
|
|
*
|
|
|
|
* @v pkb Packet buffer
|
|
|
|
* @ret rc Return status code
|
|
|
|
*
|
2006-04-29 17:17:43 +00:00
|
|
|
* Transmits the packet via the appropriate network device. This
|
|
|
|
* function takes ownership of the packet buffer.
|
2006-04-28 14:13:50 +00:00
|
|
|
*/
|
|
|
|
int net_transmit ( struct pk_buff *pkb ) {
|
|
|
|
return net_transmit_via ( pkb, NULL );
|
2006-04-19 12:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-04-24 15:33:06 +00:00
|
|
|
* Poll for packet on all network devices
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* @ret True There are packets present in the receive queue
|
|
|
|
* @ret False There are no packets present in the receive queue
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* Polls all network devices for received packets. Any received
|
|
|
|
* packets will be added to the RX packet queue via netdev_rx().
|
2006-04-19 12:07:46 +00:00
|
|
|
*/
|
2006-04-24 15:33:06 +00:00
|
|
|
int net_poll ( void ) {
|
|
|
|
struct net_device *netdev = &static_single_netdev;
|
|
|
|
|
2006-04-28 14:13:50 +00:00
|
|
|
DBG ( "Polling network\n" );
|
2006-04-24 15:33:06 +00:00
|
|
|
netdev->poll ( netdev );
|
2006-04-19 12:07:46 +00:00
|
|
|
|
2006-04-24 15:33:06 +00:00
|
|
|
return ( ! list_empty ( &rx_queue ) );
|
2006-04-19 12:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-04-24 15:33:06 +00:00
|
|
|
* Remove packet from receive queue
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* @ret pkb Packet buffer, or NULL
|
2006-04-19 12:07:46 +00:00
|
|
|
*
|
2006-04-24 15:33:06 +00:00
|
|
|
* Removes the first packet from the RX queue and returns it.
|
|
|
|
* Ownership of the packet is transferred to the caller.
|
2006-04-19 12:07:46 +00:00
|
|
|
*/
|
2006-04-24 15:33:06 +00:00
|
|
|
struct pk_buff * net_rx_dequeue ( void ) {
|
|
|
|
struct pk_buff *pkb;
|
2006-04-19 12:07:46 +00:00
|
|
|
|
2006-04-24 15:33:06 +00:00
|
|
|
list_for_each_entry ( pkb, &rx_queue, list ) {
|
|
|
|
list_del ( &pkb->list );
|
|
|
|
return pkb;
|
2006-04-19 12:07:46 +00:00
|
|
|
}
|
2006-04-24 15:33:06 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2006-04-19 12:07:46 +00:00
|
|
|
|
2006-04-29 17:17:43 +00:00
|
|
|
/**
|
|
|
|
* Process received packet
|
|
|
|
*
|
|
|
|
* @v pkb Packet buffer
|
|
|
|
* @ret rc Return status code
|
|
|
|
*
|
|
|
|
* Processes a packet received from the network (and, usually, removed
|
|
|
|
* from the RX queue by net_rx_dequeue()). This call takes ownership
|
|
|
|
* of the packet buffer.
|
|
|
|
*/
|
|
|
|
int net_rx_process ( struct pk_buff *pkb ) {
|
2006-04-24 15:33:06 +00:00
|
|
|
struct ll_protocol *ll_protocol;
|
|
|
|
struct ll_header llhdr;
|
|
|
|
struct net_protocol *net_protocol;
|
2006-04-29 17:17:43 +00:00
|
|
|
int rc;
|
2006-04-24 15:33:06 +00:00
|
|
|
|
2006-04-29 17:17:43 +00:00
|
|
|
/* Parse link-layer header */
|
|
|
|
ll_protocol = pkb->ll_protocol;
|
|
|
|
ll_protocol->parse_llh ( pkb, &llhdr );
|
|
|
|
|
|
|
|
/* Identify network-layer protocol */
|
|
|
|
net_protocol = find_net_protocol ( llhdr.net_proto );
|
|
|
|
if ( ! net_protocol ) {
|
|
|
|
DBG ( "Unknown network-layer protocol %x\n",
|
|
|
|
ntohs ( llhdr.net_proto ) );
|
|
|
|
free_pkb ( pkb );
|
|
|
|
return -EPROTONOSUPPORT;
|
|
|
|
}
|
|
|
|
pkb->net_protocol = net_protocol;
|
|
|
|
|
|
|
|
/* Strip off link-layer header */
|
|
|
|
pkb_pull ( pkb, ll_protocol->ll_header_len );
|
|
|
|
|
|
|
|
/* Hand off to network layer */
|
|
|
|
if ( ( rc = net_protocol->rx_process ( pkb ) ) != 0 ) {
|
|
|
|
DBG ( "Network-layer protocol dropped packet\n" );
|
|
|
|
return rc;
|
|
|
|
}
|
2006-04-24 15:33:06 +00:00
|
|
|
|
2006-04-29 17:17:43 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2006-04-24 15:33:06 +00:00
|
|
|
|
2006-04-29 17:17:43 +00:00
|
|
|
/**
|
|
|
|
* Single-step the network stack
|
|
|
|
*
|
|
|
|
* @v process Network stack process
|
|
|
|
*
|
|
|
|
* This polls all interfaces for any received packets, and processes
|
|
|
|
* any packets that are received during this poll.
|
|
|
|
*/
|
|
|
|
static void net_step ( struct process *process ) {
|
|
|
|
struct pk_buff *pkb;
|
|
|
|
|
|
|
|
/* Poll for new packets */
|
|
|
|
net_poll();
|
2006-04-28 14:13:50 +00:00
|
|
|
|
2006-04-29 17:17:43 +00:00
|
|
|
/* Handle any received packets */
|
|
|
|
while ( ( pkb = net_rx_dequeue () ) ) {
|
|
|
|
net_rx_process ( pkb );
|
2006-04-28 14:13:50 +00:00
|
|
|
DBG ( "Processed received packet\n" );
|
2006-04-24 15:33:06 +00:00
|
|
|
}
|
2006-04-29 17:17:43 +00:00
|
|
|
|
|
|
|
/* Re-schedule ourself */
|
|
|
|
schedule ( process );
|
2006-04-19 12:07:46 +00:00
|
|
|
}
|
2006-04-24 15:33:06 +00:00
|
|
|
|
2006-04-29 17:17:43 +00:00
|
|
|
/** Networking stack process */
|
|
|
|
static struct process net_process = {
|
|
|
|
.step = net_step,
|
|
|
|
};
|
|
|
|
|
2006-04-30 01:08:52 +00:00
|
|
|
/** Initialise the networking stack process */
|
2006-04-29 17:17:43 +00:00
|
|
|
static void init_net ( void ) {
|
|
|
|
schedule ( &net_process );
|
|
|
|
}
|
2006-04-24 15:33:06 +00:00
|
|
|
|
2006-04-30 01:08:52 +00:00
|
|
|
INIT_FN ( INIT_PROCESS, init_net, NULL, NULL );
|