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>
capable. The ``--tun-ipv6`` option is ignored (behaves like it is always
on).
+- 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.
Maintainer-visible changes
--------------------------
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
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
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).
"--connect-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"
}
#endif /* WIN32 */
#endif /* P2MP_SERVER */
+ o->allow_recursive_routing = false;
}
void
msg (M_USAGE, "--ifconfig-pool-persist must be used with --ifconfig-pool");
if (options->ifconfig_ipv6_pool_defined && !options->ifconfig_ipv6_local )
msg (M_USAGE, "--ifconfig-ipv6-pool needs --ifconfig-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)
options->keying_material_exporter_length = ekm_length;
}
#endif
+ else if (streq (p[0], "allow-recursive-routing") && !p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->allow_recursive_routing = true;
+ }
else
{
int i;
#endif
struct pull_filter_list *pull_filter_list;
+
+ /* 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)))
#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.