- 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
... | ... |
@@ -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; |