[ipv6] Fix multicast routing in some conditions
[people/meteger/gpxe.git] / src / net / ipv6.c
index a58b5fc..d928934 100644 (file)
 #include <gpxe/iobuf.h>
 #include <gpxe/netdevice.h>
 #include <gpxe/if_ether.h>
+#include <gpxe/dhcp6.h>
 
 struct net_protocol ipv6_protocol;
 
+#define is_linklocal( a ) ( ( (a).in6_u.u6_addr16[0] & htons ( 0xFE80 ) ) == htons ( 0xFE80 ) )
+
 char * inet6_ntoa ( struct in6_addr in6 );
 
 /* Unspecified IP6 address */
@@ -216,7 +219,7 @@ static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) {
        memset ( &pshdr, 0, sizeof ( pshdr ) );
        pshdr.src = ip6hdr->src;
        pshdr.dest = ip6hdr->dest;
-       pshdr.len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
+       pshdr.len = ip6hdr->payload_len;
        pshdr.nxt_hdr = ip6hdr->nxt_hdr;
 
        /* Update checksum value */
@@ -278,13 +281,13 @@ int ipv6_tx ( struct io_buffer *iobuf,
                }
                
                /* Link-local route? */
-               linklocal = (miniroute->address.in6_u.u6_addr16[0] & htons(0xFE80)) == htons(0xFE80);
+               linklocal = is_linklocal ( miniroute->address ); // (.in6_u.u6_addr16[0] & htons(0xFE80)) == htons(0xFE80);
 
                /* Handle link-local for multicast. */
                if ( multicast )
                {
                        /* Link-local scope? */
-                       if ( next_hop.in6_u.u6_addr8[0] & 0x2 ) {
+                       if ( is_linklocal ( next_hop ) ) { // .in6_u.u6_addr8[0] & 0x2 ) {
                                if ( linklocal ) {
                                        netdev = miniroute->netdev;
                                        ip6hdr->src = miniroute->address;
@@ -294,13 +297,12 @@ int ipv6_tx ( struct io_buffer *iobuf,
                                        continue;
                                }
                        } else {
-                               /* Can we route on this interface?
-                                  (assume non-link-local means routable) */
-                               if ( ! linklocal ) {
-                                       netdev = miniroute->netdev;
-                                       ip6hdr->src = miniroute->address;
-                                       break;
-                               }
+                               /* Assume we can TX on this interface, even if
+                                * it is link-local. For multicast this should
+                                * not be too much of a problem. */
+                               netdev = miniroute->netdev;
+                               ip6hdr->src = miniroute->address;
+                               break;
                        }
                }
                
@@ -323,7 +325,7 @@ int ipv6_tx ( struct io_buffer *iobuf,
        }
        /* No network interface identified */
        if ( ( ! netdev ) ) {
-               DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
+               DBG ( "No route to host %s\n", inet6_ntoa ( dest->sin6_addr ) );
                rc = -ENETUNREACH;
                goto err;
        } else if ( ! IP6_EQUAL ( gateway, ip6_none ) ) {
@@ -411,7 +413,6 @@ static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr,
 static int ipv6_rx ( struct io_buffer *iobuf,
                     __unused struct net_device *netdev,
                     __unused const void *ll_source ) {
-
        struct ip6_header *ip6hdr = iobuf->data;
        union {
                struct sockaddr_in6 sin6;
@@ -543,18 +544,12 @@ char * inet6_ntoa ( struct in6_addr in6 ) {
 int inet6_aton ( const char *cp, struct in6_addr *inp ) {
        char convbuf[40];
        char *tmp = convbuf, *next = convbuf;
-       size_t i = 0, len = strlen ( cp );
+       size_t i = 0;
        int ok;
        char c;
        
        /* Verify a valid address. */
-       if ( ! len ) {
-               return 0;
-       }
-       
-       for ( ; i < len; i++ ) {
-               c = cp[i];
-               
+       while ( ( c = cp[i++] ) ) {
                ok = c == ':';
                ok = ok || ( ( c >= '0' ) && ( c <= '9' ) );
                ok = ok || ( ( c >= 'a' ) && ( c <= 'f' ) );
@@ -564,6 +559,9 @@ int inet6_aton ( const char *cp, struct in6_addr *inp ) {
                        return 0;
                }
        }
+       if ( ! i ) {
+               return 0;
+       }
        
        strcpy ( convbuf, cp );
        
@@ -648,3 +646,94 @@ struct icmp6_net_protocol ipv6_icmp6_protocol __icmp6_net_protocol = {
        .check = ipv6_check,
 };
 
+
+
+/******************************************************************************
+ *
+ * Settings
+ *
+ ******************************************************************************
+ */
+
+/** IPv6 address setting */
+struct setting ip6_setting __setting = {
+       .name = "ip6",
+       .description = "IPv6 address",
+       .tag = DHCP6_OPT_IAADDR,
+       .type = &setting_type_ipv6,
+};
+
+/** IPv6 prefix setting */
+struct setting prefix_setting __setting = {
+       .name = "prefix",
+       .description = "IPv6 address prefix length",
+       .tag = 0,
+       .type = &setting_type_int32,
+};
+
+/** Default IPv6 gateway setting */
+struct setting gateway6_setting __setting = {
+       .name = "gateway6",
+       .description = "IPv6 Default gateway",
+       .tag = 0,
+       .type = &setting_type_ipv4,
+};
+
+/**
+ * Create IPv6 routes based on configured settings.
+ *
+ * @ret rc             Return status code
+ */
+static int ipv6_create_routes ( void ) {
+       struct ipv6_miniroute *miniroute;
+       struct ipv6_miniroute *tmp;
+       struct net_device *netdev;
+       struct settings *settings;
+       struct in6_addr address;
+       struct in6_addr gateway;
+       long prefix = 0;
+       int rc = 0;
+       
+       /* Create a route for each configured network device */
+       for_each_netdev ( netdev ) {
+               settings = netdev_settings ( netdev );
+       
+               /* Read the settings first. We may need to clear routes. */
+               fetch_ipv6_setting ( settings, &ip6_setting, &address );
+               fetch_ipv6_setting ( settings, &gateway6_setting, &gateway );
+               fetch_int_setting ( settings, &prefix_setting, &prefix );
+       
+               /* Sanity check! */
+               if ( ( prefix <= 0 ) || ( prefix > 128 ) ) {
+                       DBG ( "ipv6: attempt to apply settings without a valid prefix, ignoring\n" );
+                       continue; /* Simply ignore this setting. */
+               }
+       
+               /* Remove any existing routes for this address. */
+               list_for_each_entry_safe ( miniroute, tmp, &miniroutes, list ) {
+                       if ( ! ipv6_match_prefix ( &address,
+                                                &miniroute->prefix,
+                                                prefix ) ) {
+                               DBG ( "ipv6: existing route for a configured setting, deleting\n" );
+                               del_ipv6_miniroute ( miniroute );
+                       }
+               }
+               
+               /* Configure route */
+               rc = add_ipv6_address ( netdev, address, prefix,
+                                       address, gateway );
+               if ( ! rc )
+                       return rc;
+       }
+       
+       return 0;
+}
+
+/** IPv6 settings applicator */
+struct settings_applicator ipv6_settings_applicator __settings_applicator = {
+       .apply = ipv6_create_routes,
+};
+
+/* Drag in ICMP6 and DHCP6 */
+REQUIRE_OBJECT ( icmpv6 );
+REQUIRE_OBJECT ( dhcp6 );