summaryrefslogtreecommitdiff
path: root/src/openvpn/route.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/route.c')
-rw-r--r--src/openvpn/route.c1517
1 files changed, 1039 insertions, 478 deletions
diff --git a/src/openvpn/route.c b/src/openvpn/route.c
index 827bd79..fec12c1 100644
--- a/src/openvpn/route.c
+++ b/src/openvpn/route.c
@@ -42,12 +42,21 @@
#include "manage.h"
#include "win32.h"
#include "options.h"
-#include "win32.h"
#include "memdbg.h"
-#ifdef WIN32
+#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
+#include <linux/rtnetlink.h> /* RTM_GETROUTE etc. */
+#endif
+
+#ifdef _WIN32
+#include "openvpn-msg.h"
+
#define METRIC_NOT_USED ((DWORD)-1)
+static bool add_route_service (const struct route_ipv4 *, const struct tuntap *);
+static bool del_route_service (const struct route_ipv4 *, const struct tuntap *);
+static bool add_route_ipv6_service (const struct route_ipv6 *, const struct tuntap *);
+static bool del_route_ipv6_service (const struct route_ipv6 *, const struct tuntap *);
#endif
static void delete_route (struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es);
@@ -93,76 +102,62 @@ add_bypass_address (struct route_bypass *rb, const in_addr_t a)
}
struct route_option_list *
-new_route_option_list (const int max_routes, struct gc_arena *a)
+new_route_option_list (struct gc_arena *a)
{
struct route_option_list *ret;
- ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_option_list, struct route_option, max_routes, a);
- ret->capacity = max_routes;
+ ALLOC_OBJ_CLEAR_GC (ret, struct route_option_list, a);
+ ret->gc = a;
return ret;
}
struct route_ipv6_option_list *
-new_route_ipv6_option_list (const int max_routes, struct gc_arena *a)
+new_route_ipv6_option_list (struct gc_arena *a)
{
struct route_ipv6_option_list *ret;
- ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_option_list, struct route_ipv6_option, max_routes, a);
- ret->capacity = max_routes;
+ ALLOC_OBJ_CLEAR_GC (ret, struct route_ipv6_option_list, a);
+ ret->gc = a;
return ret;
}
+/*
+ * NOTE: structs are cloned/copied shallow by design.
+ * The routes list from src will stay intact since it is allocated using
+ * the options->gc. The cloned/copied lists will share this common tail
+ * to avoid copying the data around between pulls. Pulled routes use
+ * the c2->gc so they get freed immediately after a reconnect.
+ */
struct route_option_list *
clone_route_option_list (const struct route_option_list *src, struct gc_arena *a)
{
- const size_t rl_size = array_mult_safe (sizeof(struct route_option), src->capacity, sizeof(struct route_option_list));
- struct route_option_list *ret = gc_malloc (rl_size, false, a);
- memcpy (ret, src, rl_size);
+ struct route_option_list *ret;
+ ALLOC_OBJ_GC (ret, struct route_option_list, a);
+ *ret = *src;
return ret;
}
struct route_ipv6_option_list *
clone_route_ipv6_option_list (const struct route_ipv6_option_list *src, struct gc_arena *a)
{
- const size_t rl_size = array_mult_safe (sizeof(struct route_ipv6_option), src->capacity, sizeof(struct route_ipv6_option_list));
- struct route_ipv6_option_list *ret = gc_malloc (rl_size, false, a);
- memcpy (ret, src, rl_size);
+ struct route_ipv6_option_list *ret;
+ ALLOC_OBJ_GC (ret, struct route_ipv6_option_list, a);
+ *ret = *src;
return ret;
}
void
-copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src)
+copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src, struct gc_arena *a)
{
- const size_t src_size = array_mult_safe (sizeof(struct route_option), src->capacity, sizeof(struct route_option_list));
- if (src->capacity > dest->capacity)
- msg (M_FATAL, PACKAGE_NAME " ROUTE: (copy) number of route options in src (%d) is greater than route list capacity in dest (%d)", src->capacity, dest->capacity);
- memcpy (dest, src, src_size);
+ *dest = *src;
+ dest->gc = a;
}
void
copy_route_ipv6_option_list (struct route_ipv6_option_list *dest,
- const struct route_ipv6_option_list *src)
+ const struct route_ipv6_option_list *src,
+ struct gc_arena *a)
{
- const size_t src_size = array_mult_safe (sizeof(struct route_ipv6_option), src->capacity, sizeof(struct route_ipv6_option_list));
- if (src->capacity > dest->capacity)
- msg (M_FATAL, PACKAGE_NAME " ROUTE: (copy) number of route options in src (%d) is greater than route list capacity in dest (%d)", src->capacity, dest->capacity);
- memcpy (dest, src, src_size);
-}
-
-struct route_list *
-new_route_list (const int max_routes, struct gc_arena *a)
-{
- struct route_list *ret;
- ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_list, struct route_ipv4, max_routes, a);
- ret->capacity = max_routes;
- return ret;
-}
-
-struct route_ipv6_list *
-new_route_ipv6_list (const int max_routes, struct gc_arena *a)
-{
- struct route_ipv6_list *ret;
- ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_list, struct route_ipv6, max_routes, a);
- ret->capacity = max_routes;
- return ret;
+ *dest = *src;
+ dest->gc = a;
}
static const char *
@@ -292,15 +287,15 @@ init_route (struct route_ipv4 *r,
/* get_special_addr replaces specialaddr with a special ip addr
like gw. getaddrinfo is called to convert a a addrinfo struct */
- if(get_special_addr (rl, ro->network, &special.s_addr, &status))
+ if(get_special_addr (rl, ro->network, (in_addr_t *) &special.s_addr, &status))
{
special.s_addr = htonl(special.s_addr);
- ret = openvpn_getaddrinfo(0, inet_ntoa(special), 0, NULL,
+ ret = openvpn_getaddrinfo(0, inet_ntoa(special), NULL, 0, NULL,
AF_INET, network_list);
}
else
ret = openvpn_getaddrinfo(GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL,
- ro->network, 0, NULL, AF_INET, network_list);
+ ro->network, NULL, 0, NULL, AF_INET, network_list);
status = (ret == 0);
@@ -389,7 +384,7 @@ init_route_ipv6 (struct route_ipv6 *r6,
const struct route_ipv6_option *r6o,
const struct route_ipv6_list *rl6 )
{
- r6->defined = false;
+ CLEAR (*r6);
if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, M_WARN ))
goto fail;
@@ -402,7 +397,7 @@ init_route_ipv6 (struct route_ipv6 *r6,
msg( M_WARN, PACKAGE_NAME "ROUTE6: cannot parse gateway spec '%s'", r6o->gateway );
}
}
- else if (rl6->remote_endpoint_defined)
+ else if (rl6->spec_flags & RTSA_REMOTE_ENDPOINT)
{
r6->gateway = rl6->remote_endpoint_ipv6;
}
@@ -414,7 +409,6 @@ init_route_ipv6 (struct route_ipv6 *r6,
/* metric */
- r6->metric_defined = false;
r6->metric = -1;
if (is_route_parm_defined (r6o->metric))
{
@@ -426,22 +420,21 @@ init_route_ipv6 (struct route_ipv6 *r6,
r6o->metric);
goto fail;
}
- r6->metric_defined = true;
+ r6->flags |= RT_METRIC_DEFINED;
}
- else if (rl6->default_metric_defined)
+ else if (rl6->spec_flags & RTSA_DEFAULT_METRIC)
{
r6->metric = rl6->default_metric;
- r6->metric_defined = true;
+ r6->flags |= RT_METRIC_DEFINED;
}
- r6->defined = true;
+ r6->flags |= RT_DEFINED;
return true;
fail:
msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
r6o->prefix);
- r6->defined = false;
return false;
}
@@ -453,15 +446,14 @@ add_route_to_option_list (struct route_option_list *l,
const char *metric)
{
struct route_option *ro;
- if (l->n >= l->capacity)
- msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d routes -- please increase the max-routes option in the client configuration file",
- l->capacity);
- ro = &l->routes[l->n];
+ ALLOC_OBJ_GC (ro, struct route_option, l->gc);
ro->network = network;
ro->netmask = netmask;
ro->gateway = gateway;
ro->metric = metric;
- ++l->n;
+ ro->next = l->routes;
+ l->routes = ro;
+
}
void
@@ -471,32 +463,26 @@ add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
const char *metric)
{
struct route_ipv6_option *ro;
- if (l->n >= l->capacity)
- msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d IPv6 routes -- please increase the max-routes option in the client configuration file",
- l->capacity);
- ro = &l->routes_ipv6[l->n];
+ ALLOC_OBJ_GC (ro, struct route_ipv6_option, l->gc);
ro->prefix = prefix;
ro->gateway = gateway;
ro->metric = metric;
- ++l->n;
+ ro->next = l->routes_ipv6;
+ l->routes_ipv6 = ro;
}
void
clear_route_list (struct route_list *rl)
{
- const int capacity = rl->capacity;
- const size_t rl_size = array_mult_safe (sizeof(struct route_ipv4), capacity, sizeof(struct route_list));
- memset(rl, 0, rl_size);
- rl->capacity = capacity;
+ gc_free (&rl->gc);
+ CLEAR (*rl);
}
void
clear_route_ipv6_list (struct route_ipv6_list *rl6)
{
- const int capacity = rl6->capacity;
- const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list));
- memset(rl6, 0, rl6_size);
- rl6->capacity = capacity;
+ gc_free (&rl6->gc);
+ CLEAR (*rl6);
}
void
@@ -517,22 +503,27 @@ add_block_local_item (struct route_list *rl,
{
const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED);
if ((rl->rgi.flags & rgi_needed) == rgi_needed
- && rl->rgi.gateway.netmask < 0xFFFFFFFF
- && (rl->n)+2 <= rl->capacity)
+ && rl->rgi.gateway.netmask < 0xFFFFFFFF)
{
- struct route_ipv4 r;
+ struct route_ipv4 *r1, *r2;
unsigned int l2;
+ ALLOC_OBJ_GC (r1, struct route_ipv4, &rl->gc);
+ ALLOC_OBJ_GC (r2, struct route_ipv4, &rl->gc);
+
/* split a route into two smaller blocking routes, and direct them to target */
- CLEAR(r);
- r.flags = RT_DEFINED;
- r.gateway = target;
- r.network = gateway->addr & gateway->netmask;
l2 = ((~gateway->netmask)+1)>>1;
- r.netmask = ~(l2-1);
- rl->routes[rl->n++] = r;
- r.network += l2;
- rl->routes[rl->n++] = r;
+ r1->flags = RT_DEFINED;
+ r1->gateway = target;
+ r1->network = gateway->addr & gateway->netmask;
+ r1->netmask = ~(l2-1);
+ r1->next = rl->routes;
+ rl->routes = r1;
+
+ *r2 = *r1;
+ r2->network += l2;
+ r2->next = rl->routes;
+ rl->routes = r2;
}
}
@@ -547,8 +538,10 @@ add_block_local (struct route_list *rl)
{
size_t i;
+#ifndef TARGET_ANDROID
/* add bypass for gateway addr */
add_bypass_address (&rl->spec.bypass, rl->rgi.gateway.addr);
+#endif
/* block access to local subnet */
add_block_local_item (rl, &rl->rgi.gateway, rl->spec.remote_endpoint);
@@ -597,7 +590,7 @@ init_route_list (struct route_list *rl,
{
setenv_route_addr (es, "net_gateway", rl->rgi.gateway.addr, -1);
#if defined(ENABLE_DEBUG) && !defined(ENABLE_SMALL)
- print_default_gateway (D_ROUTE, &rl->rgi);
+ print_default_gateway (D_ROUTE, &rl->rgi, NULL);
#endif
}
else
@@ -644,72 +637,108 @@ init_route_list (struct route_list *rl,
/* parse the routes from opt to rl */
{
- int i = 0;
- int j = rl->n;
- bool warned = false;
- for (i = 0; i < opt->n; ++i)
+ struct route_option *ro;
+ for (ro = opt->routes; ro; ro = ro->next)
{
struct addrinfo* netlist = NULL;
struct route_ipv4 r;
- if (!init_route (&r,
- &netlist,
- &opt->routes[i],
- rl))
+ if (!init_route (&r, &netlist, ro, rl))
ret = false;
else
{
struct addrinfo* curele;
for (curele = netlist; curele; curele = curele->ai_next)
{
- if (j < rl->capacity)
- {
- r.network = ntohl(((struct sockaddr_in*)(curele)->ai_addr)->sin_addr.s_addr);
- rl->routes[j++] = r;
- }
- else
- {
- if (!warned)
- {
- msg (M_WARN, PACKAGE_NAME " ROUTE: routes dropped because number of expanded routes is greater than route list capacity (%d)", rl->capacity);
- warned = true;
- }
- }
+ struct route_ipv4 *new;
+ ALLOC_OBJ_GC (new, struct route_ipv4, &rl->gc);
+ *new = r;
+ new->network = ntohl (((struct sockaddr_in*)curele->ai_addr)->sin_addr.s_addr);
+ new->next = rl->routes;
+ rl->routes = new;
}
}
if (netlist)
- freeaddrinfo(netlist);
+ gc_addspecial(netlist, &gc_freeaddrinfo_callback, &gc);
}
- rl->n = j;
}
gc_free (&gc);
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->default_metric_defined = true;
+ 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");
}
@@ -719,36 +748,81 @@ init_route_ipv6_list (struct route_ipv6_list *rl6,
if ( inet_pton( AF_INET6, remote_endpoint,
&rl6->remote_endpoint_ipv6) == 1 )
{
- rl6->remote_endpoint_defined = true;
+ rl6->spec_flags |= RTSA_REMOTE_ENDPOINT;
}
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;
}
}
- else
- rl6->remote_endpoint_defined = false;
+ /* parse the routes from opt6 to rl6
+ * discovering potential overlaps with remote_host_ipv6 in the process
+ */
+ need_remote_ipv6_route = false;
- if (!(opt6->n >= 0 && opt6->n <= rl6->capacity))
- msg (M_FATAL, PACKAGE_NAME " ROUTE6: (init) number of route options (%d) is greater than route list capacity (%d)", opt6->n, rl6->capacity);
-
- /* parse the routes from opt to rl6 */
{
- int i, j = 0;
- for (i = 0; i < opt6->n; ++i)
+ struct route_ipv6_option *ro6;
+ for (ro6 = opt6->routes_ipv6; ro6; ro6 = ro6->next)
{
- if (!init_route_ipv6 (&rl6->routes_ipv6[j],
- &opt6->routes_ipv6[i],
- rl6 ))
+ struct route_ipv6 *r6;
+ ALLOC_OBJ_GC (r6, struct route_ipv6, &rl6->gc);
+ if (!init_route_ipv6 (r6, ro6, rl6))
ret = false;
else
- ++j;
+ {
+ r6->next = rl6->routes_ipv6;
+ rl6->routes_ipv6 = r6;
+
+#ifndef TARGET_ANDROID
+ /* On Android the VPNService protect function call will take of
+ * avoiding routing loops, so ignore this part and let
+ * need_remote_ipv6_route always evaluate to false
+ */
+ 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));
+ }
+#endif
+ }
}
- rl6->n = j;
}
+ /* 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;
}
@@ -854,6 +928,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
}
else
{
+#ifndef TARGET_ANDROID
bool local = BOOL_CAST(rl->flags & RG_LOCAL);
if (rl->flags & RG_AUTO_LOCAL) {
const int tla = rl->spec.remote_host_local;
@@ -886,6 +961,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
dmsg (D_ROUTE, "ROUTE remote_host protocol differs from tunneled");
}
}
+#endif
/* route DHCP/DNS server traffic through original default gateway */
add_bypass_routes (&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es);
@@ -1015,22 +1091,23 @@ add_routes (struct route_list *rl, struct route_ipv6_list *rl6, const struct tun
redirect_default_route_to_vpn (rl, tt, flags, es);
if ( rl && !(rl->iflags & RL_ROUTES_ADDED) )
{
- int i;
+ struct route_ipv4 *r;
#ifdef ENABLE_MANAGEMENT
- if (management && rl->n)
+ if (management && rl->routes)
{
management_set_state (management,
OPENVPN_STATE_ADD_ROUTES,
NULL,
- 0,
- 0);
+ NULL,
+ NULL,
+ NULL,
+ NULL);
}
#endif
-
- for (i = 0; i < rl->n; ++i)
+
+ for (r = rl->routes; r; r = r->next)
{
- struct route_ipv4 *r = &rl->routes[i];
check_subnet_conflict (r->network, r->netmask, "route");
if (flags & ROUTE_DELETE_FIRST)
delete_route (r, tt, flags, &rl->rgi, es);
@@ -1038,18 +1115,16 @@ add_routes (struct route_list *rl, struct route_ipv6_list *rl6, const struct tun
}
rl->iflags |= RL_ROUTES_ADDED;
}
- if (rl6 && !rl6->routes_added)
+ if (rl6 && !(rl6->iflags & RL_ROUTES_ADDED) )
{
- int i;
-
- for (i = 0; i < rl6->n; ++i)
+ struct route_ipv6 *r;
+ for (r = rl6->routes_ipv6; r; r = r->next)
{
- struct route_ipv6 *r = &rl6->routes_ipv6[i];
if (flags & ROUTE_DELETE_FIRST)
delete_route_ipv6 (r, tt, flags, es);
add_route_ipv6 (r, tt, flags, es);
}
- rl6->routes_added = true;
+ rl6->iflags |= RL_ROUTES_ADDED;
}
}
@@ -1059,10 +1134,9 @@ delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
{
if ( rl && rl->iflags & RL_ROUTES_ADDED )
{
- int i;
- for (i = rl->n - 1; i >= 0; --i)
+ struct route_ipv4 *r;
+ for (r = rl->routes; r; r = r->next)
{
- struct route_ipv4 * r = &rl->routes[i];
delete_route (r, tt, flags, &rl->rgi, es);
}
rl->iflags &= ~RL_ROUTES_ADDED;
@@ -1075,15 +1149,14 @@ delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
clear_route_list (rl);
}
- if ( rl6 && rl6->routes_added )
+ if ( rl6 && (rl6->iflags & RL_ROUTES_ADDED) )
{
- int i;
- for (i = rl6->n - 1; i >= 0; --i)
+ struct route_ipv6 *r6;
+ for (r6 = rl6->routes_ipv6; r6; r6 = r6->next)
{
- const struct route_ipv6 *r6 = &rl6->routes_ipv6[i];
delete_route_ipv6 (r6, tt, flags, es);
}
- rl6->routes_added = false;
+ rl6->iflags &= ~RL_ROUTES_ADDED;
}
if ( rl6 )
@@ -1098,7 +1171,7 @@ static const char *
show_opt (const char *option)
{
if (!option)
- return "nil";
+ return "default (not set)";
else
return option;
}
@@ -1117,19 +1190,21 @@ void
print_route_options (const struct route_option_list *rol,
int level)
{
- int i;
+ struct route_option *ro;
if (rol->flags & RG_ENABLE)
msg (level, " [redirect_default_gateway local=%d]",
(rol->flags & RG_LOCAL) != 0);
- for (i = 0; i < rol->n; ++i)
- print_route_option (&rol->routes[i], level);
+ for (ro = rol->routes; ro; ro = ro->next)
+ print_route_option (ro, level);
}
void
-print_default_gateway(const int msglevel, const struct route_gateway_info *rgi)
+print_default_gateway(const int msglevel,
+ const struct route_gateway_info *rgi,
+ const struct route_ipv6_gateway_info *rgi6)
{
struct gc_arena gc = gc_new ();
- if (rgi->flags & RGI_ADDR_DEFINED)
+ if (rgi && (rgi->flags & RGI_ADDR_DEFINED))
{
struct buffer out = alloc_buf_gc (256, &gc);
buf_printf (&out, "ROUTE_GATEWAY");
@@ -1139,7 +1214,7 @@ print_default_gateway(const int msglevel, const struct route_gateway_info *rgi)
buf_printf (&out, " %s", print_in_addr_t (rgi->gateway.addr, 0, &gc));
if (rgi->flags & RGI_NETMASK_DEFINED)
buf_printf (&out, "/%s", print_in_addr_t (rgi->gateway.netmask, 0, &gc));
-#ifdef WIN32
+#ifdef _WIN32
if (rgi->flags & RGI_IFACE_DEFINED)
buf_printf (&out, " I=%u", (unsigned int)rgi->adapter_index);
#else
@@ -1150,6 +1225,27 @@ print_default_gateway(const int msglevel, const struct route_gateway_info *rgi)
buf_printf (&out, " HWADDR=%s", format_hex_ex (rgi->hwaddr, 6, 0, 1, ":", &gc));
msg (msglevel, "%s", BSTR (&out));
}
+
+ if (rgi6 && (rgi6->flags & RGI_ADDR_DEFINED))
+ {
+ struct buffer out = alloc_buf_gc (256, &gc);
+ buf_printf (&out, "ROUTE6_GATEWAY");
+ buf_printf (&out, " %s", print_in6_addr (rgi6->gateway.addr_ipv6, 0, &gc));
+ if (rgi6->flags & RGI_ON_LINK)
+ buf_printf (&out, " ON_LINK");
+ if (rgi6->flags & RGI_NETMASK_DEFINED)
+ buf_printf (&out, "/%d", rgi6->gateway.netbits_ipv6);
+#ifdef _WIN32
+ if (rgi6->flags & RGI_IFACE_DEFINED)
+ buf_printf (&out, " I=%u", (unsigned int)rgi6->adapter_index);
+#else
+ if (rgi6->flags & RGI_IFACE_DEFINED)
+ buf_printf (&out, " IFACE=%s", rgi6->iface);
+#endif
+ if (rgi6->flags & RGI_HWADDR_DEFINED)
+ buf_printf (&out, " HWADDR=%s", format_hex_ex (rgi6->hwaddr, 6, 0, 1, ":", &gc));
+ msg (msglevel, "%s", BSTR (&out));
+ }
gc_free (&gc);
}
@@ -1167,9 +1263,9 @@ print_route (const struct route_ipv4 *r, int level)
void
print_routes (const struct route_list *rl, int level)
{
- int i;
- for (i = 0; i < rl->n; ++i)
- print_route (&rl->routes[i], level);
+ struct route_ipv4 *r;
+ for (r = rl->routes; r; r = r->next)
+ print_route (r, level);
}
static void
@@ -1195,16 +1291,17 @@ setenv_route (struct env_set *es, const struct route_ipv4 *r, int i)
void
setenv_routes (struct env_set *es, const struct route_list *rl)
{
- int i;
- for (i = 0; i < rl->n; ++i)
- setenv_route (es, &rl->routes[i], i + 1);
+ int i = 1;
+ struct route_ipv4 *r;
+ for (r = rl->routes; r; r = r->next)
+ setenv_route (es, r, i++);
}
static void
setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i)
{
struct gc_arena gc = gc_new ();
- if (r6->defined)
+ if (r6->flags & RT_DEFINED)
{
struct buffer name1 = alloc_buf_gc( 256, &gc );
struct buffer val = alloc_buf_gc( 256, &gc );
@@ -1223,9 +1320,10 @@ setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i)
void
setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6)
{
- int i;
- for (i = 0; i < rl6->n; ++i)
- setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1);
+ int i = 1;
+ struct route_ipv6 *r6;
+ for (r6 = rl6->routes_ipv6; r6; r6 = r6->next)
+ setenv_route_ipv6 (es, r6, i++);
}
/*
@@ -1297,7 +1395,7 @@ add_route (struct route_ipv4 *r,
const struct env_set *es)
{
struct gc_arena gc;
- struct argv argv;
+ struct argv argv = argv_new ();
const char *network;
const char *netmask;
const char *gateway;
@@ -1308,7 +1406,6 @@ add_route (struct route_ipv4 *r,
return;
gc_init (&gc);
- argv_init (&argv);
network = print_in_addr_t (r->network, 0, &gc);
netmask = print_in_addr_t (r->netmask, 0, &gc);
@@ -1323,7 +1420,7 @@ add_route (struct route_ipv4 *r,
argv_printf (&argv, "%s route add %s/%d",
iproute_path,
network,
- count_netmask_bits(netmask));
+ netmask_to_netbits2(r->netmask));
if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "metric %d", r->metric);
@@ -1348,7 +1445,16 @@ add_route (struct route_ipv4 *r,
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");
-#elif defined (WIN32)
+#elif defined (TARGET_ANDROID)
+ struct buffer out = alloc_buf_gc (128, &gc);
+
+ if (rgi)
+ buf_printf (&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface);
+ else
+ buf_printf (&out, "%s %s %s", network, netmask, gateway);
+ management_android_control (management, "ROUTE", buf_bptr(&out));
+
+#elif defined (_WIN32)
{
DWORD ai = TUN_ADAPTER_INDEX_INVALID;
argv_printf (&argv, "%s%sc ADD %s MASK %s %s",
@@ -1367,7 +1473,12 @@ add_route (struct route_ipv4 *r,
argv_msg (D_ROUTE, &argv);
- if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
+ if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_SERVICE)
+ {
+ status = add_route_service (r, tt);
+ msg (D_ROUTE, "Route addition via service %s", status ? "succeeded" : "failed");
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
{
status = add_route_ipapi (r, tt, ai);
msg (D_ROUTE, "Route addition via IPAPI %s", status ? "succeeded" : "failed");
@@ -1511,6 +1622,17 @@ add_route (struct route_ipv4 *r,
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
+#elif defined(TARGET_AIX)
+
+ {
+ int netbits = netmask_to_netbits2(r->netmask);
+ argv_printf (&argv, "%s add -net %s/%d %s",
+ ROUTE_PATH,
+ network, netbits, gateway);
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: AIX route add command failed");
+ }
+
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script");
#endif
@@ -1525,33 +1647,30 @@ add_route (struct route_ipv4 *r,
}
-static const char *
-print_in6_addr_netbits_only( struct in6_addr network_copy, int netbits,
- struct gc_arena * gc)
+static void
+route_ipv6_clear_host_bits( struct route_ipv6 *r6 )
{
/* clear host bit parts of route
* (needed if routes are specified improperly, or if we need to
* explicitely setup/clear the "connected" network routes on some OSes)
*/
int byte = 15;
- int bits_to_clear = 128 - netbits;
+ int bits_to_clear = 128 - r6->netbits;
while( byte >= 0 && bits_to_clear > 0 )
{
if ( bits_to_clear >= 8 )
- { network_copy.s6_addr[byte--] = 0; bits_to_clear -= 8; }
+ { r6->network.s6_addr[byte--] = 0; bits_to_clear -= 8; }
else
- { network_copy.s6_addr[byte--] &= (0xff << bits_to_clear); bits_to_clear = 0; }
+ { r6->network.s6_addr[byte--] &= (0xff << bits_to_clear); bits_to_clear = 0; }
}
-
- return print_in6_addr( network_copy, 0, gc);
}
void
add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
struct gc_arena gc;
- struct argv argv;
+ struct argv argv = argv_new ();
const char *network;
const char *gateway;
@@ -1560,19 +1679,48 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla
bool gateway_needed = false;
- if (!r6->defined)
+ 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);
- network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc);
+ route_ipv6_clear_host_bits (r6);
+
+ network = print_in6_addr( r6->network, 0, &gc);
gateway = print_in6_addr( r6->gateway, 0, &gc);
- if ( !tt->ipv6 )
+#if defined(TARGET_DARWIN) || \
+ defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) || \
+ defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
+
+ /* the BSD platforms cannot specify gateway and interface independently,
+ * but for link-local destinations, we MUST specify the interface, so
+ * we build a combined "$gateway%$interface" gateway string
+ */
+ if ( r6->iface != NULL && gateway_needed &&
+ IN6_IS_ADDR_LINKLOCAL(&r6->gateway) ) /* fe80::...%intf */
{
- msg( M_INFO, "add_route_ipv6(): not adding %s/%d, no IPv6 on if %s",
- network, r6->netbits, device );
+ int len = strlen(gateway) + 1 + strlen(r6->iface)+1;
+ char * tmp = gc_malloc( len, true, &gc );
+ snprintf( tmp, len, "%s%%%s", gateway, r6->iface );
+ gateway = tmp;
+ }
+#endif
+
+ if (!tt->did_ifconfig_ipv6_setup)
+ {
+ msg( M_INFO, "add_route_ipv6(): not adding %s/%d: "
+ "no IPv6 address been configured on interface %s",
+ network, r6->netbits, device);
return;
}
@@ -1591,7 +1739,7 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla
* gateway unless the route is to be an on-link network
*/
if ( tt->type == DEV_TYPE_TAP &&
- !(r6->metric_defined && r6->metric == 0 ) )
+ !( (r6->flags & RT_METRIC_DEFINED) && r6->metric == 0 ) )
{
gateway_needed = true;
}
@@ -1605,7 +1753,7 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla
device);
if (gateway_needed)
argv_printf_cat (&argv, "via %s", gateway);
- if (r6->metric_defined && r6->metric > 0 )
+ if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0 )
argv_printf_cat (&argv, " metric %d", r6->metric);
#else
@@ -1616,70 +1764,95 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla
device);
if (gateway_needed)
argv_printf_cat (&argv, "gw %s", gateway);
- if (r6->metric_defined && r6->metric > 0 )
+ if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0 )
argv_printf_cat (&argv, " metric %d", r6->metric);
#endif /*ENABLE_IPROUTE*/
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed");
-#elif defined (WIN32)
+#elif defined (TARGET_ANDROID)
+ struct buffer out = alloc_buf_gc (64, &gc);
+
+ buf_printf (&out, "%s/%d %s", network, r6->netbits, device);
+
+ management_android_control (management, "ROUTE6", buf_bptr(&out));
+
+#elif defined (_WIN32)
- if (win32_version_info() != WIN_XP)
+ if (tt->options.msg_channel)
+ status = add_route_ipv6_service (r6, tt);
+ else
{
struct buffer out = alloc_buf_gc (64, &gc);
- buf_printf (&out, "interface=%d", tt->adapter_index );
+ if ( r6->adapter_index ) /* vpn server special route */
+ {
+ buf_printf (&out, "interface=%d", r6->adapter_index );
+ gateway_needed = true;
+ }
+ else
+ {
+ buf_printf (&out, "interface=%d", tt->adapter_index );
+ }
device = buf_bptr(&out);
- }
- /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */
- argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s",
- get_win_sys_path(),
- NETSH_PATH_SUFFIX,
- network,
- r6->netbits,
- device);
-
- /* next-hop depends on TUN or TAP mode:
- * - in TAP mode, we use the "real" next-hop
- * - 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 )
- argv_printf_cat( &argv, " %s", "fe80::8" );
- else
- argv_printf_cat( &argv, " %s", gateway );
+ /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */
+ argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ network,
+ r6->netbits,
+ device);
+
+ /* next-hop depends on TUN or TAP mode:
+ * - in TAP mode, we use the "real" next-hop
+ * - 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 && !gateway_needed )
+ argv_printf_cat( &argv, " %s", "fe80::8" );
+ else if ( !IN6_IS_ADDR_UNSPECIFIED(&r6->gateway) )
+ argv_printf_cat( &argv, " %s", gateway );
#if 0
- if (r->metric_defined)
- argv_printf_cat (&argv, " METRIC %d", r->metric);
+ if (r6->flags & RT_METRIC_DEFINED)
+ argv_printf_cat (&argv, " METRIC %d", r->metric);
#endif
- /* in some versions of Windows, routes are persistent across reboots by
- * default, unless "store=active" is set (pointed out by Tony Lim, thanks)
- */
- argv_printf_cat( &argv, " store=active" );
+ /* in some versions of Windows, routes are persistent across reboots by
+ * default, unless "store=active" is set (pointed out by Tony Lim, thanks)
+ */
+ argv_printf_cat( &argv, " store=active" );
- argv_msg (D_ROUTE, &argv);
+ argv_msg (D_ROUTE, &argv);
- netcmd_semaphore_lock ();
- status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed");
- netcmd_semaphore_release ();
+ netcmd_semaphore_lock ();
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed");
+ netcmd_semaphore_release ();
+ }
#elif defined (TARGET_SOLARIS)
/* example: route add -inet6 2001:db8::/32 somegateway 0 */
- /* for some weird reason, this does not work for me unless I set
+ /* for some reason, routes to tun/tap do not work for me unless I set
* "metric 0" - otherwise, the routes will be nicely installed, but
- * packets will just disappear somewhere. So we use "0" now...
+ * packets will just disappear somewhere. So we always use "0" now,
+ * unless the route points to "gateway on other interface"...
+ *
+ * (Note: OpenSolaris can not specify host%interface gateways, so we just
+ * use the GW addresses - it seems to still work for fe80:: addresses,
+ * however this is done internally. NUD maybe?)
*/
-
- argv_printf (&argv, "%s add -inet6 %s/%d %s 0",
+ argv_printf (&argv, "%s add -inet6 %s/%d %s",
ROUTE_PATH,
network,
r6->netbits,
gateway );
+ /* on tun/tap, not "elsewhere"? -> metric 0 */
+ if ( !r6->iface )
+ argv_printf_cat (&argv, "0");
+
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add -inet6 command failed");
@@ -1730,11 +1903,22 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route add -inet6 command failed");
+#elif defined(TARGET_AIX)
+
+ argv_printf (&argv, "%s add -inet6 %s/%d %s",
+ ROUTE_PATH,
+ network, r6->netbits, gateway);
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: AIX route add command failed");
+
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-up script");
#endif
- r6->defined = status;
+ if (status)
+ r6->flags |= RT_ADDED;
+ else
+ r6->flags &= ~RT_ADDED;
argv_reset (&argv);
gc_free (&gc);
}
@@ -1747,7 +1931,7 @@ delete_route (struct route_ipv4 *r,
const struct env_set *es)
{
struct gc_arena gc;
- struct argv argv;
+ struct argv argv = argv_new ();
const char *network;
const char *netmask;
const char *gateway;
@@ -1757,7 +1941,6 @@ delete_route (struct route_ipv4 *r,
return;
gc_init (&gc);
- argv_init (&argv);
network = print_in_addr_t (r->network, 0, &gc);
netmask = print_in_addr_t (r->netmask, 0, &gc);
@@ -1772,7 +1955,7 @@ delete_route (struct route_ipv4 *r,
argv_printf (&argv, "%s route del %s/%d",
iproute_path,
network,
- count_netmask_bits(netmask));
+ netmask_to_netbits2(r->netmask));
#else
argv_printf (&argv, "%s del -net %s netmask %s",
ROUTE_PATH,
@@ -1784,7 +1967,7 @@ delete_route (struct route_ipv4 *r,
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed");
-#elif defined (WIN32)
+#elif defined (_WIN32)
argv_printf (&argv, "%s%sc DELETE %s MASK %s %s",
get_win_sys_path(),
@@ -1795,7 +1978,12 @@ delete_route (struct route_ipv4 *r,
argv_msg (D_ROUTE, &argv);
- if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
+ if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_SERVICE)
+ {
+ const bool status = del_route_service (r, tt);
+ msg (D_ROUTE, "Route deletion via service %s", status ? "succeeded" : "failed");
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
{
const bool status = del_route_ipapi (r, tt);
msg (D_ROUTE, "Route deletion via IPAPI %s", status ? "succeeded" : "failed");
@@ -1889,6 +2077,20 @@ delete_route (struct route_ipv4 *r,
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
+#elif defined(TARGET_ANDROID)
+ msg (M_NONFATAL, "Sorry, deleting routes on Android is not possible. The VpnService API allows routes to be set on connect only.");
+
+#elif defined(TARGET_AIX)
+
+ {
+ int netbits = netmask_to_netbits2(r->netmask);
+ argv_printf (&argv, "%s delete -net %s/%d %s",
+ ROUTE_PATH,
+ network, netbits, gateway);
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: AIX route delete command failed");
+ }
+
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script");
#endif
@@ -1903,27 +2105,45 @@ void
delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
struct gc_arena gc;
- struct argv argv;
+ struct argv argv = argv_new ();
const char *network;
const char *gateway;
const char *device = tt->actual_name;
bool gateway_needed = false;
- if (!r6->defined)
+ 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);
- network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc);
+ network = print_in6_addr( r6->network, 0, &gc);
gateway = print_in6_addr( r6->gateway, 0, &gc);
- if ( !tt->ipv6 )
+#if defined(TARGET_DARWIN) || \
+ defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) || \
+ defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
+
+ /* the BSD platforms cannot specify gateway and interface independently,
+ * but for link-local destinations, we MUST specify the interface, so
+ * we build a combined "$gateway%$interface" gateway string
+ */
+ if ( r6->iface != NULL && gateway_needed &&
+ IN6_IS_ADDR_LINKLOCAL(&r6->gateway) ) /* fe80::...%intf */
{
- msg( M_INFO, "delete_route_ipv6(): not deleting %s/%d, no IPv6 on if %s",
- network, r6->netbits, device );
- return;
+ int len = strlen(gateway) + 1 + strlen(r6->iface)+1;
+ char * tmp = gc_malloc( len, true, &gc );
+ snprintf( tmp, len, "%s%%%s", gateway, r6->iface );
+ gateway = tmp;
}
+#endif
msg( M_INFO, "delete_route_ipv6(%s/%d)", network, r6->netbits );
@@ -1931,7 +2151,7 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne
* delete, otherwise some OSes will refuse to delete the route
*/
if ( tt->type == DEV_TYPE_TAP &&
- !(r6->metric_defined && r6->metric == 0 ) )
+ !( (r6->flags & RT_METRIC_DEFINED) && r6->metric == 0 ) )
{
gateway_needed = true;
}
@@ -1954,61 +2174,70 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne
device);
if (gateway_needed)
argv_printf_cat (&argv, "gw %s", gateway);
- if (r6->metric_defined && r6->metric > 0 )
+ if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0 )
argv_printf_cat (&argv, " metric %d", r6->metric);
#endif /*ENABLE_IPROUTE*/
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed");
-#elif defined (WIN32)
+#elif defined (_WIN32)
- if (win32_version_info() != WIN_XP)
+ if (tt->options.msg_channel)
+ del_route_ipv6_service (r6, tt);
+ else
{
struct buffer out = alloc_buf_gc (64, &gc);
- buf_printf (&out, "interface=%d", tt->adapter_index );
+ if ( r6->adapter_index ) /* vpn server special route */
+ {
+ buf_printf (&out, "interface=%d", r6->adapter_index );
+ gateway_needed = true;
+ }
+ else
+ {
+ buf_printf (&out, "interface=%d", tt->adapter_index );
+ }
device = buf_bptr(&out);
- }
- /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */
- argv_printf (&argv, "%s%sc interface ipv6 delete route %s/%d %s",
- get_win_sys_path(),
- NETSH_PATH_SUFFIX,
- network,
- r6->netbits,
- device);
-
- /* next-hop depends on TUN or TAP mode:
- * - in TAP mode, we use the "real" next-hop
- * - in TUN mode we use a special-case link-local address that the tapdrvr
- * knows about and will answer ND (neighbor discovery) packets for
- * (and "route deletion without specifying next-hop" does not work...)
- */
- if ( tt->type == DEV_TYPE_TUN )
- argv_printf_cat( &argv, " %s", "fe80::8" );
- else
- argv_printf_cat( &argv, " %s", gateway );
+ /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */
+ argv_printf (&argv, "%s%sc interface ipv6 delete route %s/%d %s",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ network,
+ r6->netbits,
+ device);
+
+ /* next-hop depends on TUN or TAP mode:
+ * - in TAP mode, we use the "real" next-hop
+ * - in TUN mode we use a special-case link-local address that the tapdrvr
+ * knows about and will answer ND (neighbor discovery) packets for
+ * (and "route deletion without specifying next-hop" does not work...)
+ */
+ if ( tt->type == DEV_TYPE_TUN && !gateway_needed )
+ argv_printf_cat( &argv, " %s", "fe80::8" );
+ else if ( !IN6_IS_ADDR_UNSPECIFIED(&r6->gateway) )
+ argv_printf_cat( &argv, " %s", gateway );
#if 0
- if (r->metric_defined)
- argv_printf_cat (&argv, "METRIC %d", r->metric);
+ if (r6->flags & RT_METRIC_DEFINED)
+ argv_printf_cat (&argv, "METRIC %d", r->metric);
#endif
- /* Windows XP to 7 "just delete" routes, wherever they came from, but
- * in Windows 8(.1?), if you create them with "store=active", this is
- * how you should delete them as well (pointed out by Cedric Tabary)
- */
- argv_printf_cat( &argv, " store=active" );
+ /* Windows XP to 7 "just delete" routes, wherever they came from, but
+ * in Windows 8(.1?), if you create them with "store=active", this is
+ * how you should delete them as well (pointed out by Cedric Tabary)
+ */
+ argv_printf_cat( &argv, " store=active" );
- argv_msg (D_ROUTE, &argv);
+ argv_msg (D_ROUTE, &argv);
- netcmd_semaphore_lock ();
- openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete ipv6 command failed");
- netcmd_semaphore_release ();
+ netcmd_semaphore_lock ();
+ openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete ipv6 command failed");
+ netcmd_semaphore_release ();
+ }
#elif defined (TARGET_SOLARIS)
/* example: route delete -inet6 2001:db8::/32 somegateway */
- /* GERT-TODO: this is untested, but should work */
argv_printf (&argv, "%s delete -inet6 %s/%d %s",
ROUTE_PATH,
@@ -2066,6 +2295,14 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route delete -inet6 command failed");
+#elif defined(TARGET_AIX)
+
+ argv_printf (&argv, "%s delete -inet6 %s/%d %s",
+ ROUTE_PATH,
+ network, r6->netbits, gateway);
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: AIX route add command failed");
+
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-down script");
#endif
@@ -2079,7 +2316,7 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne
* to get the current default gateway.
*/
-#if defined(WIN32)
+#if defined(_WIN32)
static const MIB_IPFORWARDTABLE *
get_windows_routing_table (struct gc_arena *gc)
@@ -2148,6 +2385,7 @@ test_routes (const struct route_list *rl, const struct tuntap *tt)
int count = 0;
int good = 0;
int ambig = 0;
+ int len = -1;
bool adapter_up = false;
if (is_adapter_up (tt, adapters))
@@ -2157,9 +2395,9 @@ test_routes (const struct route_list *rl, const struct tuntap *tt)
if (rl)
{
- int i;
- for (i = 0; i < rl->n; ++i)
- test_route_helper (&ret, &count, &good, &ambig, adapters, rl->routes[i].gateway);
+ struct route_ipv4 *r;
+ for (r = rl->routes, len = 0; r; r = r->next, ++len)
+ test_route_helper (&ret, &count, &good, &ambig, adapters, r->gateway);
if ((rl->flags & RG_ENABLE) && (rl->spec.flags & RTSA_REMOTE_ENDPOINT))
test_route_helper (&ret, &count, &good, &ambig, adapters, rl->spec.remote_endpoint);
@@ -2169,7 +2407,7 @@ test_routes (const struct route_list *rl, const struct tuntap *tt)
msg (D_ROUTE, "TEST ROUTES: %d/%d succeeded len=%d ret=%d a=%d u/d=%s",
good,
count,
- rl ? rl->n : -1,
+ len,
(int)ret,
ambig,
adapter_up ? "up" : "down");
@@ -2301,6 +2539,81 @@ windows_route_find_if_index (const struct route_ipv4 *r, const struct tuntap *tt
return ret;
}
+/* IPv6 implementation using GetBestRoute2()
+ * (TBD: dynamic linking so the binary can still run on XP?)
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365922(v=vs.85).aspx
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa814411(v=vs.85).aspx
+ */
+void
+get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
+ const struct in6_addr *dest)
+{
+ struct gc_arena gc = gc_new ();
+ MIB_IPFORWARD_ROW2 BestRoute;
+ SOCKADDR_INET DestinationAddress, BestSourceAddress;
+ DWORD BestIfIndex;
+ DWORD status;
+ NET_LUID InterfaceLuid;
+
+ CLEAR(*rgi6);
+ CLEAR(InterfaceLuid); // cleared = not used for lookup
+ CLEAR(DestinationAddress);
+
+ DestinationAddress.si_family = AF_INET6;
+ if ( dest )
+ {
+ DestinationAddress.Ipv6.sin6_addr = *dest;
+ }
+
+ status = GetBestInterfaceEx( &DestinationAddress, &BestIfIndex );
+
+ if (status != NO_ERROR)
+ {
+ msg (D_ROUTE, "NOTE: GetBestInterfaceEx returned error: %s (code=%u)",
+ strerror_win32 (status, &gc),
+ (unsigned int)status);
+ goto done;
+ }
+
+ msg( D_ROUTE, "GetBestInterfaceEx() returned if=%d", (int) BestIfIndex );
+
+ status = GetBestRoute2( &InterfaceLuid, BestIfIndex, NULL,
+ &DestinationAddress, 0,
+ &BestRoute, &BestSourceAddress );
+
+ if (status != NO_ERROR)
+ {
+ msg (D_ROUTE, "NOTE: GetIpForwardEntry2 returned error: %s (code=%u)",
+ strerror_win32 (status, &gc),
+ (unsigned int)status);
+ goto done;
+ }
+
+ msg( D_ROUTE, "GDG6: II=%d DP=%s/%d NH=%s",
+ BestRoute.InterfaceIndex,
+ print_in6_addr( BestRoute.DestinationPrefix.Prefix.Ipv6.sin6_addr, 0, &gc),
+ BestRoute.DestinationPrefix.PrefixLength,
+ print_in6_addr( BestRoute.NextHop.Ipv6.sin6_addr, 0, &gc) );
+ msg( D_ROUTE, "GDG6: Metric=%d, Loopback=%d, AA=%d, I=%d",
+ (int) BestRoute.Metric,
+ (int) BestRoute.Loopback,
+ (int) BestRoute.AutoconfigureAddress,
+ (int) BestRoute.Immortal );
+
+ rgi6->gateway.addr_ipv6 = BestRoute.NextHop.Ipv6.sin6_addr;
+ rgi6->adapter_index = BestRoute.InterfaceIndex;
+ rgi6->flags |= RGI_ADDR_DEFINED | RGI_IFACE_DEFINED;
+
+ /* on-link is signalled by receiving an empty (::) NextHop */
+ if ( IN6_IS_ADDR_UNSPECIFIED(&BestRoute.NextHop.Ipv6.sin6_addr) )
+ {
+ rgi6->flags |= RGI_ON_LINK;
+ }
+
+ done:
+ gc_free (&gc);
+}
+
bool
add_route_ipapi (const struct route_ipv4 *r, const struct tuntap *tt, DWORD adapter_index)
{
@@ -2407,6 +2720,126 @@ del_route_ipapi (const struct route_ipv4 *r, const struct tuntap *tt)
return ret;
}
+static bool
+do_route_service (const bool add, const route_message_t *rt, const size_t size, HANDLE pipe)
+{
+ DWORD len;
+ bool ret = false;
+ ack_message_t ack;
+ struct gc_arena gc = gc_new ();
+
+ if (!WriteFile (pipe, rt, size, &len, NULL) ||
+ !ReadFile (pipe, &ack, sizeof (ack), &len, NULL))
+ {
+ msg (M_WARN, "ROUTE: could not talk to service: %s [%lu]",
+ strerror_win32 (GetLastError (), &gc), GetLastError ());
+ goto out;
+ }
+
+ if (ack.error_number != NO_ERROR)
+ {
+ msg (M_WARN, "ROUTE: route %s failed using service: %s [status=%u if_index=%lu]",
+ (add ? "addition" : "deletion"), strerror_win32 (ack.error_number, &gc),
+ ack.error_number, rt->iface.index);
+ goto out;
+ }
+
+ ret = true;
+
+out:
+ gc_free (&gc);
+ return ret;
+}
+
+static bool
+do_route_ipv4_service (const bool add, const struct route_ipv4 *r, const struct tuntap *tt)
+{
+ DWORD if_index = windows_route_find_if_index (r, tt);
+ if (if_index == ~0)
+ return false;
+
+ route_message_t msg = {
+ .header = {
+ (add ? msg_add_route : msg_del_route),
+ sizeof (route_message_t),
+ 0 },
+ .family = AF_INET,
+ .prefix.ipv4.s_addr = htonl(r->network),
+ .gateway.ipv4.s_addr = htonl(r->gateway),
+ .iface = { .index = if_index, .name = "" },
+ .metric = (r->flags & RT_METRIC_DEFINED ? r->metric : -1)
+ };
+
+ netmask_to_netbits (r->network, r->netmask, &msg.prefix_len);
+ if (msg.prefix_len == -1)
+ msg.prefix_len = 32;
+
+ return do_route_service (add, &msg, sizeof (msg), tt->options.msg_channel);
+}
+
+static bool
+do_route_ipv6_service (const bool add, const struct route_ipv6 *r, const struct tuntap *tt)
+{
+ bool status;
+ route_message_t msg = {
+ .header = {
+ (add ? msg_add_route : msg_del_route),
+ sizeof (route_message_t),
+ 0 },
+ .family = AF_INET6,
+ .prefix.ipv6 = r->network,
+ .prefix_len = r->netbits,
+ .gateway.ipv6 = r->gateway,
+ .iface = { .index = tt->adapter_index, .name = "" },
+ .metric = ( (r->flags & RT_METRIC_DEFINED) ? r->metric : -1)
+ };
+
+ if ( r->adapter_index ) /* vpn server special route */
+ msg.iface.index = r->adapter_index;
+
+ /* In TUN mode we use a special link-local address as the next hop.
+ * The tapdrvr knows about it and will answer neighbor discovery packets.
+ */
+ if (tt->type == DEV_TYPE_TUN)
+ inet_pton (AF_INET6, "fe80::8", &msg.gateway.ipv6);
+
+ if (msg.iface.index == TUN_ADAPTER_INDEX_INVALID)
+ {
+ strncpy (msg.iface.name, tt->actual_name, sizeof (msg.iface.name));
+ msg.iface.name[sizeof (msg.iface.name) - 1] = '\0';
+ }
+
+ status = do_route_service (add, &msg, sizeof (msg), tt->options.msg_channel);
+ msg (D_ROUTE, "IPv6 route %s via service %s",
+ add ? "addition" : "deletion",
+ status ? "succeeded" : "failed");
+ return status;
+}
+
+static bool
+add_route_service (const struct route_ipv4 *r, const struct tuntap *tt)
+{
+ return do_route_ipv4_service (true, r, tt);
+}
+
+static bool
+del_route_service (const struct route_ipv4 *r, const struct tuntap *tt)
+{
+ return do_route_ipv4_service (false, r, tt);
+}
+
+static bool
+add_route_ipv6_service (const struct route_ipv6 *r, const struct tuntap *tt)
+{
+ return do_route_ipv6_service (true, r, tt);
+}
+
+static bool
+del_route_ipv6_service (const struct route_ipv6 *r, const struct tuntap *tt)
+{
+ return do_route_ipv6_service (false, r, tt);
+}
+
static const char *
format_route_entry (const MIB_IPFORWARDROW *r, struct gc_arena *gc)
{
@@ -2451,7 +2884,7 @@ show_routes (int msglev)
gc_free (&gc);
}
-#elif defined(TARGET_LINUX)
+#elif defined(TARGET_LINUX) || defined(TARGET_ANDROID)
void
get_default_gateway (struct route_gateway_info *rgi)
@@ -2463,6 +2896,7 @@ get_default_gateway (struct route_gateway_info *rgi)
CLEAR(*rgi);
+#ifndef TARGET_ANDROID
/* get default gateway IP addr */
{
FILE *fp = fopen ("/proc/net/route", "r");
@@ -2519,6 +2953,19 @@ get_default_gateway (struct route_gateway_info *rgi)
}
}
}
+#else
+ /* Android, set some pseudo GW, addr is in host byte order,
+ * Determining the default GW on Android 5.0+ is non trivial
+ * and serves almost no purpose since OpenVPN only uses the
+ * default GW address to add routes for networks that should
+ * NOT be routed over the VPN. Using a well known address
+ * (127.'d'.'g'.'w') for the default GW make detecting
+ * these routes easier from the controlling app.
+ */
+ rgi->gateway.addr = 127 << 24 | 'd' << 16 | 'g' << 8 | 'w';
+ rgi->flags |= RGI_ADDR_DEFINED;
+ strcpy(best_name, "android-gw");
+#endif
/* scan adapter list */
if (rgi->flags & RGI_ADDR_DEFINED)
@@ -2617,136 +3064,149 @@ get_default_gateway (struct route_gateway_info *rgi)
gc_free (&gc);
}
-#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)||defined(TARGET_SOLARIS)
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <net/route.h>
-
-struct {
- struct rt_msghdr m_rtm;
- char m_space[512];
-} m_rtmsg;
-
-#define ROUNDUP(a) \
- ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
-
-/*
- * FIXME -- add support for netmask, hwaddr, and iface
+/* IPv6 implementation using netlink
+ * http://www.linuxjournal.com/article/7356
+ * netlink(3), netlink(7), rtnetlink(7)
+ * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/NetworkServices/NAT/rtmon_linux.c
*/
-void
-get_default_gateway (struct route_gateway_info *rgi)
-{
- struct gc_arena gc = gc_new ();
- int s, seq, l, pid, rtm_addrs;
- unsigned int i;
- struct sockaddr so_dst, so_mask;
- char *cp = m_rtmsg.m_space;
- struct sockaddr *gate = NULL, *sa;
- struct rt_msghdr *rtm_aux;
-
-#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
-
-#define NEXTADDR(w, u) \
- if (rtm_addrs & (w)) {\
- l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
- }
-
-#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
-
-#else /* TARGET_SOLARIS */
-
-#define NEXTADDR(w, u) \
- if (rtm_addrs & (w)) {\
- l = ROUNDUP(sizeof(struct sockaddr_in)); memmove(cp, &(u), l); cp += l;\
- }
-
-#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in)))
+struct rtreq {
+ struct nlmsghdr nh;
+ struct rtmsg rtm;
+ char attrbuf[512];
+};
-#endif
+void
+get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
+ const struct in6_addr *dest)
+{
+ int nls = -1;
+ struct rtreq rtreq;
+ struct rtattr *rta;
+
+ char rtbuf[2000];
+ ssize_t ssize;
+
+ CLEAR(*rgi6);
+
+ nls = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
+ if ( nls < 0 )
+ { msg(M_WARN|M_ERRNO, "GDG6: socket() failed" ); goto done; }
+
+ /* bind() is not needed, no unsolicited msgs coming in */
+
+ /* request best matching route, see netlink(7) for explanations
+ */
+ CLEAR(rtreq);
+ rtreq.nh.nlmsg_type = RTM_GETROUTE;
+ rtreq.nh.nlmsg_flags = NLM_F_REQUEST; /* best match only */
+ rtreq.rtm.rtm_family = AF_INET6;
+ rtreq.rtm.rtm_src_len = 0; /* not source dependent */
+ rtreq.rtm.rtm_dst_len = 128; /* exact dst */
+ rtreq.rtm.rtm_table = RT_TABLE_MAIN;
+ rtreq.rtm.rtm_protocol = RTPROT_UNSPEC;
+ rtreq.nh.nlmsg_len = NLMSG_SPACE(sizeof(rtreq.rtm));
+
+ /* set RTA_DST for target IPv6 address we want */
+ rta = (struct rtattr *)(((char *) &rtreq)+NLMSG_ALIGN(rtreq.nh.nlmsg_len));
+ rta->rta_type = RTA_DST;
+ rta->rta_len = RTA_LENGTH(16);
+ rtreq.nh.nlmsg_len = NLMSG_ALIGN(rtreq.nh.nlmsg_len) +
+ RTA_LENGTH(16);
+
+ if ( dest == NULL ) /* ::, unspecified */
+ memset( RTA_DATA(rta), 0, 16 ); /* :: = all-zero */
+ else
+ memcpy( RTA_DATA(rta), (void *)dest, 16 );
+ /* send and receive reply */
+ if ( send( nls, &rtreq, rtreq.nh.nlmsg_len, 0 ) < 0 )
+ { msg(M_WARN|M_ERRNO, "GDG6: send() failed" ); goto done; }
-#define rtm m_rtmsg.m_rtm
+ ssize = recv(nls, rtbuf, sizeof(rtbuf), MSG_TRUNC);
- CLEAR(*rgi);
+ if (ssize < 0)
+ { msg(M_WARN|M_ERRNO, "GDG6: recv() failed" ); goto done; }
- pid = getpid();
- seq = 0;
- rtm_addrs = RTA_DST | RTA_NETMASK;
+ if (ssize > sizeof(rtbuf))
+ {
+ msg(M_WARN, "get_default_gateway_ipv6: returned message too big for buffer (%d>%d)", (int)ssize, (int)sizeof(rtbuf) );
+ goto done;
+ }
- bzero(&so_dst, sizeof(so_dst));
- bzero(&so_mask, sizeof(so_mask));
- bzero(&rtm, sizeof(struct rt_msghdr));
+ struct nlmsghdr *nh;
- rtm.rtm_type = RTM_GET;
- rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
- rtm.rtm_version = RTM_VERSION;
- rtm.rtm_seq = ++seq;
- rtm.rtm_addrs = rtm_addrs;
+ for (nh = (struct nlmsghdr *)rtbuf;
+ NLMSG_OK(nh, ssize);
+ nh = NLMSG_NEXT(nh, ssize))
+ {
+ struct rtmsg *rtm;
+ int attrlen;
- so_dst.sa_family = AF_INET;
- so_mask.sa_family = AF_INET;
+ if (nh->nlmsg_type == NLMSG_DONE) { break; }
-#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
- so_dst.sa_len = sizeof(struct sockaddr_in);
- so_mask.sa_len = sizeof(struct sockaddr_in);
-#endif
+ if (nh->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *ne = (struct nlmsgerr *)NLMSG_DATA(nh);
+ msg(M_WARN, "GDG6: NLSMG_ERROR: error %d\n", ne->error);
+ break;
+ }
- NEXTADDR(RTA_DST, so_dst);
- NEXTADDR(RTA_NETMASK, so_mask);
+ if (nh->nlmsg_type != RTM_NEWROUTE) {
+ /* shouldn't happen */
+ msg(M_WARN, "GDG6: unexpected msg_type %d", nh->nlmsg_type );
+ continue;
+ }
- rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+ rtm = (struct rtmsg *)NLMSG_DATA(nh);
+ attrlen = RTM_PAYLOAD(nh);
- s = socket(PF_ROUTE, SOCK_RAW, 0);
+ /* we're only looking for routes in the main table, as "we have
+ * no IPv6" will lead to a lookup result in "Local" (::/0 reject)
+ */
+ if (rtm->rtm_family != AF_INET6 ||
+ rtm->rtm_table != RT_TABLE_MAIN)
+ { continue; } /* we're not interested */
- if (write(s, (char *)&m_rtmsg, l) < 0)
- {
- msg(M_WARN|M_ERRNO, "Could not retrieve default gateway from route socket:");
- gc_free (&gc);
- close(s);
- return;
+ for (rta = RTM_RTA(rtm);
+ RTA_OK(rta, attrlen);
+ rta = RTA_NEXT(rta, attrlen))
+ {
+ if (rta->rta_type == RTA_GATEWAY) {
+ if ( RTA_PAYLOAD(rta) != sizeof(struct in6_addr) )
+ { msg(M_WARN, "GDG6: RTA_GW size mismatch"); continue; }
+ rgi6->gateway.addr_ipv6 = *(struct in6_addr*) RTA_DATA(rta);
+ rgi6->flags |= RGI_ADDR_DEFINED;
+ }
+ else if (rta->rta_type == RTA_OIF) {
+ char ifname[IF_NAMESIZE+1];
+ int oif;
+ if ( RTA_PAYLOAD(rta) != sizeof(oif) )
+ { msg(M_WARN, "GDG6: oif size mismatch"); continue; }
+
+ memcpy(&oif, RTA_DATA(rta), sizeof(oif));
+ if_indextoname(oif,ifname);
+ strncpy( rgi6->iface, ifname, sizeof(rgi6->iface)-1 );
+ rgi6->flags |= RGI_IFACE_DEFINED;
+ }
+ }
}
- do {
- l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
- } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
-
- close(s);
-
- rtm_aux = &rtm;
-
- cp = ((char *)(rtm_aux + 1));
- if (rtm_aux->rtm_addrs) {
- for (i = 1; i; i <<= 1)
- if (i & rtm_aux->rtm_addrs) {
- sa = (struct sockaddr *)cp;
- if (i == RTA_GATEWAY )
- gate = sa;
- ADVANCE(cp, sa);
- }
- }
- else
+ /* if we have an interface but no gateway, the destination is on-link */
+ if ( ( rgi6->flags & (RGI_IFACE_DEFINED|RGI_ADDR_DEFINED) ) ==
+ RGI_IFACE_DEFINED )
{
- gc_free (&gc);
- return;
+ rgi6->flags |= (RGI_ADDR_DEFINED | RGI_ON_LINK);
+ if ( dest )
+ rgi6->gateway.addr_ipv6 = *dest;
}
-
- if (gate != NULL )
- {
- rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
- rgi->flags |= RGI_ADDR_DEFINED;
-
- gc_free (&gc);
- }
- else
- {
- gc_free (&gc);
- }
+ done:
+ if (nls >= 0)
+ close (nls);
}
-#elif defined(TARGET_DARWIN)
+#elif defined(TARGET_DARWIN) || defined(TARGET_SOLARIS) || \
+ defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) || \
+ defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
#include <sys/types.h>
#include <sys/socket.h>
@@ -2759,15 +3219,41 @@ struct rtmsg {
char m_space[512];
};
-#define ROUNDUP(a) \
+/* the route socket code is identical for all 4 supported BSDs and for
+ * MacOS X (Darwin), with one crucial difference: when going from
+ * 32 bit to 64 bit, the BSDs increased the structure size but kept
+ * source code compatibility by keeping the use of "long", while
+ * MacOS X decided to keep binary compatibility by *changing* the API
+ * to use "uint32_t", thus 32 bit on all OS X variants
+ *
+ * We used to have a large amount of duplicate code here which really
+ * differed only in this (long) vs. (uint32_t) - IMHO, worse than
+ * having a combined block for all BSDs with this single #ifdef inside
+ */
+
+#if defined(TARGET_DARWIN)
+# define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
+#else
+# define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#endif
+
+#if defined(TARGET_SOLARIS)
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) {\
+ l = ROUNDUP(sizeof(u)); memmove(cp, &(u), l); cp += l;\
+ }
+#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in)))
+#else
#define NEXTADDR(w, u) \
if (rtm_addrs & (w)) {\
- l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
+ l = ROUNDUP( ((struct sockaddr *)&(u))->sa_len); memmove(cp, &(u), l); cp += l;\
}
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+#endif
#define max(a,b) ((a) > (b) ? (a) : (b))
@@ -2805,9 +3291,12 @@ get_default_gateway (struct route_gateway_info *rgi)
rtm.rtm_addrs = rtm_addrs;
so_dst.sa_family = AF_INET;
- so_dst.sa_len = sizeof(struct sockaddr_in);
so_mask.sa_family = AF_INET;
+
+#ifndef TARGET_SOLARIS
+ so_dst.sa_len = sizeof(struct sockaddr_in);
so_mask.sa_len = sizeof(struct sockaddr_in);
+#endif
NEXTADDR(RTA_DST, so_dst);
NEXTADDR(RTA_NETMASK, so_mask);
@@ -2865,7 +3354,6 @@ get_default_gateway (struct route_gateway_info *rgi)
{
/* get interface name */
const struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
- int len = adl->sdl_nlen;
if (adl->sdl_nlen && adl->sdl_nlen < sizeof(rgi->iface))
{
memcpy (rgi->iface, adl->sdl_data, adl->sdl_nlen);
@@ -2932,7 +3420,12 @@ get_default_gateway (struct route_gateway_info *rgi)
for (cp = buffer; cp <= buffer + ifc.ifc_len - sizeof(struct ifreq); )
{
ifr = (struct ifreq *)cp;
+#if defined(TARGET_SOLARIS)
+ const size_t len = sizeof(ifr->ifr_name) + sizeof(ifr->ifr_addr);
+#else
const size_t len = sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
+#endif
+
if (!ifr->ifr_addr.sa_family)
break;
if (!strncmp(ifr->ifr_name, rgi->iface, IFNAMSIZ))
@@ -2954,121 +3447,162 @@ get_default_gateway (struct route_gateway_info *rgi)
gc_free (&gc);
}
-#undef max
-
-#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <net/route.h>
-
-struct {
- struct rt_msghdr m_rtm;
- char m_space[512];
-} m_rtmsg;
-
-#define ROUNDUP(a) \
- ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+/* BSD implementation using routing socket (as does IPv4)
+ * (the code duplication is somewhat unavoidable if we want this to
+ * work on OpenSolaris as well. *sigh*)
+ */
-/*
- * FIXME -- add support for netmask, hwaddr, and iface
+/* Solaris has no length field - this is ugly, but less #ifdef in total
*/
+#if defined(TARGET_SOLARIS)
+# undef ADVANCE
+# define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in6)))
+#endif
+
void
-get_default_gateway (struct route_gateway_info *rgi)
+get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
+ const struct in6_addr *dest)
{
- struct gc_arena gc = gc_new ();
- int s, seq, l, rtm_addrs;
- unsigned int i;
- pid_t pid;
- struct sockaddr so_dst, so_mask;
- char *cp = m_rtmsg.m_space;
- struct sockaddr *gate = NULL, *sa;
- struct rt_msghdr *rtm_aux;
-#define NEXTADDR(w, u) \
- if (rtm_addrs & (w)) {\
- l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
- }
+ struct rtmsg m_rtmsg;
+ int sockfd = -1;
+ int seq, l, pid, rtm_addrs;
+ unsigned int i;
+ struct sockaddr_in6 so_dst, so_mask;
+ char *cp = m_rtmsg.m_space;
+ struct sockaddr *gate = NULL, *ifp = NULL, *sa;
+ struct rt_msghdr *rtm_aux;
-#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+ CLEAR(*rgi6);
-#define rtm m_rtmsg.m_rtm
+ /* setup data to send to routing socket */
+ pid = getpid();
+ seq = 0;
+ rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP;
- CLEAR(*rgi);
+ bzero(&m_rtmsg, sizeof(m_rtmsg));
+ bzero(&so_dst, sizeof(so_dst));
+ bzero(&so_mask, sizeof(so_mask));
+ bzero(&rtm, sizeof(struct rt_msghdr));
- pid = getpid();
- seq = 0;
- rtm_addrs = RTA_DST | RTA_NETMASK;
+ rtm.rtm_type = RTM_GET;
+ rtm.rtm_flags = RTF_UP;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
- bzero(&so_dst, sizeof(so_dst));
- bzero(&so_mask, sizeof(so_mask));
- bzero(&rtm, sizeof(struct rt_msghdr));
+ so_dst.sin6_family = AF_INET6;
+ so_mask.sin6_family = AF_INET6;
- rtm.rtm_type = RTM_GET;
- rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
- rtm.rtm_version = RTM_VERSION;
- rtm.rtm_seq = ++seq;
- rtm.rtm_addrs = rtm_addrs;
+ if ( dest != NULL && /* specific host? */
+ !IN6_IS_ADDR_UNSPECIFIED(dest) )
+ {
+ so_dst.sin6_addr = *dest;
+ /* :: needs /0 "netmask", host route wants "no netmask */
+ rtm_addrs &= ~RTA_NETMASK;
+ }
- so_dst.sa_family = AF_INET;
- so_dst.sa_len = sizeof(struct sockaddr_in);
- so_mask.sa_family = AF_INET;
- so_mask.sa_len = sizeof(struct sockaddr_in);
+ rtm.rtm_addrs = rtm_addrs;
- NEXTADDR(RTA_DST, so_dst);
- NEXTADDR(RTA_NETMASK, so_mask);
+#ifndef TARGET_SOLARIS
+ so_dst.sin6_len = sizeof(struct sockaddr_in6);
+ so_mask.sin6_len = sizeof(struct sockaddr_in6);
+#endif
- rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_NETMASK, so_mask);
- s = socket(PF_ROUTE, SOCK_RAW, 0);
+ rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
- if (write(s, (char *)&m_rtmsg, l) < 0)
+ /* transact with routing socket */
+ sockfd = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (sockfd < 0)
{
- msg(M_WARN|M_ERRNO, "Could not retrieve default gateway from route socket:");
- gc_free (&gc);
- close(s);
- return;
+ msg (M_WARN, "GDG6: socket #1 failed");
+ goto done;
+ }
+ if (write(sockfd, (char *)&m_rtmsg, l) < 0)
+ {
+ msg (M_WARN, "GDG6: problem writing to routing socket");
+ goto done;
}
- do {
- l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
- } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
-
- close(s);
-
- rtm_aux = &rtm;
-
- cp = ((char *)(rtm_aux + 1));
- if (rtm_aux->rtm_addrs) {
- for (i = 1; i; i <<= 1)
- if (i & rtm_aux->rtm_addrs) {
- sa = (struct sockaddr *)cp;
- if (i == RTA_GATEWAY )
- gate = sa;
- ADVANCE(cp, sa);
- }
- }
- else
+ do
{
- gc_free (&gc);
- return;
+ l = read(sockfd, (char *)&m_rtmsg, sizeof(m_rtmsg));
}
+ while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+ close(sockfd);
+ sockfd = -1;
- if (gate != NULL )
+ /* extract return data from routing socket */
+ rtm_aux = &rtm;
+ cp = ((char *)(rtm_aux + 1));
+ if (rtm_aux->rtm_addrs)
{
- rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
- rgi->flags |= RGI_ADDR_DEFINED;
-
- gc_free (&gc);
+ for (i = 1; i; i <<= 1)
+ {
+ if (i & rtm_aux->rtm_addrs)
+ {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_GATEWAY )
+ gate = sa;
+ else if (i == RTA_IFP)
+ ifp = sa;
+ ADVANCE(cp, sa);
+ }
+ }
}
- else
+ else
+ goto done;
+
+ /* get gateway addr and interface name */
+ if (gate != NULL )
{
- gc_free (&gc);
+ struct sockaddr_in6 * s6 = (struct sockaddr_in6 *)gate;
+ struct in6_addr gw = s6->sin6_addr;
+
+#ifndef TARGET_SOLARIS
+ /* You do not really want to know... from FreeBSD's route.c
+ * (KAME encodes the 16 bit scope_id in s6_addr[2] + [3],
+ * but for a correct link-local address these must be :0000: )
+ */
+ if ( gate->sa_len == sizeof(struct sockaddr_in6) &&
+ IN6_IS_ADDR_LINKLOCAL(&gw) )
+ {
+ gw.s6_addr[2] = gw.s6_addr[3] = 0;
+ }
+
+ if ( gate->sa_len != sizeof(struct sockaddr_in6) ||
+ IN6_IS_ADDR_UNSPECIFIED(&gw) )
+ {
+ rgi6->flags |= RGI_ON_LINK;
+ }
+ else
+#endif
+
+ rgi6->gateway.addr_ipv6 = gw;
+ rgi6->flags |= RGI_ADDR_DEFINED;
+
+ if (ifp)
+ {
+ /* get interface name */
+ const struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
+ if (adl->sdl_nlen && adl->sdl_nlen < sizeof(rgi6->iface))
+ {
+ memcpy (rgi6->iface, adl->sdl_data, adl->sdl_nlen);
+ rgi6->flags |= RGI_IFACE_DEFINED;
+ }
+ }
}
+
+ done:
+ if (sockfd >= 0)
+ close(sockfd);
}
+#undef max
+
#else
/*
@@ -3100,6 +3634,13 @@ get_default_gateway (struct route_gateway_info *rgi)
{
CLEAR(*rgi);
}
+void
+get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
+ const struct in6_addr *dest)
+{
+ msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system");
+ CLEAR(*rgi6);
+}
#endif
@@ -3127,13 +3668,33 @@ netmask_to_netbits (const in_addr_t network, const in_addr_t netmask, int *netbi
return false;
}
+/* similar to netmask_to_netbits(), but don't mess with base address
+ * etc., just convert to netbits - non-mappable masks are returned as "-1"
+ */
+int netmask_to_netbits2 (in_addr_t netmask)
+{
+ int i;
+ const int addrlen = sizeof (in_addr_t) * 8;
+
+ for (i = 0; i <= addrlen; ++i)
+ {
+ in_addr_t mask = netbits_to_netmask (i);
+ if (mask == netmask)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
/*
* get_bypass_addresses() is used by the redirect-gateway bypass-x
* functions to build a route bypass to selected DHCP/DNS servers,
* so that outgoing packets to these servers don't end up in the tunnel.
*/
-#if defined(WIN32)
+#if defined(_WIN32)
static void
add_host_route_if_nonlocal (struct route_bypass *rb, const in_addr_t addr)
@@ -3207,7 +3768,7 @@ get_bypass_addresses (struct route_bypass *rb, const unsigned int flags) /* PLA
* Used by redirect-gateway autolocal feature
*/
-#if defined(WIN32)
+#if defined(_WIN32)
int
test_local_addr (const in_addr_t addr, const struct route_gateway_info *rgi)