2
0
mirror of https://github.com/xcat2/xNBA.git synced 2024-11-25 02:50:17 +00:00
Conflicts:
	src/config/general.h
This commit is contained in:
Jarrod Johnson 2013-10-01 08:46:03 -04:00
commit d603ea68c4
53 changed files with 3156 additions and 1267 deletions

View File

@ -103,13 +103,15 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) {
/* Call loader */
undi_loader_entry = undirom->loader_entry;
__asm__ __volatile__ ( REAL_CODE ( "pushw %%ds\n\t"
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"pushw %%ds\n\t"
"pushw %%ax\n\t"
"lcall *undi_loader_entry\n\t"
"addw $4, %%sp\n\t" )
"popl %%ebp\n\t" /* discard */
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( exit )
: "a" ( __from_data16 ( &undi_loader ) )
: "ebx", "ecx", "edx", "esi", "edi", "ebp" );
: "ebx", "ecx", "edx", "esi", "edi" );
if ( exit != PXENV_EXIT_SUCCESS ) {
/* Clear entry point */

View File

@ -167,7 +167,8 @@ static void bios_putchar ( int character ) {
return;
/* Print character with attribute */
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"sti\n\t"
/* Skip non-printable characters */
"cmpb $0x20, %%al\n\t"
"jb 1f\n\t"
@ -188,11 +189,11 @@ static void bios_putchar ( int character ) {
"xorw %%bx, %%bx\n\t"
"movb $0x0e, %%ah\n\t"
"int $0x10\n\t"
"cli\n\t" )
"cli\n\t"
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( discard_a ), "=b" ( discard_b ),
"=c" ( discard_c )
: "a" ( character ), "b" ( bios_attr )
: "ebp" );
: "a" ( character ), "b" ( bios_attr ) );
}
/**

View File

@ -80,6 +80,8 @@ int call_bootsector ( unsigned int segment, unsigned int offset,
"movw %%ss, %%ax\n\t"
"movw %%ax, %%cs:saved_ss\n\t"
"movw %%sp, %%cs:saved_sp\n\t"
/* Save frame pointer (gcc bug) */
"movl %%ebp, %%cs:saved_ebp\n\t"
/* Prepare jump to boot sector */
"pushw %%bx\n\t"
"pushw %%di\n\t"
@ -99,11 +101,14 @@ int call_bootsector ( unsigned int segment, unsigned int offset,
"sti\n\t"
"lret\n\t"
/* Preserved variables */
"\nsaved_ebp: .long 0\n\t"
"\nsaved_ss: .word 0\n\t"
"\nsaved_sp: .word 0\n\t"
"\nsaved_retaddr: .word 0\n\t"
/* Boot failure return point */
"\nbootsector_exec_fail:\n\t"
/* Restore frame pointer (gcc bug) */
"movl %%cs:saved_ebp, %%ebp\n\t"
/* Restore stack pointer */
"movw %%cs:saved_ss, %%ax\n\t"
"movw %%ax, %%ss\n\t"
@ -114,7 +119,7 @@ int call_bootsector ( unsigned int segment, unsigned int offset,
"=d" ( discard_d )
: "b" ( segment ), "D" ( offset ),
"d" ( drive )
: "eax", "ecx", "esi", "ebp" );
: "eax", "ecx", "esi" );
DBG ( "Booted disk returned via INT 18 or 19\n" );

View File

@ -60,10 +60,11 @@ static int elfboot_exec ( struct image *image ) {
/* Jump to OS with flat physical addressing */
DBGC ( image, "ELF %p starting execution at %lx\n", image, entry );
__asm__ __volatile__ ( PHYS_CODE ( "call *%%edi\n\t" )
__asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"call *%%edi\n\t"
"popl %%ebp\n\t" /* gcc bug */ )
: : "D" ( entry )
: "eax", "ebx", "ecx", "edx", "esi", "ebp",
"memory" );
: "eax", "ebx", "ecx", "edx", "esi", "memory" );
DBGC ( image, "ELF %p returned\n", image );

View File

@ -248,7 +248,8 @@ static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
imgheader->execaddr.segoff.offset );
__asm__ __volatile__ (
REAL_CODE ( "pushw %%ds\n\t" /* far pointer to bootp data */
REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"pushw %%ds\n\t" /* far pointer to bootp data */
"pushw %%bx\n\t"
"pushl %%esi\n\t" /* location */
"pushw %%cs\n\t" /* lcall execaddr */
@ -258,13 +259,14 @@ static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
"pushl %%edi\n\t"
"lret\n\t"
"\n2:\n\t"
"addw $8,%%sp\n\t" /* clean up stack */ )
"addw $8,%%sp\n\t" /* clean up stack */
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ),
"=b" ( discard_b )
: "D" ( imgheader->execaddr.segoff ),
"S" ( imgheader->location ),
"b" ( __from_data16 ( basemem_packet ) )
: "ecx", "edx", "ebp" );
: "ecx", "edx" );
return rc;
}
@ -288,11 +290,13 @@ static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
/* Jump to OS with flat physical addressing */
__asm__ __volatile__ (
PHYS_CODE ( "pushl %%ebx\n\t" /* bootp data */
PHYS_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"pushl %%ebx\n\t" /* bootp data */
"pushl %%esi\n\t" /* imgheader */
"pushl %%eax\n\t" /* loaderinfo */
"call *%%edi\n\t"
"addl $12, %%esp\n\t" /* clean up stack */ )
"addl $12, %%esp\n\t" /* clean up stack */
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ),
"=b" ( discard_b )
: "D" ( imgheader->execaddr.linear ),
@ -300,7 +304,7 @@ static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
imgheader->location.offset ),
"b" ( virt_to_phys ( basemem_packet ) ),
"a" ( virt_to_phys ( &loaderinfo ) )
: "ecx", "edx", "ebp", "memory" );
: "ecx", "edx", "memory" );
return rc;
}

View File

@ -143,16 +143,18 @@ int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
/* Call real-mode entry point. This calling convention will
* work with both the !PXE and the PXENV+ entry points.
*/
__asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"pushw %%es\n\t"
"pushw %%di\n\t"
"pushw %%bx\n\t"
"lcall *pxeparent_entry_point\n\t"
"addw $6, %%sp\n\t" )
"addw $6, %%sp\n\t"
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( exit ), "=b" ( discard_b ),
"=D" ( discard_D )
: "b" ( function ),
"D" ( __from_data16 ( &pxeparent_params ) )
: "ecx", "edx", "esi", "ebp" );
: "ecx", "edx", "esi" );
/* Determine return status code based on PXENV_EXIT and
* PXENV_STATUS

View File

@ -101,6 +101,9 @@ REQUIRE_OBJECT ( debugcon );
#ifdef NET_PROTO_IPV4
REQUIRE_OBJECT ( ipv4 );
#endif
#ifdef NET_PROTO_IPV6
REQUIRE_OBJECT ( ipv6 );
#endif
/*
* Drag in all requested PXE support
@ -266,6 +269,9 @@ REQUIRE_OBJECT ( pci_cmd );
#ifdef PARAM_CMD
REQUIRE_OBJECT ( param_cmd );
#endif
#ifdef NEIGHBOUR_CMD
REQUIRE_OBJECT ( neighbour_cmd );
#endif
/*
* Drag in miscellaneous objects

27
src/config/config_route.c Normal file
View File

@ -0,0 +1,27 @@
/*
* 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, or (at
* your option) any later version.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <config/general.h>
/** @file
*
* Routing management configuration options
*
*/
/*
* Drag in routing management for relevant protocols
*
*/
#ifdef NET_PROTO_IPV4
REQUIRE_OBJECT ( route_ipv4 );
#endif
#ifdef NET_PROTO_IPV6
REQUIRE_OBJECT ( route_ipv6 );
#endif

View File

@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
*/
#define NET_PROTO_IPV4 /* IPv4 protocol */
#undef NET_PROTO_IPV6 /* IPv6 protocol */
#undef NET_PROTO_FCOE /* Fibre Channel over Ethernet protocol */
/*
@ -128,6 +129,17 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define IMAGE_TRUST_CMD /* Image trust management commands */
#undef SYNC_CMD /* Sync command */
#undef NSLOOKUP_CMD /* DNS resolving command */
//#define TIME_CMD /* Time commands */
//#define DIGEST_CMD /* Image crypto digest commands */
//#define LOTEST_CMD /* Loopback testing commands */
//#define VLAN_CMD /* VLAN commands */
//#define PXE_CMD /* PXE commands */
//#define REBOOT_CMD /* Reboot command */
//#define POWEROFF_CMD /* Power off command */
//#define IMAGE_TRUST_CMD /* Image trust management commands */
//#define PCI_CMD /* PCI commands */
//#define PARAM_CMD /* Form parameter commands */
//#define NEIGHBOUR_CMD /* Neighbour management commands */
/*
* ROM-specific options

View File

@ -2247,29 +2247,8 @@ struct setting priority_setting __setting ( SETTING_MISC ) = {
******************************************************************************
*/
/** A built-in setting operation */
struct builtin_setting_operation {
/** Setting */
struct setting *setting;
/** Fetch setting value
*
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
int ( * fetch ) ( void *data, size_t len );
};
/** Built-in setting scope */
static struct settings_scope builtin_scope;
/** Error number setting */
struct setting errno_setting __setting ( SETTING_MISC ) = {
.name = "errno",
.description = "Last error",
.type = &setting_type_uint32,
.scope = &builtin_scope,
};
struct settings_scope builtin_scope;
/**
* Fetch error number setting
@ -2289,14 +2268,20 @@ static int errno_fetch ( void *data, size_t len ) {
return sizeof ( content );
}
/** Build architecture setting */
struct setting buildarch_setting __setting ( SETTING_MISC ) = {
.name = "buildarch",
.description = "Build architecture",
.type = &setting_type_string,
/** Error number setting */
struct setting errno_setting __setting ( SETTING_MISC ) = {
.name = "errno",
.description = "Last error",
.type = &setting_type_uint32,
.scope = &builtin_scope,
};
/** Error number built-in setting */
struct builtin_setting errno_builtin_setting __builtin_setting = {
.setting = &errno_setting,
.fetch = errno_fetch,
};
/**
* Fetch build architecture setting
*
@ -2311,14 +2296,20 @@ static int buildarch_fetch ( void *data, size_t len ) {
return ( sizeof ( buildarch ) - 1 /* NUL */ );
}
/** Platform setting */
struct setting platform_setting __setting ( SETTING_MISC ) = {
.name = "platform",
.description = "Platform",
/** Build architecture setting */
struct setting buildarch_setting __setting ( SETTING_MISC ) = {
.name = "buildarch",
.description = "Build architecture",
.type = &setting_type_string,
.scope = &builtin_scope,
};
/** Build architecture built-in setting */
struct builtin_setting buildarch_builtin_setting __builtin_setting = {
.setting = &buildarch_setting,
.fetch = buildarch_fetch,
};
/**
* Fetch platform setting
*
@ -2333,14 +2324,20 @@ static int platform_fetch ( void *data, size_t len ) {
return ( sizeof ( platform ) - 1 /* NUL */ );
}
/** Version setting */
struct setting version_setting __setting ( SETTING_MISC ) = {
.name = "version",
.description = "Version",
/** Platform setting */
struct setting platform_setting __setting ( SETTING_MISC ) = {
.name = "platform",
.description = "Platform",
.type = &setting_type_string,
.scope = &builtin_scope,
};
/** Platform built-in setting */
struct builtin_setting platform_builtin_setting __builtin_setting = {
.setting = &platform_setting,
.fetch = platform_fetch,
};
/**
* Fetch version setting
*
@ -2353,12 +2350,18 @@ static int version_fetch ( void *data, size_t len ) {
return ( strlen ( product_version ) );
}
/** List of built-in setting operations */
static struct builtin_setting_operation builtin_setting_operations[] = {
{ &errno_setting, errno_fetch },
{ &buildarch_setting, buildarch_fetch },
{ &platform_setting, platform_fetch },
{ &version_setting, version_fetch },
/** Version setting */
struct setting version_setting __setting ( SETTING_MISC ) = {
.name = "version",
.description = "Version",
.type = &setting_type_string,
.scope = &builtin_scope,
};
/** Version built-in setting */
struct builtin_setting version_builtin_setting __builtin_setting = {
.setting = &version_setting,
.fetch = version_fetch,
};
/**
@ -2373,12 +2376,9 @@ static struct builtin_setting_operation builtin_setting_operations[] = {
static int builtin_fetch ( struct settings *settings __unused,
struct setting *setting,
void *data, size_t len ) {
struct builtin_setting_operation *builtin;
unsigned int i;
struct builtin_setting *builtin;
for ( i = 0 ; i < ( sizeof ( builtin_setting_operations ) /
sizeof ( builtin_setting_operations[0] ) ) ; i++ ) {
builtin = &builtin_setting_operations[i];
for_each_table_entry ( builtin, BUILTIN_SETTINGS ) {
if ( setting_cmp ( setting, builtin->setting ) == 0 )
return builtin->fetch ( data, len );
}

View File

@ -200,11 +200,6 @@ static int tap_probe(struct linux_device *device, struct linux_device_request *r
netdev->dev = &device->dev;
memset(nic, 0, sizeof(*nic));
if ((rc = register_netdev(netdev)) != 0)
goto err_register;
netdev_link_up(netdev);
/* Look for the mandatory if setting */
if_setting = linux_find_setting("if", &request->settings);
@ -216,11 +211,20 @@ static int tap_probe(struct linux_device *device, struct linux_device_request *r
}
nic->interface = if_setting->value;
snprintf ( device->dev.name, sizeof ( device->dev.name ), "%s",
nic->interface );
device->dev.desc.bus_type = BUS_TYPE_TAP;
if_setting->applied = 1;
/* Apply rest of the settings */
linux_apply_settings(&request->settings, &netdev->settings.settings);
/* Register network device */
if ((rc = register_netdev(netdev)) != 0)
goto err_register;
netdev_link_up(netdev);
return 0;
err_settings:

View File

@ -265,6 +265,7 @@ struct ll_protocol ipoib_protocol __ll_protocol = {
.ntoa = eth_ntoa,
.mc_hash = eth_mc_hash,
.eth_addr = eth_eth_addr,
.eui64 = eth_eui64,
.flags = LL_NAME_ONLY,
};

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
/** @file
*
* Neighbour management commands
*
*/
#include <getopt.h>
#include <ipxe/parseopt.h>
#include <ipxe/command.h>
#include <usr/neighmgmt.h>
/** "nstat" options */
struct nstat_options {};
/** "nstat" option list */
static struct option_descriptor nstat_opts[] = {};
/** "nstat" command descriptor */
static struct command_descriptor nstat_cmd =
COMMAND_DESC ( struct nstat_options, nstat_opts, 0, 0, "" );
/**
* The "nstat" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int nstat_exec ( int argc, char **argv ) {
struct nstat_options opts;
int rc;
/* Parse options */
if ( ( rc = parse_options ( argc, argv, &nstat_cmd, &opts ) ) != 0)
return rc;
nstat();
return 0;
}
/** Neighbour management commands */
struct command neighbour_commands[] __command = {
{
.name = "nstat",
.exec = nstat_exec,
},
};

View File

@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/tables.h>
#include <ipxe/netdevice.h>
#include <ipxe/neighbour.h>
/** A network-layer protocol that relies upon ARP */
struct arp_net_protocol {
@ -34,9 +35,26 @@ struct arp_net_protocol {
#define __arp_net_protocol __table_entry ( ARP_NET_PROTOCOLS, 01 )
extern struct net_protocol arp_protocol __net_protocol;
extern struct neighbour_discovery arp_discovery;
extern int arp_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol, const void *net_dest,
const void *net_source, const void *ll_source );
/**
* Transmit packet, determining link-layer address via ARP
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @v net_source Source network-layer address
* @v ll_source Source link-layer address
* @ret rc Return status code
*/
static inline int arp_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *net_source,
const void *ll_source ) {
return neighbour_tx ( iobuf, netdev, net_protocol, net_dest,
&arp_discovery, net_source, ll_source );
}
#endif /* _IPXE_ARP_H */

View File

@ -54,6 +54,9 @@ struct device_description {
/** ISA bus type */
#define BUS_TYPE_ISA 5
/** TAP bus type */
#define BUS_TYPE_TAP 6
/** A hardware device */
struct device {
/** Name */

View File

@ -214,6 +214,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_nfs_open ( ERRFILE_NET | 0x00340000 )
#define ERRFILE_mount ( ERRFILE_NET | 0x00350000 )
#define ERRFILE_oncrpc_iob ( ERRFILE_NET | 0x00360000 )
#define ERRFILE_neighbour ( ERRFILE_NET | 0x00370000 )
#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )

View File

@ -91,6 +91,7 @@ extern const char * eth_ntoa ( const void *ll_addr );
extern int eth_mc_hash ( unsigned int af, const void *net_addr,
void *ll_addr );
extern int eth_eth_addr ( const void *ll_addr, void *eth_addr );
extern int eth_eui64 ( const void *ll_addr, void *eui64 );
extern struct net_device * alloc_etherdev ( size_t priv_size );
#endif /* _IPXE_ETHERNET_H */

View File

@ -0,0 +1,68 @@
#ifndef _IPXE_FRAGMENT_H
#define _IPXE_FRAGMENT_H
/** @file
*
* Fragment reassembly
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <ipxe/list.h>
#include <ipxe/iobuf.h>
#include <ipxe/retry.h>
/** Fragment reassembly timeout */
#define FRAGMENT_TIMEOUT ( TICKS_PER_SEC / 2 )
/** A fragment reassembly buffer */
struct fragment {
/* List of fragment reassembly buffers */
struct list_head list;
/** Reassembled packet */
struct io_buffer *iobuf;
/** Length of non-fragmentable portion of reassembled packet */
size_t hdrlen;
/** Reassembly timer */
struct retry_timer timer;
};
/** A fragment reassembler */
struct fragment_reassembler {
/** List of fragment reassembly buffers */
struct list_head list;
/**
* Check if fragment matches fragment reassembly buffer
*
* @v fragment Fragment reassembly buffer
* @v iobuf I/O buffer
* @v hdrlen Length of non-fragmentable potion of I/O buffer
* @ret is_fragment Fragment matches this reassembly buffer
*/
int ( * is_fragment ) ( struct fragment *fragment,
struct io_buffer *iobuf, size_t hdrlen );
/**
* Get fragment offset
*
* @v iobuf I/O buffer
* @v hdrlen Length of non-fragmentable potion of I/O buffer
* @ret offset Offset
*/
size_t ( * fragment_offset ) ( struct io_buffer *iobuf, size_t hdrlen );
/**
* Check if more fragments exist
*
* @v iobuf I/O buffer
* @v hdrlen Length of non-fragmentable potion of I/O buffer
* @ret more_frags More fragments exist
*/
int ( * more_fragments ) ( struct io_buffer *iobuf, size_t hdrlen );
};
extern struct io_buffer *
fragment_reassemble ( struct fragment_reassembler *fragments,
struct io_buffer *iobuf, size_t *hdrlen );
#endif /* _IPXE_FRAGMENT_H */

View File

@ -1,59 +0,0 @@
#ifndef _IPXE_ICMP6_H
#define _IPXE_ICMP6_H
/** @file
*
* ICMP6 protocol
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/ip6.h>
#include <ipxe/ndp.h>
#define ICMP6_NSOLICIT 135
#define ICMP6_NADVERT 136
extern struct tcpip_protocol icmp6_protocol __tcpip_protocol;
struct icmp6_header {
uint8_t type;
uint8_t code;
uint16_t csum;
/* Message body */
};
struct neighbour_solicit {
uint8_t type;
uint8_t code;
uint16_t csum;
uint32_t reserved;
struct in6_addr target;
/* "Compulsory" options */
uint8_t opt_type;
uint8_t opt_len;
/* FIXME: hack alert */
uint8_t opt_ll_addr[6];
};
struct neighbour_advert {
uint8_t type;
uint8_t code;
uint16_t csum;
uint8_t flags;
uint8_t reserved;
struct in6_addr target;
uint8_t opt_type;
uint8_t opt_len;
/* FIXME: hack alert */
uint8_t opt_ll_addr[6];
};
#define ICMP6_FLAGS_ROUTER 0x80
#define ICMP6_FLAGS_SOLICITED 0x40
#define ICMP6_FLAGS_OVERRIDE 0x20
int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest );
#endif /* _IPXE_ICMP6_H */

78
src/include/ipxe/icmpv6.h Normal file
View File

@ -0,0 +1,78 @@
#ifndef _IPXE_ICMP6_H
#define _IPXE_ICMP6_H
/** @file
*
* ICMPv6 protocol
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <ipxe/tables.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
/** An ICMPv6 header */
struct icmpv6_header {
/** Type */
uint8_t type;
/** Code */
uint8_t code;
/** Checksum */
uint16_t chksum;
} __attribute__ (( packed ));
/** An ICMPv6 echo request/reply */
struct icmpv6_echo {
/** ICMPv6 header */
struct icmpv6_header icmp;
/** Identifier */
uint16_t ident;
/** Sequence number */
uint16_t sequence;
/** Data */
uint8_t data[0];
} __attribute__ (( packed ));
/** An ICMPv6 handler */
struct icmpv6_handler {
/** Type */
unsigned int type;
/** Process received packet
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v sin6_src Source socket address
* @v sin6_dest Destination socket address
* @ret rc Return status code
*
* This function takes ownership of the I/O buffer.
*/
int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev,
struct sockaddr_in6 *sin6_src,
struct sockaddr_in6 *sin6_dest );
};
/** ICMPv6 handler table */
#define ICMPV6_HANDLERS __table ( struct icmpv6_handler, "icmpv6_handlers" )
/** Declare an ICMPv6 handler */
#define __icmpv6_handler __table_entry ( ICMPV6_HANDLERS, 01 )
/** ICMPv6 echo request */
#define ICMPV6_ECHO_REQUEST 128
/** ICMPv6 echo reply */
#define ICMPV6_ECHO_REPLY 129
/** ICMPv6 neighbour solicitation */
#define ICMPV6_NDP_NEIGHBOUR_SOLICITATION 135
/** ICMPv6 neighbour advertisement */
#define ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT 136
extern struct tcpip_protocol icmpv6_protocol __tcpip_protocol;
#endif /* _IPXE_ICMP6_H */

View File

@ -50,6 +50,13 @@ struct in6_addr {
#define s6_addr32 in6_u.u6_addr32
};
#define IN6_IS_ADDR_MULTICAST( addr ) \
( *( ( const uint8_t * ) (addr) ) == 0xff )
#define IN6_IS_ADDR_LINKLOCAL( addr ) \
( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \
htonl ( 0xfe80 ) )
/**
* IPv4 socket address
*/
@ -90,9 +97,13 @@ struct sockaddr_in6 {
uint16_t sin6_flags;
/** TCP/IP port (part of struct @c sockaddr_tcpip) */
uint16_t sin6_port;
uint32_t sin6_flowinfo; /* Flow number */
struct in6_addr sin6_addr; /* 128-bit destination address */
uint32_t sin6_scope_id; /* Scope ID */
/** Scope ID
*
* For link-local addresses, this is the network device index.
*/
uint16_t sin6_scope_id;
/** IPv6 address */
struct in6_addr sin6_addr;
/** Padding
*
* This ensures that a struct @c sockaddr_in6 is large
@ -103,20 +114,13 @@ struct sockaddr_in6 {
( sizeof ( sa_family_t ) /* sin6_family */ +
sizeof ( uint16_t ) /* sin6_flags */ +
sizeof ( uint16_t ) /* sin6_port */ +
sizeof ( uint32_t ) /* sin6_flowinfo */ +
sizeof ( struct in6_addr ) /* sin6_addr */ +
sizeof ( uint32_t ) /* sin6_scope_id */ ) ];
sizeof ( uint16_t ) /* sin6_scope_id */ +
sizeof ( struct in6_addr ) /* sin6_addr */ ) ];
} __attribute__ (( may_alias ));
extern int inet_aton ( const char *cp, struct in_addr *inp );
extern char * inet_ntoa ( struct in_addr in );
/* Adding the following for IP6 support
*
extern int inet6_aton ( const char *cp, struct in6_addr *inp );
extern char * inet6_ntoa ( struct in_addr in );
*/
extern int inet6_aton ( const char *string, struct in6_addr *in );
extern char * inet6_ntoa ( const struct in6_addr *in );
#endif /* _IPXE_IN_H */

View File

@ -70,18 +70,6 @@ struct ipv4_miniroute {
struct in_addr gateway;
};
/* IPv4 fragment reassembly buffer */
struct ipv4_fragment {
/* List of fragment reassembly buffers */
struct list_head list;
/** Reassembled packet */
struct io_buffer *iobuf;
/** Current offset */
size_t offset;
/** Reassembly timer */
struct retry_timer timer;
};
extern struct list_head ipv4_miniroutes;
extern struct net_protocol ipv4_protocol __net_protocol;

View File

@ -1,80 +0,0 @@
#ifndef _IPXE_IP6_H
#define _IPXE_IP6_H
/** @file
*
* IP6 protocol
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <ipxe/in.h>
#include <ipxe/netdevice.h>
#include <ipxe/tcpip.h>
/* IP6 constants */
#define IP6_VERSION 0x6
#define IP6_HOP_LIMIT 255
/**
* I/O buffer contents
* This is duplicated in tcp.h and here. Ideally it should go into iobuf.h
*/
#define MAX_HDR_LEN 100
#define MAX_IOB_LEN 1500
#define MIN_IOB_LEN MAX_HDR_LEN + 100 /* To account for padding by LL */
#define IP6_EQUAL( in6_addr1, in6_addr2 ) \
( memcmp ( ( char* ) &( in6_addr1 ), ( char* ) &( in6_addr2 ),\
sizeof ( struct in6_addr ) ) == 0 )
#define IS_UNSPECIFIED( addr ) \
( ( (addr).in6_u.u6_addr32[0] == 0x00000000 ) && \
( (addr).in6_u.u6_addr32[1] == 0x00000000 ) && \
( (addr).in6_u.u6_addr32[2] == 0x00000000 ) && \
( (addr).in6_u.u6_addr32[3] == 0x00000000 ) )
/* IP6 header */
struct ip6_header {
uint32_t ver_traffic_class_flow_label;
uint16_t payload_len;
uint8_t nxt_hdr;
uint8_t hop_limit;
struct in6_addr src;
struct in6_addr dest;
};
/* IP6 pseudo header */
struct ipv6_pseudo_header {
struct in6_addr src;
struct in6_addr dest;
uint8_t zero_padding;
uint8_t nxt_hdr;
uint16_t len;
};
/* Next header numbers */
#define IP6_HOPBYHOP 0x00
#define IP6_ROUTING 0x43
#define IP6_FRAGMENT 0x44
#define IP6_AUTHENTICATION 0x51
#define IP6_DEST_OPTS 0x60
#define IP6_ESP 0x50
#define IP6_ICMP6 0x58
#define IP6_NO_HEADER 0x59
struct io_buffer;
extern struct net_protocol ipv6_protocol __net_protocol;
extern struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol;
extern char * inet6_ntoa ( struct in6_addr in6 );
extern int add_ipv6_address ( struct net_device *netdev,
struct in6_addr prefix, int prefix_len,
struct in6_addr address,
struct in6_addr gateway );
extern void del_ipv6_address ( struct net_device *netdev );
#endif /* _IPXE_IP6_H */

218
src/include/ipxe/ipv6.h Normal file
View File

@ -0,0 +1,218 @@
#ifndef _IPXE_IPV6_H
#define _IPXE_IPV6_H
/** @file
*
* IPv6 protocol
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <string.h>
#include <byteswap.h>
#include <ipxe/in.h>
#include <ipxe/list.h>
#include <ipxe/netdevice.h>
/** IPv6 version */
#define IPV6_VER 0x60000000UL
/** IPv6 version mask */
#define IPV6_MASK_VER 0xf0000000UL
/** IPv6 maximum hop limit */
#define IPV6_HOP_LIMIT 0xff
/** IPv6 header */
struct ipv6_header {
/** Version (4 bits), Traffic class (8 bits), Flow label (20 bits) */
uint32_t ver_tc_label;
/** Payload length, including any extension headers */
uint16_t len;
/** Next header type */
uint8_t next_header;
/** Hop limit */
uint8_t hop_limit;
/** Source address */
struct in6_addr src;
/** Destination address */
struct in6_addr dest;
} __attribute__ (( packed ));
/** IPv6 extension header common fields */
struct ipv6_extension_header_common {
/** Next header type */
uint8_t next_header;
/** Header extension length (excluding first 8 bytes) */
uint8_t len;
} __attribute__ (( packed ));
/** IPv6 type-length-value options */
struct ipv6_option {
/** Type */
uint8_t type;
/** Length */
uint8_t len;
/** Value */
uint8_t value[0];
} __attribute__ (( packed ));
/** IPv6 option types */
enum ipv6_option_type {
/** Pad1 */
IPV6_OPT_PAD1 = 0x00,
/** PadN */
IPV6_OPT_PADN = 0x01,
};
/** Test if IPv6 option can be safely ignored */
#define IPV6_CAN_IGNORE_OPT( type ) ( ( (type) & 0xc0 ) == 0x00 )
/** IPv6 option-based extension header */
struct ipv6_options_header {
/** Extension header common fields */
struct ipv6_extension_header_common common;
/** Options */
struct ipv6_option options[0];
} __attribute__ (( packed ));
/** IPv6 routing header */
struct ipv6_routing_header {
/** Extension header common fields */
struct ipv6_extension_header_common common;
/** Routing type */
uint8_t type;
/** Segments left */
uint8_t remaining;
/** Type-specific data */
uint8_t data[0];
} __attribute__ (( packed ));
/** IPv6 fragment header */
struct ipv6_fragment_header {
/** Extension header common fields */
struct ipv6_extension_header_common common;
/** Fragment offset (13 bits), reserved, more fragments (1 bit) */
uint16_t offset_more;
/** Identification */
uint32_t ident;
} __attribute__ (( packed ));
/** Fragment offset mask */
#define IPV6_MASK_OFFSET 0xfff8
/** More fragments */
#define IPV6_MASK_MOREFRAGS 0x0001
/** IPv6 extension header */
union ipv6_extension_header {
/** Extension header common fields */
struct ipv6_extension_header_common common;
/** Minimum size padding */
uint8_t pad[8];
/** Generic options header */
struct ipv6_options_header options;
/** Hop-by-hop options header */
struct ipv6_options_header hopbyhop;
/** Routing header */
struct ipv6_routing_header routing;
/** Fragment header */
struct ipv6_fragment_header fragment;
/** Destination options header */
struct ipv6_options_header destination;
};
/** IPv6 header types */
enum ipv6_header_type {
/** IPv6 hop-by-hop options header type */
IPV6_HOPBYHOP = 0,
/** IPv6 routing header type */
IPV6_ROUTING = 43,
/** IPv6 fragment header type */
IPV6_FRAGMENT = 44,
/** IPv6 no next header type */
IPV6_NO_HEADER = 59,
/** IPv6 destination options header type */
IPV6_DESTINATION = 60,
};
/** IPv6 pseudo-header */
struct ipv6_pseudo_header {
/** Source address */
struct in6_addr src;
/** Destination address */
struct in6_addr dest;
/** Upper-layer packet length */
uint32_t len;
/** Zero padding */
uint8_t zero[3];
/** Next header */
uint8_t next_header;
} __attribute__ (( packed ));
/** An IPv6 address/routing table entry */
struct ipv6_miniroute {
/** List of miniroutes */
struct list_head list;
/** Network device */
struct net_device *netdev;
/** IPv6 address */
struct in6_addr address;
/** Prefix length */
unsigned int prefix_len;
/** IPv6 prefix mask (derived from prefix length) */
struct in6_addr prefix_mask;
/** Router address is present */
int has_router;
/** Router address */
struct in6_addr router;
};
/**
* Construct link-local address (via EUI-64)
*
* @v addr Address to construct
* @v netdev Network device
* @ret prefix_len Prefix length, or negative error
*/
static inline int ipv6_link_local ( struct in6_addr *addr,
struct net_device *netdev ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
const void *ll_addr = netdev->ll_addr;
int rc;
memset ( addr, 0, sizeof ( *addr ) );
addr->s6_addr16[0] = htons ( 0xfe80 );
if ( ( rc = ll_protocol->eui64 ( ll_addr, &addr->s6_addr[8] ) ) != 0 )
return rc;
addr->s6_addr[8] ^= 0x02;
return 64;
}
/**
* Construct solicited-node multicast address
*
* @v addr Address to construct
* @v unicast Unicast address
*/
static inline void ipv6_solicited_node ( struct in6_addr *addr,
const struct in6_addr *unicast ) {
memset ( addr, 0, sizeof ( *addr ) );
addr->s6_addr16[0] = htons ( 0xff02 );
addr->s6_addr[11] = 1;
addr->s6_addr[12] = 0xff;
memcpy ( &addr->s6_addr[13], &unicast->s6_addr[13], 3 );
}
extern struct list_head ipv6_miniroutes;
extern struct net_protocol ipv6_protocol __net_protocol;
extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr );
#endif /* _IPXE_IPV6_H */

View File

@ -1,21 +1,80 @@
#ifndef _IPXE_NDP_H
#define _IPXE_NDP_H
/** @file
*
* Neighbour discovery protocol
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <byteswap.h>
#include <string.h>
#include <ipxe/icmp6.h>
#include <ipxe/ip6.h>
#include <ipxe/in.h>
#include <ipxe/netdevice.h>
#include <ipxe/iobuf.h>
#include <ipxe/tcpip.h>
#include <ipxe/ipv6.h>
#include <ipxe/icmpv6.h>
#include <ipxe/neighbour.h>
#define NDP_STATE_INVALID 0
#define NDP_STATE_INCOMPLETE 1
#define NDP_STATE_REACHABLE 2
#define NDP_STATE_DELAY 3
#define NDP_STATE_PROBE 4
#define NDP_STATE_STALE 5
/** An NDP option */
struct ndp_option {
/** Type */
uint8_t type;
/** Length (in blocks of 8 bytes) */
uint8_t blocks;
/** Value */
uint8_t value[0];
} __attribute__ (( packed ));
int ndp_resolve ( struct net_device *netdev, struct in6_addr *src,
struct in6_addr *dest, void *dest_ll_addr );
int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest );
/** NDP option block size */
#define NDP_OPTION_BLKSZ 8
/** An NDP header */
struct ndp_header {
/** ICMPv6 header */
struct icmpv6_header icmp;
/** Flags */
uint8_t flags;
/** Reserved */
uint8_t reserved[3];
/** Target address */
struct in6_addr target;
/** Options */
struct ndp_option option[0];
} __attribute__ (( packed ));
/** NDP router flag */
#define NDP_ROUTER 0x80
/** NDP solicited flag */
#define NDP_SOLICITED 0x40
/** NDP override flag */
#define NDP_OVERRIDE 0x20
/** NDP source link-layer address option */
#define NDP_OPT_LL_SOURCE 1
/** NDP target link-layer address option */
#define NDP_OPT_LL_TARGET 2
extern struct neighbour_discovery ndp_discovery;
/**
* Transmit packet, determining link-layer address via NDP
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v net_dest Destination network-layer address
* @v net_source Source network-layer address
* @v ll_source Source link-layer address
* @ret rc Return status code
*/
static inline int ndp_tx ( struct io_buffer *iobuf, struct net_device *netdev,
const void *net_dest, const void *net_source,
const void *ll_source ) {
return neighbour_tx ( iobuf, netdev, &ipv6_protocol, net_dest,
&ndp_discovery, net_source, ll_source );
}
#endif /* _IPXE_NDP_H */

View File

@ -0,0 +1,88 @@
#ifndef _IPXE_NEIGHBOUR_H
#define _IPXE_NEIGHBOUR_H
/** @file
*
* Neighbour discovery
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <ipxe/refcnt.h>
#include <ipxe/list.h>
#include <ipxe/netdevice.h>
#include <ipxe/retry.h>
/** A neighbour discovery protocol */
struct neighbour_discovery {
/** Name */
const char *name;
/**
* Transmit neighbour discovery request
*
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @v net_source Source network-layer address
* @ret rc Return status code
*/
int ( * tx_request ) ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *net_source );
};
/** A neighbour cache entry */
struct neighbour {
/** Reference count */
struct refcnt refcnt;
/** List of neighbour cache entries */
struct list_head list;
/** Network device */
struct net_device *netdev;
/** Network-layer protocol */
struct net_protocol *net_protocol;
/** Network-layer destination address */
uint8_t net_dest[MAX_NET_ADDR_LEN];
/** Link-layer destination address */
uint8_t ll_dest[MAX_LL_ADDR_LEN];
/** Neighbour discovery protocol (if any) */
struct neighbour_discovery *discovery;
/** Network-layer source address (if any) */
uint8_t net_source[MAX_NET_ADDR_LEN];
/** Retransmission timer */
struct retry_timer timer;
/** Pending I/O buffers */
struct list_head tx_queue;
};
/**
* Test if neighbour cache entry has a valid link-layer address
*
* @v neighbour Neighbour cache entry
* @ret has_ll_dest Neighbour cache entry has a valid link-layer address
*/
static inline __attribute__ (( always_inline )) int
neighbour_has_ll_dest ( struct neighbour *neighbour ) {
return ( ! timer_running ( &neighbour->timer ) );
}
extern struct list_head neighbours;
extern int neighbour_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest,
struct neighbour_discovery *discovery,
const void *net_source, const void *ll_source );
extern int neighbour_update ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *ll_dest );
extern int neighbour_define ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *ll_dest );
#endif /* _IPXE_NEIGHBOUR_H */

View File

@ -44,7 +44,7 @@ struct device;
#define MAX_LL_HEADER_LEN 36
/** Maximum length of a network-layer address */
#define MAX_NET_ADDR_LEN 4
#define MAX_NET_ADDR_LEN 16
/** Maximum length of a network-layer header
*
@ -175,8 +175,17 @@ struct ll_protocol {
*
* @v ll_addr Link-layer address
* @v eth_addr Ethernet-compatible address to fill in
* @ret rc Return status code
*/
int ( * eth_addr ) ( const void *ll_addr, void *eth_addr );
/**
* Generate EUI-64 address
*
* @v ll_addr Link-layer address
* @v eui64 EUI-64 address to fill in
* @ret rc Return status code
*/
int ( * eui64 ) ( const void *ll_addr, void *eui64 );
/** Link-layer protocol
*
* This is an ARPHRD_XXX constant, in network byte order.
@ -300,6 +309,8 @@ struct net_device {
struct list_head list;
/** List of open network devices */
struct list_head open_list;
/** Index of this network device */
unsigned int index;
/** Name of this network device */
char name[12];
/** Underlying hardware device */

View File

@ -253,6 +253,28 @@ struct settings_applicator {
/** Declare a settings applicator */
#define __settings_applicator __table_entry ( SETTINGS_APPLICATORS, 01 )
/** A built-in setting */
struct builtin_setting {
/** Setting */
struct setting *setting;
/** Fetch setting value
*
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
int ( * fetch ) ( void *data, size_t len );
};
/** Built-in settings table */
#define BUILTIN_SETTINGS __table ( struct builtin_setting, "builtin_settings" )
/** Declare a built-in setting */
#define __builtin_setting __table_entry ( BUILTIN_SETTINGS, 01 )
/** Built-in setting scope */
extern struct settings_scope builtin_scope;
/**
* A generic settings block
*

View File

@ -316,10 +316,19 @@ struct tcp_options {
/**
* Path MTU
*
* We really ought to implement Path MTU discovery. Until we do,
* anything with a path MTU greater than this may fail.
* IPv6 requires all data link layers to support a datagram size of
* 1280 bytes. We choose to use this as our maximum transmitted
* datagram size, on the assumption that any practical link layer we
* encounter will allow this size. This is a very conservative
* assumption in practice, but the impact of making such a
* conservative assumption is insignificant since the amount of data
* that we transmit (rather than receive) is negligible.
*
* We allow space within this 1280 bytes for an IPv6 header, a TCP
* header, and a (padded) TCP timestamp option.
*/
#define TCP_PATH_MTU 1460
#define TCP_PATH_MTU \
( 1280 - 40 /* IPv6 */ - 20 /* TCP */ - 12 /* TCP timestamp */ )
/**
* Advertised TCP MSS

View File

@ -69,6 +69,7 @@ struct tcpip_protocol {
* Process received packet
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v st_src Partially-filled source address
* @v st_dest Partially-filled destination address
* @v pshdr_csum Pseudo-header checksum
@ -76,7 +77,8 @@ struct tcpip_protocol {
*
* This method takes ownership of the I/O buffer.
*/
int ( * rx ) ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev,
struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum );
/**
* Transport-layer protocol number
@ -128,8 +130,8 @@ struct tcpip_net_protocol {
/** Declare a TCP/IP network-layer protocol */
#define __tcpip_net_protocol __table_entry ( TCPIP_NET_PROTOCOLS, 01 )
extern int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
struct sockaddr_tcpip *st_src,
extern int tcpip_rx ( struct io_buffer *iobuf, struct net_device *netdev,
uint8_t tcpip_proto, struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum );
extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip,
struct sockaddr_tcpip *st_src,

View File

@ -0,0 +1,14 @@
#ifndef _USR_NEIGHMGMT_H
#define _USR_NEIGHMGMT_H
/** @file
*
* Neighbour management
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
extern void nstat ( void );
#endif /* _USR_NEIGHMGMT_H */

View File

@ -3,12 +3,33 @@
/** @file
*
* Routing table management
* Routing management
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/tables.h>
/** A routing family */
struct routing_family {
/**
* Print routes for a network device
*
* @v netdev Network device
*/
void ( * print ) ( struct net_device *netdev );
};
/** Routing family table */
#define ROUTING_FAMILIES __table ( struct routing_family, "routing_families" )
/** Declare a routing family */
#define __routing_family( order ) __table_entry ( ROUTING_FAMILIES, order )
#define ROUTING_IPV4 01
#define ROUTING_IPV6 02
extern void route ( void );
#endif /* _USR_ROUTE_H */

View File

@ -599,6 +599,7 @@ static struct ll_protocol net80211_ll_protocol __ll_protocol = {
.ntoa = eth_ntoa,
.mc_hash = eth_mc_hash,
.eth_addr = eth_eth_addr,
.eui64 = eth_eui64,
.ll_proto = htons ( ARPHRD_ETHER ), /* "encapsulated Ethernet" */
.hw_addr_len = ETH_ALEN,
.ll_addr_len = ETH_ALEN,

View File

@ -28,11 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/if_arp.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/list.h>
#include <ipxe/retry.h>
#include <ipxe/timer.h>
#include <ipxe/malloc.h>
#include <ipxe/refcnt.h>
#include <ipxe/neighbour.h>
#include <ipxe/arp.h>
/** @file
@ -45,291 +41,30 @@ FILE_LICENCE ( GPL2_OR_LATER );
*
*/
/** ARP minimum timeout */
#define ARP_MIN_TIMEOUT ( TICKS_PER_SEC / 8 )
/** ARP maximum timeout */
#define ARP_MAX_TIMEOUT ( TICKS_PER_SEC * 3 )
/** An ARP cache entry */
struct arp_entry {
/** Reference count */
struct refcnt refcnt;
/** List of ARP cache entries */
struct list_head list;
/** Network device */
struct net_device *netdev;
/** Network-layer protocol */
struct net_protocol *net_protocol;
/** Network-layer destination address */
uint8_t net_dest[MAX_NET_ADDR_LEN];
/** Network-layer source address */
uint8_t net_source[MAX_NET_ADDR_LEN];
/** Link-layer destination address */
uint8_t ll_dest[MAX_LL_ADDR_LEN];
/** Retransmission timer */
struct retry_timer timer;
/** Pending I/O buffers */
struct list_head tx_queue;
};
/** The ARP cache */
static LIST_HEAD ( arp_entries );
struct net_protocol arp_protocol __net_protocol;
static void arp_expired ( struct retry_timer *timer, int over );
/**
* Free ARP cache entry
*
* @v refcnt Reference count
*/
static void arp_free ( struct refcnt *refcnt ) {
struct arp_entry *arp =
container_of ( refcnt, struct arp_entry, refcnt );
/* Sanity check */
assert ( list_empty ( &arp->tx_queue ) );
/* Drop reference to network device */
netdev_put ( arp->netdev );
/* Free entry */
free ( arp );
}
/**
* Create ARP cache entry
* Transmit ARP request
*
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @v net_source Source network-layer address
* @ret arp ARP cache entry, or NULL if allocation failed
*/
static struct arp_entry * arp_create ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest,
const void *net_source ) {
struct arp_entry *arp;
/* Allocate and initialise entry */
arp = zalloc ( sizeof ( *arp ) );
if ( ! arp )
return NULL;
ref_init ( &arp->refcnt, arp_free );
arp->netdev = netdev_get ( netdev );
arp->net_protocol = net_protocol;
memcpy ( arp->net_dest, net_dest,
net_protocol->net_addr_len );
memcpy ( arp->net_source, net_source,
net_protocol->net_addr_len );
timer_init ( &arp->timer, arp_expired, &arp->refcnt );
arp->timer.min_timeout = ARP_MIN_TIMEOUT;
arp->timer.max_timeout = ARP_MAX_TIMEOUT;
INIT_LIST_HEAD ( &arp->tx_queue );
/* Start timer running to trigger initial transmission */
start_timer_nodelay ( &arp->timer );
/* Transfer ownership to cache */
list_add ( &arp->list, &arp_entries );
DBGC ( arp, "ARP %p %s %s %s created\n", arp, netdev->name,
net_protocol->name, net_protocol->ntoa ( net_dest ) );
return arp;
}
/**
* Find entry in the ARP cache
*
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @ret arp ARP cache entry, or NULL if not found
*/
static struct arp_entry * arp_find ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest ) {
struct arp_entry *arp;
list_for_each_entry ( arp, &arp_entries, list ) {
if ( ( arp->netdev == netdev ) &&
( arp->net_protocol == net_protocol ) &&
( memcmp ( arp->net_dest, net_dest,
net_protocol->net_addr_len ) == 0 ) ) {
/* Move to start of cache */
list_del ( &arp->list );
list_add ( &arp->list, &arp_entries );
return arp;
}
}
return NULL;
}
/**
* Destroy ARP cache entry
*
* @v arp ARP cache entry
* @v rc Reason for destruction
*/
static void arp_destroy ( struct arp_entry *arp, int rc ) {
struct net_device *netdev = arp->netdev;
struct net_protocol *net_protocol = arp->net_protocol;
struct io_buffer *iobuf;
/* Take ownership from cache */
list_del ( &arp->list );
/* Stop timer */
stop_timer ( &arp->timer );
/* Discard any outstanding I/O buffers */
while ( ( iobuf = list_first_entry ( &arp->tx_queue, struct io_buffer,
list ) ) != NULL ) {
DBGC2 ( arp, "ARP %p %s %s %s discarding deferred packet: "
"%s\n", arp, netdev->name, net_protocol->name,
net_protocol->ntoa ( arp->net_dest ), strerror ( rc ) );
list_del ( &iobuf->list );
netdev_tx_err ( arp->netdev, iobuf, rc );
}
DBGC ( arp, "ARP %p %s %s %s destroyed: %s\n", arp, netdev->name,
net_protocol->name, net_protocol->ntoa ( arp->net_dest ),
strerror ( rc ) );
/* Drop remaining reference */
ref_put ( &arp->refcnt );
}
/**
* Test if ARP cache entry has a valid link-layer address
*
* @v arp ARP cache entry
* @ret resolved ARP cache entry is resolved
*/
static inline int arp_resolved ( struct arp_entry *arp ) {
return ( ! timer_running ( &arp->timer ) );
}
/**
* Transmit packet, determining link-layer address via ARP
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @v net_source Source network-layer address
* @v ll_source Source link-layer address
* @ret rc Return status code
*/
int arp_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol, const void *net_dest,
const void *net_source, const void *ll_source ) {
struct arp_entry *arp;
/* Find or create ARP cache entry */
arp = arp_find ( netdev, net_protocol, net_dest );
if ( ! arp ) {
arp = arp_create ( netdev, net_protocol, net_dest,
net_source );
if ( ! arp )
return -ENOMEM;
}
/* If a link-layer address is available then transmit
* immediately, otherwise queue for later transmission.
*/
if ( arp_resolved ( arp ) ) {
return net_tx ( iobuf, netdev, net_protocol, arp->ll_dest,
ll_source );
} else {
DBGC2 ( arp, "ARP %p %s %s %s deferring packet\n",
arp, netdev->name, net_protocol->name,
net_protocol->ntoa ( net_dest ) );
list_add_tail ( &iobuf->list, &arp->tx_queue );
return -EAGAIN;
}
}
/**
* Update ARP cache entry
*
* @v arp ARP cache entry
* @v ll_dest Destination link-layer address
*/
static void arp_update ( struct arp_entry *arp, const void *ll_dest ) {
struct net_device *netdev = arp->netdev;
static int arp_tx_request ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *net_source ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct net_protocol *net_protocol = arp->net_protocol;
struct io_buffer *iobuf;
int rc;
DBGC ( arp, "ARP %p %s %s %s updated => %s\n", arp, netdev->name,
net_protocol->name, net_protocol->ntoa ( arp->net_dest ),
ll_protocol->ntoa ( ll_dest ) );
/* Fill in link-layer address */
memcpy ( arp->ll_dest, ll_dest, ll_protocol->ll_addr_len );
/* Stop retransmission timer */
stop_timer ( &arp->timer );
/* Transmit any packets in queue. Take out a temporary
* reference on the entry to prevent it from going out of
* scope during the call to net_tx().
*/
ref_get ( &arp->refcnt );
while ( ( iobuf = list_first_entry ( &arp->tx_queue, struct io_buffer,
list ) ) != NULL ) {
DBGC2 ( arp, "ARP %p %s %s %s transmitting deferred packet\n",
arp, netdev->name, net_protocol->name,
net_protocol->ntoa ( arp->net_dest ) );
list_del ( &iobuf->list );
if ( ( rc = net_tx ( iobuf, netdev, net_protocol, ll_dest,
netdev->ll_addr ) ) != 0 ) {
DBGC ( arp, "ARP %p could not transmit deferred "
"packet: %s\n", arp, strerror ( rc ) );
/* Ignore error and continue */
}
}
ref_put ( &arp->refcnt );
}
/**
* Handle ARP timer expiry
*
* @v timer Retry timer
* @v fail Failure indicator
*/
static void arp_expired ( struct retry_timer *timer, int fail ) {
struct arp_entry *arp = container_of ( timer, struct arp_entry, timer );
struct net_device *netdev = arp->netdev;
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct net_protocol *net_protocol = arp->net_protocol;
struct io_buffer *iobuf;
struct arphdr *arphdr;
int rc;
/* If we have failed, destroy the cache entry */
if ( fail ) {
arp_destroy ( arp, -ETIMEDOUT );
return;
}
/* Restart the timer */
start_timer ( &arp->timer );
/* Allocate ARP packet */
iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *arphdr ) +
( 2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) ) );
if ( ! iobuf ) {
/* Leave timer running and try again later */
return;
}
if ( ! iobuf )
return -ENOMEM;
iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
/* Build up ARP request */
@ -342,21 +77,30 @@ static void arp_expired ( struct retry_timer *timer, int fail ) {
memcpy ( iob_put ( iobuf, ll_protocol->ll_addr_len ),
netdev->ll_addr, ll_protocol->ll_addr_len );
memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ),
arp->net_source, net_protocol->net_addr_len );
net_source, net_protocol->net_addr_len );
memset ( iob_put ( iobuf, ll_protocol->ll_addr_len ),
0, ll_protocol->ll_addr_len );
memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ),
arp->net_dest, net_protocol->net_addr_len );
net_dest, net_protocol->net_addr_len );
/* Transmit ARP request */
if ( ( rc = net_tx ( iobuf, netdev, &arp_protocol,
netdev->ll_broadcast, netdev->ll_addr ) ) != 0 ) {
DBGC ( arp, "ARP %p could not transmit request: %s\n",
arp, strerror ( rc ) );
return;
DBGC ( netdev, "ARP %s %s %s could not transmit request: %s\n",
netdev->name, net_protocol->name,
net_protocol->ntoa ( net_dest ), strerror ( rc ) );
return rc;
}
return 0;
}
/** ARP neighbour discovery protocol */
struct neighbour_discovery arp_discovery = {
.name = "ARP",
.tx_request = arp_tx_request,
};
/**
* Identify ARP protocol
*
@ -368,9 +112,8 @@ static struct arp_net_protocol * arp_find_protocol ( uint16_t net_proto ) {
struct arp_net_protocol *arp_net_protocol;
for_each_table_entry ( arp_net_protocol, ARP_NET_PROTOCOLS ) {
if ( arp_net_protocol->net_protocol->net_proto == net_proto ) {
if ( arp_net_protocol->net_protocol->net_proto == net_proto )
return arp_net_protocol;
}
}
return NULL;
}
@ -392,7 +135,6 @@ static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
struct arp_net_protocol *arp_net_protocol;
struct net_protocol *net_protocol;
struct ll_protocol *ll_protocol;
struct arp_entry *arp;
int rc;
/* Identify network-layer and link-layer protocols */
@ -412,11 +154,9 @@ static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
goto done;
}
/* See if we have an entry for this sender, and update it if so */
arp = arp_find ( netdev, net_protocol, arp_sender_pa ( arphdr ) );
if ( arp ) {
arp_update ( arp, arp_sender_ha ( arphdr ) );
}
/* Update neighbour cache entry for this sender, if any */
neighbour_update ( netdev, net_protocol, arp_sender_pa ( arphdr ),
arp_sender_ha ( arphdr ) );
/* If it's not a request, there's nothing more to do */
if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) ) {
@ -431,10 +171,10 @@ static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
}
/* Change request to a reply */
DBGC ( netdev, "ARP reply %s %s %s => %s %s\n",
netdev->name, net_protocol->name,
net_protocol->ntoa ( arp_target_pa ( arphdr ) ),
ll_protocol->name, ll_protocol->ntoa ( netdev->ll_addr ) );
DBGC2 ( netdev, "ARP %s %s %s reply => %s %s\n",
netdev->name, net_protocol->name,
net_protocol->ntoa ( arp_target_pa ( arphdr ) ),
ll_protocol->name, ll_protocol->ntoa ( netdev->ll_addr ) );
arphdr->ar_op = htons ( ARPOP_REPLY );
memswap ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ),
arphdr->ar_hln + arphdr->ar_pln );
@ -444,8 +184,10 @@ static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
if ( ( rc = net_tx ( iob_disown ( iobuf ), netdev, &arp_protocol,
arp_target_ha ( arphdr ),
netdev->ll_addr ) ) != 0 ) {
DBGC ( netdev, "ARP could not transmit reply via %s: %s\n",
netdev->name, strerror ( rc ) );
DBGC ( netdev, "ARP %s %s %s could not transmit reply: %s\n",
netdev->name, net_protocol->name,
net_protocol->ntoa ( arp_target_pa ( arphdr ) ),
strerror ( rc ) );
goto done;
}
@ -469,72 +211,10 @@ static const char * arp_ntoa ( const void *net_addr __unused ) {
return "<ARP>";
}
/** ARP protocol */
/** ARP network protocol */
struct net_protocol arp_protocol __net_protocol = {
.name = "ARP",
.net_proto = htons ( ETH_P_ARP ),
.rx = arp_rx,
.ntoa = arp_ntoa,
};
/**
* Update ARP cache on network device creation
*
* @v netdev Network device
*/
static int arp_probe ( struct net_device *netdev __unused ) {
/* Nothing to do */
return 0;
}
/**
* Update ARP cache on network device state change or removal
*
* @v netdev Network device
*/
static void arp_flush ( struct net_device *netdev ) {
struct arp_entry *arp;
struct arp_entry *tmp;
/* Remove all ARP cache entries when a network device is closed */
if ( ! netdev_is_open ( netdev ) ) {
list_for_each_entry_safe ( arp, tmp, &arp_entries, list )
arp_destroy ( arp, -ENODEV );
}
}
/** ARP driver (for net device notifications) */
struct net_driver arp_net_driver __net_driver = {
.name = "ARP",
.probe = arp_probe,
.notify = arp_flush,
.remove = arp_flush,
};
/**
* Discard some cached ARP entries
*
* @ret discarded Number of cached items discarded
*/
static unsigned int arp_discard ( void ) {
struct arp_entry *arp;
/* Drop oldest cache entry, if any */
arp = list_last_entry ( &arp_entries, struct arp_entry, list );
if ( arp ) {
arp_destroy ( arp, -ENOBUFS );
return 1;
} else {
return 0;
}
}
/** ARP cache discarder
*
* ARP cache entries are deemed to have a high replacement cost, since
* flushing an active ARP cache entry midway through a TCP transfer
* will cause substantial disruption.
*/
struct cache_discarder arp_discarder __cache_discarder ( CACHE_EXPENSIVE ) = {
.discard = arp_discard,
};

View File

@ -149,6 +149,11 @@ int eth_mc_hash ( unsigned int af, const void *net_addr, void *ll_addr ) {
ll_addr_bytes[4] = net_addr_bytes[2];
ll_addr_bytes[5] = net_addr_bytes[3];
return 0;
case AF_INET6:
ll_addr_bytes[0] = 0x33;
ll_addr_bytes[1] = 0x33;
memcpy ( &ll_addr_bytes[2], &net_addr_bytes[12], 4 );
return 0;
default:
return -ENOTSUP;
}
@ -165,6 +170,21 @@ int eth_eth_addr ( const void *ll_addr, void *eth_addr ) {
return 0;
}
/**
* Generate EUI-64 address
*
* @v ll_addr Link-layer address
* @v eui64 EUI-64 address to fill in
* @ret rc Return status code
*/
int eth_eui64 ( const void *ll_addr, void *eui64 ) {
memcpy ( ( eui64 + 0 ), ( ll_addr + 0 ), 3 );
memcpy ( ( eui64 + 5 ), ( ll_addr + 3 ), 3 );
*( ( uint16_t * ) ( eui64 + 3 ) ) = htons ( 0xfffe );
return 0;
}
/** Ethernet protocol */
struct ll_protocol ethernet_protocol __ll_protocol = {
.name = "Ethernet",
@ -178,6 +198,7 @@ struct ll_protocol ethernet_protocol __ll_protocol = {
.ntoa = eth_ntoa,
.mc_hash = eth_mc_hash,
.eth_addr = eth_eth_addr,
.eui64 = eth_eui64,
};
/**

172
src/net/fragment.c Normal file
View File

@ -0,0 +1,172 @@
/*
* Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ipxe/retry.h>
#include <ipxe/timer.h>
#include <ipxe/fragment.h>
/** @file
*
* Fragment reassembly
*
*/
/**
* Expire fragment reassembly buffer
*
* @v timer Retry timer
* @v fail Failure indicator
*/
static void fragment_expired ( struct retry_timer *timer, int fail __unused ) {
struct fragment *fragment =
container_of ( timer, struct fragment, timer );
DBGC ( fragment, "FRAG %p expired\n", fragment );
free_iob ( fragment->iobuf );
list_del ( &fragment->list );
free ( fragment );
}
/**
* Find fragment reassembly buffer
*
* @v fragments Fragment reassembler
* @v iobuf I/O buffer
* @v hdrlen Length of non-fragmentable potion of I/O buffer
* @ret fragment Fragment reassembly buffer, or NULL if not found
*/
static struct fragment * fragment_find ( struct fragment_reassembler *fragments,
struct io_buffer *iobuf,
size_t hdrlen ) {
struct fragment *fragment;
list_for_each_entry ( fragment, &fragments->list, list ) {
if ( fragments->is_fragment ( fragment, iobuf, hdrlen ) )
return fragment;
}
return NULL;
}
/**
* Reassemble packet
*
* @v fragments Fragment reassembler
* @v iobuf I/O buffer
* @v hdrlen Length of non-fragmentable potion of I/O buffer
* @ret iobuf Reassembled packet, or NULL
*
* This function takes ownership of the I/O buffer. Note that the
* length of the non-fragmentable portion may be modified.
*/
struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments,
struct io_buffer *iobuf,
size_t *hdrlen ) {
struct fragment *fragment;
struct io_buffer *new_iobuf;
size_t new_len;
size_t offset;
size_t expected_offset;
int more_frags;
/* Find matching fragment reassembly buffer, if any */
fragment = fragment_find ( fragments, iobuf, *hdrlen );
/* Drop out-of-order fragments */
offset = fragments->fragment_offset ( iobuf, *hdrlen );
expected_offset = ( fragment ? ( iob_len ( fragment->iobuf ) -
fragment->hdrlen ) : 0 );
if ( offset != expected_offset ) {
DBGC ( fragment, "FRAG %p dropping out-of-sequence fragment "
"[%zd,%zd), expected [%zd,...)\n", fragment, offset,
( offset + iob_len ( iobuf ) - *hdrlen ),
expected_offset );
goto drop;
}
/* Create or extend fragment reassembly buffer as applicable */
if ( ! fragment ) {
/* Create new fragment reassembly buffer */
fragment = zalloc ( sizeof ( *fragment ) );
if ( ! fragment )
goto drop;
list_add ( &fragment->list, &fragments->list );
fragment->iobuf = iobuf;
fragment->hdrlen = *hdrlen;
timer_init ( &fragment->timer, fragment_expired, NULL );
DBGC ( fragment, "FRAG %p [0,%zd)\n", fragment,
( iob_len ( iobuf ) - *hdrlen ) );
} else {
/* Check if this is the final fragment */
more_frags = fragments->more_fragments ( iobuf, *hdrlen );
DBGC ( fragment, "FRAG %p [%zd,%zd)%s\n", fragment,
offset, ( offset + iob_len ( iobuf ) - *hdrlen ),
( more_frags ? "" : " complete" ) );
/* Extend fragment reassembly buffer. Preserve I/O
* buffer headroom to allow for code which modifies
* and resends the buffer (e.g. ICMP echo responses).
*/
iob_pull ( iobuf, *hdrlen );
new_len = ( iob_headroom ( fragment->iobuf ) +
iob_len ( fragment->iobuf ) + iob_len ( iobuf ) );
new_iobuf = alloc_iob ( new_len );
if ( ! new_iobuf ) {
DBGC ( fragment, "FRAG %p could not extend reassembly "
"buffer to %zd bytes\n", fragment, new_len );
goto drop;
}
iob_reserve ( new_iobuf, iob_headroom ( fragment->iobuf ) );
memcpy ( iob_put ( new_iobuf, iob_len ( fragment->iobuf ) ),
fragment->iobuf->data, iob_len ( fragment->iobuf ) );
memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
iobuf->data, iob_len ( iobuf ) );
free_iob ( fragment->iobuf );
fragment->iobuf = new_iobuf;
free_iob ( iobuf );
/* Stop fragment reassembly timer */
stop_timer ( &fragment->timer );
/* If this is the final fragment, return it */
if ( ! more_frags ) {
iobuf = fragment->iobuf;
*hdrlen = fragment->hdrlen;
list_del ( &fragment->list );
free ( fragment );
return iobuf;
}
}
/* (Re)start fragment reassembly timer */
start_timer_fixed ( &fragment->timer, FRAGMENT_TIMEOUT );
return NULL;
drop:
free_iob ( iobuf );
return NULL;
}

View File

@ -38,12 +38,15 @@ struct tcpip_protocol icmp_protocol __tcpip_protocol;
* Process a received packet
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v st_src Partially-filled source address
* @v st_dest Partially-filled destination address
* @v pshdr_csum Pseudo-header checksum
* @ret rc Return status code
*/
static int icmp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
static int icmp_rx ( struct io_buffer *iobuf,
struct net_device *netdev __unused,
struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest,
uint16_t pshdr_csum __unused ) {
struct icmp_header *icmp = iobuf->data;

View File

@ -1,126 +1,172 @@
#include <stdint.h>
/*
* Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
#include <byteswap.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/in.h>
#include <ipxe/ip6.h>
#include <ipxe/if_ether.h>
#include <ipxe/iobuf.h>
#include <ipxe/ndp.h>
#include <ipxe/icmp6.h>
#include <ipxe/tcpip.h>
#include <ipxe/netdevice.h>
#include <ipxe/icmpv6.h>
/** @file
*
* ICMPv6 protocol
*
*/
/**
* Send neighbour solicitation packet
* Process received ICMPv6 echo request packet
*
* @v netdev Network device
* @v src Source address
* @v dest Destination address
*
* This function prepares a neighbour solicitation packet and sends it to the
* network layer.
* @v iobuf I/O buffer
* @v netdev Network device
* @v sin6_src Source socket address
* @v sin6_dest Destination socket address
* @ret rc Return status code
*/
int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unused,
struct in6_addr *dest ) {
union {
struct sockaddr_in6 sin6;
struct sockaddr_tcpip st;
} st_dest;
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct neighbour_solicit *nsolicit;
struct io_buffer *iobuf = alloc_iob ( sizeof ( *nsolicit ) + MIN_IOB_LEN );
iob_reserve ( iobuf, MAX_HDR_LEN );
nsolicit = iob_put ( iobuf, sizeof ( *nsolicit ) );
/* Fill up the headers */
memset ( nsolicit, 0, sizeof ( *nsolicit ) );
nsolicit->type = ICMP6_NSOLICIT;
nsolicit->code = 0;
nsolicit->target = *dest;
nsolicit->opt_type = 1;
nsolicit->opt_len = ( 2 + ll_protocol->ll_addr_len ) / 8;
memcpy ( nsolicit->opt_ll_addr, netdev->ll_addr,
netdev->ll_protocol->ll_addr_len );
/* Partial checksum */
nsolicit->csum = 0;
nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) );
/* Solicited multicast address */
st_dest.sin6.sin6_family = AF_INET6;
st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff;
st_dest.sin6.sin6_addr.in6_u.u6_addr8[2] = 0x02;
st_dest.sin6.sin6_addr.in6_u.u6_addr16[1] = 0x0000;
st_dest.sin6.sin6_addr.in6_u.u6_addr32[1] = 0x00000000;
st_dest.sin6.sin6_addr.in6_u.u6_addr16[4] = 0x0000;
st_dest.sin6.sin6_addr.in6_u.u6_addr16[5] = 0x0001;
st_dest.sin6.sin6_addr.in6_u.u6_addr32[3] = dest->in6_u.u6_addr32[3];
st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff;
/* Send packet over IP6 */
return tcpip_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st,
NULL, &nsolicit->csum );
}
/**
* Process ICMP6 headers
*
* @v iobuf I/O buffer
* @v st_src Source address
* @v st_dest Destination address
*/
static int icmp6_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest, __unused uint16_t pshdr_csum ) {
struct icmp6_header *icmp6hdr = iobuf->data;
static int icmpv6_rx_echo ( struct io_buffer *iobuf,
struct net_device *netdev,
struct sockaddr_in6 *sin6_src,
struct sockaddr_in6 *sin6_dest __unused ) {
struct sockaddr_tcpip *st_src =
( ( struct sockaddr_tcpip * ) sin6_src );
struct icmpv6_echo *echo = iobuf->data;
size_t len = iob_len ( iobuf );
int rc;
/* Sanity check */
if ( iob_len ( iobuf ) < sizeof ( *icmp6hdr ) ) {
DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
free_iob ( iobuf );
return -EINVAL;
if ( iob_len ( iobuf ) < sizeof ( *echo ) ) {
DBGC ( netdev, "ICMPv6 echo request too short at %zd bytes "
"(min %zd bytes)\n", iob_len ( iobuf ),
sizeof ( *echo ) );
rc = -EINVAL;
goto done;
}
DBGC ( netdev, "ICMPv6 echo request from %s (id %#04x seq %#04x)\n",
inet6_ntoa ( &sin6_dest->sin6_addr ), ntohs ( echo->ident ),
ntohs ( echo->sequence ) );
/* Convert echo request to echo reply and recalculate checksum */
echo->icmp.type = ICMPV6_ECHO_REPLY;
echo->icmp.chksum = 0;
echo->icmp.chksum = tcpip_chksum ( echo, len );
/* Transmit echo reply */
if ( ( rc = tcpip_tx ( iob_disown ( iobuf ), &icmpv6_protocol, NULL,
st_src, netdev, &echo->icmp.chksum ) ) != 0 ) {
DBGC ( netdev, "ICMPv6 could not transmit reply: %s\n",
strerror ( rc ) );
goto done;
}
/* TODO: Verify checksum */
/* Process the ICMP header */
switch ( icmp6hdr->type ) {
case ICMP6_NADVERT:
return ndp_process_advert ( iobuf, st_src, st_dest );
}
return -ENOSYS;
done:
free_iob ( iobuf );
return rc;
}
#if 0
void icmp6_test_nadvert (struct net_device *netdev, struct sockaddr_in6 *server_p, char *ll_addr) {
struct sockaddr_in6 server;
memcpy ( &server, server_p, sizeof ( server ) );
struct io_buffer *rxiobuf = alloc_iob ( 500 );
iob_reserve ( rxiobuf, MAX_HDR_LEN );
struct neighbour_advert *nadvert = iob_put ( rxiobuf, sizeof ( *nadvert ) );
nadvert->type = 136;
nadvert->code = 0;
nadvert->flags = ICMP6_FLAGS_SOLICITED;
nadvert->csum = 0xffff;
nadvert->target = server.sin6_addr;
nadvert->opt_type = 2;
nadvert->opt_len = 1;
memcpy ( nadvert->opt_ll_addr, ll_addr, 6 );
struct ip6_header *ip6hdr = iob_push ( rxiobuf, sizeof ( *ip6hdr ) );
ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );
ip6hdr->hop_limit = 255;
ip6hdr->nxt_hdr = 58;
ip6hdr->payload_len = htons ( sizeof ( *nadvert ) );
ip6hdr->src = server.sin6_addr;
ip6hdr->dest = server.sin6_addr;
hex_dump ( rxiobuf->data, iob_len ( rxiobuf ) );
net_rx ( rxiobuf, netdev, htons ( ETH_P_IPV6 ), ll_addr );
}
#endif
/** ICMP6 protocol */
struct tcpip_protocol icmp6_protocol __tcpip_protocol = {
.name = "ICMP6",
.rx = icmp6_rx,
.tcpip_proto = IP_ICMP6, // 58
/** ICMPv6 echo request handlers */
struct icmpv6_handler icmpv6_echo_handler __icmpv6_handler = {
.type = ICMPV6_ECHO_REQUEST,
.rx = icmpv6_rx_echo,
};
/**
* Identify ICMPv6 handler
*
* @v type ICMPv6 type
* @ret handler ICMPv6 handler, or NULL if not found
*/
static struct icmpv6_handler * icmpv6_handler ( unsigned int type ) {
struct icmpv6_handler *handler;
for_each_table_entry ( handler, ICMPV6_HANDLERS ) {
if ( handler->type == type )
return handler;
}
return NULL;
}
/**
* Process a received packet
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v st_src Partially-filled source address
* @v st_dest Partially-filled destination address
* @v pshdr_csum Pseudo-header checksum
* @ret rc Return status code
*/
static int icmpv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
struct sockaddr_in6 *sin6_src = ( ( struct sockaddr_in6 * ) st_src );
struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest );
struct icmpv6_header *icmp = iobuf->data;
size_t len = iob_len ( iobuf );
struct icmpv6_handler *handler;
unsigned int csum;
int rc;
/* Sanity check */
if ( len < sizeof ( *icmp ) ) {
DBGC ( netdev, "ICMPv6 packet too short at %zd bytes (min %zd "
"bytes)\n", len, sizeof ( *icmp ) );
rc = -EINVAL;
goto done;
}
/* Verify checksum */
csum = tcpip_continue_chksum ( pshdr_csum, icmp, len );
if ( csum != 0 ) {
DBGC ( netdev, "ICMPv6 checksum incorrect (is %04x, should be "
"0000)\n", csum );
DBGC_HDA ( netdev, 0, icmp, len );
rc = -EINVAL;
goto done;
}
/* Identify handler */
handler = icmpv6_handler ( icmp->type );
if ( ! handler ) {
DBGC ( netdev, "ICMPv6 unrecognised type %d\n", icmp->type );
rc = -ENOTSUP;
goto done;
}
/* Pass to handler */
if ( ( rc = handler->rx ( iob_disown ( iobuf ), netdev, sin6_src,
sin6_dest ) ) != 0 ) {
DBGC ( netdev, "ICMPv6 could not handle type %d: %s\n",
icmp->type, strerror ( rc ) );
goto done;
}
done:
free_iob ( iobuf );
return rc;
}
/** ICMPv6 TCP/IP protocol */
struct tcpip_protocol icmpv6_protocol __tcpip_protocol = {
.name = "ICMPv6",
.rx = icmpv6_rx,
.tcpip_proto = IP_ICMP6,
};

View File

@ -14,7 +14,7 @@
#include <ipxe/tcpip.h>
#include <ipxe/dhcp.h>
#include <ipxe/settings.h>
#include <ipxe/timer.h>
#include <ipxe/fragment.h>
/** @file
*
@ -30,12 +30,6 @@ static uint8_t next_ident_high = 0;
/** List of IPv4 miniroutes */
struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes );
/** List of fragment reassembly buffers */
static LIST_HEAD ( ipv4_fragments );
/** Fragment reassembly timeout */
#define IP_FRAG_TIMEOUT ( TICKS_PER_SEC / 2 )
/**
* Add IPv4 minirouting table entry
*
@ -133,131 +127,59 @@ static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
}
/**
* Expire fragment reassembly buffer
* Check if IPv4 fragment matches fragment reassembly buffer
*
* @v timer Retry timer
* @v fail Failure indicator
* @v fragment Fragment reassembly buffer
* @v iobuf I/O buffer
* @v hdrlen Length of non-fragmentable potion of I/O buffer
* @ret is_fragment Fragment matches this reassembly buffer
*/
static void ipv4_fragment_expired ( struct retry_timer *timer,
int fail __unused ) {
struct ipv4_fragment *frag =
container_of ( timer, struct ipv4_fragment, timer );
struct iphdr *iphdr = frag->iobuf->data;
static int ipv4_is_fragment ( struct fragment *fragment,
struct io_buffer *iobuf,
size_t hdrlen __unused ) {
struct iphdr *frag_iphdr = fragment->iobuf->data;
struct iphdr *iphdr = iobuf->data;
DBGC ( iphdr->src, "IPv4 fragment %04x expired\n",
ntohs ( iphdr->ident ) );
free_iob ( frag->iobuf );
list_del ( &frag->list );
free ( frag );
return ( ( iphdr->src.s_addr == frag_iphdr->src.s_addr ) &&
( iphdr->ident == frag_iphdr->ident ) );
}
/**
* Find matching fragment reassembly buffer
*
* @v iphdr IPv4 header
* @ret frag Fragment reassembly buffer, or NULL
*/
static struct ipv4_fragment * ipv4_fragment ( struct iphdr *iphdr ) {
struct ipv4_fragment *frag;
struct iphdr *frag_iphdr;
list_for_each_entry ( frag, &ipv4_fragments, list ) {
frag_iphdr = frag->iobuf->data;
if ( ( iphdr->src.s_addr == frag_iphdr->src.s_addr ) &&
( iphdr->ident == frag_iphdr->ident ) ) {
return frag;
}
}
return NULL;
}
/**
* Fragment reassembler
* Get IPv4 fragment offset
*
* @v iobuf I/O buffer
* @ret iobuf Reassembled packet, or NULL
* @v hdrlen Length of non-fragmentable potion of I/O buffer
* @ret offset Offset
*/
static struct io_buffer * ipv4_reassemble ( struct io_buffer *iobuf ) {
static size_t ipv4_fragment_offset ( struct io_buffer *iobuf,
size_t hdrlen __unused ) {
struct iphdr *iphdr = iobuf->data;
size_t offset = ( ( ntohs ( iphdr->frags ) & IP_MASK_OFFSET ) << 3 );
unsigned int more_frags = ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ));
size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
struct ipv4_fragment *frag;
size_t expected_offset;
struct io_buffer *new_iobuf;
/* Find matching fragment reassembly buffer, if any */
frag = ipv4_fragment ( iphdr );
/* Drop out-of-order fragments */
expected_offset = ( frag ? frag->offset : 0 );
if ( offset != expected_offset ) {
DBGC ( iphdr->src, "IPv4 dropping out-of-sequence fragment "
"%04x (%zd+%zd, expected %zd)\n",
ntohs ( iphdr->ident ), offset,
( iob_len ( iobuf ) - hdrlen ), expected_offset );
goto drop;
}
/* Create or extend fragment reassembly buffer as applicable */
if ( frag == NULL ) {
/* Create new fragment reassembly buffer */
frag = zalloc ( sizeof ( *frag ) );
if ( ! frag )
goto drop;
list_add ( &frag->list, &ipv4_fragments );
frag->iobuf = iobuf;
frag->offset = ( iob_len ( iobuf ) - hdrlen );
timer_init ( &frag->timer, ipv4_fragment_expired, NULL );
} else {
/* Extend reassembly buffer */
iob_pull ( iobuf, hdrlen );
new_iobuf = alloc_iob ( iob_len ( frag->iobuf ) +
iob_len ( iobuf ) );
if ( ! new_iobuf ) {
DBGC ( iphdr->src, "IPv4 could not extend reassembly "
"buffer to %zd bytes\n",
iob_len ( frag->iobuf ) + iob_len ( iobuf ) );
goto drop;
}
memcpy ( iob_put ( new_iobuf, iob_len ( frag->iobuf ) ),
frag->iobuf->data, iob_len ( frag->iobuf ) );
memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
iobuf->data, iob_len ( iobuf ) );
free_iob ( frag->iobuf );
frag->iobuf = new_iobuf;
frag->offset += iob_len ( iobuf );
free_iob ( iobuf );
iphdr = frag->iobuf->data;
iphdr->len = ntohs ( iob_len ( frag->iobuf ) );
/* Stop fragment reassembly timer */
stop_timer ( &frag->timer );
/* If this is the final fragment, return it */
if ( ! more_frags ) {
iobuf = frag->iobuf;
list_del ( &frag->list );
free ( frag );
return iobuf;
}
}
/* (Re)start fragment reassembly timer */
start_timer_fixed ( &frag->timer, IP_FRAG_TIMEOUT );
return NULL;
drop:
free_iob ( iobuf );
return NULL;
return ( ( ntohs ( iphdr->frags ) & IP_MASK_OFFSET ) << 3 );
}
/**
* Check if more fragments exist
*
* @v iobuf I/O buffer
* @v hdrlen Length of non-fragmentable potion of I/O buffer
* @ret more_frags More fragments exist
*/
static int ipv4_more_fragments ( struct io_buffer *iobuf,
size_t hdrlen __unused ) {
struct iphdr *iphdr = iobuf->data;
return ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) );
}
/** IPv4 fragment reassembler */
static struct fragment_reassembler ipv4_reassembler = {
.list = LIST_HEAD_INIT ( ipv4_reassembler.list ),
.is_fragment = ipv4_is_fragment,
.fragment_offset = ipv4_fragment_offset,
.more_fragments = ipv4_more_fragments,
};
/**
* Add IPv4 pseudo-header checksum to existing checksum
*
@ -368,7 +290,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
DBGC ( sin_dest->sin_addr, "IPv4 could not hash "
"multicast %s: %s\n",
inet_ntoa ( next_hop ), strerror ( rc ) );
return rc;
goto err;
}
ll_dest = ll_dest_buf;
} else {
@ -526,14 +448,14 @@ static int ipv4_rx ( struct io_buffer *iobuf,
/* Perform fragment reassembly if applicable */
if ( iphdr->frags & htons ( IP_MASK_OFFSET | IP_MASK_MOREFRAGS ) ) {
/* Pass the fragment to ipv4_reassemble() which returns
/* Pass the fragment to fragment_reassemble() which returns
* either a fully reassembled I/O buffer or NULL.
*/
iobuf = ipv4_reassemble ( iobuf );
iobuf = fragment_reassemble ( &ipv4_reassembler, iobuf,
&hdrlen );
if ( ! iobuf )
return 0;
iphdr = iobuf->data;
hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
}
/* Construct socket addresses, calculate pseudo-header
@ -547,7 +469,7 @@ static int ipv4_rx ( struct io_buffer *iobuf,
dest.sin.sin_addr = iphdr->dest;
pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM );
iob_pull ( iobuf, hdrlen );
if ( ( rc = tcpip_rx ( iobuf, iphdr->protocol, &src.st,
if ( ( rc = tcpip_rx ( iobuf, netdev, iphdr->protocol, &src.st,
&dest.st, pshdr_csum ) ) != 0 ) {
DBGC ( src.sin.sin_addr, "IPv4 received packet rejected by "
"stack: %s\n", strerror ( rc ) );

File diff suppressed because it is too large Load Diff

View File

@ -1,180 +1,373 @@
#include <stdint.h>
/*
* Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
#include <byteswap.h>
#include <errno.h>
#include <ipxe/if_ether.h>
#include <byteswap.h>
#include <ipxe/in.h>
#include <ipxe/iobuf.h>
#include <ipxe/tcpip.h>
#include <ipxe/ipv6.h>
#include <ipxe/icmpv6.h>
#include <ipxe/neighbour.h>
#include <ipxe/ndp.h>
#include <ipxe/icmp6.h>
#include <ipxe/ip6.h>
#include <ipxe/netdevice.h>
/** @file
*
* Neighbour Discovery Protocol
* IPv6 neighbour discovery protocol
*
* This file implements address resolution as specified by the neighbour
* discovery protocol in RFC2461. This protocol is part of the IPv6 protocol
* family.
*/
/* A neighbour entry */
struct ndp_entry {
/** Target IP6 address */
struct in6_addr in6;
/** Link layer protocol */
struct ll_protocol *ll_protocol;
/** Link-layer address */
uint8_t ll_addr[MAX_LL_ADDR_LEN];
/** State of the neighbour entry */
int state;
};
/** Number of entries in the neighbour cache table */
#define NUM_NDP_ENTRIES 4
/** The neighbour cache table */
static struct ndp_entry ndp_table[NUM_NDP_ENTRIES];
#define ndp_table_end &ndp_table[NUM_NDP_ENTRIES]
static unsigned int next_new_ndp_entry = 0;
/**
* Find entry in the neighbour cache
*
* @v in6 IP6 address
*/
static struct ndp_entry *
ndp_find_entry ( struct in6_addr *in6 ) {
struct ndp_entry *ndp;
for ( ndp = ndp_table ; ndp < ndp_table_end ; ndp++ ) {
if ( IP6_EQUAL ( ( *in6 ), ndp->in6 ) &&
( ndp->state != NDP_STATE_INVALID ) ) {
return ndp;
}
}
return NULL;
}
/**
* Add NDP entry
*
* @v netdev Network device
* @v in6 IP6 address
* @v ll_addr Link-layer address
* @v state State of the entry - one of the NDP_STATE_XXX values
*/
static void
add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
void *ll_addr, int state ) {
struct ndp_entry *ndp;
ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES];
/* Fill up entry */
ndp->ll_protocol = netdev->ll_protocol;
memcpy ( &ndp->in6, &( *in6 ), sizeof ( *in6 ) );
if ( ll_addr ) {
memcpy ( ndp->ll_addr, ll_addr, netdev->ll_protocol->ll_addr_len );
} else {
memset ( ndp->ll_addr, 0, netdev->ll_protocol->ll_addr_len );
}
ndp->state = state;
DBG ( "New neighbour cache entry: IP6 %s => %s %s\n",
inet6_ntoa ( ndp->in6 ), netdev->ll_protocol->name,
netdev->ll_protocol->ntoa ( ndp->ll_addr ) );
}
/**
* Resolve the link-layer address
* Transmit NDP neighbour solicitation/advertisement packet
*
* @v netdev Network device
* @v dest Destination address
* @v src Source address
* @ret dest_ll_addr Destination link-layer address or NULL
* @ret rc Status
*
* This function looks up the neighbour cache for an entry corresponding to the
* destination address. If it finds a valid entry, it fills up dest_ll_addr and
* returns 0. Otherwise it sends a neighbour solicitation to the solicited
* multicast address.
* @v sin6_src Source socket address
* @v sin6_dest Destination socket address
* @v target Neighbour target address
* @v icmp_type ICMPv6 type
* @v flags NDP flags
* @v option_type NDP option type
* @ret rc Return status code
*/
int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
struct in6_addr *src, void *dest_ll_addr ) {
static int ndp_tx_neighbour ( struct net_device *netdev,
struct sockaddr_in6 *sin6_src,
struct sockaddr_in6 *sin6_dest,
const struct in6_addr *target,
unsigned int icmp_type,
unsigned int flags,
unsigned int option_type ) {
struct sockaddr_tcpip *st_src =
( ( struct sockaddr_tcpip * ) sin6_src );
struct sockaddr_tcpip *st_dest =
( ( struct sockaddr_tcpip * ) sin6_dest );
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct ndp_entry *ndp;
struct io_buffer *iobuf;
struct ndp_header *ndp;
size_t option_len;
size_t len;
int rc;
ndp = ndp_find_entry ( dest );
/* Check if the entry is valid */
if ( ndp && ndp->state == NDP_STATE_REACHABLE ) {
DBG ( "Neighbour cache hit: IP6 %s => %s %s\n",
inet6_ntoa ( *dest ), ll_protocol->name,
ll_protocol->ntoa ( ndp->ll_addr ) );
memcpy ( dest_ll_addr, ndp->ll_addr, ll_protocol->ll_addr_len );
return 0;
}
/* Allocate and populate buffer */
option_len = ( ( sizeof ( ndp->option[0] ) + ll_protocol->ll_addr_len +
NDP_OPTION_BLKSZ - 1 ) &
~( NDP_OPTION_BLKSZ - 1 ) );
len = ( sizeof ( *ndp ) + option_len );
iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len );
if ( ! iobuf )
return -ENOMEM;
iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN );
ndp = iob_put ( iobuf, len );
memset ( ndp, 0, len );
ndp->icmp.type = icmp_type;
ndp->flags = flags;
memcpy ( &ndp->target, target, sizeof ( ndp->target ) );
ndp->option[0].type = option_type;
ndp->option[0].blocks = ( option_len / NDP_OPTION_BLKSZ );
memcpy ( ndp->option[0].value, netdev->ll_addr,
ll_protocol->ll_addr_len );
ndp->icmp.chksum = tcpip_chksum ( ndp, len );
/* Check if the entry was already created */
if ( ndp ) {
DBG ( "Awaiting neighbour advertisement\n" );
/* For test */
// ndp->state = NDP_STATE_REACHABLE;
// memcpy ( ndp->ll_addr, netdev->ll_addr, 6 );
// assert ( ndp->ll_protocol->ll_addr_len == 6 );
// icmp6_test_nadvert ( netdev, dest, ndp->ll_addr );
// assert ( ndp->state == NDP_STATE_REACHABLE );
/* Take it out till here */
return -ENOENT;
}
DBG ( "Neighbour cache miss: IP6 %s\n", inet6_ntoa ( *dest ) );
/* Add entry in the neighbour cache */
add_ndp_entry ( netdev, dest, NULL, NDP_STATE_INCOMPLETE );
/* Send neighbour solicitation */
if ( ( rc = icmp6_send_solicit ( netdev, src, dest ) ) != 0 ) {
/* Transmit packet */
if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest,
netdev, &ndp->icmp.chksum ) ) != 0 ) {
DBGC ( netdev, "NDP could not transmit packet: %s\n",
strerror ( rc ) );
return rc;
}
return -ENOENT;
return 0;
}
/**
* Process neighbour advertisement
* Transmit NDP neighbour discovery request
*
* @v iobuf I/O buffer
* @v st_src Source address
* @v st_dest Destination address
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @v net_source Source network-layer address
* @ret rc Return status code
*/
int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src __unused,
struct sockaddr_tcpip *st_dest __unused ) {
struct neighbour_advert *nadvert = iobuf->data;
struct ndp_entry *ndp;
static int ndp_tx_request ( struct net_device *netdev,
struct net_protocol *net_protocol __unused,
const void *net_dest, const void *net_source ) {
struct sockaddr_in6 sin6_src;
struct sockaddr_in6 sin6_dest;
/* Construct source address */
memset ( &sin6_src, 0, sizeof ( sin6_src ) );
sin6_src.sin6_family = AF_INET6;
memcpy ( &sin6_src.sin6_addr, net_source,
sizeof ( sin6_src.sin6_addr ) );
sin6_src.sin6_scope_id = htons ( netdev->index );
/* Construct multicast destination address */
memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
sin6_dest.sin6_family = AF_INET6;
sin6_dest.sin6_scope_id = htons ( netdev->index );
ipv6_solicited_node ( &sin6_dest.sin6_addr, net_dest );
/* Transmit neighbour discovery packet */
return ndp_tx_neighbour ( netdev, &sin6_src, &sin6_dest, net_dest,
ICMPV6_NDP_NEIGHBOUR_SOLICITATION, 0,
NDP_OPT_LL_SOURCE );
}
/** NDP neighbour discovery protocol */
struct neighbour_discovery ndp_discovery = {
.name = "NDP",
.tx_request = ndp_tx_request,
};
/**
* Process NDP neighbour solicitation source link-layer address option
*
* @v netdev Network device
* @v sin6_src Source socket address
* @v ndp NDP packet
* @v ll_addr Source link-layer address
* @v ll_addr_len Source link-layer address length
* @ret rc Return status code
*/
static int ndp_rx_neighbour_solicitation ( struct net_device *netdev,
struct sockaddr_in6 *sin6_src,
struct ndp_header *ndp __unused,
const void *ll_addr,
size_t ll_addr_len ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
int rc;
/* Silently ignore neighbour solicitations for addresses we do
* not own.
*/
if ( ! ipv6_has_addr ( netdev, &ndp->target ) )
return 0;
/* Sanity check */
if ( iob_len ( iobuf ) < sizeof ( *nadvert ) ) {
DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
if ( ll_addr_len < ll_protocol->ll_addr_len ) {
DBGC ( netdev, "NDP neighbour solicitation link-layer address "
"too short at %zd bytes (min %d bytes)\n",
ll_addr_len, ll_protocol->ll_addr_len );
return -EINVAL;
}
assert ( nadvert->code == 0 );
assert ( nadvert->flags & ICMP6_FLAGS_SOLICITED );
assert ( nadvert->opt_type == 2 );
/* Update the neighbour cache, if entry is present */
ndp = ndp_find_entry ( &nadvert->target );
if ( ndp ) {
assert ( nadvert->opt_len ==
( ( 2 + ndp->ll_protocol->ll_addr_len ) / 8 ) );
if ( IP6_EQUAL ( ndp->in6, nadvert->target ) ) {
memcpy ( ndp->ll_addr, nadvert->opt_ll_addr,
ndp->ll_protocol->ll_addr_len );
ndp->state = NDP_STATE_REACHABLE;
return 0;
}
/* Create or update neighbour cache entry */
if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
&sin6_src->sin6_addr,
ll_addr ) ) != 0 ) {
DBGC ( netdev, "NDP could not define %s => %s: %s\n",
inet6_ntoa ( &sin6_src->sin6_addr ),
ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
return rc;
}
DBG ( "Unsolicited advertisement (dropping packet)\n" );
/* Send neighbour advertisement */
if ( ( rc = ndp_tx_neighbour ( netdev, NULL, sin6_src, &ndp->target,
ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT,
( NDP_SOLICITED | NDP_OVERRIDE ),
NDP_OPT_LL_TARGET ) ) != 0 ) {
return rc;
}
return 0;
}
/**
* Process NDP neighbour advertisement target link-layer address option
*
* @v netdev Network device
* @v sin6_src Source socket address
* @v ndp NDP packet
* @v ll_addr Target link-layer address
* @v ll_addr_len Target link-layer address length
* @ret rc Return status code
*/
static int
ndp_rx_neighbour_advertisement ( struct net_device *netdev,
struct sockaddr_in6 *sin6_src __unused,
struct ndp_header *ndp, const void *ll_addr,
size_t ll_addr_len ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
int rc;
/* Sanity check */
if ( ll_addr_len < ll_protocol->ll_addr_len ) {
DBGC ( netdev, "NDP neighbour advertisement link-layer address "
"too short at %zd bytes (min %d bytes)\n",
ll_addr_len, ll_protocol->ll_addr_len );
return -EINVAL;
}
/* Update neighbour cache entry, if any */
if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &ndp->target,
ll_addr ) ) != 0 ) {
DBGC ( netdev, "NDP could not update %s => %s: %s\n",
inet6_ntoa ( &ndp->target ),
ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
return rc;
}
return 0;
}
/** An NDP option handler */
struct ndp_option_handler {
/** ICMPv6 type */
uint8_t icmp_type;
/** Option type */
uint8_t option_type;
/**
* Handle received option
*
* @v netdev Network device
* @v sin6_src Source socket address
* @v ndp NDP packet
* @v value Option value
* @v len Option length
* @ret rc Return status code
*/
int ( * rx ) ( struct net_device *netdev, struct sockaddr_in6 *sin6_src,
struct ndp_header *ndp, const void *value, size_t len );
};
/** NDP option handlers */
static struct ndp_option_handler ndp_option_handlers[] = {
{
.icmp_type = ICMPV6_NDP_NEIGHBOUR_SOLICITATION,
.option_type = NDP_OPT_LL_SOURCE,
.rx = ndp_rx_neighbour_solicitation,
},
{
.icmp_type = ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT,
.option_type = NDP_OPT_LL_TARGET,
.rx = ndp_rx_neighbour_advertisement,
},
};
/**
* Process received NDP option
*
* @v netdev Network device
* @v sin6_src Source socket address
* @v ndp NDP packet
* @v type Option type
* @v value Option value
* @v len Option length
* @ret rc Return status code
*/
static int ndp_rx_option ( struct net_device *netdev,
struct sockaddr_in6 *sin6_src,
struct ndp_header *ndp, unsigned int type,
const void *value, size_t len ) {
struct ndp_option_handler *handler;
unsigned int i;
/* Locate a suitable option handler, if any */
for ( i = 0 ; i < ( sizeof ( ndp_option_handlers ) /
sizeof ( ndp_option_handlers[0] ) ) ; i++ ) {
handler = &ndp_option_handlers[i];
if ( ( handler->icmp_type == ndp->icmp.type ) &&
( handler->option_type == type ) ) {
return handler->rx ( netdev, sin6_src, ndp,
value, len );
}
}
/* Silently ignore unknown options as per RFC 4861 */
return 0;
}
/**
* Process received NDP packet
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v sin6_src Source socket address
* @v sin6_dest Destination socket address
* @ret rc Return status code
*/
static int ndp_rx ( struct io_buffer *iobuf,
struct net_device *netdev,
struct sockaddr_in6 *sin6_src,
struct sockaddr_in6 *sin6_dest __unused ) {
struct ndp_header *ndp = iobuf->data;
struct ndp_option *option;
size_t remaining;
size_t option_len;
size_t option_value_len;
int rc;
/* Sanity check */
if ( iob_len ( iobuf ) < sizeof ( *ndp ) ) {
DBGC ( netdev, "NDP packet too short at %zd bytes (min %zd "
"bytes)\n", iob_len ( iobuf ), sizeof ( *ndp ) );
rc = -EINVAL;
goto done;
}
/* Search for option */
option = ndp->option;
remaining = ( iob_len ( iobuf ) - offsetof ( typeof ( *ndp ), option ));
while ( remaining ) {
/* Sanity check */
if ( ( remaining < sizeof ( *option ) ) ||
( option->blocks == 0 ) ||
( remaining < ( option->blocks * NDP_OPTION_BLKSZ ) ) ) {
DBGC ( netdev, "NDP bad option length:\n" );
DBGC_HDA ( netdev, 0, option, remaining );
rc = -EINVAL;
goto done;
}
option_len = ( option->blocks * NDP_OPTION_BLKSZ );
option_value_len = ( option_len - sizeof ( *option ) );
/* Handle option */
if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp,
option->type, option->value,
option_value_len ) ) != 0 ) {
goto done;
}
/* Move to next option */
option = ( ( ( void * ) option ) + option_len );
remaining -= option_len;
}
/* Success */
rc = 0;
done:
free_iob ( iobuf );
return rc;
}
/** NDP ICMPv6 handlers */
struct icmpv6_handler ndp_handlers[] __icmpv6_handler = {
{
.type = ICMPV6_NDP_NEIGHBOUR_SOLICITATION,
.rx = ndp_rx,
},
{
.type = ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT,
.rx = ndp_rx,
},
};

439
src/net/neighbour.c Normal file
View File

@ -0,0 +1,439 @@
/*
* Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ipxe/iobuf.h>
#include <ipxe/retry.h>
#include <ipxe/timer.h>
#include <ipxe/malloc.h>
#include <ipxe/neighbour.h>
/** @file
*
* Neighbour discovery
*
* This file implements the abstract functions of neighbour discovery,
* independent of the underlying network protocol (e.g. ARP or NDP).
*
*/
/** Neighbour discovery minimum timeout */
#define NEIGHBOUR_MIN_TIMEOUT ( TICKS_PER_SEC / 8 )
/** Neighbour discovery maximum timeout */
#define NEIGHBOUR_MAX_TIMEOUT ( TICKS_PER_SEC * 3 )
/** The neighbour cache */
struct list_head neighbours = LIST_HEAD_INIT ( neighbours );
static void neighbour_expired ( struct retry_timer *timer, int over );
/**
* Free neighbour cache entry
*
* @v refcnt Reference count
*/
static void neighbour_free ( struct refcnt *refcnt ) {
struct neighbour *neighbour =
container_of ( refcnt, struct neighbour, refcnt );
/* Sanity check */
assert ( list_empty ( &neighbour->tx_queue ) );
/* Drop reference to network device */
netdev_put ( neighbour->netdev );
/* Free neighbour */
free ( neighbour );
}
/**
* Create neighbour cache entry
*
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @ret neighbour Neighbour cache entry, or NULL if allocation failed
*/
static struct neighbour * neighbour_create ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest ) {
struct neighbour *neighbour;
/* Allocate and initialise entry */
neighbour = zalloc ( sizeof ( *neighbour ) );
if ( ! neighbour )
return NULL;
ref_init ( &neighbour->refcnt, neighbour_free );
neighbour->netdev = netdev_get ( netdev );
neighbour->net_protocol = net_protocol;
memcpy ( neighbour->net_dest, net_dest,
net_protocol->net_addr_len );
timer_init ( &neighbour->timer, neighbour_expired, &neighbour->refcnt );
neighbour->timer.min_timeout = NEIGHBOUR_MIN_TIMEOUT;
neighbour->timer.max_timeout = NEIGHBOUR_MAX_TIMEOUT;
INIT_LIST_HEAD ( &neighbour->tx_queue );
/* Transfer ownership to cache */
list_add ( &neighbour->list, &neighbours );
DBGC ( neighbour, "NEIGHBOUR %s %s %s created\n", netdev->name,
net_protocol->name, net_protocol->ntoa ( net_dest ) );
return neighbour;
}
/**
* Find neighbour cache entry
*
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @ret neighbour Neighbour cache entry, or NULL if not found
*/
static struct neighbour * neighbour_find ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest ) {
struct neighbour *neighbour;
list_for_each_entry ( neighbour, &neighbours, list ) {
if ( ( neighbour->netdev == netdev ) &&
( neighbour->net_protocol == net_protocol ) &&
( memcmp ( neighbour->net_dest, net_dest,
net_protocol->net_addr_len ) == 0 ) ) {
/* Move to start of cache */
list_del ( &neighbour->list );
list_add ( &neighbour->list, &neighbours );
return neighbour;
}
}
return NULL;
}
/**
* Start neighbour discovery
*
* @v neighbour Neighbour cache entry
* @v discovery Neighbour discovery protocol
* @v net_source Source network-layer address
*/
static void neighbour_discover ( struct neighbour *neighbour,
struct neighbour_discovery *discovery,
const void *net_source ) {
struct net_device *netdev = neighbour->netdev;
struct net_protocol *net_protocol = neighbour->net_protocol;
/* Record discovery protocol and source network-layer address */
neighbour->discovery = discovery;
memcpy ( neighbour->net_source, net_source,
net_protocol->net_addr_len );
/* Start timer to trigger neighbour discovery */
start_timer_nodelay ( &neighbour->timer );
DBGC ( neighbour, "NEIGHBOUR %s %s %s discovering via %s\n",
netdev->name, net_protocol->name,
net_protocol->ntoa ( neighbour->net_dest ),
neighbour->discovery->name );
}
/**
* Complete neighbour discovery
*
* @v neighbour Neighbour cache entry
* @v ll_dest Destination link-layer address
*/
static void neighbour_discovered ( struct neighbour *neighbour,
const void *ll_dest ) {
struct net_device *netdev = neighbour->netdev;
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct net_protocol *net_protocol = neighbour->net_protocol;
struct io_buffer *iobuf;
int rc;
/* Fill in link-layer address */
memcpy ( neighbour->ll_dest, ll_dest, ll_protocol->ll_addr_len );
DBGC ( neighbour, "NEIGHBOUR %s %s %s is %s %s\n", netdev->name,
net_protocol->name, net_protocol->ntoa ( neighbour->net_dest ),
ll_protocol->name, ll_protocol->ntoa ( neighbour->ll_dest ) );
/* Stop retransmission timer */
stop_timer ( &neighbour->timer );
/* Transmit any packets in queue. Take out a temporary
* reference on the entry to prevent it from going out of
* scope during the call to net_tx().
*/
ref_get ( &neighbour->refcnt );
while ( ( iobuf = list_first_entry ( &neighbour->tx_queue,
struct io_buffer, list )) != NULL){
DBGC2 ( neighbour, "NEIGHBOUR %s %s %s transmitting deferred "
"packet\n", netdev->name, net_protocol->name,
net_protocol->ntoa ( neighbour->net_dest ) );
list_del ( &iobuf->list );
if ( ( rc = net_tx ( iobuf, netdev, net_protocol, ll_dest,
netdev->ll_addr ) ) != 0 ) {
DBGC ( neighbour, "NEIGHBOUR %s %s %s could not "
"transmit deferred packet: %s\n",
netdev->name, net_protocol->name,
net_protocol->ntoa ( neighbour->net_dest ),
strerror ( rc ) );
/* Ignore error and continue */
}
}
ref_put ( &neighbour->refcnt );
}
/**
* Destroy neighbour cache entry
*
* @v neighbour Neighbour cache entry
* @v rc Reason for destruction
*/
static void neighbour_destroy ( struct neighbour *neighbour, int rc ) {
struct net_device *netdev = neighbour->netdev;
struct net_protocol *net_protocol = neighbour->net_protocol;
struct io_buffer *iobuf;
/* Take ownership from cache */
list_del ( &neighbour->list );
/* Stop timer */
stop_timer ( &neighbour->timer );
/* Discard any outstanding I/O buffers */
while ( ( iobuf = list_first_entry ( &neighbour->tx_queue,
struct io_buffer, list )) != NULL){
DBGC2 ( neighbour, "NEIGHBOUR %s %s %s discarding deferred "
"packet: %s\n", netdev->name, net_protocol->name,
net_protocol->ntoa ( neighbour->net_dest ),
strerror ( rc ) );
list_del ( &iobuf->list );
netdev_tx_err ( neighbour->netdev, iobuf, rc );
}
DBGC ( neighbour, "NEIGHBOUR %s %s %s destroyed: %s\n", netdev->name,
net_protocol->name, net_protocol->ntoa ( neighbour->net_dest ),
strerror ( rc ) );
/* Drop remaining reference */
ref_put ( &neighbour->refcnt );
}
/**
* Handle neighbour timer expiry
*
* @v timer Retry timer
* @v fail Failure indicator
*/
static void neighbour_expired ( struct retry_timer *timer, int fail ) {
struct neighbour *neighbour =
container_of ( timer, struct neighbour, timer );
struct net_device *netdev = neighbour->netdev;
struct net_protocol *net_protocol = neighbour->net_protocol;
struct neighbour_discovery *discovery =
neighbour->discovery;
const void *net_dest = neighbour->net_dest;
const void *net_source = neighbour->net_source;
int rc;
/* If we have failed, destroy the cache entry */
if ( fail ) {
neighbour_destroy ( neighbour, -ETIMEDOUT );
return;
}
/* Restart the timer */
start_timer ( &neighbour->timer );
/* Transmit neighbour request */
if ( ( rc = discovery->tx_request ( netdev, net_protocol, net_dest,
net_source ) ) != 0 ) {
DBGC ( neighbour, "NEIGHBOUR %s %s %s could not transmit %s "
"request: %s\n", netdev->name, net_protocol->name,
net_protocol->ntoa ( neighbour->net_dest ),
neighbour->discovery->name, strerror ( rc ) );
/* Retransmit when timer expires */
return;
}
}
/**
* Transmit packet, determining link-layer address via neighbour discovery
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v discovery Neighbour discovery protocol
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @v net_source Source network-layer address
* @v ll_source Source link-layer address
* @ret rc Return status code
*/
int neighbour_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol, const void *net_dest,
struct neighbour_discovery *discovery,
const void *net_source, const void *ll_source ) {
struct neighbour *neighbour;
/* Find or create neighbour cache entry */
neighbour = neighbour_find ( netdev, net_protocol, net_dest );
if ( ! neighbour ) {
neighbour = neighbour_create ( netdev, net_protocol, net_dest );
if ( ! neighbour )
return -ENOMEM;
neighbour_discover ( neighbour, discovery, net_source );
}
/* If a link-layer address is available then transmit
* immediately, otherwise queue for later transmission.
*/
if ( neighbour_has_ll_dest ( neighbour ) ) {
return net_tx ( iobuf, netdev, net_protocol, neighbour->ll_dest,
ll_source );
} else {
DBGC2 ( neighbour, "NEIGHBOUR %s %s %s deferring packet\n",
netdev->name, net_protocol->name,
net_protocol->ntoa ( net_dest ) );
list_add_tail ( &iobuf->list, &neighbour->tx_queue );
return -EAGAIN;
}
}
/**
* Update existing neighbour cache entry
*
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @v ll_dest Destination link-layer address
* @ret rc Return status code
*/
int neighbour_update ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *ll_dest ) {
struct neighbour *neighbour;
/* Find neighbour cache entry */
neighbour = neighbour_find ( netdev, net_protocol, net_dest );
if ( ! neighbour )
return -ENOENT;
/* Set destination address */
neighbour_discovered ( neighbour, ll_dest );
return 0;
}
/**
* Define neighbour cache entry
*
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v net_dest Destination network-layer address
* @v ll_dest Destination link-layer address, if known
* @ret rc Return status code
*/
int neighbour_define ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *ll_dest ) {
struct neighbour *neighbour;
/* Find or create neighbour cache entry */
neighbour = neighbour_find ( netdev, net_protocol, net_dest );
if ( ! neighbour ) {
neighbour = neighbour_create ( netdev, net_protocol, net_dest );
if ( ! neighbour )
return -ENOMEM;
}
/* Set destination address */
neighbour_discovered ( neighbour, ll_dest );
return 0;
}
/**
* Update neighbour cache on network device creation
*
* @v netdev Network device
*/
static int neighbour_probe ( struct net_device *netdev __unused ) {
/* Nothing to do */
return 0;
}
/**
* Update neighbour cache on network device state change or removal
*
* @v netdev Network device
*/
static void neighbour_flush ( struct net_device *netdev ) {
struct neighbour *neighbour;
struct neighbour *tmp;
/* Remove all neighbour cache entries when a network device is closed */
if ( ! netdev_is_open ( netdev ) ) {
list_for_each_entry_safe ( neighbour, tmp, &neighbours, list )
neighbour_destroy ( neighbour, -ENODEV );
}
}
/** Neighbour driver (for net device notifications) */
struct net_driver neighbour_net_driver __net_driver = {
.name = "Neighbour",
.probe = neighbour_probe,
.notify = neighbour_flush,
.remove = neighbour_flush,
};
/**
* Discard some cached neighbour entries
*
* @ret discarded Number of cached items discarded
*/
static unsigned int neighbour_discard ( void ) {
struct neighbour *neighbour;
/* Drop oldest cache entry, if any */
neighbour = list_last_entry ( &neighbours, struct neighbour, list );
if ( neighbour ) {
neighbour_destroy ( neighbour, -ENOBUFS );
return 1;
} else {
return 0;
}
}
/**
* Neighbour cache discarder
*
* Neighbour cache entries are deemed to have a high replacement cost,
* since flushing an active neighbour cache entry midway through a TCP
* transfer will cause substantial disruption.
*/
struct cache_discarder neighbour_discarder __cache_discarder (CACHE_EXPENSIVE)={
.discard = neighbour_discard,
};

View File

@ -125,6 +125,7 @@ static int netdev_fetch_bustype ( struct net_device *netdev, void *data,
[BUS_TYPE_EISA] = "EISA",
[BUS_TYPE_MCA] = "MCA",
[BUS_TYPE_ISA] = "ISA",
[BUS_TYPE_TAP] = "TAP",
};
struct device_description *desc = &netdev->dev->desc;
const char *bustype;
@ -132,7 +133,7 @@ static int netdev_fetch_bustype ( struct net_device *netdev, void *data,
assert ( desc->bus_type < ( sizeof ( bustypes ) /
sizeof ( bustypes[0] ) ) );
bustype = bustypes[desc->bus_type];
assert ( bustypes != NULL );
assert ( bustype != NULL );
strncpy ( data, bustype, len );
return strlen ( bustype );
}

View File

@ -498,10 +498,11 @@ int register_netdev ( struct net_device *netdev ) {
uint32_t seed;
int rc;
/* Create device name */
/* Record device index and create device name */
netdev->index = ifindex++;
if ( netdev->name[0] == '\0' ) {
snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
ifindex++ );
netdev->index );
}
/* Set initial link-layer address, if not already set */

View File

@ -1115,12 +1115,14 @@ static void tcp_process_rx_queue ( struct tcp_connection *tcp ) {
* Process received packet
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v st_src Partially-filled source address
* @v st_dest Partially-filled destination address
* @v pshdr_csum Pseudo-header checksum
* @ret rc Return status code
*/
static int tcp_rx ( struct io_buffer *iobuf,
struct net_device *netdev __unused,
struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest __unused,
uint16_t pshdr_csum ) {

View File

@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
/** Process a received TCP/IP packet
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v tcpip_proto Transport-layer protocol number
* @v st_src Partially-filled source address
* @v st_dest Partially-filled destination address
@ -32,8 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
* address family and the network-layer addresses, but leave the ports
* and the rest of the structures as zero).
*/
int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
struct sockaddr_tcpip *st_src,
int tcpip_rx ( struct io_buffer *iobuf, struct net_device *netdev,
uint8_t tcpip_proto, struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest,
uint16_t pshdr_csum ) {
struct tcpip_protocol *tcpip;
@ -42,7 +43,8 @@ int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
for_each_table_entry ( tcpip, TCPIP_PROTOCOLS ) {
if ( tcpip->tcpip_proto == tcpip_proto ) {
DBG ( "TCP/IP received %s packet\n", tcpip->name );
return tcpip->rx ( iobuf, st_src, st_dest, pshdr_csum );
return tcpip->rx ( iobuf, netdev, st_src, st_dest,
pshdr_csum );
}
}

View File

@ -247,12 +247,15 @@ static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) {
* Process a received packet
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v st_src Partially-filled source address
* @v st_dest Partially-filled destination address
* @v pshdr_csum Pseudo-header checksum
* @ret rc Return status code
*/
static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
static int udp_rx ( struct io_buffer *iobuf,
struct net_device *netdev __unused,
struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
struct udp_header *udphdr = iobuf->data;
struct udp_connection *udp;

181
src/tests/ipv6_test.c Normal file
View File

@ -0,0 +1,181 @@
/*
* Copyright (C) 2013 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
/** @file
*
* IPv6 tests
*
*/
/* Forcibly enable assertions */
#undef NDEBUG
#include <stdint.h>
#include <string.h>
#include <byteswap.h>
#include <ipxe/ipv6.h>
#include <ipxe/test.h>
/** Define inline IPv6 address */
#define IPV6(...) { __VA_ARGS__ }
/**
* Report an inet6_ntoa() test result
*
* @v addr IPv6 address
* @v text Expected textual representation
*/
#define inet6_ntoa_ok( addr, text ) do { \
static const struct in6_addr in = { \
.s6_addr = addr, \
}; \
static const char expected[] = text; \
char *actual; \
\
actual = inet6_ntoa ( &in ); \
DBG ( "inet6_ntoa ( %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ) " \
"= %s\n", ntohs ( in.s6_addr16[0] ), \
ntohs ( in.s6_addr16[1] ), ntohs ( in.s6_addr16[2] ), \
ntohs ( in.s6_addr16[3] ), ntohs ( in.s6_addr16[4] ), \
ntohs ( in.s6_addr16[5] ), ntohs ( in.s6_addr16[6] ), \
ntohs ( in.s6_addr16[7] ), actual ); \
ok ( strcmp ( actual, expected ) == 0 ); \
} while ( 0 )
/**
* Report an inet6_aton() test result
*
* @v text Textual representation
* @v addr Expected IPv6 address
*/
#define inet6_aton_ok( text, addr ) do { \
static const char string[] = text; \
static const struct in6_addr expected = { \
.s6_addr = addr, \
}; \
struct in6_addr actual; \
\
ok ( inet6_aton ( string, &actual ) == 0 ); \
DBG ( "inet6_aton ( \"%s\" ) = %s\n", string, \
inet6_ntoa ( &actual ) ); \
ok ( memcmp ( &actual, &expected, sizeof ( actual ) ) == 0 ); \
} while ( 0 )
/**
* Report an inet6_aton() failure test result
*
* @v text Textual representation
*/
#define inet6_aton_fail_ok( text ) do { \
static const char string[] = text; \
struct in6_addr dummy; \
\
ok ( inet6_aton ( string, &dummy ) != 0 ); \
} while ( 0 )
/**
* Perform IPv6 self-tests
*
*/
static void ipv6_test_exec ( void ) {
/* inet6_ntoa() tests */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
0x00, 0x00, 0x00, 0x00, 0x69, 0x50, 0x58, 0x45 ),
"2001:ba8:0:1d4::6950:5845" );
/* No zeros */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 ),
"2001:db8:1:1:1:1:1:1" );
/* Run of zeros */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
"2001:db8::1" );
/* No "::" for single zero */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 ),
"2001:db8:0:1:1:1:1:1" );
/* Use "::" for longest run of zeros */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
"2001:0:0:1::1" );
/* Use "::" for leftmost equal-length run of zeros */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
"2001:db8::1:0:0:1" );
/* Trailing run of zeros */
inet6_ntoa_ok ( IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ),
"fe80::" );
/* Leading run of zeros */
inet6_ntoa_ok ( IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
"::1" );
/* All zeros */
inet6_ntoa_ok ( IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ),
"::" );
/* Maximum length */
inet6_ntoa_ok ( IPV6 ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ),
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" );
/* inet6_aton() tests */
inet6_aton_ok ( "2001:ba8:0:1d4::6950:5845",
IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
0x00, 0x00, 0x00, 0x00, 0x69, 0x50, 0x58, 0x45));
/* No zeros */
inet6_aton_ok ( "2001:db8:1:1:1:1:1:1",
IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01));
/* All intervening zeros */
inet6_aton_ok ( "fe80::1",
IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01));
/* Trailing run of zeros */
inet6_aton_ok ( "fe80::",
IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
/* Leading run of zeros */
inet6_aton_ok ( "::1",
IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01));
/* All zeros */
inet6_aton_ok ( "::",
IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
/* inet6_aton() failure tests */
inet6_aton_fail_ok ( "20012:ba8:0:1d4::6950:5845" );
inet6_aton_fail_ok ( "200z:ba8:0:1d4::6950:5845" );
inet6_aton_fail_ok ( "2001.ba8:0:1d4::6950:5845" );
inet6_aton_fail_ok ( "2001:db8:1:1:1:1:1" );
inet6_aton_fail_ok ( "2001:db8:1:1:1:1:1:1:2" );
inet6_aton_fail_ok ( "2001:db8::1::2" );
inet6_aton_fail_ok ( "2001:ba8:0:1d4:::6950:5845" );
inet6_aton_fail_ok ( ":::" );
}
/** IPv6 self-test */
struct self_test ipv6_test __self_test = {
.name = "ipv6",
.exec = ipv6_test_exec,
};

View File

@ -36,6 +36,7 @@ REQUIRE_OBJECT ( base16_test );
REQUIRE_OBJECT ( settings_test );
REQUIRE_OBJECT ( time_test );
REQUIRE_OBJECT ( tcpip_test );
REQUIRE_OBJECT ( ipv6_test );
REQUIRE_OBJECT ( crc32_test );
REQUIRE_OBJECT ( md5_test );
REQUIRE_OBJECT ( sha1_test );

56
src/usr/neighmgmt.c Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdio.h>
#include <ipxe/neighbour.h>
#include <usr/neighmgmt.h>
/** @file
*
* Neighbour management
*
*/
/**
* Print neighbour table
*
*/
void nstat ( void ) {
struct neighbour *neighbour;
struct net_device *netdev;
struct ll_protocol *ll_protocol;
struct net_protocol *net_protocol;
list_for_each_entry ( neighbour, &neighbours, list ) {
netdev = neighbour->netdev;
ll_protocol = netdev->ll_protocol;
net_protocol = neighbour->net_protocol;
printf ( "%s %s %s is %s %s", netdev->name, net_protocol->name,
net_protocol->ntoa ( neighbour->net_dest ),
ll_protocol->name,
( neighbour_has_ll_dest ( neighbour ) ?
ll_protocol->ntoa ( neighbour->ll_dest ) :
"(incomplete)" ) );
if ( neighbour->discovery )
printf ( " (%s)", neighbour->discovery->name );
printf ( "\n" );
}
}

View File

@ -19,28 +19,26 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdio.h>
#include <ipxe/netdevice.h>
#include <ipxe/ip.h>
#include <usr/route.h>
/** @file
*
* Routing table management
* Routing management
*
*/
/**
* Print routing table
*
*/
void route ( void ) {
struct ipv4_miniroute *miniroute;
struct net_device *netdev;
struct routing_family *family;
list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
printf ( "%s: %s/", miniroute->netdev->name,
inet_ntoa ( miniroute->address ) );
printf ( "%s", inet_ntoa ( miniroute->netmask ) );
if ( miniroute->gateway.s_addr )
printf ( " gw %s", inet_ntoa ( miniroute->gateway ) );
if ( ! netdev_is_open ( miniroute->netdev ) )
printf ( " (inaccessible)" );
printf ( "\n" );
for_each_netdev ( netdev ) {
for_each_table_entry ( family, ROUTING_FAMILIES ) {
family->print ( netdev );
}
}
}

58
src/usr/route_ipv4.c Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdio.h>
#include <ipxe/netdevice.h>
#include <ipxe/ip.h>
#include <usr/route.h>
/** @file
*
* IPv4 routing management
*
*/
/**
* Print IPv4 routing table
*
* @v netdev Network device
*/
static void route_ipv4_print ( struct net_device *netdev ) {
struct ipv4_miniroute *miniroute;
list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
if ( miniroute->netdev != netdev )
continue;
printf ( "%s: %s/", netdev->name,
inet_ntoa ( miniroute->address ) );
printf ( "%s", inet_ntoa ( miniroute->netmask ) );
if ( miniroute->gateway.s_addr )
printf ( " gw %s", inet_ntoa ( miniroute->gateway ) );
if ( ! netdev_is_open ( miniroute->netdev ) )
printf ( " (inaccessible)" );
printf ( "\n" );
}
}
/** IPv4 routing family */
struct routing_family ipv4_routing_family __routing_family ( ROUTING_IPV4 ) = {
.print = route_ipv4_print,
};

58
src/usr/route_ipv6.c Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdio.h>
#include <ipxe/netdevice.h>
#include <ipxe/ipv6.h>
#include <usr/route.h>
/** @file
*
* IPv6 routing management
*
*/
/**
* Print IPv6 routing table
*
* @v netdev Network device
*/
static void route_ipv6_print ( struct net_device *netdev ) {
struct ipv6_miniroute *miniroute;
list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
if ( miniroute->netdev != netdev )
continue;
printf ( "%s: %s/%d", netdev->name,
inet6_ntoa ( &miniroute->address ),
miniroute->prefix_len );
if ( miniroute->has_router )
printf ( " gw %s", inet6_ntoa ( &miniroute->router ) );
if ( ! netdev_is_open ( miniroute->netdev ) )
printf ( " (inaccessible)" );
printf ( "\n" );
}
}
/** IPv6 routing family */
struct routing_family ipv6_routing_family __routing_family ( ROUTING_IPV6 ) = {
.print = route_ipv6_print,
};