Blame SOURCES/0172-send-router-solicitation-for-ipv6-address-autoconf-v.patch

28f7f8
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
ecb9bb
From: Michael Chang <mchang@suse.com>
ecb9bb
Date: Tue, 18 Nov 2014 16:03:08 +0800
28f7f8
Subject: [PATCH] send router solicitation for ipv6 address autoconf v2
ecb9bb
ecb9bb
Many routers have long router advertisment interval configured by
ecb9bb
default. The Neighbor Discovery protocol (RFC4861) has defined default
ecb9bb
MaxRtrAdvInterval value as 600 seconds and
ecb9bb
MinRtrAdvInterval as 0.33*MaxRtrAdvInterval. This makes
ecb9bb
net_ipv6_autoconf fails more often than not as currently it passively
ecb9bb
listens the RA message to perfom address autoconfiguration.
ecb9bb
ecb9bb
This patch tries to send router solicitation to overcome the problem of
ecb9bb
long RA interval.
ecb9bb
ecb9bb
v2:
ecb9bb
use cpu_to_be macro for network byte order conversion
ecb9bb
add missing error handling
ecb9bb
---
ecb9bb
 grub-core/net/icmp6.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++
ecb9bb
 grub-core/net/net.c   |  4 ++-
ecb9bb
 include/grub/net/ip.h |  2 ++
ecb9bb
 3 files changed, 88 insertions(+), 1 deletion(-)
ecb9bb
ecb9bb
diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c
28f7f8
index bbc902014fe..0843a15afda 100644
ecb9bb
--- a/grub-core/net/icmp6.c
ecb9bb
+++ b/grub-core/net/icmp6.c
ecb9bb
@@ -72,6 +72,11 @@ struct neighbour_advertise
ecb9bb
   grub_uint64_t target[2];
ecb9bb
 } GRUB_PACKED;
ecb9bb
 
ecb9bb
+struct router_solicit
ecb9bb
+{
ecb9bb
+  grub_uint32_t reserved;
ecb9bb
+} GRUB_PACKED;
ecb9bb
+
ecb9bb
 enum
ecb9bb
   {
ecb9bb
     FLAG_SLAAC = 0x40
ecb9bb
@@ -81,6 +86,7 @@ enum
ecb9bb
   {
ecb9bb
     ICMP6_ECHO = 128,
ecb9bb
     ICMP6_ECHO_REPLY = 129,
ecb9bb
+    ICMP6_ROUTER_SOLICIT = 133,
ecb9bb
     ICMP6_ROUTER_ADVERTISE = 134,
ecb9bb
     ICMP6_NEIGHBOUR_SOLICIT = 135,
ecb9bb
     ICMP6_NEIGHBOUR_ADVERTISE = 136,
ecb9bb
@@ -533,3 +539,80 @@ grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf,
ecb9bb
   grub_netbuff_free (nb);
ecb9bb
   return err;
ecb9bb
 }
ecb9bb
+
ecb9bb
+grub_err_t
ecb9bb
+grub_net_icmp6_send_router_solicit (struct grub_net_network_level_interface *inf)
ecb9bb
+{
ecb9bb
+  struct grub_net_buff *nb;
ecb9bb
+  grub_err_t err = GRUB_ERR_NONE;
ecb9bb
+  grub_net_network_level_address_t multicast;
ecb9bb
+  grub_net_link_level_address_t ll_multicast;
ecb9bb
+  struct option_header *ohdr;
ecb9bb
+  struct router_solicit *sol;
ecb9bb
+  struct icmp_header *icmphr;
ecb9bb
+
ecb9bb
+  multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
ecb9bb
+  multicast.ipv6[0] = grub_cpu_to_be64 (0xff02ULL << 48);
ecb9bb
+  multicast.ipv6[1] = grub_cpu_to_be64 (0x02ULL);
ecb9bb
+
ecb9bb
+  err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast);
ecb9bb
+  if (err)
ecb9bb
+    return err;
ecb9bb
+
ecb9bb
+  nb = grub_netbuff_alloc (sizeof (struct router_solicit)
ecb9bb
+			   + sizeof (struct option_header)
ecb9bb
+			   + 6
ecb9bb
+			   + sizeof (struct icmp_header)
ecb9bb
+			   + GRUB_NET_OUR_IPV6_HEADER_SIZE
ecb9bb
+			   + GRUB_NET_MAX_LINK_HEADER_SIZE);
ecb9bb
+  if (!nb)
ecb9bb
+    return grub_errno;
ecb9bb
+  err = grub_netbuff_reserve (nb,
ecb9bb
+			      sizeof (struct router_solicit)
ecb9bb
+			      + sizeof (struct option_header)
ecb9bb
+			      + 6
ecb9bb
+			      + sizeof (struct icmp_header)
ecb9bb
+			      + GRUB_NET_OUR_IPV6_HEADER_SIZE
ecb9bb
+			      + GRUB_NET_MAX_LINK_HEADER_SIZE);
ecb9bb
+  if (err)
ecb9bb
+    goto fail;
ecb9bb
+
ecb9bb
+  err = grub_netbuff_push (nb, 6);
ecb9bb
+  if (err)
ecb9bb
+    goto fail;
ecb9bb
+
ecb9bb
+  grub_memcpy (nb->data, inf->hwaddress.mac, 6);
ecb9bb
+
ecb9bb
+  err = grub_netbuff_push (nb, sizeof (*ohdr));
ecb9bb
+  if (err)
ecb9bb
+    goto fail;
ecb9bb
+
ecb9bb
+  ohdr = (struct option_header *) nb->data;
ecb9bb
+  ohdr->type = OPTION_SOURCE_LINK_LAYER_ADDRESS;
ecb9bb
+  ohdr->len = 1;
ecb9bb
+
ecb9bb
+  err = grub_netbuff_push (nb, sizeof (*sol));
ecb9bb
+  if (err)
ecb9bb
+    goto fail;
ecb9bb
+
ecb9bb
+  sol = (struct router_solicit *) nb->data;
ecb9bb
+  sol->reserved = 0;
ecb9bb
+
ecb9bb
+  err = grub_netbuff_push (nb, sizeof (*icmphr));
ecb9bb
+  if (err)
ecb9bb
+    goto fail;
ecb9bb
+
ecb9bb
+  icmphr = (struct icmp_header *) nb->data;
ecb9bb
+  icmphr->type = ICMP6_ROUTER_SOLICIT;
ecb9bb
+  icmphr->code = 0;
ecb9bb
+  icmphr->checksum = 0;
ecb9bb
+  icmphr->checksum = grub_net_ip_transport_checksum (nb,
ecb9bb
+						     GRUB_NET_IP_ICMPV6,
ecb9bb
+						     &inf->address,
ecb9bb
+						     &multicast);
ecb9bb
+  err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
ecb9bb
+				 GRUB_NET_IP_ICMPV6);
ecb9bb
+ fail:
ecb9bb
+  grub_netbuff_free (nb);
ecb9bb
+  return err;
ecb9bb
+}
ecb9bb
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
28f7f8
index 10bfed31b2b..b10addbe27b 100644
ecb9bb
--- a/grub-core/net/net.c
ecb9bb
+++ b/grub-core/net/net.c
ecb9bb
@@ -380,12 +380,14 @@ grub_cmd_ipv6_autoconf (struct grub_command *cmd __attribute__ ((unused)),
ecb9bb
 
ecb9bb
   for (interval = 200; interval < 10000; interval *= 2)
ecb9bb
     {
ecb9bb
-      /* FIXME: send router solicitation.  */
ecb9bb
       int done = 1;
ecb9bb
       for (j = 0; j < ncards; j++)
ecb9bb
 	{
ecb9bb
 	  if (slaacs[j]->slaac_counter)
ecb9bb
 	    continue;
ecb9bb
+	  err = grub_net_icmp6_send_router_solicit (ifaces[j]);
ecb9bb
+	  if (err)
ecb9bb
+	    err = GRUB_ERR_NONE;
ecb9bb
 	  done = 0;
ecb9bb
 	}
ecb9bb
       if (done)
ecb9bb
diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h
28f7f8
index 7a8e614794d..dcceaa56894 100644
ecb9bb
--- a/include/grub/net/ip.h
ecb9bb
+++ b/include/grub/net/ip.h
ecb9bb
@@ -92,4 +92,6 @@ grub_err_t
ecb9bb
 grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf,
ecb9bb
 			     const grub_net_network_level_address_t *proto_addr);
ecb9bb
 
ecb9bb
+grub_err_t
ecb9bb
+grub_net_icmp6_send_router_solicit (struct grub_net_network_level_interface *inf);
ecb9bb
 #endif