shill: vpn: Add support for creating a tunnel interface
Use the ioctl inteface for creating a TUN device (no RTNL interface
for this appears available). Also create a means for removing
interfaces, using the RTNL interface.
BUG=chromium-os:26841
TEST=New unit test for RTNL interface, manual testing for ioctl
interface and for TUN interface detection and handling.
Change-Id: If70eeeecd5d2a2e3c348b56c297c0f3dd4226b8f
Reviewed-on: https://gerrit.chromium.org/gerrit/17127
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/device_info.cc b/device_info.cc
index 3419146..75f4734 100644
--- a/device_info.cc
+++ b/device_info.cc
@@ -2,18 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <time.h>
+#include "shill/device_info.h"
-#include <unistd.h>
-#include <string.h>
-#include <sys/socket.h>
#include <arpa/inet.h>
-#include <netinet/ether.h>
-#include <net/if.h>
-#include <net/if_arp.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
-#include <fcntl.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/ether.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <unistd.h>
+
#include <string>
#include <base/callback_old.h>
@@ -27,7 +31,6 @@
#include "shill/control_interface.h"
#include "shill/device.h"
-#include "shill/device_info.h"
#include "shill/device_stub.h"
#include "shill/ethernet.h"
#include "shill/manager.h"
@@ -50,6 +53,8 @@
// static
const char DeviceInfo::kInterfaceDriver[] = "/sys/class/net/%s/device/driver";
// static
+const char DeviceInfo::kInterfaceTunFlags[] = "/sys/class/net/%s/tun_flags";
+// static
const char DeviceInfo::kInterfaceType[] = "/sys/class/net/%s/type";
// static
const char *DeviceInfo::kModemDrivers[] = {
@@ -59,6 +64,8 @@
"cdc_ether",
NULL
};
+// static
+const char DeviceInfo::kTunDeviceName[] = "/dev/net/tun";
DeviceInfo::DeviceInfo(ControlInterface *control_interface,
EventDispatcher *dispatcher,
@@ -162,7 +169,19 @@
if (!file_util::ReadSymbolicLink(driver_file, &driver_path)) {
VLOG(2) << StringPrintf("%s: device %s has no device symlink",
__func__, iface_name.c_str());
- return Technology::kUnknown;
+ FilePath tun_flags_file(StringPrintf(kInterfaceTunFlags,
+ iface_name.c_str()));
+ string tun_flags_string;
+ int tun_flags = 0;
+ if (file_util::ReadFileToString(tun_flags_file, &tun_flags_string) &&
+ TrimString(tun_flags_string, "\n", &tun_flags_string) &&
+ base::HexStringToInt(tun_flags_string, &tun_flags) &&
+ (tun_flags & IFF_TUN)) {
+ VLOG(2) << StringPrintf("%s: device %s is tun device",
+ __func__, iface_name.c_str());
+ return Technology::kTunnel;
+ }
+ return Technology::kUnknown;
}
string driver_name(driver_path.BaseName().value());
@@ -200,14 +219,6 @@
DeviceRefPtr device = GetDevice(dev_index);
if (!device.get()) {
- if (msg.HasAttribute(IFLA_ADDRESS)) {
- infos_[dev_index].mac_address = msg.GetAttribute(IFLA_ADDRESS);
- VLOG(2) << "link index " << dev_index << " address "
- << infos_[dev_index].mac_address.HexEncode();
- } else {
- LOG(ERROR) << "Add Link message does not have IFLA_ADDRESS!";
- return;
- }
if (!msg.HasAttribute(IFLA_IFNAME)) {
LOG(ERROR) << "Add Link message does not have IFLA_IFNAME!";
return;
@@ -223,8 +234,16 @@
technology = GetDeviceTechnology(link_name);
}
}
- string address =
- StringToLowerASCII(infos_[dev_index].mac_address.HexEncode());
+ string address;
+ if (msg.HasAttribute(IFLA_ADDRESS)) {
+ infos_[dev_index].mac_address = msg.GetAttribute(IFLA_ADDRESS);
+ address = StringToLowerASCII(infos_[dev_index].mac_address.HexEncode());
+ VLOG(2) << "link index " << dev_index << " address "
+ << infos_[dev_index].mac_address.HexEncode();
+ } else if (technology != Technology::kTunnel) {
+ LOG(ERROR) << "Add Link message does not have IFLA_ADDRESS!";
+ return;
+ }
switch (technology) {
case Technology::kCellular:
// Cellular devices are managed by ModemInfo.
@@ -242,6 +261,12 @@
link_name, address, dev_index);
device->EnableIPv6Privacy();
break;
+ case Technology::kTunnel:
+ // Tunnel devices are managed by the VPN code.
+ VLOG(2) << "Tunnel link " << link_name << " at index " << dev_index
+ << " -- notifying VPNProvider.";
+ // TODO(pstew): Notify VPNProvider once that method exists.
+ return;
default:
device = new DeviceStub(control_interface_, dispatcher_, metrics_,
manager_, link_name, address, dev_index,
@@ -317,6 +342,36 @@
return true;
}
+bool DeviceInfo::CreateTunnelInterface(string *interface_name) {
+ int fd = HANDLE_EINTR(open(kTunDeviceName, O_RDWR));
+ if (fd < 0) {
+ PLOG(ERROR) << "failed to open " << kTunDeviceName;
+ return false;
+ }
+ file_util::ScopedFD scoped_fd(&fd);
+
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+ if (HANDLE_EINTR(ioctl(fd, TUNSETIFF, &ifr))) {
+ PLOG(ERROR) << "failed to create tunnel interface";
+ return false;
+ }
+
+ if (HANDLE_EINTR(ioctl(fd, TUNSETPERSIST, 1))) {
+ PLOG(ERROR) << "failed to set tunnel interface to be persistent";
+ return false;
+ }
+
+ *interface_name = string(ifr.ifr_name);
+
+ return true;
+}
+
+bool DeviceInfo::DeleteInterface(int interface_index) {
+ return rtnl_handler_->RemoveInterface(interface_index);
+}
+
const DeviceInfo::Info *DeviceInfo::GetInfo(int interface_index) const {
map<int, Info>::const_iterator iter = infos_.find(interface_index);
if (iter == infos_.end()) {