// OpenVPN -- An application to securely tunnel IP networks // over a single port, with support for SSL/TLS-based // session authentication and key exchange, // packet encryption, packet authentication, and // packet compression. // // Copyright (C) 2012-2020 OpenVPN Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License Version 3 // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program in the COPYING file. // If not, see <http://www.gnu.org/licenses/>. // Find default gateways on Linux using ip route command #ifndef OPENVPN_NETCONF_LINUX_GW_H #define OPENVPN_NETCONF_LINUX_GW_H #include <string> #include <limits> #include <openvpn/common/exception.hpp> #include <openvpn/common/number.hpp> #include <openvpn/common/split.hpp> #include <openvpn/common/splitlines.hpp> #include <openvpn/common/process.hpp> #include <openvpn/addr/route.hpp> namespace openvpn { class LinuxGW { public: OPENVPN_EXCEPTION(linux_gw_error); LinuxGW(const std::string& ip_route_show_txt, const bool ignore_errors) { int best_metric = std::numeric_limits<int>::max(); SplitLines sl(ip_route_show_txt); while (sl()) { const std::string& line = sl.line_ref(); try { // parse an output line generated by "ip [-6] route show" const std::vector<std::string> v = Split::by_space<std::vector<std::string>, NullLex, SpaceMatch, Split::NullLimit>(line); // blank line? if (v.empty()) continue; // only interested in default routes if (v[0] != "default") continue; // parse out route information enum RouteInfo { INITIAL, VIA, DEV, METRIC, }; std::string d; IP::Addr a; int m = std::numeric_limits<int>::max(); RouteInfo ri = INITIAL; for (const auto &term : v) { switch (ri) { case INITIAL: if (term == "via") ri = VIA; else if (term == "dev") ri = DEV; else if (term == "metric") ri = METRIC; else ri = INITIAL; break; case VIA: a = IP::Addr(term, "via"); ri = INITIAL; break; case DEV: d = validate_dev(term); ri = INITIAL; break; case METRIC: m = parse_number_throw<int>(term, "bad metric"); ri = INITIAL; break; } } // best metric? if (m < best_metric || best_metric == std::numeric_limits<int>::max()) { best_metric = m; dev_ = d; addr_ = a; } } catch (const std::exception& e) { if (!ignore_errors) OPENVPN_THROW(linux_gw_error, "error parsing line: " << line << " : " << e.what()); } } } static std::string ip_route_show(const bool ipv6) { RedirectPipe::InOut pipe; Argv argv; argv.emplace_back("/sbin/ip"); if (ipv6) argv.emplace_back("-6"); argv.emplace_back("route"); argv.emplace_back("show"); const int status = system_cmd(argv[0], argv, nullptr, pipe, 0, nullptr); if (status != 0) OPENVPN_THROW(linux_gw_error, "command returned error status " << status << " : " << argv.to_string()); return pipe.out; } const std::string& dev() const { return dev_; } const IP::Addr& addr() const { return addr_; } bool defined() const { return !dev_.empty() && addr_.defined(); } std::string to_string() const { return dev_ + '/' + addr_.to_string(); } private: std::string validate_dev(const std::string& dev) { if (dev.empty()) OPENVPN_THROW_EXCEPTION("dev is empty"); return dev; } std::string dev_; IP::Addr addr_; }; struct LinuxGW46 { LinuxGW46(const bool ignore_errors) : v4(LinuxGW::ip_route_show(false), ignore_errors), v6(LinuxGW::ip_route_show(true), ignore_errors) { } std::string to_string() const { std::string ret = "["; if (v4.defined()) { ret += "4:"; ret += v4.to_string(); } if (v6.defined()) { if (v4.defined()) ret += ' '; ret += "6:"; ret += v6.to_string(); } ret += "]"; return ret; } std::string dev() const { if (v4.defined()) return v4.dev(); else if (v6.defined()) return v6.dev(); else throw LinuxGW::linux_gw_error("cannot determine gateway interface"); } LinuxGW v4; LinuxGW v6; }; } #endif