Browse code

Added support for "on-link" routes on Linux client

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>

James Yonan authored on 2011/12/26 09:18:50
Showing 3 changed files
... ...
@@ -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 */