]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
net: fix ipv6 routing
authorJosef Bacik <jbacik@fb.com>
Wed, 10 Feb 2016 21:21:00 +0000 (13:21 -0800)
committerAndrei Borzenkov <arvidjaar@gmail.com>
Thu, 25 Feb 2016 19:38:52 +0000 (22:38 +0300)
ipv6 routing in grub2 is broken, we cannot talk to anything outside our local
network or anything that doesn't route in our global namespace.  This patch
fixes this by doing a couple of things

1) Read the router information off of the router advertisement.  If we have a
router lifetime we need to take the source address and create a route from it.

2) Changes the routing stuff slightly to allow you to specify a gateway _and_ an
interface.  Since the router advertisements come in on the link local address we
need to associate it with the global address on the card.  So when we are
processing the router advertisement, either use the SLAAC interface we create
and add the route to that interface, or loop through the global addresses we
currently have on our interface and associate it with one of those addresses.
We need to have a special case here for the default route so that it gets used,
we do this by setting the masksize to 0 to mean it encompasses all networks.
The routing code will automatically select the best route so if there is a
closer match we will use that.

With this patch I can now talk to ipv6 addresses outside of my local network.
Thanks,

Signed-off-by: Josef Bacik <jbacik@fb.com>
grub-core/net/bootp.c
grub-core/net/drivers/ieee1275/ofnet.c
grub-core/net/icmp6.c
grub-core/net/net.c
include/grub/net.h

index 4fdeac3ef5883287b3219af767cfd10600bdc12b..a088244b4cb1ce9f21d02c1f3024a99e681ccb1a 100644 (file)
@@ -82,7 +82,7 @@ parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask)
              grub_memcpy (&gw.ipv4, ptr, sizeof (gw.ipv4));
              rname = grub_xasprintf ("%s:default", name);
              if (rname)
-               grub_net_add_route_gw (rname, target, gw);
+               grub_net_add_route_gw (rname, target, gw, NULL);
              grub_free (rname);
            }
          break;
index eff9085ed7aeec7647c1f942e06d914ebf46cb4b..6bd3b922e82bf0ee942287ba67c147c037b6dd46 100644 (file)
@@ -151,7 +151,7 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath,
   grub_net_network_level_address_t client_addr, gateway_addr, subnet_mask;
   grub_net_link_level_address_t hw_addr;
   grub_net_interface_flags_t flags = 0;
-  struct grub_net_network_level_interface *inter;
+  struct grub_net_network_level_interface *inter = NULL;
 
   hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
 
@@ -221,7 +221,7 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath,
       target.ipv4.masksize = 0;
       rname = grub_xasprintf ("%s:default", ((*card)->name));
       if (rname)
-        grub_net_add_route_gw (rname, target, gateway_addr);
+        grub_net_add_route_gw (rname, target, gateway_addr, inter);
       else
         return grub_errno;
     }
index 7953e68ecfafa7b463271b78f0cc41e08205910f..2cbd95dce253e3b30d942ee04aebf44ca3a0d6a5 100644 (file)
@@ -115,6 +115,7 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
                            grub_uint8_t ttl)
 {
   struct icmp_header *icmph;
+  struct grub_net_network_level_interface *orig_inf = inf;
   grub_err_t err;
   grub_uint16_t checksum;
 
@@ -345,14 +346,31 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
       {
        grub_uint8_t *ptr;
        struct option_header *ohdr;
+       struct router_adv *radv;
+       struct grub_net_network_level_interface *route_inf = NULL;
+       int default_route = 0;
        if (icmph->code)
          break;
+       radv = (struct router_adv *)nb->data;
        err = grub_netbuff_pull (nb, sizeof (struct router_adv));
        if (err)
          {
            grub_netbuff_free (nb);
            return err;
          }
+       if (grub_be_to_cpu16 (radv->router_lifetime) > 0)
+         {
+           struct grub_net_route *route;
+
+           FOR_NET_ROUTES (route)
+           {
+             if (!grub_memcmp (&route->gw, source, sizeof (route->gw)))
+               break;
+           }
+           if (route == NULL)
+             default_route = 1;
+         }
+
        for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail;
             ptr += ohdr->len * 8)
          {
@@ -413,7 +431,11 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
                    /* Update lease time if needed here once we have
                       lease times.  */
                    if (inf)
-                     continue;
+                     {
+                       if (!route_inf)
+                         route_inf = inf;
+                       continue;
+                     }
 
                    grub_dprintf ("net", "creating slaac\n");
 
@@ -429,12 +451,51 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
                      inf = grub_net_add_addr (name, 
                                               card, &addr,
                                               &slaac->address, 0);
+                     if (!route_inf)
+                       route_inf = inf;
                      grub_net_add_route (name, netaddr, inf);
                      grub_free (name);
                    }
                  }
              }
          }
+       if (default_route)
+         {
+           char *name;
+           grub_net_network_level_netaddress_t netaddr;
+           name = grub_xasprintf ("%s:ra:default6", card->name);
+           if (!name)
+             {
+               grub_errno = GRUB_ERR_NONE;
+               goto next;
+             }
+           /* Default routes take alll of the traffic, so make the mask huge */
+           netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
+           netaddr.ipv6.masksize = 0;
+           netaddr.ipv6.base[0] = 0;
+           netaddr.ipv6.base[1] = 0;
+
+           /* May not have gotten slaac info, find a global address on this
+             card.  */
+           if (route_inf == NULL)
+             {
+               FOR_NET_NETWORK_LEVEL_INTERFACES (inf)
+               {
+                 if (inf->card == card && inf != orig_inf
+                     && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6
+                     && grub_net_hwaddr_cmp(&inf->hwaddress,
+                                            &orig_inf->hwaddress) == 0)
+                   {
+                     route_inf = inf;
+                     break;
+                   }
+               }
+             }
+           if (route_inf != NULL)
+             grub_net_add_route_gw (name, netaddr, *source, route_inf);
+           grub_free (name);
+         }
+next:
        if (ptr != nb->tail)
          break;
       }
index d07029b3468d3d33687ac4a3dc144532839746c0..9424595278c1a09174a32ad0f467d7790a5225e8 100644 (file)
@@ -37,21 +37,6 @@ GRUB_MOD_LICENSE ("GPLv3+");
 
 char *grub_net_default_server;
 
-struct grub_net_route
-{
-  struct grub_net_route *next;
-  struct grub_net_route **prev;
-  grub_net_network_level_netaddress_t target;
-  char *name;
-  struct grub_net_network_level_protocol *prot;
-  int is_gateway;
-  union
-  {
-    struct grub_net_network_level_interface *interface;
-    grub_net_network_level_address_t gw;
-  };
-};
-
 struct grub_net_route *grub_net_routes = NULL;
 struct grub_net_network_level_interface *grub_net_network_level_interfaces = NULL;
 struct grub_net_card *grub_net_cards = NULL;
@@ -410,14 +395,6 @@ grub_cmd_ipv6_autoconf (struct grub_command *cmd __attribute__ ((unused)),
   return err;
 }
 
-static inline void
-grub_net_route_register (struct grub_net_route *route)
-{
-  grub_list_push (GRUB_AS_LIST_P (&grub_net_routes),
-                 GRUB_AS_LIST (route));
-}
-
-#define FOR_NET_ROUTES(var) for (var = grub_net_routes; var; var = var->next)
 
 static int
 parse_ip (const char *val, grub_uint32_t *ip, const char **rest)
@@ -524,6 +501,8 @@ match_net (const grub_net_network_level_netaddress_t *net,
     case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6:
       {
        grub_uint64_t mask[2];
+       if (net->ipv6.masksize == 0)
+         return 1;
        if (net->ipv6.masksize <= 64)
          {
            mask[0] = 0xffffffffffffffffULL << (64 - net->ipv6.masksize);
@@ -687,7 +666,14 @@ grub_net_route_address (grub_net_network_level_address_t addr,
          return GRUB_ERR_NONE;
        }
       if (depth == 0)
-       *gateway = bestroute->gw;
+       {
+         *gateway = bestroute->gw;
+         if (bestroute->interface != NULL)
+           {
+             *interf = bestroute->interface;
+             return GRUB_ERR_NONE;
+           }
+       }
       curtarget = bestroute->gw;
     }
 
@@ -1109,7 +1095,8 @@ grub_net_add_route (const char *name,
 grub_err_t
 grub_net_add_route_gw (const char *name,
                       grub_net_network_level_netaddress_t target,
-                      grub_net_network_level_address_t gw)
+                      grub_net_network_level_address_t gw,
+                      struct grub_net_network_level_interface *inter)
 {
   struct grub_net_route *route;
 
@@ -1127,6 +1114,7 @@ grub_net_add_route_gw (const char *name,
   route->target = target;
   route->is_gateway = 1;
   route->gw = gw;
+  route->interface = inter;
 
   grub_net_route_register (route);
 
@@ -1152,7 +1140,7 @@ grub_cmd_addroute (struct grub_command *cmd __attribute__ ((unused)),
       err = grub_net_resolve_address (args[3], &gw);
       if (err)
        return err;
-      return grub_net_add_route_gw (args[0], target, gw);
+      return grub_net_add_route_gw (args[0], target, gw, NULL);
     }
   else
     {
index 538baa33ecacd7c1a34e7dd46742feaf3d9a3ea2..2192fa18628862e8e3a6046d854b12613e914ff9 100644 (file)
@@ -191,6 +191,18 @@ typedef struct grub_net_network_level_netaddress
   };
 } grub_net_network_level_netaddress_t;
 
+struct grub_net_route
+{
+  struct grub_net_route *next;
+  struct grub_net_route **prev;
+  grub_net_network_level_netaddress_t target;
+  char *name;
+  struct grub_net_network_level_protocol *prot;
+  int is_gateway;
+  struct grub_net_network_level_interface *interface;
+  grub_net_network_level_address_t gw;
+};
+
 #define FOR_PACKETS(cont,var) for (var = (cont).first; var; var = var->next)
 
 static inline grub_err_t
@@ -367,6 +379,16 @@ grub_net_card_unregister (struct grub_net_card *card);
 #define FOR_NET_CARDS_SAFE(var, next) for (var = grub_net_cards, next = (var ? var->next : 0); var; var = next, next = (var ? var->next : 0))
 
 
+extern struct grub_net_route *grub_net_routes;
+
+static inline void
+grub_net_route_register (struct grub_net_route *route)
+{
+  grub_list_push (GRUB_AS_LIST_P (&grub_net_routes),
+                 GRUB_AS_LIST (route));
+}
+
+#define FOR_NET_ROUTES(var) for (var = grub_net_routes; var; var = var->next)
 struct grub_net_session *
 grub_net_open_tcp (char *address, grub_uint16_t port);
 
@@ -392,7 +414,8 @@ grub_net_add_route (const char *name,
 grub_err_t
 grub_net_add_route_gw (const char *name,
                       grub_net_network_level_netaddress_t target,
-                      grub_net_network_level_address_t gw);
+                      grub_net_network_level_address_t gw,
+                      struct grub_net_network_level_interface *inter);
 
 
 #define GRUB_NET_BOOTP_MAC_ADDR_LEN    16