]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ipv6] Add ndp_tx_router_solicitation() to send router solicitations
authorMichael Brown <mcb30@ipxe.org>
Thu, 24 Oct 2013 13:06:33 +0000 (14:06 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 25 Oct 2013 16:29:25 +0000 (17:29 +0100)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/icmpv6.h
src/include/ipxe/ipv6.h
src/include/ipxe/ndp.h
src/net/ipv6.c
src/net/ndp.c

index 15f8edd83fa1007636ef32ece4d1e7af71d64b29..b5ea54eabad9beceb9cd418c4fc75ad4a9bfc4e7 100644 (file)
@@ -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
 
index 6a9aa606c2b02ce741a205dcbf482bb56101062d..3cb87826229557f19a8e8aac475f635d92e2363a 100644 (file)
@@ -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;
index 4edd96a40479c3fdfa09964336ed46e29de19384..f8c7e3fb37857f8850b871027c5b99057b27973e 100644 (file)
@@ -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 */
index 540ed05b6f7ccfbcd6831082af4de1006af29fd2..cbd4e3e59fefdc0c1cf1b828df4583eaa433d5b7 100644 (file)
@@ -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;
index c440435d168483d12e5198767f6f9a65b1f0180e..f4c3152aeadaa146fca4b27d1316007de9e6d7a4 100644 (file)
@@ -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 = {
        {