shill: vpn: Create a VPN device when the tunnel index is available.
BUG=chromium-os:27278
TEST=unit tests
Change-Id: Ie352618d0bf276a7de2673d29620c0fe4275b0ca
Reviewed-on: https://gerrit.chromium.org/gerrit/17321
Commit-Ready: Darin Petkov <petkov@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/Makefile b/Makefile
index 4e6b25f..b068a48 100644
--- a/Makefile
+++ b/Makefile
@@ -177,6 +177,7 @@
rpc_task.o \
rpc_task_dbus_adaptor.o \
technology.o \
+ vpn.o \
vpn_provider.o \
vpn_service.o \
wifi.o \
@@ -286,6 +287,7 @@
testrunner.o \
vpn_provider_unittest.o \
vpn_service_unittest.o \
+ vpn_unittest.o \
wifi_endpoint_unittest.o \
wifi_service_unittest.o \
wifi_unittest.o \
diff --git a/openvpn_driver.cc b/openvpn_driver.cc
index 69a6d33..9ffaf62 100644
--- a/openvpn_driver.cc
+++ b/openvpn_driver.cc
@@ -15,6 +15,7 @@
#include "shill/dhcp_config.h"
#include "shill/error.h"
#include "shill/rpc_task.h"
+#include "shill/vpn.h"
using std::map;
using std::string;
@@ -36,12 +37,17 @@
} // namespace
OpenVPNDriver::OpenVPNDriver(ControlInterface *control,
+ EventDispatcher *dispatcher,
+ Metrics *metrics,
+ Manager *manager,
DeviceInfo *device_info,
const KeyValueStore &args)
: control_(control),
+ dispatcher_(dispatcher),
+ metrics_(metrics),
+ manager_(manager),
device_info_(device_info),
- args_(args),
- interface_index_(-1) {}
+ args_(args) {}
OpenVPNDriver::~OpenVPNDriver() {}
@@ -53,8 +59,13 @@
VLOG(2) << "Claiming " << link_name << " for OpenVPN tunnel";
- // TODO(petkov): Could create a VPNDevice or DeviceStub here instead.
- interface_index_ = interface_index;
+ CHECK(!device_);
+ device_ = new VPN(control_, dispatcher_, metrics_, manager_,
+ link_name, interface_index);
+
+ // TODO(petkov): Allocate rpc_task_.
+
+ // TODO(petkov): Initialize options and spawn openvpn.
return true;
}
@@ -146,8 +157,12 @@
}
void OpenVPNDriver::Connect(Error *error) {
- // TODO(petkov): Allocate rpc_task_.
- error->Populate(Error::kNotSupported);
+ if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
+ Error::PopulateAndLog(
+ error, Error::kInternalError, "Could not create tunnel interface.");
+ return;
+ }
+ // Wait for the ClaimInterface callback to continue the connection process.
}
void OpenVPNDriver::InitOptions(vector<string> *options, Error *error) {
@@ -168,13 +183,7 @@
options->push_back("--persist-key");
options->push_back("--persist-tun");
- if (tunnel_interface_.empty() &&
- !device_info_->CreateTunnelInterface(&tunnel_interface_)) {
- Error::PopulateAndLog(
- error, Error::kInternalError, "Could not create tunnel interface.");
- return;
- }
-
+ CHECK(!tunnel_interface_.empty());
options->push_back("--dev");
options->push_back(tunnel_interface_);
options->push_back("--dev-type");
diff --git a/openvpn_driver.h b/openvpn_driver.h
index 00f7eab..bcefc36 100644
--- a/openvpn_driver.h
+++ b/openvpn_driver.h
@@ -14,6 +14,7 @@
#include "shill/ipconfig.h"
#include "shill/key_value_store.h"
+#include "shill/refptr_types.h"
#include "shill/vpn_driver.h"
namespace shill {
@@ -21,12 +22,18 @@
class ControlInterface;
class DeviceInfo;
class Error;
+class EventDispatcher;
+class Manager;
+class Metrics;
class RPCTask;
class DeviceStub;
class OpenVPNDriver : public VPNDriver {
public:
OpenVPNDriver(ControlInterface *control,
+ EventDispatcher *dispatcher,
+ Metrics *metrics,
+ Manager *manager,
DeviceInfo *device_info,
const KeyValueStore &args);
virtual ~OpenVPNDriver();
@@ -34,16 +41,22 @@
bool Notify(const std::string &reason,
const std::map<std::string, std::string> &dict);
- // Inherited from VPNDriver.
+ // Inherited from VPNDriver. |Connect| initiates the VPN connection by
+ // creating a tunnel device. When the device index becomes available, this
+ // instance is notified through |ClaimInterface| and resumes the connection
+ // process by setting up and spawning an external 'openvpn' process. IP
+ // configuration settings are passed back from the external process through
+ // the |Notify| RPC service method.
+ virtual void Connect(Error *error);
virtual bool ClaimInterface(const std::string &link_name,
int interface_index);
- virtual void Connect(Error *error);
private:
friend class OpenVPNDriverTest;
FRIEND_TEST(OpenVPNDriverTest, AppendFlag);
FRIEND_TEST(OpenVPNDriverTest, AppendValueOption);
FRIEND_TEST(OpenVPNDriverTest, ClaimInterface);
+ FRIEND_TEST(OpenVPNDriverTest, Connect);
FRIEND_TEST(OpenVPNDriverTest, InitOptions);
FRIEND_TEST(OpenVPNDriverTest, InitOptionsNoHost);
FRIEND_TEST(OpenVPNDriverTest, ParseForeignOption);
@@ -72,11 +85,14 @@
std::vector<std::string> *options);
ControlInterface *control_;
+ EventDispatcher *dispatcher_;
+ Metrics *metrics_;
+ Manager *manager_;
DeviceInfo *device_info_;
KeyValueStore args_;
scoped_ptr<RPCTask> rpc_task_;
std::string tunnel_interface_;
- int interface_index_;
+ VPNRefPtr device_;
DISALLOW_COPY_AND_ASSIGN(OpenVPNDriver);
};
diff --git a/openvpn_driver_unittest.cc b/openvpn_driver_unittest.cc
index a587aa5..514ed20 100644
--- a/openvpn_driver_unittest.cc
+++ b/openvpn_driver_unittest.cc
@@ -13,8 +13,12 @@
#include "shill/ipconfig.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_device_info.h"
+#include "shill/mock_glib.h"
+#include "shill/mock_manager.h"
+#include "shill/mock_metrics.h"
#include "shill/nice_mock_control.h"
#include "shill/rpc_task.h"
+#include "shill/vpn.h"
using std::map;
using std::string;
@@ -31,7 +35,9 @@
public:
OpenVPNDriverTest()
: device_info_(&control_, NULL, NULL, NULL),
- driver_(&control_, &device_info_, args_) {}
+ manager_(&control_, &dispatcher_, &metrics_, &glib_),
+ driver_(&control_, &dispatcher_, &metrics_, &manager_, &device_info_,
+ args_) {}
virtual ~OpenVPNDriverTest() {}
@@ -57,6 +63,10 @@
NiceMockControl control_;
MockDeviceInfo device_info_;
+ EventDispatcher dispatcher_;
+ MockMetrics metrics_;
+ MockGLib glib_;
+ MockManager manager_;
KeyValueStore args_;
OpenVPNDriver driver_;
};
@@ -89,9 +99,22 @@
TEST_F(OpenVPNDriverTest, Connect) {
- Error error;
- driver_.Connect(&error);
- EXPECT_EQ(Error::kNotSupported, error.type());
+ const string kInterfaceName("tun0");
+ EXPECT_CALL(device_info_, CreateTunnelInterface(_))
+ .WillOnce(Return(false))
+ .WillOnce(DoAll(SetArgumentPointee<0>(kInterfaceName), Return(true)));
+ {
+ Error error;
+ driver_.Connect(&error);
+ EXPECT_EQ(Error::kInternalError, error.type());
+ EXPECT_TRUE(driver_.tunnel_interface_.empty());
+ }
+ {
+ Error error;
+ driver_.Connect(&error);
+ EXPECT_TRUE(error.IsSuccess());
+ EXPECT_EQ(kInterfaceName, driver_.tunnel_interface_);
+ }
}
TEST_F(OpenVPNDriverTest, Notify) {
@@ -174,8 +197,7 @@
Error error;
vector<string> options;
const string kInterfaceName("tun0");
- EXPECT_CALL(device_info_, CreateTunnelInterface(_))
- .WillOnce(DoAll(SetArgumentPointee<0>(kInterfaceName), Return(true)));
+ driver_.tunnel_interface_ = kInterfaceName;
driver_.InitOptions(&options, &error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ("--client", options[0]);
@@ -228,10 +250,11 @@
driver_.tunnel_interface_ = kInterfaceName;
const int kInterfaceIndex = 1122;
EXPECT_FALSE(driver_.ClaimInterface(kInterfaceName + "XXX", kInterfaceIndex));
- EXPECT_EQ(-1, driver_.interface_index_);
+ EXPECT_FALSE(driver_.device_);
EXPECT_TRUE(driver_.ClaimInterface(kInterfaceName, kInterfaceIndex));
- EXPECT_EQ(kInterfaceIndex, driver_.interface_index_);
+ ASSERT_TRUE(driver_.device_);
+ EXPECT_EQ(kInterfaceIndex, driver_.device_->interface_index());
}
} // namespace shill
diff --git a/refptr_types.h b/refptr_types.h
index c2648ad..811c6f8 100644
--- a/refptr_types.h
+++ b/refptr_types.h
@@ -21,6 +21,10 @@
typedef scoped_refptr<const Ethernet> EthernetConstRefPtr;
typedef scoped_refptr<Ethernet> EthernetRefPtr;
+class VPN;
+typedef scoped_refptr<const VPN> VPNConstRefPtr;
+typedef scoped_refptr<VPN> VPNRefPtr;
+
class WiFi;
typedef scoped_refptr<const WiFi> WiFiConstRefPtr;
typedef scoped_refptr<WiFi> WiFiRefPtr;
diff --git a/vpn.cc b/vpn.cc
new file mode 100644
index 0000000..0fa1589
--- /dev/null
+++ b/vpn.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/vpn.h"
+
+using std::string;
+
+namespace shill {
+
+VPN::VPN(ControlInterface *control,
+ EventDispatcher *dispatcher,
+ Metrics *metrics,
+ Manager *manager,
+ const string &link_name,
+ int interface_index)
+ : Device(control, dispatcher, metrics, manager, link_name, "",
+ interface_index, Technology::kVPN) {}
+
+VPN::~VPN() {}
+
+bool VPN::TechnologyIs(const Technology::Identifier type) const {
+ return type == Technology::kVPN;
+}
+
+} // namespace shill
diff --git a/vpn.h b/vpn.h
new file mode 100644
index 0000000..2201831
--- /dev/null
+++ b/vpn.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_VPN_
+#define SHILL_VPN_
+
+#include "shill/device.h"
+
+namespace shill {
+
+class VPN : public Device {
+ public:
+ VPN(ControlInterface *control,
+ EventDispatcher *dispatcher,
+ Metrics *metrics,
+ Manager *manager,
+ const std::string &link_name,
+ int interface_index);
+
+ virtual ~VPN();
+
+ virtual bool TechnologyIs(const Technology::Identifier type) const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VPN);
+};
+
+} // namespace shill
+
+#endif // SHILL_VPN_
diff --git a/vpn_provider.cc b/vpn_provider.cc
index 0adffcb..24ac690 100644
--- a/vpn_provider.cc
+++ b/vpn_provider.cc
@@ -44,8 +44,9 @@
const string &type = args.GetString(flimflam::kProviderTypeProperty);
scoped_ptr<VPNDriver> driver;
if (type == flimflam::kProviderOpenVpn) {
- driver.reset(new OpenVPNDriver(control_interface_,
- manager_->device_info(), args));
+ driver.reset(new OpenVPNDriver(
+ control_interface_, dispatcher_, metrics_, manager_,
+ manager_->device_info(), args));
} else {
Error::PopulateAndLog(
error, Error::kNotSupported, "Unsupported VPN type: " + type);
diff --git a/vpn_unittest.cc b/vpn_unittest.cc
new file mode 100644
index 0000000..f4a7dac
--- /dev/null
+++ b/vpn_unittest.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/vpn.h"
+
+#include <gtest/gtest.h>
+
+#include "shill/event_dispatcher.h"
+#include "shill/mock_glib.h"
+#include "shill/mock_manager.h"
+#include "shill/mock_metrics.h"
+#include "shill/nice_mock_control.h"
+
+namespace shill {
+
+class VPNTest : public testing::Test {
+ public:
+ VPNTest()
+ : manager_(&control_, &dispatcher_, &metrics_, &glib_),
+ vpn_(new VPN(&control_,
+ &dispatcher_,
+ &metrics_,
+ &manager_,
+ kTestDeviceName,
+ kTestInterfaceIndex)) {}
+
+ virtual ~VPNTest() {}
+
+ protected:
+ static const char kTestDeviceName[];
+ static const int kTestInterfaceIndex;
+
+ NiceMockControl control_;
+ EventDispatcher dispatcher_;
+ MockMetrics metrics_;
+ MockGLib glib_;
+ MockManager manager_;
+
+ VPNRefPtr vpn_;
+};
+
+const char VPNTest::kTestDeviceName[] = "tun0";
+const int VPNTest::kTestInterfaceIndex = 5;
+
+TEST_F(VPNTest, TechnologyIs) {
+ EXPECT_TRUE(vpn_->TechnologyIs(Technology::kVPN));
+ EXPECT_FALSE(vpn_->TechnologyIs(Technology::kEthernet));
+}
+
+} // namespace shill