mirror of https://github.com/ipxe/ipxe.git
[ipv6] Support stateless address autoconfiguration (SLAAC)
Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/15/head
parent
2dca2e6ade
commit
33652880a7
|
@ -173,7 +173,26 @@ struct ipv6_miniroute {
|
|||
};
|
||||
|
||||
/**
|
||||
* Construct link-local address (via EUI-64)
|
||||
* Construct local IPv6 address via EUI-64
|
||||
*
|
||||
* @v addr Prefix to be completed
|
||||
* @v netdev Network device
|
||||
* @ret prefix_len Prefix length, or negative error
|
||||
*/
|
||||
static inline int ipv6_eui64 ( 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;
|
||||
|
||||
if ( ( rc = ll_protocol->eui64 ( ll_addr, &addr->s6_addr[8] ) ) != 0 )
|
||||
return rc;
|
||||
addr->s6_addr[8] ^= 0x02;
|
||||
return 64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct link-local address via EUI-64
|
||||
*
|
||||
* @v addr Address to construct
|
||||
* @v netdev Network device
|
||||
|
@ -181,16 +200,10 @@ struct ipv6_miniroute {
|
|||
*/
|
||||
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;
|
||||
return ipv6_eui64 ( addr, netdev );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,5 +227,7 @@ 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 );
|
||||
extern int ipv6_slaac ( struct net_device *netdev, struct in6_addr *prefix,
|
||||
unsigned int prefix_len, struct in6_addr *router );
|
||||
|
||||
#endif /* _IPXE_IPV6_H */
|
||||
|
|
|
@ -15,19 +15,68 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||
#include <ipxe/icmpv6.h>
|
||||
#include <ipxe/neighbour.h>
|
||||
|
||||
/** An NDP option */
|
||||
struct ndp_option {
|
||||
/** An NDP option header */
|
||||
struct ndp_option_header {
|
||||
/** Type */
|
||||
uint8_t type;
|
||||
/** Length (in blocks of 8 bytes) */
|
||||
uint8_t blocks;
|
||||
/** Value */
|
||||
uint8_t value[0];
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** NDP option block size */
|
||||
#define NDP_OPTION_BLKSZ 8
|
||||
|
||||
/** NDP source link-layer address option */
|
||||
#define NDP_OPT_LL_SOURCE 1
|
||||
|
||||
/** NDP target link-layer address option */
|
||||
#define NDP_OPT_LL_TARGET 2
|
||||
|
||||
/** NDP source or target link-layer address option */
|
||||
struct ndp_ll_addr_option {
|
||||
/** NDP option header */
|
||||
struct ndp_option_header header;
|
||||
/** Link-layer address */
|
||||
uint8_t ll_addr[0];
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** NDP prefix information option */
|
||||
#define NDP_OPT_PREFIX 3
|
||||
|
||||
/** NDP prefix information */
|
||||
struct ndp_prefix_information_option {
|
||||
/** NDP option header */
|
||||
struct ndp_option_header header;
|
||||
/** Prefix length */
|
||||
uint8_t prefix_len;
|
||||
/** Flags */
|
||||
uint8_t flags;
|
||||
/** Valid lifetime */
|
||||
uint32_t valid;
|
||||
/** Preferred lifetime */
|
||||
uint32_t preferred;
|
||||
/** Reserved */
|
||||
uint32_t reserved;
|
||||
/** Prefix */
|
||||
struct in6_addr prefix;
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** NDP on-link flag */
|
||||
#define NDP_PREFIX_ON_LINK 0x80
|
||||
|
||||
/** NDP autonomous address configuration flag */
|
||||
#define NDP_PREFIX_AUTONOMOUS 0x40
|
||||
|
||||
/** An NDP option */
|
||||
union ndp_option {
|
||||
/** Option header */
|
||||
struct ndp_option_header header;
|
||||
/** Source or target link-layer address option */
|
||||
struct ndp_ll_addr_option ll_addr;
|
||||
/** Prefix information option */
|
||||
struct ndp_prefix_information_option prefix;
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** An NDP neighbour solicitation or advertisement header */
|
||||
struct ndp_neighbour_header {
|
||||
/** ICMPv6 header */
|
||||
|
@ -39,7 +88,7 @@ struct ndp_neighbour_header {
|
|||
/** Target address */
|
||||
struct in6_addr target;
|
||||
/** Options */
|
||||
struct ndp_option option[0];
|
||||
union ndp_option option[0];
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** NDP router flag */
|
||||
|
@ -66,7 +115,7 @@ struct ndp_router_advertisement_header {
|
|||
/** Retransmission timer */
|
||||
uint32_t retransmit;
|
||||
/** Options */
|
||||
struct ndp_option option[0];
|
||||
union ndp_option option[0];
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** NDP managed address configuration */
|
||||
|
@ -85,12 +134,6 @@ union ndp_header {
|
|||
struct ndp_router_advertisement_header radv;
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** 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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -862,6 +862,53 @@ struct sockaddr_converter ipv6_sockaddr_converter __sockaddr_converter = {
|
|||
.aton = ipv6_sock_aton,
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform IPv6 stateless address autoconfiguration (SLAAC)
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v prefix Prefix
|
||||
* @v prefix_len Prefix length
|
||||
* @v router Router address (or NULL)
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ipv6_slaac ( struct net_device *netdev, struct in6_addr *prefix,
|
||||
unsigned int prefix_len, struct in6_addr *router ) {
|
||||
struct ipv6_miniroute *miniroute;
|
||||
struct ipv6_miniroute *tmp;
|
||||
struct in6_addr address;
|
||||
int check_prefix_len;
|
||||
int rc;
|
||||
|
||||
/* Construct local address */
|
||||
memcpy ( &address, prefix, sizeof ( address ) );
|
||||
check_prefix_len = ipv6_eui64 ( &address, netdev );
|
||||
if ( check_prefix_len < 0 ) {
|
||||
rc = check_prefix_len;
|
||||
DBGC ( netdev, "IPv6 %s could not construct SLAAC address: "
|
||||
"%s\n", netdev->name, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
if ( check_prefix_len != ( int ) prefix_len ) {
|
||||
DBGC ( netdev, "IPv6 %s incorrect SLAAC prefix length %d "
|
||||
"(expected %d)\n", netdev->name, prefix_len,
|
||||
check_prefix_len );
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Delete any existing SLAAC miniroutes for this prefix */
|
||||
list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) {
|
||||
if ( ipv6_is_local ( miniroute, &address ) )
|
||||
del_ipv6_miniroute ( miniroute );
|
||||
}
|
||||
|
||||
/* Add miniroute */
|
||||
miniroute = add_ipv6_miniroute ( netdev, &address, prefix_len, router );
|
||||
if ( ! miniroute )
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create IPv6 network device
|
||||
*
|
||||
|
|
164
src/net/ndp.c
164
src/net/ndp.c
|
@ -62,12 +62,13 @@ static int ndp_tx_neighbour ( struct net_device *netdev,
|
|||
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
||||
struct io_buffer *iobuf;
|
||||
struct ndp_neighbour_header *neigh;
|
||||
struct ndp_ll_addr_option *ll_addr_opt;
|
||||
size_t option_len;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
/* Allocate and populate buffer */
|
||||
option_len = ( ( sizeof ( neigh->option[0] ) +
|
||||
option_len = ( ( sizeof ( *ll_addr_opt ) +
|
||||
ll_protocol->ll_addr_len + NDP_OPTION_BLKSZ - 1 ) &
|
||||
~( NDP_OPTION_BLKSZ - 1 ) );
|
||||
len = ( sizeof ( *neigh ) + option_len );
|
||||
|
@ -80,9 +81,10 @@ static int ndp_tx_neighbour ( struct net_device *netdev,
|
|||
neigh->icmp.type = icmp_type;
|
||||
neigh->flags = flags;
|
||||
memcpy ( &neigh->target, target, sizeof ( neigh->target ) );
|
||||
neigh->option[0].type = option_type;
|
||||
neigh->option[0].blocks = ( option_len / NDP_OPTION_BLKSZ );
|
||||
memcpy ( neigh->option[0].value, netdev->ll_addr,
|
||||
ll_addr_opt = &neigh->option[0].ll_addr;
|
||||
ll_addr_opt->header.type = option_type;
|
||||
ll_addr_opt->header.blocks = ( option_len / NDP_OPTION_BLKSZ );
|
||||
memcpy ( ll_addr_opt->ll_addr, netdev->ll_addr,
|
||||
ll_protocol->ll_addr_len );
|
||||
neigh->icmp.chksum = tcpip_chksum ( neigh, len );
|
||||
|
||||
|
@ -143,17 +145,18 @@ struct neighbour_discovery ndp_discovery = {
|
|||
* @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
|
||||
* @v option NDP option
|
||||
* @v len NDP option length
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int
|
||||
ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
|
||||
struct sockaddr_in6 *sin6_src,
|
||||
union ndp_header *ndp,
|
||||
const void *ll_addr,
|
||||
size_t ll_addr_len ) {
|
||||
union ndp_option *option,
|
||||
size_t len ) {
|
||||
struct ndp_neighbour_header *neigh = &ndp->neigh;
|
||||
struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
|
||||
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
||||
int rc;
|
||||
|
||||
|
@ -164,20 +167,21 @@ ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
|
|||
return 0;
|
||||
|
||||
/* Sanity check */
|
||||
if ( ll_addr_len < ll_protocol->ll_addr_len ) {
|
||||
if ( offsetof ( typeof ( *ll_addr_opt ),
|
||||
ll_addr[ll_protocol->ll_addr_len] ) > 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 );
|
||||
"option too short at %zd bytes\n", len );
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create or update neighbour cache entry */
|
||||
if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
|
||||
&sin6_src->sin6_addr,
|
||||
ll_addr ) ) != 0 ) {
|
||||
ll_addr_opt->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 ) );
|
||||
ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -199,8 +203,8 @@ ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
|
|||
* @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
|
||||
* @v option NDP option
|
||||
* @v len NDP option length
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int
|
||||
|
@ -208,26 +212,28 @@ ndp_rx_neighbour_advertisement_ll_target ( struct net_device *netdev,
|
|||
struct sockaddr_in6 *sin6_src
|
||||
__unused,
|
||||
union ndp_header *ndp,
|
||||
const void *ll_addr,
|
||||
size_t ll_addr_len ) {
|
||||
union ndp_option *option,
|
||||
size_t len ) {
|
||||
struct ndp_neighbour_header *neigh = &ndp->neigh;
|
||||
struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
|
||||
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
if ( ll_addr_len < ll_protocol->ll_addr_len ) {
|
||||
if ( offsetof ( typeof ( *ll_addr_opt ),
|
||||
ll_addr[ll_protocol->ll_addr_len] ) > 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 );
|
||||
"option too short at %zd bytes\n", len );
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Update neighbour cache entry, if any */
|
||||
if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &neigh->target,
|
||||
ll_addr ) ) != 0 ) {
|
||||
ll_addr_opt->ll_addr ) ) != 0 ) {
|
||||
DBGC ( netdev, "NDP could not update %s => %s: %s\n",
|
||||
inet6_ntoa ( &neigh->target ),
|
||||
ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
|
||||
ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -240,40 +246,94 @@ ndp_rx_neighbour_advertisement_ll_target ( struct net_device *netdev,
|
|||
* @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
|
||||
* @v option NDP option
|
||||
* @v len NDP option length
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int
|
||||
ndp_rx_router_advertisement_ll_source ( struct net_device *netdev,
|
||||
struct sockaddr_in6 *sin6_src,
|
||||
union ndp_header *ndp __unused,
|
||||
const void *ll_addr,
|
||||
size_t ll_addr_len ) {
|
||||
union ndp_option *option, size_t len ) {
|
||||
struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
|
||||
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
if ( ll_addr_len < ll_protocol->ll_addr_len ) {
|
||||
if ( offsetof ( typeof ( *ll_addr_opt ),
|
||||
ll_addr[ll_protocol->ll_addr_len] ) > len ) {
|
||||
DBGC ( netdev, "NDP router advertisement link-layer address "
|
||||
"too short at %zd bytes (min %d bytes)\n",
|
||||
ll_addr_len, ll_protocol->ll_addr_len );
|
||||
"option too short at %zd bytes\n", len );
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Define neighbour cache entry */
|
||||
if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
|
||||
&sin6_src->sin6_addr,
|
||||
ll_addr ) ) != 0 ) {
|
||||
ll_addr_opt->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 ) );
|
||||
ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process NDP router advertisement prefix information option
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v sin6_src Source socket address
|
||||
* @v ndp NDP packet
|
||||
* @v option NDP option
|
||||
* @v len NDP option length
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int
|
||||
ndp_rx_router_advertisement_prefix ( struct net_device *netdev,
|
||||
struct sockaddr_in6 *sin6_src,
|
||||
union ndp_header *ndp,
|
||||
union ndp_option *option, size_t len ) {
|
||||
struct ndp_router_advertisement_header *radv = &ndp->radv;
|
||||
struct ndp_prefix_information_option *prefix_opt = &option->prefix;
|
||||
struct in6_addr *router = &sin6_src->sin6_addr;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
if ( sizeof ( *prefix_opt ) > len ) {
|
||||
DBGC ( netdev, "NDP router advertisement prefix option too "
|
||||
"short at %zd bytes\n", len );
|
||||
return -EINVAL;
|
||||
}
|
||||
DBGC ( netdev, "NDP found %sdefault router %s ",
|
||||
( radv->lifetime ? "" : "non-" ),
|
||||
inet6_ntoa ( &sin6_src->sin6_addr ) );
|
||||
DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n",
|
||||
( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ),
|
||||
( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ),
|
||||
inet6_ntoa ( &prefix_opt->prefix ),
|
||||
prefix_opt->prefix_len );
|
||||
|
||||
/* Perform stateless address autoconfiguration, if applicable */
|
||||
if ( ( prefix_opt->flags &
|
||||
( NDP_PREFIX_ON_LINK | NDP_PREFIX_AUTONOMOUS ) ) ==
|
||||
( NDP_PREFIX_ON_LINK | NDP_PREFIX_AUTONOMOUS ) ) {
|
||||
if ( ( rc = ipv6_slaac ( netdev, &prefix_opt->prefix,
|
||||
prefix_opt->prefix_len,
|
||||
( radv->lifetime ?
|
||||
router : NULL ) ) ) != 0 ) {
|
||||
DBGC ( netdev, "NDP could not autoconfigure prefix %s/"
|
||||
"%d: %s\n", inet6_ntoa ( &prefix_opt->prefix ),
|
||||
prefix_opt->prefix_len, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** An NDP option handler */
|
||||
struct ndp_option_handler {
|
||||
/** ICMPv6 type */
|
||||
|
@ -286,12 +346,12 @@ struct ndp_option_handler {
|
|||
* @v netdev Network device
|
||||
* @v sin6_src Source socket address
|
||||
* @v ndp NDP packet
|
||||
* @v value Option value
|
||||
* @v len Option length
|
||||
* @v option NDP option
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ( * rx ) ( struct net_device *netdev, struct sockaddr_in6 *sin6_src,
|
||||
union ndp_header *ndp, const void *value, size_t len );
|
||||
union ndp_header *ndp, union ndp_option *option,
|
||||
size_t len );
|
||||
};
|
||||
|
||||
/** NDP option handlers */
|
||||
|
@ -311,6 +371,11 @@ static struct ndp_option_handler ndp_option_handlers[] = {
|
|||
.option_type = NDP_OPT_LL_SOURCE,
|
||||
.rx = ndp_rx_router_advertisement_ll_source,
|
||||
},
|
||||
{
|
||||
.icmp_type = ICMPV6_ROUTER_ADVERTISEMENT,
|
||||
.option_type = NDP_OPT_PREFIX,
|
||||
.rx = ndp_rx_router_advertisement_prefix,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -319,15 +384,13 @@ static struct ndp_option_handler ndp_option_handlers[] = {
|
|||
* @v netdev Network device
|
||||
* @v sin6_src Source socket address
|
||||
* @v ndp NDP packet
|
||||
* @v type Option type
|
||||
* @v value Option value
|
||||
* @v option NDP option
|
||||
* @v len Option length
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int ndp_rx_option ( struct net_device *netdev,
|
||||
struct sockaddr_in6 *sin6_src,
|
||||
union ndp_header *ndp, unsigned int type,
|
||||
const void *value, size_t len ) {
|
||||
struct sockaddr_in6 *sin6_src, union ndp_header *ndp,
|
||||
union ndp_option *option, size_t len ) {
|
||||
struct ndp_option_handler *handler;
|
||||
unsigned int i;
|
||||
|
||||
|
@ -336,9 +399,9 @@ static int ndp_rx_option ( struct net_device *netdev,
|
|||
sizeof ( ndp_option_handlers[0] ) ) ; i++ ) {
|
||||
handler = &ndp_option_handlers[i];
|
||||
if ( ( handler->icmp_type == ndp->icmp.type ) &&
|
||||
( handler->option_type == type ) ) {
|
||||
( handler->option_type == option->header.type ) ) {
|
||||
return handler->rx ( netdev, sin6_src, ndp,
|
||||
value, len );
|
||||
option, len );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,10 +423,9 @@ static int ndp_rx ( struct io_buffer *iobuf,
|
|||
struct sockaddr_in6 *sin6_src,
|
||||
size_t offset ) {
|
||||
union ndp_header *ndp = iobuf->data;
|
||||
struct ndp_option *option;
|
||||
union ndp_option *option;
|
||||
size_t remaining;
|
||||
size_t option_len;
|
||||
size_t option_value_len;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
|
@ -380,23 +442,21 @@ static int ndp_rx ( struct io_buffer *iobuf,
|
|||
while ( remaining ) {
|
||||
|
||||
/* Sanity check */
|
||||
if ( ( remaining < sizeof ( *option ) ) ||
|
||||
( option->blocks == 0 ) ||
|
||||
( remaining < ( option->blocks * NDP_OPTION_BLKSZ ) ) ) {
|
||||
if ( ( remaining < sizeof ( option->header ) ) ||
|
||||
( option->header.blocks == 0 ) ||
|
||||
( remaining < ( option->header.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 ) );
|
||||
option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
|
||||
|
||||
/* Handle option */
|
||||
if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp,
|
||||
option->type, option->value,
|
||||
option_value_len ) ) != 0 ) {
|
||||
if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp, option,
|
||||
option_len ) ) != 0 )
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Move to next option */
|
||||
option = ( ( ( void * ) option ) + option_len );
|
||||
|
|
Loading…
Reference in New Issue