shill: vpn: Parse route options through openvpn notify callback.
BUG=chromium-os:27056
TEST=unit tests
Change-Id: Ia69e87c040936a7375cb31a69b3724f39be580d6
Reviewed-on: https://gerrit.chromium.org/gerrit/17314
Commit-Ready: Darin Petkov <petkov@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/ipconfig.h b/ipconfig.h
index ed2fc35..b3afed2 100644
--- a/ipconfig.h
+++ b/ipconfig.h
@@ -17,6 +17,7 @@
#include "shill/ip_address.h"
#include "shill/property_store.h"
#include "shill/refptr_types.h"
+#include "shill/routing_table_entry.h"
namespace shill {
class ControlInterface;
@@ -28,6 +29,12 @@
// class.
class IPConfig : public base::RefCounted<IPConfig> {
public:
+ struct Route {
+ std::string host;
+ std::string netmask;
+ std::string gateway;
+ };
+
struct Properties {
Properties() : address_family(IPAddress::kFamilyUnknown),
subnet_cidr(0),
@@ -44,6 +51,7 @@
std::string method;
std::string peer_address;
int32 mtu;
+ std::vector<Route> routes;
};
IPConfig(ControlInterface *control_interface, const std::string &device_name);
diff --git a/openvpn_driver.cc b/openvpn_driver.cc
index 9ffaf62..de28daf 100644
--- a/openvpn_driver.cc
+++ b/openvpn_driver.cc
@@ -86,6 +86,7 @@
const map<string, string> &configuration,
IPConfig::Properties *properties) {
ForeignOptions foreign_options;
+ RouteOptions routes;
string trusted_ip;
properties->address_family = IPAddress::kFamilyIPv4;
for (map<string, string>::const_iterator it = configuration.begin();
@@ -122,13 +123,15 @@
LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
}
} else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
- // TODO(petkov): Process the route.
+ ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
+ value, &routes);
} else {
VLOG(2) << "Key ignored.";
}
}
// TODO(petkov): If gateway and trusted_ip, pin a host route to VPN server.
ParseForeignOptions(foreign_options, properties);
+ SetRoutes(routes, properties);
}
// static
@@ -156,6 +159,52 @@
}
}
+// static
+IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
+ const string &prefix, const string &key, RouteOptions *routes) {
+ int order = 0;
+ if (!StartsWithASCII(key, prefix, false) ||
+ !base::StringToInt(key.substr(prefix.size()), &order)) {
+ return NULL;
+ }
+ return &(*routes)[order];
+}
+
+// static
+void OpenVPNDriver::ParseRouteOption(
+ const string &key, const string &value, RouteOptions *routes) {
+ IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
+ if (route) {
+ route->host = value;
+ return;
+ }
+ route = GetRouteOptionEntry("netmask_", key, routes);
+ if (route) {
+ route->netmask = value;
+ return;
+ }
+ route = GetRouteOptionEntry("gateway_", key, routes);
+ if (route) {
+ route->gateway = value;
+ return;
+ }
+ LOG(WARNING) << "Unknown route option ignored: " << key;
+}
+
+// static
+void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
+ IPConfig::Properties *properties) {
+ for (RouteOptions::const_iterator it = routes.begin();
+ it != routes.end(); ++it) {
+ const IPConfig::Route &route = it->second;
+ if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
+ LOG(WARNING) << "Ignoring incomplete route: " << it->first;
+ continue;
+ }
+ properties->routes.push_back(route);
+ }
+}
+
void OpenVPNDriver::Connect(Error *error) {
if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
Error::PopulateAndLog(
diff --git a/openvpn_driver.h b/openvpn_driver.h
index bcefc36..6201e01 100644
--- a/openvpn_driver.h
+++ b/openvpn_driver.h
@@ -57,15 +57,19 @@
FRIEND_TEST(OpenVPNDriverTest, AppendValueOption);
FRIEND_TEST(OpenVPNDriverTest, ClaimInterface);
FRIEND_TEST(OpenVPNDriverTest, Connect);
+ FRIEND_TEST(OpenVPNDriverTest, GetRouteOptionEntry);
FRIEND_TEST(OpenVPNDriverTest, InitOptions);
FRIEND_TEST(OpenVPNDriverTest, InitOptionsNoHost);
FRIEND_TEST(OpenVPNDriverTest, ParseForeignOption);
FRIEND_TEST(OpenVPNDriverTest, ParseForeignOptions);
FRIEND_TEST(OpenVPNDriverTest, ParseIPConfiguration);
+ FRIEND_TEST(OpenVPNDriverTest, ParseRouteOption);
+ FRIEND_TEST(OpenVPNDriverTest, SetRoutes);
// The map is a sorted container that allows us to iterate through the options
// in order.
typedef std::map<int, std::string> ForeignOptions;
+ typedef std::map<int, IPConfig::Route> RouteOptions;
static void ParseIPConfiguration(
const std::map<std::string, std::string> &configuration,
@@ -74,6 +78,14 @@
IPConfig::Properties *properties);
static void ParseForeignOption(const std::string &option,
IPConfig::Properties *properties);
+ static IPConfig::Route *GetRouteOptionEntry(const std::string &prefix,
+ const std::string &key,
+ RouteOptions *routes);
+ static void ParseRouteOption(const std::string &key,
+ const std::string &value,
+ RouteOptions *routes);
+ static void SetRoutes(const RouteOptions &routes,
+ IPConfig::Properties *properties);
void InitOptions(std::vector<std::string> *options, Error *error);
diff --git a/openvpn_driver_unittest.cc b/openvpn_driver_unittest.cc
index 514ed20..e4fc075 100644
--- a/openvpn_driver_unittest.cc
+++ b/openvpn_driver_unittest.cc
@@ -51,6 +51,12 @@
static const char kOption2[];
static const char kProperty2[];
static const char kValue2[];
+ static const char kGateway1[];
+ static const char kNetmask1[];
+ static const char kNetwork1[];
+ static const char kGateway2[];
+ static const char kNetmask2[];
+ static const char kNetwork2[];
void SetArgs() {
driver_.args_ = args_;
@@ -77,6 +83,12 @@
const char OpenVPNDriverTest::kOption2[] = "--openvpn-option2";
const char OpenVPNDriverTest::kProperty2[] = "OpenVPN.SomeProperty2";
const char OpenVPNDriverTest::kValue2[] = "some-property-value2";
+const char OpenVPNDriverTest::kGateway1[] = "10.242.2.13";
+const char OpenVPNDriverTest::kNetmask1[] = "255.255.255.255";
+const char OpenVPNDriverTest::kNetwork1[] = "10.242.2.1";
+const char OpenVPNDriverTest::kGateway2[] = "10.242.2.14";
+const char OpenVPNDriverTest::kNetmask2[] = "255.255.0.0";
+const char OpenVPNDriverTest::kNetwork2[] = "192.168.0.0";
void OpenVPNDriverTest::Notify(const string &/*reason*/,
const map<string, string> &/*dict*/) {}
@@ -123,6 +135,68 @@
EXPECT_TRUE(driver_.Notify("up", dict));
}
+TEST_F(OpenVPNDriverTest, GetRouteOptionEntry) {
+ OpenVPNDriver::RouteOptions routes;
+ EXPECT_EQ(NULL, OpenVPNDriver::GetRouteOptionEntry("foo", "bar", &routes));
+ EXPECT_TRUE(routes.empty());
+ EXPECT_EQ(NULL, OpenVPNDriver::GetRouteOptionEntry("foo", "foo", &routes));
+ EXPECT_TRUE(routes.empty());
+ EXPECT_EQ(NULL, OpenVPNDriver::GetRouteOptionEntry("foo", "fooZ", &routes));
+ EXPECT_TRUE(routes.empty());
+ IPConfig::Route *route =
+ OpenVPNDriver::GetRouteOptionEntry("foo", "foo12", &routes);
+ EXPECT_EQ(1, routes.size());
+ EXPECT_EQ(route, &routes[12]);
+ route = OpenVPNDriver::GetRouteOptionEntry("foo", "foo13", &routes);
+ EXPECT_EQ(2, routes.size());
+ EXPECT_EQ(route, &routes[13]);
+}
+
+TEST_F(OpenVPNDriverTest, ParseRouteOption) {
+ OpenVPNDriver::RouteOptions routes;
+ OpenVPNDriver::ParseRouteOption("foo", "bar", &routes);
+ EXPECT_TRUE(routes.empty());
+ OpenVPNDriver::ParseRouteOption("gateway_2", kGateway2, &routes);
+ OpenVPNDriver::ParseRouteOption("netmask_2", kNetmask2, &routes);
+ OpenVPNDriver::ParseRouteOption("network_2", kNetwork2, &routes);
+ EXPECT_EQ(1, routes.size());
+ OpenVPNDriver::ParseRouteOption("gateway_1", kGateway1, &routes);
+ OpenVPNDriver::ParseRouteOption("netmask_1", kNetmask1, &routes);
+ OpenVPNDriver::ParseRouteOption("network_1", kNetwork1, &routes);
+ EXPECT_EQ(2, routes.size());
+ EXPECT_EQ(kGateway1, routes[1].gateway);
+ EXPECT_EQ(kNetmask1, routes[1].netmask);
+ EXPECT_EQ(kNetwork1, routes[1].host);
+ EXPECT_EQ(kGateway2, routes[2].gateway);
+ EXPECT_EQ(kNetmask2, routes[2].netmask);
+ EXPECT_EQ(kNetwork2, routes[2].host);
+}
+
+TEST_F(OpenVPNDriverTest, SetRoutes) {
+ OpenVPNDriver::RouteOptions routes;
+ routes[1].gateway = "1.2.3.4";
+ routes[1].host= "1.2.3.4";
+ routes[2].host = "2.3.4.5";
+ routes[2].netmask = "255.0.0.0";
+ routes[3].netmask = "255.0.0.0";
+ routes[3].gateway = "1.2.3.5";
+ routes[5].host = kNetwork2;
+ routes[5].netmask = kNetmask2;
+ routes[5].gateway = kGateway2;
+ routes[4].host = kNetwork1;
+ routes[4].netmask = kNetmask1;
+ routes[4].gateway = kGateway1;
+ IPConfig::Properties props;
+ OpenVPNDriver::SetRoutes(routes, &props);
+ ASSERT_EQ(2, props.routes.size());
+ EXPECT_EQ(kGateway1, props.routes[0].gateway);
+ EXPECT_EQ(kNetmask1, props.routes[0].netmask);
+ EXPECT_EQ(kNetwork1, props.routes[0].host);
+ EXPECT_EQ(kGateway2, props.routes[1].gateway);
+ EXPECT_EQ(kNetmask2, props.routes[1].netmask);
+ EXPECT_EQ(kNetwork2, props.routes[1].host);
+}
+
TEST_F(OpenVPNDriverTest, ParseForeignOption) {
IPConfig::Properties props;
OpenVPNDriver::ParseForeignOption("", &props);
@@ -165,6 +239,12 @@
config["foreign_option_2"] = "dhcp-option DNS 4.4.4.4";
config["foreign_option_1"] = "dhcp-option DNS 1.1.1.1";
config["foreign_option_3"] = "dhcp-option DNS 2.2.2.2";
+ config["route_network_2"] = kNetwork2;
+ config["route_network_1"] = kNetwork1;
+ config["route_netmask_2"] = kNetmask2;
+ config["route_netmask_1"] = kNetmask1;
+ config["route_gateway_2"] = kGateway2;
+ config["route_gateway_1"] = kGateway1;
config["foo"] = "bar";
IPConfig::Properties props;
OpenVPNDriver::ParseIPConfiguration(config, &props);
@@ -179,6 +259,13 @@
EXPECT_EQ("1.1.1.1", props.dns_servers[0]);
EXPECT_EQ("4.4.4.4", props.dns_servers[1]);
EXPECT_EQ("2.2.2.2", props.dns_servers[2]);
+ ASSERT_EQ(2, props.routes.size());
+ EXPECT_EQ(kGateway1, props.routes[0].gateway);
+ EXPECT_EQ(kNetmask1, props.routes[0].netmask);
+ EXPECT_EQ(kNetwork1, props.routes[0].host);
+ EXPECT_EQ(kGateway2, props.routes[1].gateway);
+ EXPECT_EQ(kNetmask2, props.routes[1].netmask);
+ EXPECT_EQ(kNetwork2, props.routes[1].host);
}
TEST_F(OpenVPNDriverTest, InitOptionsNoHost) {