david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[ipv6] Extract link layer addresses from router advertisements

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2013-10-22 23:39:29 +01:00
parent 595e32d7ab
commit 2dca2e6ade
3 changed files with 188 additions and 54 deletions

View File

@ -46,11 +46,14 @@ struct icmpv6_handler {
/** ICMPv6 echo reply */ /** ICMPv6 echo reply */
#define ICMPV6_ECHO_REPLY 129 #define ICMPV6_ECHO_REPLY 129
/** ICMPv6 router advertisement */
#define ICMPV6_ROUTER_ADVERTISEMENT 134
/** ICMPv6 neighbour solicitation */ /** ICMPv6 neighbour solicitation */
#define ICMPV6_NDP_NEIGHBOUR_SOLICITATION 135 #define ICMPV6_NEIGHBOUR_SOLICITATION 135
/** ICMPv6 neighbour advertisement */ /** ICMPv6 neighbour advertisement */
#define ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT 136 #define ICMPV6_NEIGHBOUR_ADVERTISEMENT 136
extern struct tcpip_protocol icmpv6_protocol __tcpip_protocol; extern struct tcpip_protocol icmpv6_protocol __tcpip_protocol;

View File

@ -28,8 +28,8 @@ struct ndp_option {
/** NDP option block size */ /** NDP option block size */
#define NDP_OPTION_BLKSZ 8 #define NDP_OPTION_BLKSZ 8
/** An NDP header */ /** An NDP neighbour solicitation or advertisement header */
struct ndp_header { struct ndp_neighbour_header {
/** ICMPv6 header */ /** ICMPv6 header */
struct icmp_header icmp; struct icmp_header icmp;
/** Flags */ /** Flags */
@ -43,13 +43,47 @@ struct ndp_header {
} __attribute__ (( packed )); } __attribute__ (( packed ));
/** NDP router flag */ /** NDP router flag */
#define NDP_ROUTER 0x80 #define NDP_NEIGHBOUR_ROUTER 0x80
/** NDP solicited flag */ /** NDP solicited flag */
#define NDP_SOLICITED 0x40 #define NDP_NEIGHBOUR_SOLICITED 0x40
/** NDP override flag */ /** NDP override flag */
#define NDP_OVERRIDE 0x20 #define NDP_NEIGHBOUR_OVERRIDE 0x20
/** An NDP router advertisement header */
struct ndp_router_advertisement_header {
/** ICMPv6 header */
struct icmp_header icmp;
/** Current hop limit */
uint8_t hop_limit;
/** Flags */
uint8_t flags;
/** Router lifetime */
uint16_t lifetime;
/** Reachable time */
uint32_t reachable;
/** Retransmission timer */
uint32_t retransmit;
/** Options */
struct ndp_option option[0];
} __attribute__ (( packed ));
/** NDP managed address configuration */
#define NDP_ROUTER_MANAGED 0x80
/** NDP other configuration */
#define NDP_ROUTER_OTHER 0x40
/** An NDP header */
union ndp_header {
/** ICMPv6 header */
struct icmp_header icmp;
/** Neighbour solicitation or advertisement header */
struct ndp_neighbour_header neigh;
/** Router advertisement header */
struct ndp_router_advertisement_header radv;
} __attribute__ (( packed ));
/** NDP source link-layer address option */ /** NDP source link-layer address option */
#define NDP_OPT_LL_SOURCE 1 #define NDP_OPT_LL_SOURCE 1

View File

@ -61,34 +61,34 @@ static int ndp_tx_neighbour ( struct net_device *netdev,
( ( struct sockaddr_tcpip * ) sin6_dest ); ( ( struct sockaddr_tcpip * ) sin6_dest );
struct ll_protocol *ll_protocol = netdev->ll_protocol; struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct io_buffer *iobuf; struct io_buffer *iobuf;
struct ndp_header *ndp; struct ndp_neighbour_header *neigh;
size_t option_len; size_t option_len;
size_t len; size_t len;
int rc; int rc;
/* Allocate and populate buffer */ /* Allocate and populate buffer */
option_len = ( ( sizeof ( ndp->option[0] ) + ll_protocol->ll_addr_len + option_len = ( ( sizeof ( neigh->option[0] ) +
NDP_OPTION_BLKSZ - 1 ) & ll_protocol->ll_addr_len + NDP_OPTION_BLKSZ - 1 ) &
~( NDP_OPTION_BLKSZ - 1 ) ); ~( NDP_OPTION_BLKSZ - 1 ) );
len = ( sizeof ( *ndp ) + option_len ); len = ( sizeof ( *neigh ) + option_len );
iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len ); iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len );
if ( ! iobuf ) if ( ! iobuf )
return -ENOMEM; return -ENOMEM;
iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN ); iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN );
ndp = iob_put ( iobuf, len ); neigh = iob_put ( iobuf, len );
memset ( ndp, 0, len ); memset ( neigh, 0, len );
ndp->icmp.type = icmp_type; neigh->icmp.type = icmp_type;
ndp->flags = flags; neigh->flags = flags;
memcpy ( &ndp->target, target, sizeof ( ndp->target ) ); memcpy ( &neigh->target, target, sizeof ( neigh->target ) );
ndp->option[0].type = option_type; neigh->option[0].type = option_type;
ndp->option[0].blocks = ( option_len / NDP_OPTION_BLKSZ ); neigh->option[0].blocks = ( option_len / NDP_OPTION_BLKSZ );
memcpy ( ndp->option[0].value, netdev->ll_addr, memcpy ( neigh->option[0].value, netdev->ll_addr,
ll_protocol->ll_addr_len ); ll_protocol->ll_addr_len );
ndp->icmp.chksum = tcpip_chksum ( ndp, len ); neigh->icmp.chksum = tcpip_chksum ( neigh, len );
/* Transmit packet */ /* Transmit packet */
if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest, if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest,
netdev, &ndp->icmp.chksum ) ) != 0 ) { netdev, &neigh->icmp.chksum ) ) != 0 ) {
DBGC ( netdev, "NDP could not transmit packet: %s\n", DBGC ( netdev, "NDP could not transmit packet: %s\n",
strerror ( rc ) ); strerror ( rc ) );
return rc; return rc;
@ -127,7 +127,7 @@ static int ndp_tx_request ( struct net_device *netdev,
/* Transmit neighbour discovery packet */ /* Transmit neighbour discovery packet */
return ndp_tx_neighbour ( netdev, &sin6_src, &sin6_dest, net_dest, return ndp_tx_neighbour ( netdev, &sin6_src, &sin6_dest, net_dest,
ICMPV6_NDP_NEIGHBOUR_SOLICITATION, 0, ICMPV6_NEIGHBOUR_SOLICITATION, 0,
NDP_OPT_LL_SOURCE ); NDP_OPT_LL_SOURCE );
} }
@ -147,18 +147,20 @@ struct neighbour_discovery ndp_discovery = {
* @v ll_addr_len Source link-layer address length * @v ll_addr_len Source link-layer address length
* @ret rc Return status code * @ret rc Return status code
*/ */
static int ndp_rx_neighbour_solicitation ( struct net_device *netdev, static int
struct sockaddr_in6 *sin6_src, ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
struct ndp_header *ndp __unused, struct sockaddr_in6 *sin6_src,
const void *ll_addr, union ndp_header *ndp,
size_t ll_addr_len ) { const void *ll_addr,
size_t ll_addr_len ) {
struct ndp_neighbour_header *neigh = &ndp->neigh;
struct ll_protocol *ll_protocol = netdev->ll_protocol; struct ll_protocol *ll_protocol = netdev->ll_protocol;
int rc; int rc;
/* Silently ignore neighbour solicitations for addresses we do /* Silently ignore neighbour solicitations for addresses we do
* not own. * not own.
*/ */
if ( ! ipv6_has_addr ( netdev, &ndp->target ) ) if ( ! ipv6_has_addr ( netdev, &neigh->target ) )
return 0; return 0;
/* Sanity check */ /* Sanity check */
@ -180,9 +182,10 @@ static int ndp_rx_neighbour_solicitation ( struct net_device *netdev,
} }
/* Send neighbour advertisement */ /* Send neighbour advertisement */
if ( ( rc = ndp_tx_neighbour ( netdev, NULL, sin6_src, &ndp->target, if ( ( rc = ndp_tx_neighbour ( netdev, NULL, sin6_src, &neigh->target,
ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT, ICMPV6_NEIGHBOUR_ADVERTISEMENT,
( NDP_SOLICITED | NDP_OVERRIDE ), ( NDP_NEIGHBOUR_SOLICITED |
NDP_NEIGHBOUR_OVERRIDE ),
NDP_OPT_LL_TARGET ) ) != 0 ) { NDP_OPT_LL_TARGET ) ) != 0 ) {
return rc; return rc;
} }
@ -201,10 +204,13 @@ static int ndp_rx_neighbour_solicitation ( struct net_device *netdev,
* @ret rc Return status code * @ret rc Return status code
*/ */
static int static int
ndp_rx_neighbour_advertisement ( struct net_device *netdev, ndp_rx_neighbour_advertisement_ll_target ( struct net_device *netdev,
struct sockaddr_in6 *sin6_src __unused, struct sockaddr_in6 *sin6_src
struct ndp_header *ndp, const void *ll_addr, __unused,
size_t ll_addr_len ) { union ndp_header *ndp,
const void *ll_addr,
size_t ll_addr_len ) {
struct ndp_neighbour_header *neigh = &ndp->neigh;
struct ll_protocol *ll_protocol = netdev->ll_protocol; struct ll_protocol *ll_protocol = netdev->ll_protocol;
int rc; int rc;
@ -217,10 +223,50 @@ ndp_rx_neighbour_advertisement ( struct net_device *netdev,
} }
/* Update neighbour cache entry, if any */ /* Update neighbour cache entry, if any */
if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &ndp->target, if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &neigh->target,
ll_addr ) ) != 0 ) { ll_addr ) ) != 0 ) {
DBGC ( netdev, "NDP could not update %s => %s: %s\n", DBGC ( netdev, "NDP could not update %s => %s: %s\n",
inet6_ntoa ( &ndp->target ), inet6_ntoa ( &neigh->target ),
ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Process NDP router advertisement source 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_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 ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
int rc;
/* Sanity check */
if ( ll_addr_len < ll_protocol->ll_addr_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 );
return -EINVAL;
}
/* Define 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 ) ); ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
return rc; return rc;
} }
@ -245,20 +291,25 @@ struct ndp_option_handler {
* @ret rc Return status code * @ret rc Return status code
*/ */
int ( * rx ) ( struct net_device *netdev, struct sockaddr_in6 *sin6_src, int ( * rx ) ( struct net_device *netdev, struct sockaddr_in6 *sin6_src,
struct ndp_header *ndp, const void *value, size_t len ); union ndp_header *ndp, const void *value, size_t len );
}; };
/** NDP option handlers */ /** NDP option handlers */
static struct ndp_option_handler ndp_option_handlers[] = { static struct ndp_option_handler ndp_option_handlers[] = {
{ {
.icmp_type = ICMPV6_NDP_NEIGHBOUR_SOLICITATION, .icmp_type = ICMPV6_NEIGHBOUR_SOLICITATION,
.option_type = NDP_OPT_LL_SOURCE, .option_type = NDP_OPT_LL_SOURCE,
.rx = ndp_rx_neighbour_solicitation, .rx = ndp_rx_neighbour_solicitation_ll_source,
}, },
{ {
.icmp_type = ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT, .icmp_type = ICMPV6_NEIGHBOUR_ADVERTISEMENT,
.option_type = NDP_OPT_LL_TARGET, .option_type = NDP_OPT_LL_TARGET,
.rx = ndp_rx_neighbour_advertisement, .rx = ndp_rx_neighbour_advertisement_ll_target,
},
{
.icmp_type = ICMPV6_ROUTER_ADVERTISEMENT,
.option_type = NDP_OPT_LL_SOURCE,
.rx = ndp_rx_router_advertisement_ll_source,
}, },
}; };
@ -275,7 +326,7 @@ static struct ndp_option_handler ndp_option_handlers[] = {
*/ */
static int ndp_rx_option ( struct net_device *netdev, static int ndp_rx_option ( struct net_device *netdev,
struct sockaddr_in6 *sin6_src, struct sockaddr_in6 *sin6_src,
struct ndp_header *ndp, unsigned int type, union ndp_header *ndp, unsigned int type,
const void *value, size_t len ) { const void *value, size_t len ) {
struct ndp_option_handler *handler; struct ndp_option_handler *handler;
unsigned int i; unsigned int i;
@ -301,14 +352,14 @@ static int ndp_rx_option ( struct net_device *netdev,
* @v iobuf I/O buffer * @v iobuf I/O buffer
* @v netdev Network device * @v netdev Network device
* @v sin6_src Source socket address * @v sin6_src Source socket address
* @v sin6_dest Destination socket address * @v offset Offset to NDP options
* @ret rc Return status code * @ret rc Return status code
*/ */
static int ndp_rx ( struct io_buffer *iobuf, static int ndp_rx ( struct io_buffer *iobuf,
struct net_device *netdev, struct net_device *netdev,
struct sockaddr_in6 *sin6_src, struct sockaddr_in6 *sin6_src,
struct sockaddr_in6 *sin6_dest __unused ) { size_t offset ) {
struct ndp_header *ndp = iobuf->data; union ndp_header *ndp = iobuf->data;
struct ndp_option *option; struct ndp_option *option;
size_t remaining; size_t remaining;
size_t option_len; size_t option_len;
@ -316,16 +367,16 @@ static int ndp_rx ( struct io_buffer *iobuf,
int rc; int rc;
/* Sanity check */ /* Sanity check */
if ( iob_len ( iobuf ) < sizeof ( *ndp ) ) { if ( iob_len ( iobuf ) < offset ) {
DBGC ( netdev, "NDP packet too short at %zd bytes (min %zd " DBGC ( netdev, "NDP packet too short at %zd bytes (min %zd "
"bytes)\n", iob_len ( iobuf ), sizeof ( *ndp ) ); "bytes)\n", iob_len ( iobuf ), offset );
rc = -EINVAL; rc = -EINVAL;
goto done; goto done;
} }
/* Search for option */ /* Search for option */
option = ndp->option; option = ( ( ( void * ) ndp ) + offset );
remaining = ( iob_len ( iobuf ) - offsetof ( typeof ( *ndp ), option )); remaining = ( iob_len ( iobuf ) - offset );
while ( remaining ) { while ( remaining ) {
/* Sanity check */ /* Sanity check */
@ -360,14 +411,60 @@ static int ndp_rx ( struct io_buffer *iobuf,
return rc; return rc;
} }
/**
* Process received NDP neighbour solicitation or advertisement
*
* @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_neighbour ( struct io_buffer *iobuf,
struct net_device *netdev,
struct sockaddr_in6 *sin6_src,
struct sockaddr_in6 *sin6_dest __unused ) {
union ndp_header *ndp = iobuf->data;
struct ndp_neighbour_header *neigh = &ndp->neigh;
return ndp_rx ( iobuf, netdev, sin6_src,
offsetof ( typeof ( *neigh ), option ) );
}
/**
* Process received NDP router advertisement
*
* @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_router_advertisement ( struct io_buffer *iobuf,
struct net_device *netdev,
struct sockaddr_in6 *sin6_src,
struct sockaddr_in6 *sin6_dest __unused ) {
union ndp_header *ndp = iobuf->data;
struct ndp_router_advertisement_header *radv = &ndp->radv;
return ndp_rx ( iobuf, netdev, sin6_src,
offsetof ( typeof ( *radv ), option ) );
}
/** NDP ICMPv6 handlers */ /** NDP ICMPv6 handlers */
struct icmpv6_handler ndp_handlers[] __icmpv6_handler = { struct icmpv6_handler ndp_handlers[] __icmpv6_handler = {
{ {
.type = ICMPV6_NDP_NEIGHBOUR_SOLICITATION, .type = ICMPV6_NEIGHBOUR_SOLICITATION,
.rx = ndp_rx, .rx = ndp_rx_neighbour,
}, },
{ {
.type = ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT, .type = ICMPV6_NEIGHBOUR_ADVERTISEMENT,
.rx = ndp_rx, .rx = ndp_rx_neighbour,
},
{
.type = ICMPV6_ROUTER_ADVERTISEMENT,
.rx = ndp_rx_router_advertisement,
}, },
}; };