summaryrefslogtreecommitdiffstats
path: root/route.c
diff options
context:
space:
mode:
Diffstat (limited to 'route.c')
-rw-r--r--route.c1245
1 files changed, 700 insertions, 545 deletions
diff --git a/route.c b/route.c
index c3e4acf..6046873 100644
--- a/route.c
+++ b/route.c
@@ -38,7 +38,7 @@
#include "memdbg.h"
-static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
+static void delete_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es);
static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags);
#ifdef ENABLE_DEBUG
@@ -124,7 +124,7 @@ route_string (const struct route *r, struct gc_arena *gc)
print_in_addr_t (r->netmask, 0, gc),
print_in_addr_t (r->gateway, 0, gc)
);
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
buf_printf (&out, " metric %d", r->metric);
return BSTR (&out);
}
@@ -153,7 +153,7 @@ setenv_route_addr (struct env_set *es, const char *key, const in_addr_t addr, in
}
static bool
-get_special_addr (const struct route_special_addr *spec,
+get_special_addr (const struct route_list *rl,
const char *string,
in_addr_t *out,
bool *status)
@@ -162,10 +162,10 @@ get_special_addr (const struct route_special_addr *spec,
*status = true;
if (!strcmp (string, "vpn_gateway"))
{
- if (spec)
+ if (rl)
{
- if (spec->remote_endpoint_defined)
- *out = spec->remote_endpoint;
+ if (rl->spec.flags & RTSA_REMOTE_ENDPOINT)
+ *out = rl->spec.remote_endpoint;
else
{
msg (M_INFO, PACKAGE_NAME " ROUTE: vpn_gateway undefined");
@@ -177,10 +177,10 @@ get_special_addr (const struct route_special_addr *spec,
}
else if (!strcmp (string, "net_gateway"))
{
- if (spec)
+ if (rl)
{
- if (spec->net_gateway_defined)
- *out = spec->net_gateway;
+ if (rl->rgi.flags & RGI_ADDR_DEFINED)
+ *out = rl->rgi.gateway.addr;
else
{
msg (M_INFO, PACKAGE_NAME " ROUTE: net_gateway undefined -- unable to get default gateway from system");
@@ -192,10 +192,10 @@ get_special_addr (const struct route_special_addr *spec,
}
else if (!strcmp (string, "remote_host"))
{
- if (spec)
+ if (rl)
{
- if (spec->remote_host_defined)
- *out = spec->remote_host;
+ if (rl->spec.flags & RTSA_REMOTE_HOST)
+ *out = rl->spec.remote_host;
else
{
msg (M_INFO, PACKAGE_NAME " ROUTE: remote_host undefined");
@@ -221,13 +221,13 @@ static bool
init_route (struct route *r,
struct resolve_list *network_list,
const struct route_option *ro,
- const struct route_special_addr *spec)
+ const struct route_list *rl)
{
const in_addr_t default_netmask = ~0;
bool status;
+ CLEAR (*r);
r->option = ro;
- r->defined = false;
/* network */
@@ -236,7 +236,7 @@ init_route (struct route *r,
goto fail;
}
- if (!get_special_addr (spec, ro->network, &r->network, &status))
+ if (!get_special_addr (rl, ro->network, &r->network, &status))
{
r->network = getaddr_multi (
GETADDR_RESOLVE
@@ -273,7 +273,7 @@ init_route (struct route *r,
if (is_route_parm_defined (ro->gateway))
{
- if (!get_special_addr (spec, ro->gateway, &r->gateway, &status))
+ if (!get_special_addr (rl, ro->gateway, &r->gateway, &status))
{
r->gateway = getaddr (
GETADDR_RESOLVE
@@ -289,8 +289,8 @@ init_route (struct route *r,
}
else
{
- if (spec->remote_endpoint_defined)
- r->gateway = spec->remote_endpoint;
+ if (rl->spec.flags & RTSA_REMOTE_ENDPOINT)
+ r->gateway = rl->spec.remote_endpoint;
else
{
msg (M_WARN, PACKAGE_NAME " ROUTE: " PACKAGE_NAME " needs a gateway parameter for a --route option and no default was specified by either --route-gateway or --ifconfig options");
@@ -300,7 +300,6 @@ init_route (struct route *r,
/* metric */
- r->metric_defined = false;
r->metric = 0;
if (is_route_parm_defined (ro->metric))
{
@@ -312,22 +311,21 @@ init_route (struct route *r,
ro->metric);
goto fail;
}
- r->metric_defined = true;
+ r->flags |= RT_METRIC_DEFINED;
}
- else if (spec->default_metric_defined)
+ else if (rl->spec.flags & RTSA_DEFAULT_METRIC)
{
- r->metric = spec->default_metric;
- r->metric_defined = true;
+ r->metric = rl->spec.default_metric;
+ r->flags |= RT_METRIC_DEFINED;
}
- r->defined = true;
+ r->flags |= RT_DEFINED;
return true;
fail:
msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
ro->network);
- r->defined = false;
return false;
}
@@ -360,15 +358,70 @@ clear_route_list (struct route_list *rl)
}
void
-route_list_add_default_gateway (struct route_list *rl,
- struct env_set *es,
- const in_addr_t addr)
+route_list_add_vpn_gateway (struct route_list *rl,
+ struct env_set *es,
+ const in_addr_t addr)
{
rl->spec.remote_endpoint = addr;
- rl->spec.remote_endpoint_defined = true;
+ rl->spec.flags |= RTSA_REMOTE_ENDPOINT;
setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
}
+static void
+add_block_local_item (struct route_list *rl,
+ const struct route_gateway_address *gateway,
+ in_addr_t target)
+{
+ 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)
+ {
+ struct route r;
+ unsigned int l2;
+
+ /* split a route into two smaller blocking routes, and direct them to target */
+ CLEAR(r);
+ r.flags = RT_DEFINED;
+ r.gateway = target;
+ l2 = ((~gateway->netmask)+1)>>1;
+ r.netmask = ~(l2-1);
+ r.network = gateway->addr & r.netmask;
+ rl->routes[rl->n++] = r;
+ r.network += l2;
+ rl->routes[rl->n++] = r;
+ }
+}
+
+static void
+add_block_local (struct route_list *rl)
+{
+ const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED);
+ if ((rl->flags & RG_BLOCK_LOCAL)
+ && (rl->rgi.flags & rgi_needed) == rgi_needed
+ && (rl->spec.flags & RTSA_REMOTE_ENDPOINT)
+ && rl->spec.remote_host_local != TLA_LOCAL)
+ {
+ size_t i;
+
+ /* add bypass for gateway addr */
+ add_bypass_address (&rl->spec.bypass, rl->rgi.gateway.addr);
+
+ /* block access to local subnet */
+ add_block_local_item (rl, &rl->rgi.gateway, rl->spec.remote_endpoint);
+
+ /* process additional subnets on gateway interface */
+ for (i = 0; i < rl->rgi.n_addrs; ++i)
+ {
+ const struct route_gateway_address *gwa = &rl->rgi.addrs[i];
+ /* omit the add/subnet in &rl->rgi which we processed above */
+ if (!((rl->rgi.gateway.addr & rl->rgi.gateway.netmask) == (gwa->addr & gwa->netmask)
+ && rl->rgi.gateway.netmask == gwa->netmask))
+ add_block_local_item (rl, gwa, rl->spec.remote_endpoint);
+ }
+ }
+}
+
bool
init_route_list (struct route_list *rl,
const struct route_option_list *opt,
@@ -387,48 +440,47 @@ init_route_list (struct route_list *rl,
if (remote_host)
{
rl->spec.remote_host = remote_host;
- rl->spec.remote_host_defined = true;
+ rl->spec.flags |= RTSA_REMOTE_HOST;
}
if (default_metric)
{
rl->spec.default_metric = default_metric;
- rl->spec.default_metric_defined = true;
+ rl->spec.flags |= RTSA_DEFAULT_METRIC;
}
- rl->spec.net_gateway_defined = get_default_gateway (&rl->spec.net_gateway, NULL);
- if (rl->spec.net_gateway_defined)
+ get_default_gateway (&rl->rgi);
+ if (rl->rgi.flags & RGI_ADDR_DEFINED)
{
- setenv_route_addr (es, "net_gateway", rl->spec.net_gateway, -1);
- dmsg (D_ROUTE, "ROUTE default_gateway=%s", print_in_addr_t (rl->spec.net_gateway, 0, &gc));
+ setenv_route_addr (es, "net_gateway", rl->rgi.gateway.addr, -1);
+#ifdef ENABLE_DEBUG
+ print_default_gateway (D_ROUTE, &rl->rgi);
+#endif
}
else
{
dmsg (D_ROUTE, "ROUTE: default_gateway=UNDEF");
}
- if (rl->flags & RG_ENABLE)
- {
- get_bypass_addresses (&rl->spec.bypass, rl->flags);
-#ifdef ENABLE_DEBUG
- print_bypass_addresses (&rl->spec.bypass);
-#endif
- }
+ if (rl->spec.flags & RTSA_REMOTE_HOST)
+ rl->spec.remote_host_local = test_local_addr (remote_host, &rl->rgi);
if (is_route_parm_defined (remote_endpoint))
{
+ bool defined = false;
rl->spec.remote_endpoint = getaddr (
GETADDR_RESOLVE
| GETADDR_HOST_ORDER
| GETADDR_WARN_ON_SIGNAL,
remote_endpoint,
0,
- &rl->spec.remote_endpoint_defined,
+ &defined,
NULL);
- if (rl->spec.remote_endpoint_defined)
+ if (defined)
{
setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
+ rl->spec.flags |= RTSA_REMOTE_ENDPOINT;
}
else
{
@@ -437,12 +489,20 @@ init_route_list (struct route_list *rl,
ret = false;
}
}
- else
- rl->spec.remote_endpoint_defined = false;
+
+ if (rl->flags & RG_ENABLE)
+ {
+ add_block_local (rl);
+ get_bypass_addresses (&rl->spec.bypass, rl->flags);
+#ifdef ENABLE_DEBUG
+ print_bypass_addresses (&rl->spec.bypass);
+#endif
+ }
/* parse the routes from opt to rl */
{
- int i, j = 0;
+ int i = 0;
+ int j = rl->n;
bool warned = false;
for (i = 0; i < opt->n; ++i)
{
@@ -455,7 +515,7 @@ init_route_list (struct route_list *rl,
if (!init_route (&r,
&netlist,
&opt->routes[i],
- &rl->spec))
+ rl))
ret = false;
else
{
@@ -495,15 +555,16 @@ add_route3 (in_addr_t network,
in_addr_t gateway,
const struct tuntap *tt,
unsigned int flags,
+ const struct route_gateway_info *rgi,
const struct env_set *es)
{
struct route r;
CLEAR (r);
- r.defined = true;
+ r.flags = RT_DEFINED;
r.network = network;
r.netmask = netmask;
r.gateway = gateway;
- add_route (&r, tt, flags, es);
+ add_route (&r, tt, flags, rgi, es);
}
static void
@@ -512,15 +573,16 @@ del_route3 (in_addr_t network,
in_addr_t gateway,
const struct tuntap *tt,
unsigned int flags,
+ const struct route_gateway_info *rgi,
const struct env_set *es)
{
struct route r;
CLEAR (r);
- r.defined = true;
+ r.flags = RT_DEFINED|RT_ADDED;
r.network = network;
r.netmask = netmask;
r.gateway = gateway;
- delete_route (&r, tt, flags, es);
+ delete_route (&r, tt, flags, rgi, es);
}
static void
@@ -528,17 +590,19 @@ add_bypass_routes (struct route_bypass *rb,
in_addr_t gateway,
const struct tuntap *tt,
unsigned int flags,
+ const struct route_gateway_info *rgi,
const struct env_set *es)
{
int i;
for (i = 0; i < rb->n_bypass; ++i)
{
- if (rb->bypass[i] != gateway)
+ if (rb->bypass[i])
add_route3 (rb->bypass[i],
~0,
gateway,
tt,
flags,
+ rgi,
es);
}
}
@@ -548,17 +612,19 @@ del_bypass_routes (struct route_bypass *rb,
in_addr_t gateway,
const struct tuntap *tt,
unsigned int flags,
+ const struct route_gateway_info *rgi,
const struct env_set *es)
{
int i;
for (i = 0; i < rb->n_bypass; ++i)
{
- if (rb->bypass[i] != gateway)
+ if (rb->bypass[i])
del_route3 (rb->bypass[i],
~0,
gateway,
tt,
flags,
+ rgi,
es);
}
}
@@ -570,15 +636,15 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
if (rl->flags & RG_ENABLE)
{
- if (!rl->spec.remote_endpoint_defined)
+ if (!(rl->spec.flags & RTSA_REMOTE_ENDPOINT))
{
msg (M_WARN, "%s VPN gateway parameter (--route-gateway or --ifconfig) is missing", err);
}
- else if (!rl->spec.net_gateway_defined)
+ else if (!(rl->rgi.flags & RGI_ADDR_DEFINED))
{
msg (M_WARN, "%s Cannot read current default gateway from system", err);
}
- else if (!rl->spec.remote_host_defined)
+ else if (!(rl->spec.flags & RTSA_REMOTE_HOST))
{
msg (M_WARN, "%s Cannot obtain current remote host address", err);
}
@@ -586,7 +652,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
{
bool local = BOOL_CAST(rl->flags & RG_LOCAL);
if (rl->flags & RG_AUTO_LOCAL) {
- const int tla = test_local_addr (rl->spec.remote_host);
+ const int tla = rl->spec.remote_host_local;
if (tla == TLA_NONLOCAL)
{
dmsg (D_ROUTE, "ROUTE remote_host is NOT LOCAL");
@@ -603,15 +669,16 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
/* route remote host to original default gateway */
add_route3 (rl->spec.remote_host,
~0,
- rl->spec.net_gateway,
+ rl->rgi.gateway.addr,
tt,
flags,
+ &rl->rgi,
es);
- rl->did_local = true;
+ rl->iflags |= RL_DID_LOCAL;
}
/* route DHCP/DNS server traffic through original default gateway */
- add_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es);
+ add_bypass_routes (&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es);
if (rl->flags & RG_REROUTE_GW)
{
@@ -623,6 +690,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
/* add new default route (2nd component) */
@@ -631,6 +699,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
}
else
@@ -638,9 +707,10 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
/* delete default route */
del_route3 (0,
0,
- rl->spec.net_gateway,
+ rl->rgi.gateway.addr,
tt,
flags,
+ &rl->rgi,
es);
/* add new default route */
@@ -649,12 +719,13 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
}
}
/* set a flag so we can undo later */
- rl->did_redirect_default_gateway = true;
+ rl->iflags |= RL_DID_REDIRECT_DEFAULT_GATEWAY;
}
}
}
@@ -662,22 +733,23 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
static void
undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
- if (rl->did_redirect_default_gateway)
+ if (rl->iflags & RL_DID_REDIRECT_DEFAULT_GATEWAY)
{
/* delete remote host route */
- if (rl->did_local)
+ if (rl->iflags & RL_DID_LOCAL)
{
del_route3 (rl->spec.remote_host,
~0,
- rl->spec.net_gateway,
+ rl->rgi.gateway.addr,
tt,
flags,
+ &rl->rgi,
es);
- rl->did_local = false;
+ rl->iflags &= ~RL_DID_LOCAL;
}
/* delete special DHCP/DNS bypass route */
- del_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es);
+ del_bypass_routes (&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es);
if (rl->flags & RG_REROUTE_GW)
{
@@ -689,6 +761,7 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
/* delete default route (2nd component) */
@@ -697,6 +770,7 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
}
else
@@ -707,19 +781,21 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
/* restore original default route */
add_route3 (0,
0,
- rl->spec.net_gateway,
+ rl->rgi.gateway.addr,
tt,
flags,
+ &rl->rgi,
es);
}
}
- rl->did_redirect_default_gateway = false;
+ rl->iflags &= ~RL_DID_REDIRECT_DEFAULT_GATEWAY;
}
}
@@ -727,7 +803,7 @@ void
add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
redirect_default_route_to_vpn (rl, tt, flags, es);
- if (!rl->routes_added)
+ if (!(rl->iflags & RL_ROUTES_ADDED))
{
int i;
@@ -747,25 +823,25 @@ add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags,
struct route *r = &rl->routes[i];
check_subnet_conflict (r->network, r->netmask, "route");
if (flags & ROUTE_DELETE_FIRST)
- delete_route (r, tt, flags, es);
- add_route (r, tt, flags, es);
+ delete_route (r, tt, flags, &rl->rgi, es);
+ add_route (r, tt, flags, &rl->rgi, es);
}
- rl->routes_added = true;
+ rl->iflags |= RL_ROUTES_ADDED;
}
}
void
delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
- if (rl->routes_added)
+ if (rl->iflags & RL_ROUTES_ADDED)
{
int i;
for (i = rl->n - 1; i >= 0; --i)
{
- const struct route *r = &rl->routes[i];
- delete_route (r, tt, flags, es);
+ struct route *r = &rl->routes[i];
+ delete_route (r, tt, flags, &rl->rgi, es);
}
- rl->routes_added = false;
+ rl->iflags &= ~RL_ROUTES_ADDED;
}
undo_redirect_default_route_to_vpn (rl, tt, flags, es);
@@ -805,13 +881,38 @@ print_route_options (const struct route_option_list *rol,
print_route_option (&rol->routes[i], level);
}
+void
+print_default_gateway(const int msglevel, const struct route_gateway_info *rgi)
+{
+ struct gc_arena gc = gc_new ();
+ if (rgi->flags & RGI_ADDR_DEFINED)
+ {
+ struct buffer out = alloc_buf_gc (256, &gc);
+ buf_printf (&out, "ROUTE_GATEWAY");
+ 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
+ if (rgi->flags & RGI_IFACE_DEFINED)
+ buf_printf (&out, " I=%u", (unsigned int)rgi->adapter_index);
+#else
+ if (rgi->flags & RGI_IFACE_DEFINED)
+ buf_printf (&out, " IFACE=%s", rgi->iface);
+#endif
+ if (rgi->flags & RGI_HWADDR_DEFINED)
+ buf_printf (&out, " HWADDR=%s", format_hex_ex (rgi->hwaddr, 6, 0, 1, ":", &gc));
+ msg (msglevel, "%s", BSTR (&out));
+ }
+ gc_free (&gc);
+}
+
#endif
static void
print_route (const struct route *r, int level)
{
struct gc_arena gc = gc_new ();
- if (r->defined)
+ if (r->flags & RT_DEFINED)
msg (level, "%s", route_string (r, &gc));
gc_free (&gc);
}
@@ -828,13 +929,13 @@ static void
setenv_route (struct env_set *es, const struct route *r, int i)
{
struct gc_arena gc = gc_new ();
- if (r->defined)
+ if (r->flags & RT_DEFINED)
{
setenv_route_addr (es, "network", r->network, i);
setenv_route_addr (es, "netmask", r->netmask, i);
setenv_route_addr (es, "gateway", r->gateway, i);
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
{
struct buffer name = alloc_buf_gc (256, &gc);
buf_printf (&name, "route_metric_%d", i);
@@ -852,8 +953,65 @@ setenv_routes (struct env_set *es, const struct route_list *rl)
setenv_route (es, &rl->routes[i], i + 1);
}
+/*
+ * local_route() determines whether the gateway of a provided host
+ * route is on the same interface that owns the default gateway.
+ * It uses the data structure
+ * returned by get_default_gateway() (struct route_gateway_info)
+ * to determine this. If the route is local, LR_MATCH is returned.
+ * When adding routes into the kernel, if LR_MATCH is defined for
+ * a given route, the route should explicitly reference the default
+ * gateway interface as the route destination. For example, here
+ * is an example on Linux that uses LR_MATCH:
+ *
+ * route add -net 10.10.0.1 netmask 255.255.255.255 dev eth0
+ *
+ * This capability is needed by the "default-gateway block-local"
+ * directive, to allow client access to the local subnet to be
+ * blocked but still allow access to the local default gateway.
+ */
+
+/* local_route() return values */
+#define LR_NOMATCH 0 /* route is not local */
+#define LR_MATCH 1 /* route is local */
+#define LR_ERROR 2 /* caller should abort adding route */
+
+static int
+local_route (in_addr_t network,
+ in_addr_t netmask,
+ in_addr_t gateway,
+ const struct route_gateway_info *rgi)
+{
+ /* set LR_MATCH on local host routes */
+ const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED|RGI_IFACE_DEFINED);
+ if (rgi
+ && (rgi->flags & rgi_needed) == rgi_needed
+ && gateway == rgi->gateway.addr
+ && netmask == 0xFFFFFFFF)
+ {
+ if (((network ^ rgi->gateway.addr) & rgi->gateway.netmask) == 0)
+ return LR_MATCH;
+ else
+ {
+ /* examine additional subnets on gateway interface */
+ size_t i;
+ for (i = 0; i < rgi->n_addrs; ++i)
+ {
+ const struct route_gateway_address *gwa = &rgi->addrs[i];
+ if (((network ^ gwa->addr) & gwa->netmask) == 0)
+ return LR_MATCH;
+ }
+ }
+ }
+ return LR_NOMATCH;
+}
+
void
-add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+add_route (struct route *r,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct route_gateway_info *rgi, /* may be NULL */
+ const struct env_set *es)
{
struct gc_arena gc;
struct argv argv;
@@ -861,8 +1019,9 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
const char *netmask;
const char *gateway;
bool status = false;
+ int is_local_route;
- if (!r->defined)
+ if (!(r->flags & RT_DEFINED))
return;
gc_init (&gc);
@@ -872,78 +1031,84 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
netmask = print_in_addr_t (r->netmask, 0, &gc);
gateway = print_in_addr_t (r->gateway, 0, &gc);
- /*
- * Filter out routes which are essentially no-ops
- */
- if (r->network == r->gateway && r->netmask == 0xFFFFFFFF)
- {
- msg (M_INFO, PACKAGE_NAME " ROUTE: omitted no-op route: %s/%s -> %s",
- network, netmask, gateway);
- goto done;
- }
+ is_local_route = local_route(r->network, r->netmask, r->gateway, rgi);
+ if (is_local_route == LR_ERROR)
+ goto done;
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
+ /* FIXME -- add LR_MATCH support for CONFIG_FEATURE_IPROUTE */
argv_printf (&argv, "%s route add %s/%d via %s",
iproute_path,
network,
count_netmask_bits(netmask),
gateway);
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "metric %d", r->metric);
#else
- argv_printf (&argv, "%s add -net %s netmask %s gw %s",
- ROUTE_PATH,
- network,
- netmask,
- gateway);
- if (r->metric_defined)
+ argv_printf (&argv, "%s add -net %s netmask %s",
+ ROUTE_PATH,
+ network,
+ netmask);
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "metric %d", r->metric);
+ if (rgi && is_local_route == LR_MATCH)
+ argv_printf_cat (&argv, "dev %s", rgi->iface);
+ else
+ argv_printf_cat (&argv, "gw %s", gateway);
+
#endif /*CONFIG_FEATURE_IPROUTE*/
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");
#elif defined (WIN32)
+ {
+ DWORD ai = ~0;
+ argv_printf (&argv, "%s%sc ADD %s MASK %s %s",
+ get_win_sys_path(),
+ WIN_ROUTE_PATH_SUFFIX,
+ network,
+ netmask,
+ gateway);
+ if (r->flags & RT_METRIC_DEFINED)
+ argv_printf_cat (&argv, "METRIC %d", r->metric);
+ if (rgi && is_local_route == LR_MATCH)
+ {
+ ai = rgi->adapter_index;
+ argv_printf_cat (&argv, "IF %u", (unsigned int)ai);
+ }
- argv_printf (&argv, "%s%sc ADD %s MASK %s %s",
- get_win_sys_path(),
- WIN_ROUTE_PATH_SUFFIX,
- network,
- netmask,
- gateway);
- if (r->metric_defined)
- argv_printf_cat (&argv, "METRIC %d", r->metric);
-
- argv_msg (D_ROUTE, &argv);
+ argv_msg (D_ROUTE, &argv);
- if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
- {
- status = add_route_ipapi (r, tt);
- msg (D_ROUTE, "Route addition via IPAPI %s", status ? "succeeded" : "failed");
- }
- else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
- {
- netcmd_semaphore_lock ();
- status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
- netcmd_semaphore_release ();
- }
- else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
- {
- status = add_route_ipapi (r, tt);
- msg (D_ROUTE, "Route addition via IPAPI %s [adaptive]", status ? "succeeded" : "failed");
- if (!status)
- {
- msg (D_ROUTE, "Route addition fallback to route.exe");
- netcmd_semaphore_lock ();
- status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
- netcmd_semaphore_release ();
- }
- }
- else
- {
- ASSERT (0);
- }
+ 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");
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
+ {
+ netcmd_semaphore_lock ();
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
+ netcmd_semaphore_release ();
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
+ {
+ status = add_route_ipapi (r, tt, ai);
+ msg (D_ROUTE, "Route addition via IPAPI %s [adaptive]", status ? "succeeded" : "failed");
+ if (!status)
+ {
+ msg (D_ROUTE, "Route addition fallback to route.exe");
+ netcmd_semaphore_lock ();
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
+ netcmd_semaphore_release ();
+ }
+ }
+ else
+ {
+ ASSERT (0);
+ }
+ }
#elif defined (TARGET_SOLARIS)
@@ -953,7 +1118,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
ROUTE_PATH);
#if 0
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
@@ -962,6 +1127,8 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
netmask,
gateway);
+ /* FIXME -- add LR_MATCH support for Solaris */
+
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");
@@ -971,7 +1138,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
ROUTE_PATH);
#if 0
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
@@ -980,6 +1147,8 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
gateway,
netmask);
+ /* FIXME -- add LR_MATCH support for FreeBSD */
+
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed");
@@ -989,7 +1158,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
ROUTE_PATH);
#if 0
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
@@ -998,6 +1167,8 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
gateway,
netmask);
+ /* FIXME -- add LR_MATCH support for Dragonfly */
+
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed");
@@ -1007,14 +1178,26 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
ROUTE_PATH);
#if 0
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
- argv_printf_cat (&argv, "-net %s %s %s",
- network,
- gateway,
- netmask);
+ if (rgi && is_local_route == LR_MATCH)
+ {
+ /* Mac OS X route syntax for LR_MATCH:
+ route add -cloning -net 10.10.0.1 -netmask 255.255.255.255 -interface en0 */
+ argv_printf_cat (&argv, "-cloning -net %s -netmask %s -interface %s",
+ network,
+ netmask,
+ rgi->iface);
+ }
+ else
+ {
+ argv_printf_cat (&argv, "-net %s %s %s",
+ network,
+ gateway,
+ netmask);
+ }
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");
@@ -1025,7 +1208,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
ROUTE_PATH);
#if 0
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
@@ -1034,6 +1217,8 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
gateway,
netmask);
+ /* FIXME -- add LR_MATCH support for OpenBSD/NetBSD */
+
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
@@ -1042,21 +1227,29 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
#endif
done:
- r->defined = status;
+ if (status)
+ r->flags |= RT_ADDED;
+ else
+ r->flags &= ~RT_ADDED;
argv_reset (&argv);
gc_free (&gc);
}
static void
-delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+delete_route (struct route *r,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct route_gateway_info *rgi,
+ const struct env_set *es)
{
struct gc_arena gc;
struct argv argv;
const char *network;
const char *netmask;
const char *gateway;
+ int is_local_route;
- if (!r->defined)
+ if ((r->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED))
return;
gc_init (&gc);
@@ -1066,6 +1259,10 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
netmask = print_in_addr_t (r->netmask, 0, &gc);
gateway = print_in_addr_t (r->gateway, 0, &gc);
+ is_local_route = local_route(r->network, r->netmask, r->gateway, rgi);
+ if (is_local_route == LR_ERROR)
+ goto done;
+
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
argv_printf (&argv, "%s route del %s/%d",
@@ -1073,13 +1270,12 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
network,
count_netmask_bits(netmask));
#else
-
argv_printf (&argv, "%s del -net %s netmask %s",
- ROUTE_PATH,
- network,
- netmask);
+ ROUTE_PATH,
+ network,
+ netmask);
#endif /*CONFIG_FEATURE_IPROUTE*/
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "metric %d", r->metric);
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed");
@@ -1158,11 +1354,22 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
#elif defined(TARGET_DARWIN)
- argv_printf (&argv, "%s delete -net %s %s %s",
- ROUTE_PATH,
- network,
- gateway,
- netmask);
+ if (rgi && is_local_route == LR_MATCH)
+ {
+ argv_printf (&argv, "%s delete -cloning -net %s -netmask %s -interface %s",
+ ROUTE_PATH,
+ network,
+ netmask,
+ rgi->iface);
+ }
+ else
+ {
+ argv_printf (&argv, "%s delete -net %s %s %s",
+ ROUTE_PATH,
+ network,
+ gateway,
+ netmask);
+ }
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: OS X route delete command failed");
@@ -1182,6 +1389,8 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
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
+ done:
+ r->flags &= ~RT_ADDED;
argv_reset (&argv);
gc_free (&gc);
}
@@ -1273,7 +1482,7 @@ test_routes (const struct route_list *rl, const struct tuntap *tt)
for (i = 0; i < rl->n; ++i)
test_route_helper (&ret, &count, &good, &ambig, adapters, rl->routes[i].gateway);
- if ((rl->flags & RG_ENABLE) && rl->spec.remote_endpoint_defined)
+ if ((rl->flags & RG_ENABLE) && (rl->spec.flags & RTSA_REMOTE_ENDPOINT))
test_route_helper (&ret, &count, &good, &ambig, adapters, rl->spec.remote_endpoint);
}
}
@@ -1331,29 +1540,41 @@ get_default_gateway_row (const MIB_IPFORWARDTABLE *routes)
return ret;
}
-bool
-get_default_gateway (in_addr_t *gw, in_addr_t *netmask)
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
- bool ret_bool = false;
const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
const MIB_IPFORWARDTABLE *routes = get_windows_routing_table (&gc);
const MIB_IPFORWARDROW *row = get_default_gateway_row (routes);
+ DWORD a_index;
+ const IP_ADAPTER_INFO *ai;
+
+ CLEAR(*rgi);
if (row)
{
- *gw = ntohl (row->dwForwardNextHop);
- if (netmask)
+ rgi->gateway.addr = ntohl (row->dwForwardNextHop);
+ if (rgi->gateway.addr)
{
- if (adapter_index_of_ip (adapters, *gw, NULL, netmask) == ~0)
- *netmask = ~0;
+ rgi->flags |= RGI_ADDR_DEFINED;
+ a_index = adapter_index_of_ip (adapters, rgi->gateway.addr, NULL, &rgi->gateway.netmask);
+ if (a_index != ~0)
+ {
+ rgi->adapter_index = a_index;
+ rgi->flags |= (RGI_IFACE_DEFINED|RGI_NETMASK_DEFINED);
+ ai = get_adapter (adapters, a_index);
+ if (ai)
+ {
+ memcpy (rgi->hwaddr, ai->Address, 6);
+ rgi->flags |= RGI_HWADDR_DEFINED;
+ }
+ }
}
- ret_bool = true;
}
gc_free (&gc);
- return ret_bool;
}
static DWORD
@@ -1402,12 +1623,12 @@ windows_route_find_if_index (const struct route *r, const struct tuntap *tt)
}
bool
-add_route_ipapi (const struct route *r, const struct tuntap *tt)
+add_route_ipapi (const struct route *r, const struct tuntap *tt, DWORD adapter_index)
{
struct gc_arena gc = gc_new ();
bool ret = false;
DWORD status;
- const DWORD if_index = windows_route_find_if_index (r, tt);
+ const DWORD if_index = (adapter_index == ~0) ? windows_route_find_if_index (r, tt) : adapter_index;
if (if_index != ~0)
{
@@ -1422,7 +1643,7 @@ add_route_ipapi (const struct route *r, const struct tuntap *tt)
fr.dwForwardProto = 3; /* PROTO_IP_NETMGMT */
fr.dwForwardAge = 0;
fr.dwForwardNextHopAS = 0;
- fr.dwForwardMetric1 = r->metric_defined ? r->metric : 1;
+ fr.dwForwardMetric1 = (r->flags & RT_METRIC_DEFINED) ? r->metric : 1;
fr.dwForwardMetric2 = ~0;
fr.dwForwardMetric3 = ~0;
fr.dwForwardMetric4 = ~0;
@@ -1553,75 +1774,136 @@ show_routes (int msglev)
#elif defined(TARGET_LINUX)
-bool
-get_default_gateway (in_addr_t *gateway, in_addr_t *netmask)
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
- bool ret = false;
- FILE *fp = fopen ("/proc/net/route", "r");
- if (fp)
- {
- char line[256];
- int count = 0;
- int best_count = 0;
- unsigned int lowest_metric = ~0;
- in_addr_t best_gw = 0;
- while (fgets (line, sizeof (line), fp) != NULL)
+ int sd = -1;
+
+ CLEAR(*rgi);
+
+ /* get default gateway IP addr */
+ {
+ FILE *fp = fopen ("/proc/net/route", "r");
+ if (fp)
+ {
+ char line[256];
+ int count = 0;
+ unsigned int lowest_metric = ~0;
+ in_addr_t best_gw = 0;
+ while (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if (count)
+ {
+ unsigned int net_x = 0;
+ unsigned int mask_x = 0;
+ unsigned int gw_x = 0;
+ unsigned int metric = 0;
+ unsigned int flags = 0;
+ const int np = sscanf (line, "%*s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x",
+ &net_x,
+ &gw_x,
+ &flags,
+ &metric,
+ &mask_x);
+ if (np == 5 && (flags & IFF_UP))
+ {
+ const in_addr_t net = ntohl (net_x);
+ const in_addr_t mask = ntohl (mask_x);
+ const in_addr_t gw = ntohl (gw_x);
+
+ if (!net && !mask && metric < lowest_metric)
+ {
+ best_gw = gw;
+ lowest_metric = metric;
+ }
+ }
+ }
+ ++count;
+ }
+ fclose (fp);
+
+ if (best_gw)
+ {
+ rgi->gateway.addr = best_gw;
+ rgi->flags |= RGI_ADDR_DEFINED;
+ }
+ }
+ }
+
+ /* scan adapter list */
+ if (rgi->flags & RGI_ADDR_DEFINED)
+ {
+ struct ifreq *ifr, *ifend;
+ in_addr_t addr, netmask;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[20]; /* Maximum number of interfaces to scan */
+
+ if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
{
- if (count)
- {
- unsigned int net_x = 0;
- unsigned int mask_x = 0;
- unsigned int gw_x = 0;
- unsigned int metric = 0;
- const int np = sscanf (line, "%*s\t%x\t%x\t%*s\t%*s\t%*s\t%d\t%x",
- &net_x,
- &gw_x,
- &metric,
- &mask_x);
- if (np == 4)
- {
- const in_addr_t net = ntohl (net_x);
- const in_addr_t mask = ntohl (mask_x);
- const in_addr_t gw = ntohl (gw_x);
-
- dmsg (D_ROUTE_DEBUG, "GDG: route[%d] %s/%s/%s m=%u",
- count,
- print_in_addr_t ((in_addr_t) net, 0, &gc),
- print_in_addr_t ((in_addr_t) mask, 0, &gc),
- print_in_addr_t ((in_addr_t) gw, 0, &gc),
- metric);
-
- if (!net && !mask && metric < lowest_metric)
- {
- best_gw = gw;
- lowest_metric = metric;
- best_count = count;
- }
- }
- }
- ++count;
+ msg (M_WARN, "GDG: socket() failed");
+ goto done;
+ }
+ ifc.ifc_len = sizeof (ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl (sd, SIOCGIFCONF, &ifc) < 0)
+ {
+ msg (M_WARN, "GDG: ioctl(SIOCGIFCONF) failed");
+ goto done;
}
- fclose (fp);
- if (best_gw)
+ /* scan through interface list */
+ ifend = ifs + (ifc.ifc_len / sizeof (struct ifreq));
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
{
- *gateway = best_gw;
- if (netmask)
+ if (ifr->ifr_addr.sa_family == AF_INET)
{
- *netmask = 0xFFFFFF00; /* FIXME -- get the real netmask of the adapter containing the default gateway */
+ /* get interface addr */
+ addr = ntohl(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr);
+
+ /* get interface name */
+ strncpynt (ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
+
+ /* check that the interface is up, and not point-to-point or loopback */
+ if (ioctl (sd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if (!(ifreq.ifr_flags & IFF_UP))
+ continue;
+
+ /* get interface netmask */
+ if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ netmask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr);
+
+ /* check that interface matches default route */
+ if (((rgi->gateway.addr ^ addr) & netmask) != 0)
+ continue;
+
+ /* save iface name and netmask */
+ strncpynt (rgi->iface, ifreq.ifr_name, sizeof(rgi->iface));
+ rgi->gateway.netmask = netmask;
+ rgi->flags |= (RGI_IFACE_DEFINED|RGI_NETMASK_DEFINED);
+
+ /* now get the hardware address. */
+ memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+ if (ioctl (sd, SIOCGIFHWADDR, &ifreq) < 0)
+ {
+ msg (M_WARN, "GDG: SIOCGIFHWADDR(%s) failed", ifreq.ifr_name);
+ goto done;
+ }
+ memcpy (rgi->hwaddr, &ifreq.ifr_hwaddr.sa_data, 6);
+ rgi->flags |= RGI_HWADDR_DEFINED;
+
+ break;
}
- ret = true;
}
-
- dmsg (D_ROUTE_DEBUG, "GDG: best=%s[%d] lm=%u",
- print_in_addr_t ((in_addr_t) best_gw, 0, &gc),
- best_count,
- (unsigned int)lowest_metric);
}
+ done:
+ if (sd >= 0)
+ close (sd);
gc_free (&gc);
- return ret;
}
#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
@@ -1685,8 +1967,11 @@ struct {
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
-bool
-get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
+/*
+ * FIXME -- add support for netmask, hwaddr, and iface
+ */
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
int s, seq, l, pid, rtm_addrs, i;
@@ -1704,6 +1989,8 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
#define rtm m_rtmsg.m_rtm
+ CLEAR(*rgi);
+
pid = getpid();
seq = 0;
rtm_addrs = RTA_DST | RTA_NETMASK;
@@ -1765,24 +2052,14 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
if (gate != NULL )
{
- *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
-#if 0
- msg (M_INFO, "gw %s",
- print_in_addr_t ((in_addr_t) *ret, 0, &gc));
-#endif
-
- if (netmask)
- {
- *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
- }
+ rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+ rgi->flags |= RGI_ADDR_DEFINED;
gc_free (&gc);
- return true;
}
else
{
gc_free (&gc);
- return false;
}
}
@@ -1802,26 +2079,32 @@ struct rtmsg {
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
-static bool
-get_default_gateway_ex (in_addr_t *ret, in_addr_t *netmask, char **ifname)
+#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))
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
struct rtmsg m_rtmsg;
- int s, seq, l, pid, rtm_addrs, i;
+ int sockfd = -1;
+ int seq, l, pid, rtm_addrs, i;
struct sockaddr so_dst, so_mask;
char *cp = m_rtmsg.m_space;
struct sockaddr *gate = NULL, *ifp = 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;\
- }
+# define rtm m_rtmsg.m_rtm
-#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
-
-#define rtm m_rtmsg.m_rtm
+ CLEAR(*rgi);
+ /* setup data to send to routing socket */
pid = getpid();
seq = 0;
rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP;
@@ -1847,85 +2130,148 @@ get_default_gateway_ex (in_addr_t *ret, in_addr_t *netmask, char **ifname)
rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
- s = socket(PF_ROUTE, SOCK_RAW, 0);
-
- 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, "ROUTE: problem writing to routing socket");
- gc_free (&gc);
- close(s);
- return false;
+ msg (M_WARN, "GDG: socket #1 failed");
+ goto done;
+ }
+ if (write(sockfd, (char *)&m_rtmsg, l) < 0)
+ {
+ msg (M_WARN, "GDG: 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);
+ l = read(sockfd, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+ close(sockfd);
+ sockfd = -1;
+ /* extract return data from routing socket */
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;
- else if (i == RTA_IFP)
- ifp = sa;
- ADVANCE(cp, sa);
- }
- }
- }
+ 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;
+ else if (i == RTA_IFP)
+ ifp = sa;
+ ADVANCE(cp, sa);
+ }
+ }
+ }
else
+ goto done;
+
+ /* get gateway addr and interface name */
+ if (gate != NULL )
{
- gc_free (&gc);
- return false;
+ /* get default gateway addr */
+ rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+ if (rgi->gateway.addr)
+ rgi->flags |= RGI_ADDR_DEFINED;
+
+ if (ifp)
+ {
+ /* 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);
+ rgi->iface[adl->sdl_nlen] = '\0';
+ rgi->flags |= RGI_IFACE_DEFINED;
+ }
+ }
}
+ /* get netmask of interface that owns default gateway */
+ if (rgi->flags & RGI_IFACE_DEFINED) {
+ struct ifreq ifr;
- if (gate != NULL )
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ msg (M_WARN, "GDG: socket #2 failed");
+ goto done;
+ }
+
+ CLEAR(ifr);
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpynt(ifr.ifr_name, rgi->iface, IFNAMSIZ);
+
+ if (ioctl(sockfd, SIOCGIFNETMASK, (char *)&ifr) < 0)
+ {
+ msg (M_WARN, "GDG: ioctl #1 failed");
+ goto done;
+ }
+ close(sockfd);
+ sockfd = -1;
+
+ rgi->gateway.netmask = ntohl(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr);
+ rgi->flags |= RGI_NETMASK_DEFINED;
+ }
+
+ /* try to read MAC addr associated with interface that owns default gateway */
+ if (rgi->flags & RGI_IFACE_DEFINED)
{
- *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
-#if 0
- msg (M_INFO, "gw %s",
- print_in_addr_t ((in_addr_t) *ret, 0, &gc));
-#endif
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ const int bufsize = 4096;
+ char *buffer;
- if (netmask)
+ buffer = (char *) gc_malloc (bufsize, true, &gc);
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
{
- *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
+ msg (M_WARN, "GDG: socket #3 failed");
+ goto done;
}
- if (ifp && ifname)
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = buffer;
+
+ if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
{
- struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
- char *name = malloc(adl->sdl_nlen+1);
- check_malloc_return(name);
- memcpy(name, adl->sdl_data, adl->sdl_nlen);
- name[adl->sdl_nlen] = '\0';
- *ifname = name;
+ msg (M_WARN, "GDG: ioctl #2 failed");
+ goto done;
}
+ close(sockfd);
+ sockfd = -1;
- gc_free (&gc);
- return true;
- }
- else
- {
- gc_free (&gc);
- return false;
+ for (cp = buffer; cp <= buffer + ifc.ifc_len - sizeof(struct ifreq); )
+ {
+ ifr = (struct ifreq *)cp;
+ const size_t len = sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
+ if (!ifr->ifr_addr.sa_family)
+ break;
+ if (!strncmp(ifr->ifr_name, rgi->iface, IFNAMSIZ))
+ {
+ if (ifr->ifr_addr.sa_family == AF_LINK)
+ {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
+ memcpy(rgi->hwaddr, LLADDR(sdl), 6);
+ rgi->flags |= RGI_HWADDR_DEFINED;
+ }
+ }
+ cp += len;
+ }
}
-}
-bool
-get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
-{
- return get_default_gateway_ex(ret, netmask, NULL);
+ done:
+ if (sockfd >= 0)
+ close(sockfd);
+ gc_free (&gc);
}
+#undef max
+
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
#include <sys/types.h>
@@ -1986,8 +2332,11 @@ struct {
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
-bool
-get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
+/*
+ * FIXME -- add support for netmask, hwaddr, and iface
+ */
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
int s, seq, l, rtm_addrs, i;
@@ -2006,6 +2355,8 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
#define rtm m_rtmsg.m_rtm
+ CLEAR(*rgi);
+
pid = getpid();
seq = 0;
rtm_addrs = RTA_DST | RTA_NETMASK;
@@ -2067,33 +2418,47 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
if (gate != NULL )
{
- *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
-#if 0
- msg (M_INFO, "gw %s",
- print_in_addr_t ((in_addr_t) *ret, 0, &gc));
-#endif
-
- if (netmask)
- {
- *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
- }
+ rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+ rgi->flags |= RGI_ADDR_DEFINED;
gc_free (&gc);
- return true;
}
else
{
gc_free (&gc);
- return false;
}
}
#else
-bool
-get_default_gateway (in_addr_t *ret, in_addr_t *netmask) /* PLATFORM-SPECIFIC */
+/*
+ * This is a platform-specific method that returns data about
+ * the current default gateway. Return data is placed into
+ * a struct route_gateway_info object provided by caller. The
+ * implementation should CLEAR the structure before adding
+ * data to it.
+ *
+ * Data returned includes:
+ * 1. default gateway address (rgi->gateway.addr)
+ * 2. netmask of interface that owns default gateway
+ * (rgi->gateway.netmask)
+ * 3. hardware address (i.e. MAC address) of interface that owns
+ * default gateway (rgi->hwaddr)
+ * 4. interface name (or adapter index on Windows) that owns default
+ * gateway (rgi->iface or rgi->adapter_index)
+ * 5. an array of additional address/netmask pairs defined by
+ * interface that owns default gateway (rgi->addrs with length
+ * given in rgi->n_addrs)
+ *
+ * The flags RGI_x_DEFINED may be used to indicate which of the data
+ * members were successfully returned (set in rgi->flags). All of
+ * the data members are optional, however certain OpenVPN functionality
+ * may be disabled by missing items.
+ */
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
- return false;
+ CLEAR(*rgi);
}
#endif
@@ -2133,7 +2498,7 @@ netmask_to_netbits (const in_addr_t network, const in_addr_t netmask, int *netbi
static void
add_host_route_if_nonlocal (struct route_bypass *rb, const in_addr_t addr)
{
- if (test_local_addr(addr) == TLA_NONLOCAL && addr != 0 && addr != ~0)
+ if (test_local_addr(addr, NULL) == TLA_NONLOCAL && addr != 0 && addr != ~0)
add_bypass_address (rb, addr);
}
@@ -2193,222 +2558,6 @@ get_bypass_addresses (struct route_bypass *rb, const unsigned int flags) /* PLA
#endif
-#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO)
-
-#if defined(TARGET_LINUX)
-
-bool
-get_default_gateway_mac_addr (unsigned char *macaddr)
-{
- struct ifreq *ifr, *ifend;
- in_addr_t ina, mask;
- struct ifreq ifreq;
- struct ifconf ifc;
- struct ifreq ifs[20]; // Maximum number of interfaces to scan
- int sd = -1;
- in_addr_t gwip = 0;
- bool ret = false;
-
- if (!get_default_gateway (&gwip, NULL))
- {
- msg (M_WARN, "GDGMA: get_default_gateway failed");
- goto err;
- }
-
- if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
- {
- msg (M_WARN, "GDGMA: socket() failed");
- goto err;
- }
-
- ifc.ifc_len = sizeof (ifs);
- ifc.ifc_req = ifs;
- if (ioctl (sd, SIOCGIFCONF, &ifc) < 0)
- {
- msg (M_WARN, "GDGMA: ioctl(SIOCGIFCONF) failed");
- goto err;
- }
-
- /* scan through interface list */
- ifend = ifs + (ifc.ifc_len / sizeof (struct ifreq));
- for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
- {
- if (ifr->ifr_addr.sa_family == AF_INET)
- {
- ina = ntohl(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr);
- strncpynt (ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
-
- dmsg (D_AUTO_USERID, "GDGMA: %s", ifreq.ifr_name);
-
- /* check that the interface is up, and not point-to-point or loopback */
- if (ioctl (sd, SIOCGIFFLAGS, &ifreq) < 0)
- {
- dmsg (D_AUTO_USERID, "GDGMA: SIOCGIFFLAGS(%s) failed", ifreq.ifr_name);
- continue;
- }
-
- if ((ifreq.ifr_flags & (IFF_UP|IFF_LOOPBACK)) != IFF_UP)
- {
- dmsg (D_AUTO_USERID, "GDGMA: interface %s is down or loopback", ifreq.ifr_name);
- continue;
- }
-
- /* get interface netmask and check for correct subnet */
- if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0)
- {
- dmsg (D_AUTO_USERID, "GDGMA: SIOCGIFNETMASK(%s) failed", ifreq.ifr_name);
- continue;
- }
-
- mask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr);
- if (((gwip ^ ina) & mask) != 0)
- {
- dmsg (D_AUTO_USERID, "GDGMA: gwip=0x%08x ina=0x%08x mask=0x%08x",
- (unsigned int)gwip,
- (unsigned int)ina,
- (unsigned int)mask);
- continue;
- }
- break;
- }
- }
- if (ifr >= ifend)
- {
- msg (M_WARN, "GDGMA: couldn't find gw interface");
- goto err;
- }
-
- /* now get the hardware address. */
- memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
- if (ioctl (sd, SIOCGIFHWADDR, &ifreq) < 0)
- {
- msg (M_WARN, "GDGMA: SIOCGIFHWADDR(%s) failed", ifreq.ifr_name);
- goto err;
- }
-
- memcpy (macaddr, &ifreq.ifr_hwaddr.sa_data, 6);
- ret = true;
-
- err:
- if (sd >= 0)
- close (sd);
- return ret;
-}
-
-#elif defined(WIN32)
-
-bool
-get_default_gateway_mac_addr (unsigned char *macaddr)
-{
- struct gc_arena gc = gc_new ();
- const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
- in_addr_t gwip = 0;
- DWORD a_index;
- const IP_ADAPTER_INFO *ai;
-
- if (!get_default_gateway (&gwip, NULL))
- {
- msg (M_WARN, "GDGMA: get_default_gateway failed");
- goto err;
- }
-
- a_index = adapter_index_of_ip (adapters, gwip, NULL, NULL);
- ai = get_adapter (adapters, a_index);
-
- if (!ai)
- {
- msg (M_WARN, "GDGMA: couldn't find gw interface");
- goto err;
- }
-
- memcpy (macaddr, ai->Address, 6);
-
- gc_free (&gc);
- return true;
-
- err:
- gc_free (&gc);
- return false;
-}
-
-#elif defined(TARGET_DARWIN)
-
-bool
-get_default_gateway_mac_addr (unsigned char *macaddr)
-{
-# define max(a,b) ((a) > (b) ? (a) : (b))
- struct gc_arena gc = gc_new ();
- struct ifconf ifc;
- struct ifreq *ifr;
- char *buffer, *cp;
- bool status = false;
- in_addr_t gw = 0;
- char *ifname = NULL;
- int sockfd = -1;
- const int bufsize = 4096;
-
- if (!get_default_gateway_ex (&gw, NULL, &ifname)) /* get interface name of default gateway */
- {
- msg (M_WARN, "GDGMA: get_default_gateway_ex failed");
- goto done;
- }
-
- if (!ifname)
- {
- msg (M_WARN, "GDGMA: cannot get default gateway ifname");
- goto done;
- }
-
- buffer = (char *) gc_malloc (bufsize, true, &gc);
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sockfd < 0)
- {
- msg (M_WARN, "GDGMA: socket failed");
- goto done;
- }
-
- ifc.ifc_len = bufsize;
- ifc.ifc_buf = buffer;
-
- if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
- {
- msg (M_WARN, "GDGMA: ioctl failed");
- goto done;
- }
-
- for (cp = buffer; cp <= buffer + bufsize - sizeof(struct ifreq); )
- {
- ifr = (struct ifreq *)cp;
- if (ifr->ifr_addr.sa_family == AF_LINK && !strncmp(ifr->ifr_name, ifname, IFNAMSIZ))
- {
- struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
- memcpy(macaddr, LLADDR(sdl), 6);
- status = true;
- }
- cp += sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
- }
-
- done:
- if (sockfd >= 0)
- close (sockfd);
- free (ifname);
- gc_free (&gc);
- return status;
-# undef max
-}
-
-#else
-
-bool
-get_default_gateway_mac_addr (unsigned char *macaddr) /* PLATFORM-SPECIFIC */
-{
- return false;
-}
-
-#endif
-#endif /* AUTO_USERID */
-
/*
* Test if addr is reachable via a local interface (return ILA_LOCAL),
* or if it needs to be routed via the default gateway (return
@@ -2421,7 +2570,7 @@ get_default_gateway_mac_addr (unsigned char *macaddr) /* PLATFORM-SPECIFIC */
#if defined(WIN32)
int
-test_local_addr (const in_addr_t addr)
+test_local_addr (const in_addr_t addr, const struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
const in_addr_t nonlocal_netmask = 0x80000000L; /* routes with netmask <= to this are considered non-local */
@@ -2451,10 +2600,16 @@ test_local_addr (const in_addr_t addr)
#else
-
int
-test_local_addr (const in_addr_t addr) /* PLATFORM-SPECIFIC */
+test_local_addr (const in_addr_t addr, const struct route_gateway_info *rgi) /* PLATFORM-SPECIFIC */
{
+ if (rgi)
+ {
+ if (local_route (addr, 0xFFFFFFFF, rgi->gateway.addr, rgi))
+ return TLA_LOCAL;
+ else
+ return TLA_NONLOCAL;
+ }
return TLA_NOT_IMPLEMENTED;
}