return ret;
}
+/* check whether an IPv6 host address is covered by a given route_ipv6
+ * (not the most beautiful implementation in the world, but portable and
+ * "good enough")
+ */
+static bool
+route_ipv6_match_host( const struct route_ipv6 *r6,
+ const struct in6_addr *host )
+{
+ unsigned int bits = r6->netbits;
+ int i;
+ unsigned int mask;
+
+ if ( bits>128 )
+ return false;
+
+ for( i=0; bits >= 8; i++, bits -= 8 )
+ {
+ if ( r6->network.s6_addr[i] != host->s6_addr[i] )
+ return false;
+ }
+
+ if ( bits == 0 )
+ return true;
+
+ mask = 0xff << (8-bits);
+
+ if ( (r6->network.s6_addr[i] & mask) == (host->s6_addr[i] & mask ))
+ return true;
+
+ return false;
+}
+
bool
init_route_ipv6_list (struct route_ipv6_list *rl6,
const struct route_ipv6_option_list *opt6,
const char *remote_endpoint,
int default_metric,
+ const struct in6_addr *remote_host_ipv6,
struct env_set *es)
{
struct gc_arena gc = gc_new ();
bool ret = true;
+ bool need_remote_ipv6_route;
clear_route_ipv6_list (rl6);
rl6->flags = opt6->flags;
+ if (remote_host_ipv6)
+ {
+ rl6->remote_host_ipv6 = *remote_host_ipv6;
+ rl6->spec_flags |= RTSA_REMOTE_HOST;
+ }
+
if (default_metric >= 0 )
{
rl6->default_metric = default_metric;
rl6->spec_flags |= RTSA_DEFAULT_METRIC;
}
- /* "default_gateway" is stuff for "redirect-gateway", which we don't
- * do for IPv6 yet -> TODO
- */
+ msg (D_ROUTE, "GDG6: remote_host_ipv6=%s",
+ remote_host_ipv6? print_in6_addr (*remote_host_ipv6, 0, &gc): "n/a" );
+
+ get_default_gateway_ipv6 (&rl6->rgi6, remote_host_ipv6);
+ if (rl6->rgi6.flags & RGI_ADDR_DEFINED)
+ {
+ setenv_str (es, "net_gateway_ipv6", print_in6_addr (rl6->rgi6.gateway.addr_ipv6, 0, &gc));
+#if defined(ENABLE_DEBUG) && !defined(ENABLE_SMALL)
+ print_default_gateway (D_ROUTE, NULL, &rl6->rgi6);
+#endif
+ }
+ else
{
dmsg (D_ROUTE, "ROUTE6: default_gateway=UNDEF");
}
}
else
{
- msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s", remote_endpoint);
+ msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve VPN endpoint: %s", remote_endpoint);
ret = false;
}
}
- /* parse the routes from opt6 to rl6 */
+ /* parse the routes from opt6 to rl6
+ * discovering potential overlaps with remote_host_ipv6 in the process
+ */
+ need_remote_ipv6_route = false;
+
{
struct route_ipv6_option *ro6;
for (ro6 = opt6->routes_ipv6; ro6; ro6 = ro6->next)
{
r6->next = rl6->routes_ipv6;
rl6->routes_ipv6 = r6;
+
+ if ( remote_host_ipv6 &&
+ route_ipv6_match_host( r6, remote_host_ipv6 ) )
+ {
+ need_remote_ipv6_route = true;
+ msg (D_ROUTE, "ROUTE6: %s/%d overlaps IPv6 remote %s, adding host route to VPN endpoint",
+ print_in6_addr (r6->network, 0, &gc), r6->netbits,
+ print_in6_addr (*remote_host_ipv6, 0, &gc));
+ }
}
}
}
+ /* add VPN server host route if needed */
+ if ( need_remote_ipv6_route )
+ {
+ if ( (rl6->rgi6.flags & (RGI_ADDR_DEFINED|RGI_IFACE_DEFINED) ) ==
+ (RGI_ADDR_DEFINED|RGI_IFACE_DEFINED) )
+ {
+ struct route_ipv6 *r6;
+ ALLOC_OBJ_CLEAR_GC (r6, struct route_ipv6, &rl6->gc);
+
+ r6->network = *remote_host_ipv6;
+ r6->netbits = 128;
+ if ( !(rl6->rgi6.flags & RGI_ON_LINK) )
+ { r6->gateway = rl6->rgi6.gateway.addr_ipv6; }
+ r6->metric = 1;
+#ifdef WIN32
+ r6->adapter_index = rl6->rgi6.adapter_index;
+#else
+ r6->iface = rl6->rgi6.iface;
+#endif
+ r6->flags = RT_DEFINED | RT_METRIC_DEFINED;
+
+ r6->next = rl6->routes_ipv6;
+ rl6->routes_ipv6 = r6;
+ }
+ else
+ {
+ msg (M_WARN, "ROUTE6: IPv6 route overlaps with IPv6 remote address, but could not determine IPv6 gateway address + interface, expect failure\n" );
+ }
+ }
+
gc_free (&gc);
return ret;
}
if (! (r6->flags & RT_DEFINED) )
return;
+#ifndef WIN32
+ if ( r6->iface != NULL ) /* vpn server special route */
+ {
+ device = r6->iface;
+ if ( !IN6_IS_ADDR_UNSPECIFIED(&r6->gateway) )
+ gateway_needed = true;
+ }
+#endif
+
gc_init (&gc);
argv_init (&argv);
* - in TUN mode we use a special-case link-local address that the tapdrvr
* knows about and will answer ND (neighbor discovery) packets for
*/
- if ( tt->type == DEV_TYPE_TUN )
+ if ( tt->type == DEV_TYPE_TUN && !gateway_needed )
argv_printf_cat( &argv, " %s", "fe80::8" );
else
argv_printf_cat( &argv, " %s", gateway );
if ((r6->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED))
return;
+#ifndef WIN32
+ if ( r6->iface != NULL ) /* vpn server special route */
+ {
+ device = r6->iface;
+ gateway_needed = true;
+ }
+#endif
+
gc_init (&gc);
argv_init (&argv);
*/
void
get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
- struct in6_addr *dest)
+ const struct in6_addr *dest)
{
msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system (windows)");
CLEAR(*rgi6);
void
get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
- struct in6_addr *dest)
+ const struct in6_addr *dest)
{
int nls = -1;
struct rtreq rtreq;
RGI_IFACE_DEFINED )
{
rgi6->flags |= (RGI_ADDR_DEFINED | RGI_ON_LINK);
- rgi6->gateway.addr_ipv6 = *dest;
+ if ( dest )
+ rgi6->gateway.addr_ipv6 = *dest;
}
done:
*/
void
get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
- struct in6_addr *dest)
+ const struct in6_addr *dest)
{
msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system (BSD and OSX)");
CLEAR(*rgi6);
}
void
get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
- struct in6_addr *dest)
+ const struct in6_addr *dest)
{
msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system");
CLEAR(*rgi6);
unsigned int netbits;
struct in6_addr gateway;
int metric;
-};
-
-struct route_ipv6_list {
- unsigned int iflags; /* RL_ flags, see route_list */
-
- unsigned int spec_flags; /* RTSA_ flags, route_special_addr */
- struct in6_addr remote_endpoint_ipv6; /* inside tun */
- struct in6_addr remote_host_ipv6; /* --remote address */
- int default_metric;
-
- unsigned int flags; /* RG_x flags, see route_option_list */
- struct route_ipv6 *routes_ipv6;
- struct gc_arena gc;
+ /* gateway interface */
+# ifdef WIN32
+ DWORD adapter_index; /* interface or ~0 if undefined */
+#else
+ char * iface; /* interface name (null terminated) */
+#endif
};
struct gc_arena gc;
};
+struct route_ipv6_list {
+ unsigned int iflags; /* RL_ flags, see route_list */
+
+ unsigned int spec_flags; /* RTSA_ flags, route_special_addr */
+ struct in6_addr remote_endpoint_ipv6; /* inside tun */
+ struct in6_addr remote_host_ipv6; /* --remote address */
+ int default_metric;
+
+ struct route_ipv6_gateway_info rgi6;
+ unsigned int flags; /* RG_x flags, see route_option_list */
+ struct route_ipv6 *routes_ipv6;
+ struct gc_arena gc;
+};
+
#if P2MP
/* internal OpenVPN route */
struct iroute {
const struct route_ipv6_option_list *opt6,
const char *remote_endpoint,
int default_metric,
+ const struct in6_addr *remote_host,
struct env_set *es);
void route_list_add_vpn_gateway (struct route_list *rl,
void get_default_gateway (struct route_gateway_info *rgi);
void get_default_gateway_ipv6 (struct route_ipv6_gateway_info *rgi,
- struct in6_addr *dest);
+ const struct in6_addr *dest);
void print_default_gateway(const int msglevel,
const struct route_gateway_info *rgi,
const struct route_ipv6_gateway_info *rgi6);