Browse code

Implement handling of overlapping IPv6 routes with IPv6 remote VPN server address

- socket.[ch]: add link_socket_current_remote_ipv6() helper to extract
current address of remote VPN server (if IPv6, NULL otherwise), IPv6
equivalent to link_socket_current_remote()

- init.c: pass remote VPN server address to init_route_ipv6_list()
(link_socket_current_remote_ipv6())

- route.h: add route_ipv6_gateway_info to route_ipv6_list, and reorder
structures so that this actually compiles. Add iface/adapter_index
to struct route_ipv6 (for non-tun/tap routes).

- route.[ch]: add "const" to *dest argument to get_default_gateway_ipv6()

- route.c: add route_ipv6_match_host() helper to check whether an IPv6
address is matched by a given "route_ipv6" IPv6 route)

- route.c: init_route_ipv6_list()
- call get_default_gateway_ipv6()
- check to-be-installed IPv6 routes against VPN server address (if IPv6)
- if an overlap is seen, add a host route for the VPN server address
via the just-discovered gateway to the list of IPv6 routes to be
installed (rl6->routes_ipv6)
- warn if overlap is detected but platform code has not been able to
discover IPv6 default gateway

- route.c: add_route_ipv6() / delete_route_ipv6(): set "device" to
"external default gateway interface" (r6->iface) instead of TUN/TAP
device (if set), which nicely enables arbitrary gateway/interface
combinations for Linux

- ssl.c: add "IV_RGI6=1" to push-peer-info data to let server know we can
handle pushed IPv6 routes that overlap with server IPv6 address

- tun.c: when adding/removing on-link routes, CLEAR(r6) first to
ensure new struct route_ipv6 members are cleared

Tested on Linux with iproute2 and /bin/route, on eth and tun routes.

Signed-off-by: Gert Doering <gert@greenie.muc.de>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <1441985627-14822-7-git-send-email-gert@greenie.muc.de>
URL: http://article.gmane.org/gmane.network.openvpn.devel/10089

Gert Doering authored on 2015/09/12 00:33:43
Showing 7 changed files
... ...
@@ -1179,6 +1179,7 @@ do_init_route_list (const struct options *options,
1179 1179
 static void
1180 1180
 do_init_route_ipv6_list (const struct options *options,
1181 1181
 		    struct route_ipv6_list *route_ipv6_list,
1182
+		    const struct link_socket_info *link_socket_info,
1182 1183
 		    bool fatal,
1183 1184
 		    struct env_set *es)
1184 1185
 {
... ...
@@ -1198,6 +1199,7 @@ do_init_route_ipv6_list (const struct options *options,
1198 1198
 			options->routes_ipv6,
1199 1199
 			gw,
1200 1200
 			metric,
1201
+			link_socket_current_remote_ipv6 (link_socket_info),
1201 1202
 			es))
1202 1203
     {
1203 1204
       if (fatal)
... ...
@@ -1391,7 +1393,7 @@ do_open_tun (struct context *c)
1391 1391
       if (c->options.routes && c->c1.route_list && c->c2.link_socket)
1392 1392
 	do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es);
1393 1393
       if (c->options.routes_ipv6 && c->c1.route_ipv6_list )
1394
-	do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, false, c->c2.es);
1394
+	do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, &c->c2.link_socket->info, false, c->c2.es);
1395 1395
 
1396 1396
       /* do ifconfig */
1397 1397
       if (!c->options.ifconfig_noexec
... ...
@@ -660,29 +660,78 @@ init_route_list (struct route_list *rl,
660 660
   return ret;
661 661
 }
662 662
 
663
+/* check whether an IPv6 host address is covered by a given route_ipv6
664
+ * (not the most beautiful implementation in the world, but portable and
665
+ * "good enough")
666
+ */
667
+static bool
668
+route_ipv6_match_host( const struct route_ipv6 *r6,
669
+		       const struct in6_addr *host )
670
+{
671
+    unsigned int bits = r6->netbits;
672
+    int i;
673
+    unsigned int mask;
674
+
675
+    if ( bits>128 )
676
+	return false;
677
+
678
+    for( i=0; bits >= 8; i++, bits -= 8 )
679
+    {
680
+	if ( r6->network.s6_addr[i] != host->s6_addr[i] )
681
+	    return false;
682
+    }
683
+
684
+    if ( bits == 0 )
685
+	return true;
686
+
687
+    mask = 0xff << (8-bits);
688
+
689
+    if ( (r6->network.s6_addr[i] & mask) == (host->s6_addr[i] & mask ))
690
+	return true;
691
+
692
+    return false;
693
+}
694
+
663 695
 bool
664 696
 init_route_ipv6_list (struct route_ipv6_list *rl6,
665 697
 		 const struct route_ipv6_option_list *opt6,
666 698
 		 const char *remote_endpoint,
667 699
 		 int default_metric,
700
+		 const struct in6_addr *remote_host_ipv6,
668 701
 		 struct env_set *es)
669 702
 {
670 703
   struct gc_arena gc = gc_new ();
671 704
   bool ret = true;
705
+  bool need_remote_ipv6_route;
672 706
 
673 707
   clear_route_ipv6_list (rl6);
674 708
 
675 709
   rl6->flags = opt6->flags;
676 710
 
711
+  if (remote_host_ipv6)
712
+    {
713
+      rl6->remote_host_ipv6 = *remote_host_ipv6;
714
+      rl6->spec_flags |= RTSA_REMOTE_HOST;
715
+    }
716
+
677 717
   if (default_metric >= 0 )
678 718
     {
679 719
       rl6->default_metric = default_metric;
680 720
       rl6->spec_flags |= RTSA_DEFAULT_METRIC;
681 721
     }
682 722
 
683
-  /* "default_gateway" is stuff for "redirect-gateway", which we don't
684
-   * do for IPv6 yet -> TODO
685
-   */
723
+  msg (D_ROUTE, "GDG6: remote_host_ipv6=%s",
724
+	remote_host_ipv6?  print_in6_addr (*remote_host_ipv6, 0, &gc): "n/a" );
725
+
726
+  get_default_gateway_ipv6 (&rl6->rgi6, remote_host_ipv6);
727
+  if (rl6->rgi6.flags & RGI_ADDR_DEFINED)
728
+    {
729
+      setenv_str (es, "net_gateway_ipv6", print_in6_addr (rl6->rgi6.gateway.addr_ipv6, 0, &gc));
730
+#if defined(ENABLE_DEBUG) && !defined(ENABLE_SMALL)
731
+      print_default_gateway (D_ROUTE, NULL, &rl6->rgi6);
732
+#endif
733
+    }
734
+  else
686 735
     {
687 736
       dmsg (D_ROUTE, "ROUTE6: default_gateway=UNDEF");
688 737
     }
... ...
@@ -696,12 +745,16 @@ init_route_ipv6_list (struct route_ipv6_list *rl6,
696 696
         }
697 697
       else
698 698
 	{
699
-	  msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s", remote_endpoint);
699
+	  msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve VPN endpoint: %s", remote_endpoint);
700 700
           ret = false;
701 701
 	}
702 702
     }
703 703
 
704
-  /* parse the routes from opt6 to rl6 */
704
+  /* parse the routes from opt6 to rl6
705
+   * discovering potential overlaps with remote_host_ipv6 in the process
706
+   */
707
+  need_remote_ipv6_route = false;
708
+
705 709
   {
706 710
     struct route_ipv6_option *ro6;
707 711
     for (ro6 = opt6->routes_ipv6; ro6; ro6 = ro6->next)
... ...
@@ -714,10 +767,49 @@ init_route_ipv6_list (struct route_ipv6_list *rl6,
714 714
           {
715 715
             r6->next = rl6->routes_ipv6;
716 716
             rl6->routes_ipv6 = r6;
717
+
718
+	    if ( remote_host_ipv6 &&
719
+		  route_ipv6_match_host( r6, remote_host_ipv6 ) )
720
+	      {
721
+		need_remote_ipv6_route = true;
722
+		msg (D_ROUTE, "ROUTE6: %s/%d overlaps IPv6 remote %s, adding host route to VPN endpoint",
723
+			print_in6_addr (r6->network, 0, &gc), r6->netbits,
724
+			print_in6_addr (*remote_host_ipv6, 0, &gc));
725
+	      }
717 726
           }
718 727
       }
719 728
   }
720 729
 
730
+  /* add VPN server host route if needed */
731
+  if ( need_remote_ipv6_route )
732
+    {
733
+      if ( (rl6->rgi6.flags & (RGI_ADDR_DEFINED|RGI_IFACE_DEFINED) ) ==
734
+				    (RGI_ADDR_DEFINED|RGI_IFACE_DEFINED) )
735
+        {
736
+	  struct route_ipv6 *r6;
737
+	  ALLOC_OBJ_CLEAR_GC (r6, struct route_ipv6, &rl6->gc);
738
+
739
+	  r6->network = *remote_host_ipv6;
740
+	  r6->netbits = 128;
741
+	  if ( !(rl6->rgi6.flags & RGI_ON_LINK) )
742
+	    { r6->gateway = rl6->rgi6.gateway.addr_ipv6; }
743
+	  r6->metric = 1;
744
+#ifdef WIN32
745
+	  r6->adapter_index = rl6->rgi6.adapter_index;
746
+#else
747
+	  r6->iface = rl6->rgi6.iface;
748
+#endif
749
+	  r6->flags = RT_DEFINED | RT_METRIC_DEFINED;
750
+
751
+	  r6->next = rl6->routes_ipv6;
752
+	  rl6->routes_ipv6 = r6;
753
+	}
754
+      else
755
+        {
756
+	  msg (M_WARN, "ROUTE6: IPv6 route overlaps with IPv6 remote address, but could not determine IPv6 gateway address + interface, expect failure\n" );
757
+	}
758
+    }
759
+
721 760
   gc_free (&gc);
722 761
   return ret;
723 762
 }
... ...
@@ -1574,6 +1666,15 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla
1574 1574
   if (! (r6->flags & RT_DEFINED) )
1575 1575
     return;
1576 1576
 
1577
+#ifndef WIN32
1578
+  if ( r6->iface != NULL )		/* vpn server special route */
1579
+    {
1580
+      device = r6->iface;
1581
+      if ( !IN6_IS_ADDR_UNSPECIFIED(&r6->gateway) )
1582
+	  gateway_needed = true;
1583
+    }
1584
+#endif
1585
+
1577 1586
   gc_init (&gc);
1578 1587
   argv_init (&argv);
1579 1588
 
... ...
@@ -1655,7 +1756,7 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla
1655 1655
    * - in TUN mode we use a special-case link-local address that the tapdrvr
1656 1656
    *   knows about and will answer ND (neighbor discovery) packets for
1657 1657
    */
1658
-  if ( tt->type == DEV_TYPE_TUN )
1658
+  if ( tt->type == DEV_TYPE_TUN && !gateway_needed )
1659 1659
 	argv_printf_cat( &argv, " %s", "fe80::8" );
1660 1660
   else
1661 1661
 	argv_printf_cat( &argv, " %s", gateway );
... ...
@@ -1948,6 +2049,14 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne
1948 1948
   if ((r6->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED))
1949 1949
     return;
1950 1950
 
1951
+#ifndef WIN32
1952
+  if ( r6->iface != NULL )		/* vpn server special route */
1953
+    {
1954
+      device = r6->iface;
1955
+      gateway_needed = true;
1956
+    }
1957
+#endif
1958
+
1951 1959
   gc_init (&gc);
1952 1960
   argv_init (&argv);
1953 1961
 
... ...
@@ -2344,7 +2453,7 @@ windows_route_find_if_index (const struct route_ipv4 *r, const struct tuntap *tt
2344 2344
  */
2345 2345
 void
2346 2346
 get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
2347
-			 struct in6_addr *dest)
2347
+			 const struct in6_addr *dest)
2348 2348
 {
2349 2349
     msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system (windows)");
2350 2350
     CLEAR(*rgi6);
... ...
@@ -2693,7 +2802,7 @@ struct rtreq {
2693 2693
 
2694 2694
 void
2695 2695
 get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
2696
-			 struct in6_addr *dest)
2696
+			 const struct in6_addr *dest)
2697 2697
 {
2698 2698
     int nls = -1;
2699 2699
     struct rtreq rtreq;
... ...
@@ -2811,7 +2920,8 @@ get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
2811 2811
               RGI_IFACE_DEFINED )
2812 2812
     {
2813 2813
 	rgi6->flags |= (RGI_ADDR_DEFINED | RGI_ON_LINK);
2814
-	rgi6->gateway.addr_ipv6 = *dest;
2814
+	if ( dest )
2815
+	    rgi6->gateway.addr_ipv6 = *dest;
2815 2816
     }
2816 2817
 
2817 2818
   done:
... ...
@@ -3065,7 +3175,7 @@ get_default_gateway (struct route_gateway_info *rgi)
3065 3065
  */
3066 3066
 void
3067 3067
 get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
3068
-			 struct in6_addr *dest)
3068
+			 const struct in6_addr *dest)
3069 3069
 {
3070 3070
     msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system (BSD and OSX)");
3071 3071
     CLEAR(*rgi6);
... ...
@@ -3106,7 +3216,7 @@ get_default_gateway (struct route_gateway_info *rgi)
3106 3106
 }
3107 3107
 void
3108 3108
 get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
3109
-			 struct in6_addr *dest)
3109
+			 const struct in6_addr *dest)
3110 3110
 {
3111 3111
     msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system");
3112 3112
     CLEAR(*rgi6);
... ...
@@ -128,19 +128,12 @@ struct route_ipv6 {
128 128
   unsigned int netbits;
129 129
   struct in6_addr gateway;
130 130
   int metric;
131
-};
132
-
133
-struct route_ipv6_list {
134
-  unsigned int iflags;			/* RL_ flags, see route_list */
135
-
136
-  unsigned int spec_flags;		/* RTSA_ flags, route_special_addr */
137
-  struct in6_addr remote_endpoint_ipv6;	/* inside tun */
138
-  struct in6_addr remote_host_ipv6;	/* --remote address */
139
-  int default_metric;
140
-
141
-  unsigned int flags;			/* RG_x flags, see route_option_list */
142
-  struct route_ipv6 *routes_ipv6;
143
-  struct gc_arena gc;
131
+  /* gateway interface */
132
+# ifdef WIN32
133
+  DWORD adapter_index;		/* interface or ~0 if undefined */
134
+#else
135
+  char * iface;			/* interface name (null terminated) */
136
+#endif
144 137
 };
145 138
 
146 139
 
... ...
@@ -218,6 +211,20 @@ struct route_list {
218 218
   struct gc_arena gc;
219 219
 };
220 220
 
221
+struct route_ipv6_list {
222
+  unsigned int iflags;			/* RL_ flags, see route_list */
223
+
224
+  unsigned int spec_flags;		/* RTSA_ flags, route_special_addr */
225
+  struct in6_addr remote_endpoint_ipv6;	/* inside tun */
226
+  struct in6_addr remote_host_ipv6;	/* --remote address */
227
+  int default_metric;
228
+
229
+  struct route_ipv6_gateway_info rgi6;
230
+  unsigned int flags;			/* RG_x flags, see route_option_list */
231
+  struct route_ipv6 *routes_ipv6;
232
+  struct gc_arena gc;
233
+};
234
+
221 235
 #if P2MP
222 236
 /* internal OpenVPN route */
223 237
 struct iroute {
... ...
@@ -274,6 +281,7 @@ bool init_route_ipv6_list (struct route_ipv6_list *rl6,
274 274
 		      const struct route_ipv6_option_list *opt6,
275 275
 		      const char *remote_endpoint,
276 276
 		      int default_metric,
277
+		      const struct in6_addr *remote_host,
277 278
 		      struct env_set *es);
278 279
 
279 280
 void route_list_add_vpn_gateway (struct route_list *rl,
... ...
@@ -301,7 +309,7 @@ bool is_special_addr (const char *addr_str);
301 301
 
302 302
 void get_default_gateway (struct route_gateway_info *rgi);
303 303
 void get_default_gateway_ipv6 (struct route_ipv6_gateway_info *rgi,
304
-				struct in6_addr *dest);
304
+				const struct in6_addr *dest);
305 305
 void print_default_gateway(const int msglevel,
306 306
                            const struct route_gateway_info *rgi,
307 307
                            const struct route_ipv6_gateway_info *rgi6);
... ...
@@ -2126,6 +2126,28 @@ link_socket_current_remote (const struct link_socket_info *info)
2126 2126
     return 0;
2127 2127
 }
2128 2128
 
2129
+const struct in6_addr *
2130
+link_socket_current_remote_ipv6 (const struct link_socket_info *info)
2131
+{
2132
+  const struct link_socket_addr *lsa = info->lsa;
2133
+
2134
+/* This logic supports "redirect-gateway" semantic,
2135
+ * for PF_INET6 routes over PF_INET6 endpoints
2136
+ *
2137
+ * For --remote entries with multiple addresses this
2138
+ * only return the actual endpoint we have sucessfully connected to
2139
+ */
2140
+  if (lsa->actual.dest.addr.sa.sa_family != AF_INET6)
2141
+    return NULL;
2142
+
2143
+  if (link_socket_actual_defined (&lsa->actual))
2144
+    return &(lsa->actual.dest.addr.in6.sin6_addr);
2145
+  else if (lsa->current_remote)
2146
+    return &(((struct sockaddr_in6*)lsa->current_remote->ai_addr) ->sin6_addr);
2147
+  else
2148
+    return NULL;
2149
+}
2150
+
2129 2151
 /*
2130 2152
  * Return a status string describing socket state.
2131 2153
  */
... ...
@@ -419,6 +419,8 @@ void bad_address_length (int actual, int expected);
419 419
  */
420 420
 #define IPV4_INVALID_ADDR 0xffffffff
421 421
 in_addr_t link_socket_current_remote (const struct link_socket_info *info);
422
+const struct in6_addr * link_socket_current_remote_ipv6
423
+				     (const struct link_socket_info *info);
422 424
 
423 425
 void link_socket_connection_initiated (const struct buffer *buf,
424 426
 				       struct link_socket_info *info,
... ...
@@ -1853,6 +1853,9 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
1853 1853
       comp_generate_peer_info_string(&session->opt->comp_options, &out);
1854 1854
 #endif
1855 1855
 
1856
+      /* support for redirecting IPv6 gateway */
1857
+      buf_printf(&out, "IV_RGI6=1\n");
1858
+
1856 1859
       if (session->opt->push_peer_info_detail >= 2)
1857 1860
         {
1858 1861
 	  /* push mac addr */
... ...
@@ -607,6 +607,7 @@ void add_route_connected_v6_net(struct tuntap * tt,
607 607
 {
608 608
     struct route_ipv6 r6;
609 609
 
610
+    CLEAR(r6);
610 611
     r6.network = tt->local_ipv6;
611 612
     r6.netbits = tt->netbits_ipv6;
612 613
     r6.gateway = tt->local_ipv6;
... ...
@@ -620,6 +621,7 @@ void delete_route_connected_v6_net(struct tuntap * tt,
620 620
 {
621 621
     struct route_ipv6 r6;
622 622
 
623
+    CLEAR(r6);
623 624
     r6.network = tt->local_ipv6;
624 625
     r6.netbits = tt->netbits_ipv6;
625 626
     r6.gateway = tt->local_ipv6;