[ipv6] Fix multicast routing in some conditions
[people/meteger/gpxe.git] / src / net / ipv6.c
index bfd78ca..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 */
@@ -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 ) ) {
@@ -644,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 );