OpenVPN on Linux (iproute2+ifconfig), FreeBSD and MacOS X (Darwin) normally points routes directly towards the "tun" interface, obviating the need for a gateway. For "tap" interfaces, now add gateway spec to linux route command, and replace "-iface <dev>" with gateway spec (both together do not work) on FreeBSD and MacOS X.
Also adapt "route delete" appropriately, otherwise route will not be found. All other platforms already use the gateway address for tun and tap, because there's no way to install a route "towards an interface" there. Remove warning about missing IPv6 route gateway handling. Signed-off-by: Gert Doering <g...@greenie.muc.de> --- src/openvpn/init.c | 3 -- src/openvpn/route.c | 75 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/openvpn/init.c b/src/openvpn/init.c index b726b93..320edcc 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -1253,9 +1253,6 @@ do_init_route_ipv6_list (const struct options *options, int dev = dev_type_enum (options->dev, options->dev_type); int metric = -1; /* no metric set */ - if (dev != DEV_TYPE_TUN ) - msg( M_WARN, "IPv6 routes on TAP devices are going to fail on some platforms (need gateway spec)" ); /* TODO-GERT */ - gw = options->ifconfig_ipv6_remote; /* default GW = remote end */ #if 0 /* not yet done for IPv6 - TODO!*/ if ( options->route_ipv6_default_gateway ) /* override? */ diff --git a/src/openvpn/route.c b/src/openvpn/route.c index aadbacc..5848d45 100644 --- a/src/openvpn/route.c +++ b/src/openvpn/route.c @@ -1550,6 +1550,8 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla bool status = false; const char *device = tt->actual_name; + bool gateway_needed = false; + if (!r6->defined) return; @@ -1574,6 +1576,18 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla * (not currently done for IPv6) */ + /* On "tun" interface, we never set a gateway if the operating system + * can do "route to interface" - it does not add value, as the target + * dev already fully qualifies the route destination on point-to-point + * interfaces. OTOH, on "tap" interface, we must always set the + * gateway unless the route is to be an on-link network + */ + if ( tt->type == DEV_TYPE_TAP && + !(r6->metric_defined && r6->metric == 0 ) ) + { + gateway_needed = true; + } + #if defined(TARGET_LINUX) #ifdef ENABLE_IPROUTE argv_printf (&argv, "%s -6 route add %s/%d dev %s", @@ -1581,6 +1595,8 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla network, r6->netbits, device); + if (gateway_needed) + argv_printf_cat (&argv, "via %s", gateway); if (r6->metric_defined && r6->metric > 0 ) argv_printf_cat (&argv, " metric %d", r6->metric); @@ -1590,6 +1606,8 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla network, r6->netbits, device); + if (gateway_needed) + argv_printf_cat (&argv, "gw %s", gateway); if (r6->metric_defined && r6->metric > 0 ) argv_printf_cat (&argv, " metric %d", r6->metric); #endif /*ENABLE_IPROUTE*/ @@ -1652,20 +1670,29 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla #elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) - argv_printf (&argv, "%s add -inet6 %s/%d -iface %s", + argv_printf (&argv, "%s add -inet6 %s/%d", ROUTE_PATH, network, - r6->netbits, - device ); + r6->netbits); + + if (gateway_needed) + argv_printf_cat (&argv, "%s", gateway); + else + argv_printf_cat (&argv, "-iface %s", device); argv_msg (D_ROUTE, &argv); status = openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route add -inet6 command failed"); #elif defined(TARGET_DARWIN) - argv_printf (&argv, "%s add -inet6 %s -prefixlen %d -iface %s", + argv_printf (&argv, "%s add -inet6 %s -prefixlen %d", ROUTE_PATH, - network, r6->netbits, device ); + network, r6->netbits ); + + if (gateway_needed) + argv_printf_cat (&argv, "%s", gateway); + else + argv_printf_cat (&argv, "-iface %s", device); argv_msg (D_ROUTE, &argv); status = openvpn_execve_check (&argv, es, 0, "ERROR: MacOS X route add -inet6 command failed"); @@ -1865,6 +1892,7 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne const char *network; const char *gateway; const char *device = tt->actual_name; + bool gateway_needed = false; if (!r6->defined) return; @@ -1884,6 +1912,16 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne msg( M_INFO, "delete_route_ipv6(%s/%d)", network, r6->netbits ); + /* if we used a gateway on "add route", we also need to specify it on + * delete, otherwise some OSes will refuse to delete the route + */ + if ( tt->type == DEV_TYPE_TAP && + !(r6->metric_defined && r6->metric == 0 ) ) + { + gateway_needed = true; + } + + #if defined(TARGET_LINUX) #ifdef ENABLE_IPROUTE argv_printf (&argv, "%s -6 route del %s/%d dev %s", @@ -1891,12 +1929,18 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne network, r6->netbits, device); + if (gateway_needed) + argv_printf_cat (&argv, "via %s", gateway); #else argv_printf (&argv, "%s -A inet6 del %s/%d dev %s", ROUTE_PATH, network, r6->netbits, device); + if (gateway_needed) + argv_printf_cat (&argv, "gw %s", gateway); + if (r6->metric_defined && r6->metric > 0 ) + argv_printf_cat (&argv, " metric %d", r6->metric); #endif /*ENABLE_IPROUTE*/ argv_msg (D_ROUTE, &argv); openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed"); @@ -1949,23 +1993,32 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne #elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) - argv_printf (&argv, "%s delete -inet6 %s/%d -iface %s", + argv_printf (&argv, "%s delete -inet6 %s/%d", ROUTE_PATH, network, - r6->netbits, - device ); + r6->netbits ); + + if (gateway_needed) + argv_printf_cat (&argv, "%s", gateway); + else + argv_printf_cat (&argv, "-iface %s", device); argv_msg (D_ROUTE, &argv); openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed"); #elif defined(TARGET_DARWIN) - argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d -iface %s", + argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d", ROUTE_PATH, - network, r6->netbits, device ); + network, r6->netbits ); + + if (gateway_needed) + argv_printf_cat (&argv, "%s", gateway); + else + argv_printf_cat (&argv, "-iface %s", device); argv_msg (D_ROUTE, &argv); - openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed"); + openvpn_execve_check (&argv, es, 0, "ERROR: MacOS X route delete -inet6 command failed"); #elif defined(TARGET_OPENBSD) -- 1.7.3.4