]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Added support for "on-link" routes on Linux client
authorJames Yonan <james@openvpn.net>
Mon, 26 Dec 2011 00:18:50 +0000 (00:18 +0000)
committerDavid Sommerseth <davids@redhat.com>
Fri, 27 Jan 2012 09:33:56 +0000 (10:33 +0100)
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>
ChangeLog
route.c
route.h

index 93ef65fd82319ab4f00d5fdc179b264155e81b4e..d6509b2e54cd06d07a66bdd481c64cb934fb8732 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,28 @@
 OpenVPN Change Log
 Copyright (C) 2002-2011 OpenVPN Technologies, Inc. <sales@openvpn.net>
 
+2011.12.25 -- Version 2.x-master
+James Yonan (1):
+      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
+
 2011.03.24 -- Version 2.2-RC2
 Alon Bar-Lev (1):
       Windows cross-compile cleanup
diff --git a/route.c b/route.c
index 5dfb9c78614ed013b7a981ce721f845211ca4d0c..cf9a5ea8b5f4439245a2e8e4b15b57374982f39d 100644 (file)
--- a/route.c
+++ b/route.c
@@ -794,7 +794,7 @@ add_bypass_routes (struct route_bypass *rb,
                    ~0,
                    gateway,
                    tt,
-                   flags,
+                   flags | ROUTE_REF_GW,
                    rgi,
                    es);
     }
@@ -816,7 +816,7 @@ del_bypass_routes (struct route_bypass *rb,
                    ~0,
                    gateway,
                    tt,
-                   flags,
+                   flags | ROUTE_REF_GW,
                    rgi,
                    es);
     }
@@ -867,7 +867,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
                            ~0,
                            rl->rgi.gateway.addr,
                            tt,
-                           flags,
+                           flags | ROUTE_REF_GW,
                            &rl->rgi,
                            es);
                rl->iflags |= RL_DID_LOCAL;
@@ -908,7 +908,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
                              0,
                              rl->rgi.gateway.addr,
                              tt,
-                             flags,
+                             flags | ROUTE_REF_GW,
                              &rl->rgi,
                              es);
 
@@ -941,7 +941,7 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
                      ~0,
                      rl->rgi.gateway.addr,
                      tt,
-                     flags,
+                     flags | ROUTE_REF_GW,
                      &rl->rgi,
                      es);
          rl->iflags &= ~RL_DID_LOCAL;
@@ -988,7 +988,7 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
                          0,
                          rl->rgi.gateway.addr,
                          tt,
-                         flags,
+                         flags | ROUTE_REF_GW,
                          &rl->rgi,
                          es);
            }
@@ -1122,7 +1122,10 @@ print_default_gateway(const int msglevel, const struct route_gateway_info *rgi)
     {
       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_ON_LINK)
+       buf_printf (&out, " ON_LINK");
+      else
+       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
@@ -1267,6 +1270,14 @@ local_route (in_addr_t network,
     return LR_NOMATCH;
 }
 
+/* Return true if the "on-link" form of the route should be used.  This is when the gateway for a
+   a route is specified as an interface rather than an address. */
+static inline bool
+is_on_link (const int is_local_route, const unsigned int flags, const struct route_gateway_info *rgi)
+{
+  return rgi && (is_local_route == LR_MATCH || ((flags & ROUTE_REF_GW) && (rgi->flags & RGI_ON_LINK)));
+}
+
 void
 add_route (struct route *r,
           const struct tuntap *tt,
@@ -1298,7 +1309,7 @@ add_route (struct route *r,
 
 #if defined(TARGET_LINUX)
 #ifdef CONFIG_FEATURE_IPROUTE
-  /* FIXME -- add LR_MATCH support for CONFIG_FEATURE_IPROUTE */
+  /* FIXME -- add on-link support for CONFIG_FEATURE_IPROUTE */
   argv_printf (&argv, "%s route add %s/%d via %s",
              iproute_path,
              network,
@@ -1314,7 +1325,7 @@ add_route (struct route *r,
               netmask);
   if (r->flags & RT_METRIC_DEFINED)
     argv_printf_cat (&argv, "metric %d", r->metric);
-  if (rgi && is_local_route == LR_MATCH)
+  if (is_on_link (is_local_route, flags, rgi))
     argv_printf_cat (&argv, "dev %s", rgi->iface);
   else
     argv_printf_cat (&argv, "gw %s", gateway);
@@ -1334,7 +1345,7 @@ add_route (struct route *r,
                 gateway);
     if (r->flags & RT_METRIC_DEFINED)
       argv_printf_cat (&argv, "METRIC %d", r->metric);
-    if (rgi && is_local_route == LR_MATCH)
+    if (is_on_link (is_local_route, flags, rgi))
       {
        ai = rgi->adapter_index;
        argv_printf_cat (&argv, "IF %u", (unsigned int)ai);
@@ -1388,7 +1399,7 @@ add_route (struct route *r,
              netmask,
              gateway);
 
-  /* FIXME -- add LR_MATCH support for Solaris */
+  /* FIXME -- add on-link support for Solaris */
 
   argv_msg (D_ROUTE, &argv);
   status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");
@@ -1408,7 +1419,7 @@ add_route (struct route *r,
              gateway,
              netmask);
 
-  /* FIXME -- add LR_MATCH support for FreeBSD */
+  /* FIXME -- add on-link support for FreeBSD */
 
   argv_msg (D_ROUTE, &argv);
   status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed");
@@ -1428,7 +1439,7 @@ add_route (struct route *r,
              gateway,
              netmask);
 
-  /* FIXME -- add LR_MATCH support for Dragonfly */
+  /* FIXME -- add on-link support for Dragonfly */
 
   argv_msg (D_ROUTE, &argv);
   status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed");
@@ -1443,9 +1454,9 @@ add_route (struct route *r,
     argv_printf_cat (&argv, "-rtt %d", r->metric);
 #endif
 
-  if (rgi && is_local_route == LR_MATCH)
+  if (is_on_link (is_local_route, flags, rgi))
     {
-      /* Mac OS X route syntax for LR_MATCH:
+      /* Mac OS X route syntax for ON_LINK:
         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,
@@ -1478,7 +1489,7 @@ add_route (struct route *r,
              gateway,
              netmask);
 
-  /* FIXME -- add LR_MATCH support for OpenBSD/NetBSD */
+  /* FIXME -- add on-link support for OpenBSD/NetBSD */
 
   argv_msg (D_ROUTE, &argv);
   status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
@@ -1796,7 +1807,7 @@ delete_route (struct route *r,
 
 #elif defined(TARGET_DARWIN)
 
-  if (rgi && is_local_route == LR_MATCH)
+  if (is_on_link (is_local_route, flags, rgi))
     {
       argv_printf (&argv, "%s delete -cloning -net %s -netmask %s -interface %s",
                   ROUTE_PATH,
@@ -2357,6 +2368,8 @@ get_default_gateway (struct route_gateway_info *rgi)
 {
   struct gc_arena gc = gc_new ();
   int sd = -1;
+  char best_name[16];
+  best_name[0] = 0;
 
   CLEAR(*rgi);
 
@@ -2369,6 +2382,7 @@ get_default_gateway (struct route_gateway_info *rgi)
        int count = 0;
        unsigned int lowest_metric = ~0;
        in_addr_t best_gw = 0;
+       bool found = false;
        while (fgets (line, sizeof (line), fp) != NULL)
          {
            if (count)
@@ -2378,13 +2392,16 @@ get_default_gateway (struct route_gateway_info *rgi)
                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",
+               char name[16];
+               name[0] = 0;
+               const int np = sscanf (line, "%15s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x",
+                                      name,
                                       &net_x,
                                       &gw_x,
                                       &flags,
                                       &metric,
                                       &mask_x);
-               if (np == 5 && (flags & IFF_UP))
+               if (np == 6 && (flags & IFF_UP))
                  {
                    const in_addr_t net = ntohl (net_x);
                    const in_addr_t mask = ntohl (mask_x);
@@ -2392,7 +2409,9 @@ get_default_gateway (struct route_gateway_info *rgi)
 
                    if (!net && !mask && metric < lowest_metric)
                      {
+                       found = true;
                        best_gw = gw;
+                       strcpy (best_name, name);
                        lowest_metric = metric;
                      }
                  }
@@ -2401,10 +2420,12 @@ get_default_gateway (struct route_gateway_info *rgi)
          }
        fclose (fp);
 
-       if (best_gw)
+       if (found)
          {
            rgi->gateway.addr = best_gw;
            rgi->flags |= RGI_ADDR_DEFINED;
+           if (!rgi->gateway.addr && best_name[0])
+             rgi->flags |= RGI_ON_LINK;
          }
       }
   }
@@ -2443,25 +2464,47 @@ get_default_gateway (struct route_gateway_info *rgi)
              /* 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 */
+             /* check that the interface is up */
              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;
+             if (rgi->flags & RGI_ON_LINK)
+               {
+                 /* check that interface name of current interface
+                    matches interface name of best default route */
+                 if (strcmp(ifreq.ifr_name, best_name))
+                   continue;
+#if 0
+                 /* if point-to-point link, use remote addr as route gateway */
+                 if ((ifreq.ifr_flags & IFF_POINTOPOINT) && ioctl (sd, SIOCGIFDSTADDR, &ifreq) >= 0)
+                   {
+                     rgi->gateway.addr = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr);
+                     if (rgi->gateway.addr)
+                       rgi->flags &= ~RGI_ON_LINK;
+                   }
+#endif
+               }
+             else
+               {
+                 /* 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 netmask */
+                 rgi->gateway.netmask = netmask;
+                 rgi->flags |= RGI_NETMASK_DEFINED;
+               }
 
-             /* save iface name and netmask */
+             /* save iface name */
              strncpynt (rgi->iface, ifreq.ifr_name, sizeof(rgi->iface));
-             rgi->gateway.netmask = netmask;
-             rgi->flags |= (RGI_IFACE_DEFINED|RGI_NETMASK_DEFINED);
+             rgi->flags |= RGI_IFACE_DEFINED;
 
              /* now get the hardware address. */
              memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
diff --git a/route.h b/route.h
index 58c7ef437ba14d06b0b98acfc0924cf5daf93260..25ecaeefa51d1e24c5923cbe7d9942c913d3ccd4 100644 (file)
--- a/route.h
+++ b/route.h
 #endif
 
 /*
- * Route add flags (must stay clear of ROUTE_METHOD bits)
+ * Route add/delete flags (must stay clear of ROUTE_METHOD bits)
  */
-#define ROUTE_DELETE_FIRST  4
+#define ROUTE_DELETE_FIRST  (1<<2)
+#define ROUTE_REF_GW        (1<<3)
 
 struct route_bypass
 {
@@ -157,6 +158,7 @@ struct route_gateway_info {
 # define RGI_HWADDR_DEFINED   (1<<2) /* set if hwaddr is defined */
 # define RGI_IFACE_DEFINED    (1<<3) /* set if iface is defined */
 # define RGI_OVERFLOW         (1<<4) /* set if more interface addresses than will fit in addrs */
+# define RGI_ON_LINK          (1<<5)
   unsigned int flags;
 
   /* gateway interface */