mirror of
https://github.com/xcat2/xNBA.git
synced 2024-11-22 01:21:45 +00:00
Merge branch 'master' of https://git.ipxe.org/ipxe
Conflicts: src/config/general.h
This commit is contained in:
commit
d603ea68c4
@ -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 */
|
||||
|
@ -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 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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" );
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
27
src/config/config_route.c
Normal 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
|
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
69
src/hci/commands/neighbour_cmd.c
Normal file
69
src/hci/commands/neighbour_cmd.c
Normal 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,
|
||||
},
|
||||
};
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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 )
|
||||
|
@ -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 */
|
||||
|
68
src/include/ipxe/fragment.h
Normal file
68
src/include/ipxe/fragment.h
Normal 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 */
|
@ -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
78
src/include/ipxe/icmpv6.h
Normal 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 */
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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
218
src/include/ipxe/ipv6.h
Normal 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 */
|
@ -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 */
|
||||
|
88
src/include/ipxe/neighbour.h
Normal file
88
src/include/ipxe/neighbour.h
Normal 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 */
|
@ -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 */
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
14
src/include/usr/neighmgmt.h
Normal file
14
src/include/usr/neighmgmt.h
Normal 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 */
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
388
src/net/arp.c
388
src/net/arp.c
@ -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,
|
||||
};
|
||||
|
@ -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
172
src/net/fragment.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
266
src/net/icmpv6.c
266
src/net/icmpv6.c
@ -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,
|
||||
};
|
||||
|
170
src/net/ipv4.c
170
src/net/ipv4.c
@ -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 ) );
|
||||
|
945
src/net/ipv6.c
945
src/net/ipv6.c
File diff suppressed because it is too large
Load Diff
487
src/net/ndp.c
487
src/net/ndp.c
@ -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
439
src/net/neighbour.c
Normal 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,
|
||||
};
|
@ -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 );
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 ) {
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
181
src/tests/ipv6_test.c
Normal 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,
|
||||
};
|
@ -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
56
src/usr/neighmgmt.c
Normal 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" );
|
||||
}
|
||||
}
|
@ -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
58
src/usr/route_ipv4.c
Normal 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
58
src/usr/route_ipv6.c
Normal 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,
|
||||
};
|
Loading…
Reference in New Issue
Block a user