diff --git a/openvpn/tun/linux/client/sitnl.hpp b/openvpn/tun/linux/client/sitnl.hpp index 805c7166..7c28926c 100644 --- a/openvpn/tun/linux/client/sitnl.hpp +++ b/openvpn/tun/linux/client/sitnl.hpp @@ -721,7 +721,7 @@ err: const std::string& iface, const uint32_t table, const int metric) { - return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE, iface, + return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE, iface, route, gw, (enum rt_class_t)(!table ? RT_TABLE_MAIN : table), metric, RT_SCOPE_UNIVERSE, RTPROT_BOOT, RTN_UNICAST); diff --git a/openvpn/tun/linux/client/tuncli.hpp b/openvpn/tun/linux/client/tuncli.hpp index 474e8443..5424a86d 100644 --- a/openvpn/tun/linux/client/tuncli.hpp +++ b/openvpn/tun/linux/client/tuncli.hpp @@ -196,6 +196,7 @@ namespace openvpn { tsconf.layer = config->tun_prop.layer; tsconf.dev_name = config->dev_name; tsconf.txqueuelen = config->txqueuelen; + tsconf.add_bypass_routes_on_establish = true; // open/config tun { diff --git a/openvpn/tun/linux/client/tuniproute.hpp b/openvpn/tun/linux/client/tuniproute.hpp index 4987a0df..130cf364 100644 --- a/openvpn/tun/linux/client/tuniproute.hpp +++ b/openvpn/tun/linux/client/tuniproute.hpp @@ -88,7 +88,7 @@ namespace openvpn { add->argv.push_back("/sbin/ip"); add->argv.push_back("-6"); add->argv.push_back("route"); - add->argv.push_back("add"); + add->argv.push_back("prepend"); add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len)); add->argv.push_back("via"); add->argv.push_back(gateway_str); @@ -121,10 +121,15 @@ namespace openvpn { add->argv.push_back("/sbin/ip"); add->argv.push_back("-4"); add->argv.push_back("route"); - add->argv.push_back("add"); + add->argv.push_back("prepend"); add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len)); add->argv.push_back("via"); add->argv.push_back(gateway_str); + if (!dev.empty()) + { + add->argv.push_back("dev"); + add->argv.push_back(dev); + } create = add; // for the destroy command, copy the add command but replace "add" with "delete" @@ -248,7 +253,8 @@ namespace openvpn { const TunBuilderCapture& pull, std::vector* rtvec, ActionList& create, - ActionList& destroy) + ActionList& destroy, + bool add_bypass_routes = true) { const LinuxGW46 gw(true); @@ -301,7 +307,7 @@ namespace openvpn { if (pull.reroute_gw.ipv4) { // add bypass route - if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL)) + if (add_bypass_routes && !pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL) && gw.v4.defined()) add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy); add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy); @@ -312,7 +318,7 @@ namespace openvpn { if (pull.reroute_gw.ipv6 && !pull.block_ipv6) { // add bypass route - if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL)) + if (add_bypass_routes && pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL) && gw.v4.defined()) add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy); add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy); @@ -323,6 +329,22 @@ namespace openvpn { // fixme -- Handle pushed DNS servers } + + static inline void add_bypass_route(const std::string& tun_iface_name, + const std::string& address, + bool ipv6, + std::vector* rtvec, + ActionList& create, + ActionList& destroy) + { + LinuxGW46 gw(true); + + if (!ipv6 && gw.v4.defined()) + add_del_route(address, 32, gw.v4.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy); + + if (ipv6 && gw.v6.defined()) + add_del_route(address, 128, gw.v6.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy); + } }; } } // namespace openvpn diff --git a/openvpn/tun/linux/client/tunnetlink.hpp b/openvpn/tun/linux/client/tunnetlink.hpp index 4e8537c1..6844ab46 100644 --- a/openvpn/tun/linux/client/tunnetlink.hpp +++ b/openvpn/tun/linux/client/tunnetlink.hpp @@ -599,13 +599,12 @@ namespace openvpn { struct TunMethods { static inline void tun_config(const std::string& iface_name, - const TunBuilderCapture& pull, - std::vector* rtvec, - ActionList& create, - ActionList& destroy) + const TunBuilderCapture& pull, + std::vector* rtvec, + ActionList& create, + ActionList& destroy, + bool add_bypass_routes) { - const LinuxGW46Netlink gw("", iface_name); - // set local4 and local6 to point to IPv4/6 route configurations const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4(); const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6(); @@ -634,7 +633,10 @@ namespace openvpn { } // Process exclude routes + if (!pull.exclude_routes.empty()) { + LinuxGW46Netlink gw(iface_name); + for (const auto &route : pull.exclude_routes) { if (route.ipv6) @@ -655,8 +657,8 @@ namespace openvpn { if (pull.reroute_gw.ipv4) { // add bypass route - if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL)) - add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy); + if (add_bypass_routes && !pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL)) + add_bypass_route(iface_name, pull.remote_address.address, false, rtvec, create, destroy); add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy); add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy); @@ -666,8 +668,8 @@ namespace openvpn { if (pull.reroute_gw.ipv6 && !pull.block_ipv6) { // add bypass route - if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL)) - add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy); + if (add_bypass_routes && pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL)) + add_bypass_route(iface_name, pull.remote_address.address, true, rtvec, create, destroy); add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy); add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy); @@ -677,6 +679,22 @@ namespace openvpn { // fixme -- Handle pushed DNS servers } + + static inline void add_bypass_route(const std::string& tun_iface_name, + const std::string& address, + bool ipv6, + std::vector* rtvec, + ActionList& create, + ActionList& destroy) + { + LinuxGW46Netlink gw(tun_iface_name, address); + + if (!ipv6 && gw.v4.defined()) + add_del_route(address, 32, gw.v4.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy); + + if (ipv6 && gw.v6.defined()) + add_del_route(address, 128, gw.v6.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy); + } }; } } // namespace openvpn diff --git a/openvpn/tun/linux/client/tunsetup.hpp b/openvpn/tun/linux/client/tunsetup.hpp index 6c6d86fb..34a4a43c 100644 --- a/openvpn/tun/linux/client/tunsetup.hpp +++ b/openvpn/tun/linux/client/tunsetup.hpp @@ -69,6 +69,7 @@ namespace openvpn { Layer layer; // OSI layer std::string dev_name; int txqueuelen; + bool add_bypass_routes_on_establish; // required when not using tunbuilder #ifdef HAVE_JSON virtual Json::Value to_json() override @@ -92,17 +93,39 @@ namespace openvpn { #endif }; - virtual void destroy(std::ostream &os) override + void destroy(std::ostream &os) override { // remove added routes - if (remove_cmds) - remove_cmds->execute(std::cout); + remove_cmds->execute(os); + + // remove bypass route + remove_cmds_bypass_gw->execute(os); } - virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base - TunBuilderSetup::Config* config, - Stop* stop, - std::ostream& os) override + bool add_bypass_route(const std::string& address, + bool ipv6, + std::ostream& os) + { + // nothing to do if we reconnect to the same gateway + if (connected_gw == address) + return true; + + // remove previous bypass route + remove_cmds_bypass_gw->execute(os); + remove_cmds_bypass_gw->clear(); + + ActionList::Ptr add_cmds = new ActionList(); + TUNMETHODS::add_bypass_route(tun_iface_name, address, ipv6, nullptr, *add_cmds, *remove_cmds_bypass_gw); + + // add gateway bypass route + add_cmds->execute(os); + return true; + } + + int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base + TunBuilderSetup::Config* config, + Stop* stop, + std::ostream& os) override { // get configuration Config *conf = dynamic_cast(config); @@ -149,15 +172,22 @@ namespace openvpn { } conf->iface_name = ifr.ifr_name; + tun_iface_name = ifr.ifr_name; ActionList::Ptr add_cmds = new ActionList(); - remove_cmds.reset(new ActionListReversed()); // remove commands executed in reversed order + ActionList::Ptr remove_cmds_new = new ActionListReversed(); // configure tun properties - TUNMETHODS::tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds); + TUNMETHODS::tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds_new, conf->add_bypass_routes_on_establish); // execute commands to bring up interface - add_cmds->execute(std::cout); + add_cmds->execute(os); + + // tear down old routes + remove_cmds->execute(os); + std::swap(remove_cmds, remove_cmds_new); + + connected_gw = pull.remote_address.to_string(); return fd.release(); } @@ -193,7 +223,12 @@ namespace openvpn { } } - ActionListReversed::Ptr remove_cmds; + ActionList::Ptr remove_cmds_bypass_gw = new ActionList(); + ActionListReversed::Ptr remove_cmds = new ActionListReversed(); + + std::string connected_gw; + + std::string tun_iface_name; // used to skip tun-based default gw when add bypass route }; } } // namespace openvpn diff --git a/openvpn/tun/mac/client/tunsetup.hpp b/openvpn/tun/mac/client/tunsetup.hpp index 3f56cc07..c4a3030d 100644 --- a/openvpn/tun/mac/client/tunsetup.hpp +++ b/openvpn/tun/mac/client/tunsetup.hpp @@ -64,6 +64,7 @@ namespace openvpn { std::string iface_name; Layer layer; // OSI layer bool tun_prefix = false; + bool add_bypass_routes_on_establish = false; #ifdef HAVE_JSON virtual Json::Value to_json() override @@ -85,6 +86,14 @@ namespace openvpn { #endif }; + bool add_bypass_route(const std::string& address, + bool ipv6, + std::ostream& os) + { + // not yet implemented + return true; + } + virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base TunBuilderSetup::Config* config, Stop* stop,