From b15dbc9cc65e8385a30513554129d7640bc8a0f9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 24 Oct 2013 14:06:33 +0100 Subject: [PATCH] [ipv6] Add ndp_tx_router_solicitation() to send router solicitations Signed-off-by: Michael Brown --- src/include/ipxe/icmpv6.h | 3 ++ src/include/ipxe/ipv6.h | 16 +++++-- src/include/ipxe/ndp.h | 14 ++++++ src/net/ipv6.c | 1 + src/net/ndp.c | 98 ++++++++++++++++++++++++++------------- 5 files changed, 95 insertions(+), 37 deletions(-) diff --git a/src/include/ipxe/icmpv6.h b/src/include/ipxe/icmpv6.h index 15f8edd8..b5ea54ea 100644 --- a/src/include/ipxe/icmpv6.h +++ b/src/include/ipxe/icmpv6.h @@ -46,6 +46,9 @@ struct icmpv6_handler { /** ICMPv6 echo reply */ #define ICMPV6_ECHO_REPLY 129 +/** ICMPv6 router solicitation */ +#define ICMPV6_ROUTER_SOLICITATION 133 + /** ICMPv6 router advertisement */ #define ICMPV6_ROUTER_ADVERTISEMENT 134 diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h index 6a9aa606..3cb87826 100644 --- a/src/include/ipxe/ipv6.h +++ b/src/include/ipxe/ipv6.h @@ -194,14 +194,13 @@ static inline int ipv6_eui64 ( struct in6_addr *addr, /** * Construct link-local address via EUI-64 * - * @v addr Address to construct + * @v addr Zeroed 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 ) { - memset ( addr, 0, sizeof ( *addr ) ); addr->s6_addr16[0] = htons ( 0xfe80 ); return ipv6_eui64 ( addr, netdev ); } @@ -209,19 +208,28 @@ static inline int ipv6_link_local ( struct in6_addr *addr, /** * Construct solicited-node multicast address * - * @v addr Address to construct + * @v addr Zeroed 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 ); } +/** + * Construct all-routers multicast address + * + * @v addr Zeroed address to construct + */ +static inline void ipv6_all_routers ( struct in6_addr *addr ) { + addr->s6_addr16[0] = htons ( 0xff02 ); + addr->s6_addr[15] = 2; +} + extern struct list_head ipv6_miniroutes; extern struct net_protocol ipv6_protocol __net_protocol; diff --git a/src/include/ipxe/ndp.h b/src/include/ipxe/ndp.h index 4edd96a4..f8c7e3fb 100644 --- a/src/include/ipxe/ndp.h +++ b/src/include/ipxe/ndp.h @@ -124,12 +124,24 @@ struct ndp_router_advertisement_header { /** NDP other configuration */ #define NDP_ROUTER_OTHER 0x40 +/** An NDP router solicitation header */ +struct ndp_router_solicitation_header { + /** ICMPv6 header */ + struct icmp_header icmp; + /** Reserved */ + uint32_t reserved; + /** Options */ + union ndp_option option[0]; +} __attribute__ (( packed )); + /** An NDP header */ union ndp_header { /** ICMPv6 header */ struct icmp_header icmp; /** Neighbour solicitation or advertisement header */ struct ndp_neighbour_header neigh; + /** Router solicitation header */ + struct ndp_router_solicitation_header rsol; /** Router advertisement header */ struct ndp_router_advertisement_header radv; } __attribute__ (( packed )); @@ -154,4 +166,6 @@ static inline int ndp_tx ( struct io_buffer *iobuf, struct net_device *netdev, &ndp_discovery, net_source, ll_source ); } +extern int ndp_tx_router_solicitation ( struct net_device *netdev ); + #endif /* _IPXE_NDP_H */ diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 540ed05b..cbd4e3e5 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -926,6 +926,7 @@ static int ipv6_probe ( struct net_device *netdev ) { int rc; /* Construct link-local address from EUI-64 as per RFC 2464 */ + memset ( &address, 0, sizeof ( address ) ); prefix_len = ipv6_link_local ( &address, netdev ); if ( prefix_len < 0 ) { rc = prefix_len; diff --git a/src/net/ndp.c b/src/net/ndp.c index c440435d..f4c3152a 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -37,60 +37,52 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** - * Transmit NDP neighbour solicitation/advertisement packet + * Transmit NDP packet with link-layer address option * * @v netdev Network device * @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 data NDP header + * @v len Size of NDP header * @v option_type NDP option type * @ret rc Return status code */ -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 ) { +static int ndp_tx_ll_addr ( struct net_device *netdev, + struct sockaddr_in6 *sin6_src, + struct sockaddr_in6 *sin6_dest, + const void *data, size_t len, + 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 io_buffer *iobuf; - struct ndp_neighbour_header *neigh; struct ndp_ll_addr_option *ll_addr_opt; + union ndp_header *ndp; size_t option_len; - size_t len; int rc; /* Allocate and populate buffer */ option_len = ( ( sizeof ( *ll_addr_opt ) + ll_protocol->ll_addr_len + NDP_OPTION_BLKSZ - 1 ) & ~( NDP_OPTION_BLKSZ - 1 ) ); - len = ( sizeof ( *neigh ) + option_len ); - iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len ); + iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len + option_len ); if ( ! iobuf ) return -ENOMEM; iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN ); - neigh = iob_put ( iobuf, len ); - memset ( neigh, 0, len ); - neigh->icmp.type = icmp_type; - neigh->flags = flags; - memcpy ( &neigh->target, target, sizeof ( neigh->target ) ); - ll_addr_opt = &neigh->option[0].ll_addr; + memcpy ( iob_put ( iobuf, len ), data, len ); + ll_addr_opt = iob_put ( iobuf, option_len ); 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 ); + ndp = iobuf->data; + ndp->icmp.chksum = tcpip_chksum ( ndp, ( len + option_len ) ); /* Transmit packet */ if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest, - netdev, &neigh->icmp.chksum ) ) != 0 ) { + netdev, &ndp->icmp.chksum ) ) != 0 ) { DBGC ( netdev, "NDP could not transmit packet: %s\n", strerror ( rc ) ); return rc; @@ -113,6 +105,8 @@ static int ndp_tx_request ( struct net_device *netdev, const void *net_dest, const void *net_source ) { struct sockaddr_in6 sin6_src; struct sockaddr_in6 sin6_dest; + struct ndp_neighbour_header neigh; + int rc; /* Construct source address */ memset ( &sin6_src, 0, sizeof ( sin6_src ) ); @@ -127,10 +121,18 @@ static int ndp_tx_request ( struct net_device *netdev, sin6_dest.sin6_scope_id = netdev->index; ipv6_solicited_node ( &sin6_dest.sin6_addr, net_dest ); + /* Construct neighbour header */ + memset ( &neigh, 0, sizeof ( neigh ) ); + neigh.icmp.type = ICMPV6_NEIGHBOUR_SOLICITATION; + memcpy ( &neigh.target, net_dest, sizeof ( neigh.target ) ); + /* Transmit neighbour discovery packet */ - return ndp_tx_neighbour ( netdev, &sin6_src, &sin6_dest, net_dest, - ICMPV6_NEIGHBOUR_SOLICITATION, 0, - NDP_OPT_LL_SOURCE ); + if ( ( rc = ndp_tx_ll_addr ( netdev, &sin6_src, &sin6_dest, &neigh, + sizeof ( neigh ), + NDP_OPT_LL_SOURCE ) ) != 0 ) + return rc; + + return 0; } /** NDP neighbour discovery protocol */ @@ -139,6 +141,35 @@ struct neighbour_discovery ndp_discovery = { .tx_request = ndp_tx_request, }; +/** + * Transmit NDP router solicitation + * + * @v netdev Network device + * @ret rc Return status code + */ +int ndp_tx_router_solicitation ( struct net_device *netdev ) { + struct ndp_router_solicitation_header rsol; + struct sockaddr_in6 sin6_dest; + int rc; + + /* Construct multicast destination address */ + memset ( &sin6_dest, 0, sizeof ( sin6_dest ) ); + sin6_dest.sin6_family = AF_INET6; + sin6_dest.sin6_scope_id = netdev->index; + ipv6_all_routers ( &sin6_dest.sin6_addr ); + + /* Construct router solicitation */ + memset ( &rsol, 0, sizeof ( rsol ) ); + rsol.icmp.type = ICMPV6_ROUTER_SOLICITATION; + + /* Transmit packet */ + if ( ( rc = ndp_tx_ll_addr ( netdev, NULL, &sin6_dest, &rsol, + sizeof ( rsol ), NDP_OPT_LL_SOURCE ) ) !=0) + return rc; + + return 0; +} + /** * Process NDP neighbour solicitation source link-layer address option * @@ -185,14 +216,16 @@ ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev, return rc; } + /* Convert neighbour header to advertisement */ + memset ( neigh, 0, offsetof ( typeof ( *neigh ), target ) ); + neigh->icmp.type = ICMPV6_NEIGHBOUR_ADVERTISEMENT; + neigh->flags = ( NDP_NEIGHBOUR_SOLICITED | NDP_NEIGHBOUR_OVERRIDE ); + /* Send neighbour advertisement */ - if ( ( rc = ndp_tx_neighbour ( netdev, NULL, sin6_src, &neigh->target, - ICMPV6_NEIGHBOUR_ADVERTISEMENT, - ( NDP_NEIGHBOUR_SOLICITED | - NDP_NEIGHBOUR_OVERRIDE ), - NDP_OPT_LL_TARGET ) ) != 0 ) { + if ( ( rc = ndp_tx_ll_addr ( netdev, NULL, sin6_src, neigh, + sizeof ( *neigh ), + NDP_OPT_LL_TARGET ) ) != 0 ) return rc; - } return 0; } @@ -512,7 +545,6 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf, offsetof ( typeof ( *radv ), option ) ); } - /** NDP ICMPv6 handlers */ struct icmpv6_handler ndp_handlers[] __icmpv6_handler = { {