]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Drop recursively routed packets
authorLev Stipakov <lev.stipakov@f-secure.com>
Thu, 3 Nov 2016 21:28:23 +0000 (23:28 +0200)
committerGert Doering <gert@greenie.muc.de>
Fri, 4 Nov 2016 09:11:15 +0000 (10:11 +0100)
v4:
 - Account for IP header offset in TAP mode
 - Correct handle of non-IP protocols in TAP mode

v3: Use better way of figuring out IP proto version which
does not break TAP mode. Add an option to allow recursive
routing, could be useful when packets sent by openvpn itself
are not subject to the routing tables that would move packets
into the tunnel.

v2: better method naming

On certain OSes (Windows, OS X) when network adapter is
disabled (ethernet cable pulled off, Wi-Fi hardware switch disabled),
operating system starts to use tun as an external interface.
Outgoing packets are routed to tun, UDP encapsulated, given to
routing table and sent to.. tun.

As a consequence, system starts talking to itself on full power,
traffic counters skyrocket and user is not happy.

To prevent that, drop packets which have gateway IP as
destination address.

Tested on Win7/10, OS X, Linux.

Trac #642

Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1478208503-25929-1-git-send-email-lstipakov@gmail.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg12894.html

Signed-off-by: Gert Doering <gert@greenie.muc.de>
(cherry picked from commit e8c42658ff8df10ad56659788a73900648b9d92d)

Changes.rst
doc/openvpn.8
src/openvpn/forward.c
src/openvpn/options.c
src/openvpn/options.h
src/openvpn/proto.h

index 92ade04a0c965a88036fec4f8f1bb7f1d0528ffc..3e3aaad6c096b4721e8106a8523c5116d3d0ff87 100644 (file)
@@ -104,6 +104,15 @@ Behavioral changes
 
 - Do not randomize resolving of IP addresses in getaddr()
 
+Version 2.3.14
+==============
+
+Behavioral changes
+------------------
+
+- On the client side recursively routed packets, which have same destination
+  as the VPN server, are dropped. This could be disabled with
+  --allow-recursive-routing option.
 
 Version 2.3.13
 ==============
index 31db2eb05a1d3ffdef825951d7a9628b2345d870..79c2e22f9c93e2444f285bebb3058e0e774d9c26 100644 (file)
@@ -3805,6 +3805,10 @@ rather than waiting for a timeout.  The
 parameter (default=1) controls the maximum number of attempts that the client
 will try to resend the exit notification message.  OpenVPN will not send any exit
 notifications unless this option is enabled.
+.TP
+.B \-\-allow\-recursive\-routing
+When this option is set, OpenVPN will not drop incoming tun packets
+with same destination as host.
 .\"*********************************************************
 .SS Data Channel Encryption Options:
 These options are meaningful for both Static & TLS-negotiated key modes
index 5ba6fcb49ceccdcc80b7bd1dd00fb493bb8ebda0..32942019c9b93daaf74dc27128b9dbc4d7bf8c1e 100644 (file)
@@ -968,6 +968,76 @@ read_incoming_tun (struct context *c)
   perf_pop ();
 }
 
+/**
+ * Drops UDP packets which OS decided to route via tun.
+ *
+ * On Windows and OS X when netwotk adapter is disabled or
+ * disconnected, platform starts to use tun as external interface.
+ * When packet is sent to tun, it comes to openvpn, encapsulated
+ * and sent to routing table, which sends it again to tun.
+ */
+static void
+drop_if_recursive_routing (struct context *c, struct buffer *buf)
+{
+  bool drop = false;
+  struct openvpn_sockaddr tun_sa;
+  int ip_hdr_offset = 0;
+
+  if (c->c2.to_link_addr == NULL) /* no remote addr known */
+    return;
+
+  tun_sa = c->c2.to_link_addr->dest;
+
+  int proto_ver = get_tun_ip_ver (TUNNEL_TYPE (c->c1.tuntap), &c->c2.buf, &ip_hdr_offset);
+
+  if (proto_ver == 4)
+    {
+      const struct openvpn_iphdr *pip;
+
+      /* make sure we got whole IP header */
+      if (BLEN (buf) < ((int) sizeof (struct openvpn_iphdr) + ip_hdr_offset))
+       return;
+
+      /* skip ipv4 packets for ipv6 tun */
+      if (tun_sa.addr.sa.sa_family != AF_INET)
+       return;
+
+      pip = (struct openvpn_iphdr *) (BPTR (buf) + ip_hdr_offset);
+
+      /* drop packets with same dest addr as gateway */
+      if (tun_sa.addr.in4.sin_addr.s_addr == pip->daddr)
+       drop = true;
+    }
+  else if (proto_ver == 6)
+    {
+      const struct openvpn_ipv6hdr *pip6;
+
+      /* make sure we got whole IPv6 header */
+      if (BLEN (buf) < ((int) sizeof (struct openvpn_ipv6hdr) + ip_hdr_offset))
+       return;
+
+      /* skip ipv6 packets for ipv4 tun */
+      if (tun_sa.addr.sa.sa_family != AF_INET6)
+       return;
+
+      /* drop packets with same dest addr as gateway */
+      pip6 = (struct openvpn_ipv6hdr *) (BPTR (buf) + ip_hdr_offset);
+      if (IN6_ARE_ADDR_EQUAL(&tun_sa.addr.in6.sin6_addr, &pip6->daddr))
+       drop = true;
+    }
+
+  if (drop)
+    {
+      struct gc_arena gc = gc_new ();
+
+      c->c2.buf.len = 0;
+
+      msg(D_LOW, "Recursive routing detected, drop tun packet to %s",
+               print_link_socket_actual(c->c2.to_link_addr, &gc));
+      gc_free (&gc);
+    }
+}
+
 /*
  * Input:  c->c2.buf
  * Output: c->c2.to_link
@@ -993,6 +1063,8 @@ process_incoming_tun (struct context *c)
 
   if (c->c2.buf.len > 0)
     {
+      if ((c->options.mode == MODE_POINT_TO_POINT) && (!c->options.allow_recursive_routing))
+       drop_if_recursive_routing (c, &c->c2.buf);
       /*
        * The --passtos and --mssfix options require
        * us to examine the IP header (IPv4 or IPv6).
index 19cd815d1e5ae111b71907c0b8583fa789c204de..b0885269ce8242547f11d74c1df07607efffe7ce 100644 (file)
@@ -500,6 +500,8 @@ static const char usage_message[] =
   "--server-poll-timeout n : when polling possible remote servers to connect to\n"
   "                  in a round-robin fashion, spend no more than n seconds\n"
   "                  waiting for a response before trying the next server.\n"
+  "--allow-recursive-routing : When this option is set, OpenVPN will not drop\n"
+  "                  incoming tun packets with same destination as host.\n"
 #endif
 #ifdef ENABLE_OCC
   "--explicit-exit-notify [n] : On exit/restart, send exit signal to\n"
@@ -876,6 +878,7 @@ init_options (struct options *o, const bool init_gc)
   }
 #endif /* WIN32 */
 #endif /* P2MP_SERVER */
+  o->allow_recursive_routing = false;
 }
 
 void
@@ -2091,6 +2094,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
       if (options->ifconfig_ipv6_local && !options->tun_ipv6 )
        msg (M_INFO, "Warning: --ifconfig-ipv6 without --tun-ipv6 will not do IPv6");
 
+      if (options->allow_recursive_routing)
+       msg (M_USAGE, "--allow-recursive-routing cannot be used with --mode server");
       if (options->auth_user_pass_file)
        msg (M_USAGE, "--auth-user-pass cannot be used with --mode server (it should be used on the client side only)");
       if (options->ccd_exclusive && !options->client_config_dir)
@@ -7122,6 +7127,11 @@ add_option (struct options *options,
       options->use_peer_id = true;
       options->peer_id = atoi(p[1]);
     }
+  else if (streq (p[0], "allow-recursive-routing") && !p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->allow_recursive_routing = true;
+    }
   else
     {
       int i;
index 26b09ea405e0bdf0c6cf7387bef6a0219c281132..86fa8c2930652067529e70a71a5f43f9cb1be9a8 100644 (file)
@@ -598,6 +598,10 @@ struct options
 
   bool use_peer_id;
   uint32_t peer_id;
+
+  /* Useful when packets sent by openvpn itself are not subject
+     to the routing tables that would move packets into the tunnel. */
+  bool allow_recursive_routing;
 };
 
 #define streq(x, y) (!strcmp((x), (y)))
index f91e787ecbe1a47aa45c6bac16511ae1e383c141..07612c8732a7e574ed1b9ba8c1aa592d645b046c 100644 (file)
@@ -218,6 +218,45 @@ struct ip_tcp_udp_hdr {
 #define MTU_TO_MSS(mtu) (mtu - sizeof(struct openvpn_iphdr) \
                              - sizeof(struct openvpn_tcphdr))
 
+/*
+ * This returns an ip protocol version of packet inside tun
+ * and offset of IP header (via parameter).
+ */
+inline static int get_tun_ip_ver(int tunnel_type, struct buffer *buf, int *ip_hdr_offset)
+{
+  int ip_ver = -1;
+
+  /* for tun get ip version from ip header */
+  if (tunnel_type == DEV_TYPE_TUN)
+    {
+      *ip_hdr_offset = 0;
+      if (likely(BLEN (buf) >= (int) sizeof (struct openvpn_iphdr)))
+       {
+         ip_ver = OPENVPN_IPH_GET_VER (*BPTR(buf));
+       }
+    }
+  else if (tunnel_type == DEV_TYPE_TAP)
+    {
+      *ip_hdr_offset = (int)(sizeof (struct openvpn_ethhdr));
+      /* for tap get ip version from eth header */
+      if (likely(BLEN (buf) >= *ip_hdr_offset))
+       {
+         const struct openvpn_ethhdr *eh = (const struct openvpn_ethhdr *) BPTR (buf);
+         uint16_t proto = ntohs (eh->proto);
+         if (proto == OPENVPN_ETH_P_IPV6)
+           {
+             ip_ver = 6;
+           }
+         else if (proto == OPENVPN_ETH_P_IPV4)
+           {
+             ip_ver = 4;
+           }
+       }
+    }
+
+  return ip_ver;
+}
+
 /*
  * If raw tunnel packet is IPv4 or IPv6, return true and increment
  * buffer offset to start of IP header.