These are routes where the gateway is specified as an interface rather
than an address. This allows redirect-gateway to work on Linux clients
whose connection to the internet is via a point-to-point link such as
PPP.
Note that at the moment, this capability is incompatible with
the "redirect-gateway block-local" directive -- this is because
the block-local directive blocks all traffic from the local LAN
except for the local and gateway addresses. Since a PPP link
is essentially a subnet of two addresses, local and remote (i.e.
gateway), the set of addresses that would be blocked by block-local
is empty. Therefore, the "redirect-gateway block-local" directive
will be ignored on PPP links.
To view the OpenVPN client's current determination of the default
gateway, use this command:
./openvpn --show-gateway
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@7794 e7ae566f-a301-0410-adde-c780ea21d3b5
Signed-off-by: James Yonan <james@openvpn.net>
Signed-off-by: David Sommerseth <davids@redhat.com>
| ... | ... |
@@ -1,6 +1,28 @@ |
| 1 | 1 |
OpenVPN Change Log |
| 2 | 2 |
Copyright (C) 2002-2011 OpenVPN Technologies, Inc. <sales@openvpn.net> |
| 3 | 3 |
|
| 4 |
+2011.12.25 -- Version 2.x-master |
|
| 5 |
+James Yonan (1): |
|
| 6 |
+ Added support for "on-link" routes on Linux client -- these are |
|
| 7 |
+ routes where the gateway is specified as an interface rather than |
|
| 8 |
+ an address. This allows redirect-gateway to work on Linux clients |
|
| 9 |
+ whose connection to the internet is via a point-to-point link |
|
| 10 |
+ such as PPP. |
|
| 11 |
+ |
|
| 12 |
+ Note that at the moment, this capability is incompatible with |
|
| 13 |
+ the "redirect-gateway block-local" directive -- this is because |
|
| 14 |
+ the block-local directive blocks all traffic from the local LAN |
|
| 15 |
+ except for the local and gateway addresses. Since a PPP link |
|
| 16 |
+ is essentially a subnet of two addresses, local and remote (i.e. |
|
| 17 |
+ gateway), the set of addresses that would be blocked by block-local |
|
| 18 |
+ is empty. Therefore, the "redirect-gateway block-local" directive |
|
| 19 |
+ will be ignored on PPP links. |
|
| 20 |
+ |
|
| 21 |
+ To view the OpenVPN client's current determination of the default |
|
| 22 |
+ gateway, use this command: |
|
| 23 |
+ |
|
| 24 |
+ ./openvpn --show-gateway |
|
| 25 |
+ |
|
| 4 | 26 |
2011.03.24 -- Version 2.2-RC2 |
| 5 | 27 |
Alon Bar-Lev (1): |
| 6 | 28 |
Windows cross-compile cleanup |
| ... | ... |
@@ -794,7 +794,7 @@ add_bypass_routes (struct route_bypass *rb, |
| 794 | 794 |
~0, |
| 795 | 795 |
gateway, |
| 796 | 796 |
tt, |
| 797 |
- flags, |
|
| 797 |
+ flags | ROUTE_REF_GW, |
|
| 798 | 798 |
rgi, |
| 799 | 799 |
es); |
| 800 | 800 |
} |
| ... | ... |
@@ -816,7 +816,7 @@ del_bypass_routes (struct route_bypass *rb, |
| 816 | 816 |
~0, |
| 817 | 817 |
gateway, |
| 818 | 818 |
tt, |
| 819 |
- flags, |
|
| 819 |
+ flags | ROUTE_REF_GW, |
|
| 820 | 820 |
rgi, |
| 821 | 821 |
es); |
| 822 | 822 |
} |
| ... | ... |
@@ -867,7 +867,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u |
| 867 | 867 |
~0, |
| 868 | 868 |
rl->rgi.gateway.addr, |
| 869 | 869 |
tt, |
| 870 |
- flags, |
|
| 870 |
+ flags | ROUTE_REF_GW, |
|
| 871 | 871 |
&rl->rgi, |
| 872 | 872 |
es); |
| 873 | 873 |
rl->iflags |= RL_DID_LOCAL; |
| ... | ... |
@@ -908,7 +908,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u |
| 908 | 908 |
0, |
| 909 | 909 |
rl->rgi.gateway.addr, |
| 910 | 910 |
tt, |
| 911 |
- flags, |
|
| 911 |
+ flags | ROUTE_REF_GW, |
|
| 912 | 912 |
&rl->rgi, |
| 913 | 913 |
es); |
| 914 | 914 |
|
| ... | ... |
@@ -941,7 +941,7 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap * |
| 941 | 941 |
~0, |
| 942 | 942 |
rl->rgi.gateway.addr, |
| 943 | 943 |
tt, |
| 944 |
- flags, |
|
| 944 |
+ flags | ROUTE_REF_GW, |
|
| 945 | 945 |
&rl->rgi, |
| 946 | 946 |
es); |
| 947 | 947 |
rl->iflags &= ~RL_DID_LOCAL; |
| ... | ... |
@@ -988,7 +988,7 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap * |
| 988 | 988 |
0, |
| 989 | 989 |
rl->rgi.gateway.addr, |
| 990 | 990 |
tt, |
| 991 |
- flags, |
|
| 991 |
+ flags | ROUTE_REF_GW, |
|
| 992 | 992 |
&rl->rgi, |
| 993 | 993 |
es); |
| 994 | 994 |
} |
| ... | ... |
@@ -1122,7 +1122,10 @@ print_default_gateway(const int msglevel, const struct route_gateway_info *rgi) |
| 1122 | 1122 |
{
|
| 1123 | 1123 |
struct buffer out = alloc_buf_gc (256, &gc); |
| 1124 | 1124 |
buf_printf (&out, "ROUTE_GATEWAY"); |
| 1125 |
- buf_printf (&out, " %s", print_in_addr_t (rgi->gateway.addr, 0, &gc)); |
|
| 1125 |
+ if (rgi->flags & RGI_ON_LINK) |
|
| 1126 |
+ buf_printf (&out, " ON_LINK"); |
|
| 1127 |
+ else |
|
| 1128 |
+ buf_printf (&out, " %s", print_in_addr_t (rgi->gateway.addr, 0, &gc)); |
|
| 1126 | 1129 |
if (rgi->flags & RGI_NETMASK_DEFINED) |
| 1127 | 1130 |
buf_printf (&out, "/%s", print_in_addr_t (rgi->gateway.netmask, 0, &gc)); |
| 1128 | 1131 |
#ifdef WIN32 |
| ... | ... |
@@ -1267,6 +1270,14 @@ local_route (in_addr_t network, |
| 1267 | 1267 |
return LR_NOMATCH; |
| 1268 | 1268 |
} |
| 1269 | 1269 |
|
| 1270 |
+/* Return true if the "on-link" form of the route should be used. This is when the gateway for a |
|
| 1271 |
+ a route is specified as an interface rather than an address. */ |
|
| 1272 |
+static inline bool |
|
| 1273 |
+is_on_link (const int is_local_route, const unsigned int flags, const struct route_gateway_info *rgi) |
|
| 1274 |
+{
|
|
| 1275 |
+ return rgi && (is_local_route == LR_MATCH || ((flags & ROUTE_REF_GW) && (rgi->flags & RGI_ON_LINK))); |
|
| 1276 |
+} |
|
| 1277 |
+ |
|
| 1270 | 1278 |
void |
| 1271 | 1279 |
add_route (struct route *r, |
| 1272 | 1280 |
const struct tuntap *tt, |
| ... | ... |
@@ -1298,7 +1309,7 @@ add_route (struct route *r, |
| 1298 | 1298 |
|
| 1299 | 1299 |
#if defined(TARGET_LINUX) |
| 1300 | 1300 |
#ifdef CONFIG_FEATURE_IPROUTE |
| 1301 |
- /* FIXME -- add LR_MATCH support for CONFIG_FEATURE_IPROUTE */ |
|
| 1301 |
+ /* FIXME -- add on-link support for CONFIG_FEATURE_IPROUTE */ |
|
| 1302 | 1302 |
argv_printf (&argv, "%s route add %s/%d via %s", |
| 1303 | 1303 |
iproute_path, |
| 1304 | 1304 |
network, |
| ... | ... |
@@ -1314,7 +1325,7 @@ add_route (struct route *r, |
| 1314 | 1314 |
netmask); |
| 1315 | 1315 |
if (r->flags & RT_METRIC_DEFINED) |
| 1316 | 1316 |
argv_printf_cat (&argv, "metric %d", r->metric); |
| 1317 |
- if (rgi && is_local_route == LR_MATCH) |
|
| 1317 |
+ if (is_on_link (is_local_route, flags, rgi)) |
|
| 1318 | 1318 |
argv_printf_cat (&argv, "dev %s", rgi->iface); |
| 1319 | 1319 |
else |
| 1320 | 1320 |
argv_printf_cat (&argv, "gw %s", gateway); |
| ... | ... |
@@ -1334,7 +1345,7 @@ add_route (struct route *r, |
| 1334 | 1334 |
gateway); |
| 1335 | 1335 |
if (r->flags & RT_METRIC_DEFINED) |
| 1336 | 1336 |
argv_printf_cat (&argv, "METRIC %d", r->metric); |
| 1337 |
- if (rgi && is_local_route == LR_MATCH) |
|
| 1337 |
+ if (is_on_link (is_local_route, flags, rgi)) |
|
| 1338 | 1338 |
{
|
| 1339 | 1339 |
ai = rgi->adapter_index; |
| 1340 | 1340 |
argv_printf_cat (&argv, "IF %u", (unsigned int)ai); |
| ... | ... |
@@ -1388,7 +1399,7 @@ add_route (struct route *r, |
| 1388 | 1388 |
netmask, |
| 1389 | 1389 |
gateway); |
| 1390 | 1390 |
|
| 1391 |
- /* FIXME -- add LR_MATCH support for Solaris */ |
|
| 1391 |
+ /* FIXME -- add on-link support for Solaris */ |
|
| 1392 | 1392 |
|
| 1393 | 1393 |
argv_msg (D_ROUTE, &argv); |
| 1394 | 1394 |
status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed"); |
| ... | ... |
@@ -1408,7 +1419,7 @@ add_route (struct route *r, |
| 1408 | 1408 |
gateway, |
| 1409 | 1409 |
netmask); |
| 1410 | 1410 |
|
| 1411 |
- /* FIXME -- add LR_MATCH support for FreeBSD */ |
|
| 1411 |
+ /* FIXME -- add on-link support for FreeBSD */ |
|
| 1412 | 1412 |
|
| 1413 | 1413 |
argv_msg (D_ROUTE, &argv); |
| 1414 | 1414 |
status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed"); |
| ... | ... |
@@ -1428,7 +1439,7 @@ add_route (struct route *r, |
| 1428 | 1428 |
gateway, |
| 1429 | 1429 |
netmask); |
| 1430 | 1430 |
|
| 1431 |
- /* FIXME -- add LR_MATCH support for Dragonfly */ |
|
| 1431 |
+ /* FIXME -- add on-link support for Dragonfly */ |
|
| 1432 | 1432 |
|
| 1433 | 1433 |
argv_msg (D_ROUTE, &argv); |
| 1434 | 1434 |
status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed"); |
| ... | ... |
@@ -1443,9 +1454,9 @@ add_route (struct route *r, |
| 1443 | 1443 |
argv_printf_cat (&argv, "-rtt %d", r->metric); |
| 1444 | 1444 |
#endif |
| 1445 | 1445 |
|
| 1446 |
- if (rgi && is_local_route == LR_MATCH) |
|
| 1446 |
+ if (is_on_link (is_local_route, flags, rgi)) |
|
| 1447 | 1447 |
{
|
| 1448 |
- /* Mac OS X route syntax for LR_MATCH: |
|
| 1448 |
+ /* Mac OS X route syntax for ON_LINK: |
|
| 1449 | 1449 |
route add -cloning -net 10.10.0.1 -netmask 255.255.255.255 -interface en0 */ |
| 1450 | 1450 |
argv_printf_cat (&argv, "-cloning -net %s -netmask %s -interface %s", |
| 1451 | 1451 |
network, |
| ... | ... |
@@ -1478,7 +1489,7 @@ add_route (struct route *r, |
| 1478 | 1478 |
gateway, |
| 1479 | 1479 |
netmask); |
| 1480 | 1480 |
|
| 1481 |
- /* FIXME -- add LR_MATCH support for OpenBSD/NetBSD */ |
|
| 1481 |
+ /* FIXME -- add on-link support for OpenBSD/NetBSD */ |
|
| 1482 | 1482 |
|
| 1483 | 1483 |
argv_msg (D_ROUTE, &argv); |
| 1484 | 1484 |
status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed"); |
| ... | ... |
@@ -1796,7 +1807,7 @@ delete_route (struct route *r, |
| 1796 | 1796 |
|
| 1797 | 1797 |
#elif defined(TARGET_DARWIN) |
| 1798 | 1798 |
|
| 1799 |
- if (rgi && is_local_route == LR_MATCH) |
|
| 1799 |
+ if (is_on_link (is_local_route, flags, rgi)) |
|
| 1800 | 1800 |
{
|
| 1801 | 1801 |
argv_printf (&argv, "%s delete -cloning -net %s -netmask %s -interface %s", |
| 1802 | 1802 |
ROUTE_PATH, |
| ... | ... |
@@ -2357,6 +2368,8 @@ get_default_gateway (struct route_gateway_info *rgi) |
| 2357 | 2357 |
{
|
| 2358 | 2358 |
struct gc_arena gc = gc_new (); |
| 2359 | 2359 |
int sd = -1; |
| 2360 |
+ char best_name[16]; |
|
| 2361 |
+ best_name[0] = 0; |
|
| 2360 | 2362 |
|
| 2361 | 2363 |
CLEAR(*rgi); |
| 2362 | 2364 |
|
| ... | ... |
@@ -2369,6 +2382,7 @@ get_default_gateway (struct route_gateway_info *rgi) |
| 2369 | 2369 |
int count = 0; |
| 2370 | 2370 |
unsigned int lowest_metric = ~0; |
| 2371 | 2371 |
in_addr_t best_gw = 0; |
| 2372 |
+ bool found = false; |
|
| 2372 | 2373 |
while (fgets (line, sizeof (line), fp) != NULL) |
| 2373 | 2374 |
{
|
| 2374 | 2375 |
if (count) |
| ... | ... |
@@ -2378,13 +2392,16 @@ get_default_gateway (struct route_gateway_info *rgi) |
| 2378 | 2378 |
unsigned int gw_x = 0; |
| 2379 | 2379 |
unsigned int metric = 0; |
| 2380 | 2380 |
unsigned int flags = 0; |
| 2381 |
- const int np = sscanf (line, "%*s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x", |
|
| 2381 |
+ char name[16]; |
|
| 2382 |
+ name[0] = 0; |
|
| 2383 |
+ const int np = sscanf (line, "%15s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x", |
|
| 2384 |
+ name, |
|
| 2382 | 2385 |
&net_x, |
| 2383 | 2386 |
&gw_x, |
| 2384 | 2387 |
&flags, |
| 2385 | 2388 |
&metric, |
| 2386 | 2389 |
&mask_x); |
| 2387 |
- if (np == 5 && (flags & IFF_UP)) |
|
| 2390 |
+ if (np == 6 && (flags & IFF_UP)) |
|
| 2388 | 2391 |
{
|
| 2389 | 2392 |
const in_addr_t net = ntohl (net_x); |
| 2390 | 2393 |
const in_addr_t mask = ntohl (mask_x); |
| ... | ... |
@@ -2392,7 +2409,9 @@ get_default_gateway (struct route_gateway_info *rgi) |
| 2392 | 2392 |
|
| 2393 | 2393 |
if (!net && !mask && metric < lowest_metric) |
| 2394 | 2394 |
{
|
| 2395 |
+ found = true; |
|
| 2395 | 2396 |
best_gw = gw; |
| 2397 |
+ strcpy (best_name, name); |
|
| 2396 | 2398 |
lowest_metric = metric; |
| 2397 | 2399 |
} |
| 2398 | 2400 |
} |
| ... | ... |
@@ -2401,10 +2420,12 @@ get_default_gateway (struct route_gateway_info *rgi) |
| 2401 | 2401 |
} |
| 2402 | 2402 |
fclose (fp); |
| 2403 | 2403 |
|
| 2404 |
- if (best_gw) |
|
| 2404 |
+ if (found) |
|
| 2405 | 2405 |
{
|
| 2406 | 2406 |
rgi->gateway.addr = best_gw; |
| 2407 | 2407 |
rgi->flags |= RGI_ADDR_DEFINED; |
| 2408 |
+ if (!rgi->gateway.addr && best_name[0]) |
|
| 2409 |
+ rgi->flags |= RGI_ON_LINK; |
|
| 2408 | 2410 |
} |
| 2409 | 2411 |
} |
| 2410 | 2412 |
} |
| ... | ... |
@@ -2443,25 +2464,47 @@ get_default_gateway (struct route_gateway_info *rgi) |
| 2443 | 2443 |
/* get interface name */ |
| 2444 | 2444 |
strncpynt (ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name)); |
| 2445 | 2445 |
|
| 2446 |
- /* check that the interface is up, and not point-to-point or loopback */ |
|
| 2446 |
+ /* check that the interface is up */ |
|
| 2447 | 2447 |
if (ioctl (sd, SIOCGIFFLAGS, &ifreq) < 0) |
| 2448 | 2448 |
continue; |
| 2449 | 2449 |
if (!(ifreq.ifr_flags & IFF_UP)) |
| 2450 | 2450 |
continue; |
| 2451 | 2451 |
|
| 2452 |
- /* get interface netmask */ |
|
| 2453 |
- if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0) |
|
| 2454 |
- continue; |
|
| 2455 |
- netmask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr); |
|
| 2456 |
- |
|
| 2457 |
- /* check that interface matches default route */ |
|
| 2458 |
- if (((rgi->gateway.addr ^ addr) & netmask) != 0) |
|
| 2459 |
- continue; |
|
| 2452 |
+ if (rgi->flags & RGI_ON_LINK) |
|
| 2453 |
+ {
|
|
| 2454 |
+ /* check that interface name of current interface |
|
| 2455 |
+ matches interface name of best default route */ |
|
| 2456 |
+ if (strcmp(ifreq.ifr_name, best_name)) |
|
| 2457 |
+ continue; |
|
| 2458 |
+#if 0 |
|
| 2459 |
+ /* if point-to-point link, use remote addr as route gateway */ |
|
| 2460 |
+ if ((ifreq.ifr_flags & IFF_POINTOPOINT) && ioctl (sd, SIOCGIFDSTADDR, &ifreq) >= 0) |
|
| 2461 |
+ {
|
|
| 2462 |
+ rgi->gateway.addr = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr); |
|
| 2463 |
+ if (rgi->gateway.addr) |
|
| 2464 |
+ rgi->flags &= ~RGI_ON_LINK; |
|
| 2465 |
+ } |
|
| 2466 |
+#endif |
|
| 2467 |
+ } |
|
| 2468 |
+ else |
|
| 2469 |
+ {
|
|
| 2470 |
+ /* get interface netmask */ |
|
| 2471 |
+ if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0) |
|
| 2472 |
+ continue; |
|
| 2473 |
+ netmask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr); |
|
| 2474 |
+ |
|
| 2475 |
+ /* check that interface matches default route */ |
|
| 2476 |
+ if (((rgi->gateway.addr ^ addr) & netmask) != 0) |
|
| 2477 |
+ continue; |
|
| 2478 |
+ |
|
| 2479 |
+ /* save netmask */ |
|
| 2480 |
+ rgi->gateway.netmask = netmask; |
|
| 2481 |
+ rgi->flags |= RGI_NETMASK_DEFINED; |
|
| 2482 |
+ } |
|
| 2460 | 2483 |
|
| 2461 |
- /* save iface name and netmask */ |
|
| 2484 |
+ /* save iface name */ |
|
| 2462 | 2485 |
strncpynt (rgi->iface, ifreq.ifr_name, sizeof(rgi->iface)); |
| 2463 |
- rgi->gateway.netmask = netmask; |
|
| 2464 |
- rgi->flags |= (RGI_IFACE_DEFINED|RGI_NETMASK_DEFINED); |
|
| 2486 |
+ rgi->flags |= RGI_IFACE_DEFINED; |
|
| 2465 | 2487 |
|
| 2466 | 2488 |
/* now get the hardware address. */ |
| 2467 | 2489 |
memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr)); |
| ... | ... |
@@ -46,9 +46,10 @@ |
| 46 | 46 |
#endif |
| 47 | 47 |
|
| 48 | 48 |
/* |
| 49 |
- * Route add flags (must stay clear of ROUTE_METHOD bits) |
|
| 49 |
+ * Route add/delete flags (must stay clear of ROUTE_METHOD bits) |
|
| 50 | 50 |
*/ |
| 51 |
-#define ROUTE_DELETE_FIRST 4 |
|
| 51 |
+#define ROUTE_DELETE_FIRST (1<<2) |
|
| 52 |
+#define ROUTE_REF_GW (1<<3) |
|
| 52 | 53 |
|
| 53 | 54 |
struct route_bypass |
| 54 | 55 |
{
|
| ... | ... |
@@ -157,6 +158,7 @@ struct route_gateway_info {
|
| 157 | 157 |
# define RGI_HWADDR_DEFINED (1<<2) /* set if hwaddr is defined */ |
| 158 | 158 |
# define RGI_IFACE_DEFINED (1<<3) /* set if iface is defined */ |
| 159 | 159 |
# define RGI_OVERFLOW (1<<4) /* set if more interface addresses than will fit in addrs */ |
| 160 |
+# define RGI_ON_LINK (1<<5) |
|
| 160 | 161 |
unsigned int flags; |
| 161 | 162 |
|
| 162 | 163 |
/* gateway interface */ |