/libs/asiotap/src/posix/posix_tap_adapter.cpp
C++ | 816 lines | 571 code | 173 blank | 72 comment | 105 complexity | 47376d90a60d990f708cf00d3aacbcae MD5 | raw file
Possible License(s): AGPL-1.0, GPL-3.0
- /*
- * libasiotap - A portable TAP adapter extension for Boost::ASIO.
- * Copyright (C) 2010-2011 Julien KAUFFMANN <julien.kauffmann@freelan.org>
- *
- * This file is part of libasiotap.
- *
- * libasiotap is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 3 of
- * the License, or (at your option) any later version.
- *
- * libasiotap 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * In addition, as a special exception, the copyright holders give
- * permission to link the code of portions of this program with the
- * OpenSSL library under certain conditions as described in each
- * individual source file, and distribute linked combinations
- * including the two.
- * You must obey the GNU General Public License in all respects
- * for all of the code used other than OpenSSL. If you modify
- * file(s) with this exception, you may extend this exception to your
- * version of the file(s), but you are not obligated to do so. If you
- * do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source
- * files in the program, then also delete it here.
- *
- * If you intend to use libasiotap in a commercial software, please
- * contact me : we may arrange this for a small fee or no fee at all,
- * depending on the nature of your project.
- */
- /**
- * \file posix_tap_adapter.cpp
- * \author Julien KAUFFMANN <julien.kauffmann@freelan.org>
- * \brief The posix tap adapter class.
- */
- #include "posix/posix_tap_adapter.hpp"
- #include <boost/lexical_cast.hpp>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <ifaddrs.h>
- #include <errno.h>
- #include <unistd.h>
- #include <fcntl.h>
- #ifdef LINUX
- #include <linux/if_tun.h>
- /**
- * \struct in6_ifreq
- * \brief Replacement structure since the include of linux/ipv6.h introduces conflicts.
- *
- * If someone comes up with a better solution, feel free to contribute :)
- */
- struct in6_ifreq
- {
- struct in6_addr ifr6_addr; /**< IPv6 address */
- uint32_t ifr6_prefixlen; /**< Length of the prefix */
- int ifr6_ifindex; /**< Interface index */
- };
- #elif defined(MACINTOSH) || defined(BSD)
- /*
- * Note for Mac OS X users : you have to download and install the tun/tap driver from (http://tuntaposx.sourceforge.net).
- */
- #include <net/if_var.h>
- #include <net/if_types.h>
- #include <net/if_dl.h>
- #include <net/if.h>
- #include <netinet/in.h>
- #include <netinet6/in6_var.h>
- #endif
- namespace asiotap
- {
- namespace
- {
- unsigned int netmask_to_prefix_len(in_addr netmask)
- {
- uint32_t bits = ~ntohl(netmask.s_addr);
- unsigned int result = 0;
- while (bits > 0) {
- bits >>= 1;
- ++result;
- }
- return (sizeof(in_addr) * 8) - result;
- }
- unsigned int netmask_to_prefix_len(in6_addr netmask)
- {
- unsigned int result = 0;
- for (size_t i = 0; i < sizeof(netmask.s6_addr); ++i)
- {
- uint8_t bits = ~netmask.s6_addr[i];
- while (bits > 0) {
- bits >>= 1;
- ++result;
- }
- }
- return (sizeof(in6_addr) * 8) - result;
- }
- class descriptor_handler
- {
- public:
- descriptor_handler() : m_fd(-1) {}
- explicit descriptor_handler(int fd) : m_fd(fd) {}
- descriptor_handler(const descriptor_handler&) = delete;
- descriptor_handler& operator=(const descriptor_handler&) = delete;
- descriptor_handler(descriptor_handler&& other) : m_fd(other.m_fd) { other.m_fd = -1; }
- descriptor_handler& operator=(descriptor_handler&& other) { using std::swap; swap(m_fd, other.m_fd); return *this; }
- ~descriptor_handler() { if (m_fd >= 0) { ::close(m_fd); }}
- int native_handle() const { return m_fd; }
- bool valid() const { return (m_fd >= 0); }
- int release() { const int result = m_fd; m_fd = -1; return result; }
- private:
- int m_fd;
- };
- descriptor_handler open_device(const std::string& name, boost::system::error_code& ec)
- {
- const int device_fd = ::open(name.c_str(), O_RDWR);
- if (device_fd < 0)
- {
- // Unable to open the device.
- ec = boost::system::error_code(errno, boost::system::system_category());
- return descriptor_handler();
- }
- return descriptor_handler(device_fd);
- }
- descriptor_handler open_socket(int family, boost::system::error_code& ec)
- {
- const int socket_fd = ::socket(family, SOCK_DGRAM, 0);
- if (socket_fd < 0)
- {
- ec = boost::system::error_code(errno, boost::system::system_category());
- return descriptor_handler();
- }
- return descriptor_handler(socket_fd);
- }
- descriptor_handler open_socket(int family)
- {
- boost::system::error_code ec;
- descriptor_handler result = open_socket(family, ec);
- if (!result.valid())
- {
- throw boost::system::system_error(ec);
- }
- return result;
- }
- }
- std::map<std::string, std::string> posix_tap_adapter::enumerate(tap_adapter_layer _layer)
- {
- std::map<std::string, std::string> result;
- struct ifaddrs* addrs = nullptr;
- if (getifaddrs(&addrs) != -1)
- {
- boost::shared_ptr<struct ifaddrs> paddrs(addrs, freeifaddrs);
- for (struct ifaddrs* ifa = paddrs.get(); ifa != NULL ; ifa = ifa->ifa_next)
- {
- const std::string name(ifa->ifa_name);
- switch (_layer)
- {
- case tap_adapter_layer::ethernet:
- {
- if (name.substr(0, 3) == "tap")
- {
- result[name] = name;
- }
- }
- case tap_adapter_layer::ip:
- {
- if (name.substr(0, 3) == "tun")
- {
- result[name] = name;
- }
- }
- }
- }
- }
- return result;
- }
- void posix_tap_adapter::open(boost::system::error_code& ec)
- {
- open("", ec);
- }
- void posix_tap_adapter::open(const std::string& _name, boost::system::error_code& ec)
- {
- ec = boost::system::error_code();
- #if defined(LINUX)
- const std::string dev_name = (layer() == tap_adapter_layer::ethernet) ? "/dev/net/tap" : "/dev/net/tun";
- if (::access(dev_name.c_str(), F_OK) == -1)
- {
- if (errno != ENOENT)
- {
- // Unable to access the tap adapter yet it exists: this is an error.
- ec = boost::system::error_code(errno, boost::system::system_category());
- return;
- }
- // No tap found, create one.
- if (::mknod(dev_name.c_str(), S_IFCHR | S_IRUSR | S_IWUSR, ::makedev(10, 200)) == -1)
- {
- ec = boost::system::error_code(errno, boost::system::system_category());
- return;
- }
- }
- descriptor_handler device = open_device(dev_name, ec);
- if (!device.valid())
- {
- return;
- }
- struct ifreq ifr {};
- ifr.ifr_flags = IFF_NO_PI;
- #if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
- ifr.ifr_flags |= IFF_ONE_QUEUE;
- #endif
- if (layer() == tap_adapter_layer::ethernet)
- {
- ifr.ifr_flags |= IFF_TAP;
- }
- else
- {
- ifr.ifr_flags |= IFF_TUN;
- }
- if (!_name.empty())
- {
- strncpy(ifr.ifr_name, _name.c_str(), IFNAMSIZ);
- }
- // Set the parameters on the tun device.
- if (::ioctl(device.native_handle(), TUNSETIFF, (void *)&ifr) < 0)
- {
- ec = boost::system::error_code(errno, boost::system::system_category());
- return;
- }
- descriptor_handler socket = open_socket(AF_INET, ec);
- if (!socket.valid())
- {
- return;
- }
- {
- struct ifreq netifr {};
- std::strncpy(netifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
- #if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
- netifr.ifr_qlen = 100; // 100 is the default value
- if (::ioctl(socket.native_handle(), SIOCSIFTXQLEN, (void *)&netifr) < 0)
- {
- ec = boost::system::error_code(errno, boost::system::system_category());
- return;
- }
- // Reset the structure for the next call.
- netifr = {};
- std::strncpy(netifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
- #endif
- // Get the interface hwaddr
- if (::ioctl(socket.native_handle(), SIOCGIFHWADDR, (void*)&netifr) < 0)
- {
- ec = boost::system::error_code(errno, boost::system::system_category());
- return;
- }
- osi::ethernet_address _ethernet_address;
- std::memcpy(_ethernet_address.data().data(), netifr.ifr_hwaddr.sa_data, _ethernet_address.data().size());
- set_ethernet_address(_ethernet_address);
- }
- set_name(ifr.ifr_name);
- #else /* *BSD and Mac OS X */
- const std::string dev_type = (layer() == tap_adapter_layer::ethernet) ? "tap" : "tun";
- std::string interface_name = _name;
- descriptor_handler device;
- if (!_name.empty())
- {
- device = open_device("/dev/" + _name, ec);
- }
- else
- {
- for (unsigned int i = 0 ; !device.valid(); ++i)
- {
- interface_name = dev_type + boost::lexical_cast<std::string>(i);
- device = open_device("/dev/" + interface_name, ec);
- if (!device.valid() && (errno == ENOENT))
- {
- // We reached the end of the available tap adapters.
- break;
- }
- }
- }
- if (!device.valid())
- {
- ec = make_error_code(asiotap_error::no_such_tap_adapter);
- return;
- }
- struct stat st {};
- if (::fstat(device.native_handle(), &st) != 0)
- {
- ec = boost::system::error_code(errno, boost::system::system_category());
- return;
- }
- char namebuf[256];
- memset(namebuf, 0x00, sizeof(namebuf));
- if (::devname_r(st.st_dev, S_IFCHR, namebuf, 255) != NULL)
- {
- set_name(namebuf);
- }
- else
- {
- set_name(interface_name);
- }
- if (if_nametoindex(name().c_str()) == 0)
- {
- ec = make_error_code(asiotap_error::no_such_tap_adapter);
- return;
- }
- // Do not pass the descriptor to child
- ::fcntl(device.native_handle(), F_SETFD, FD_CLOEXEC);
- descriptor_handler socket = open_socket(AF_INET, ec);
- if (!socket.valid())
- {
- return;
- }
- /* Get the hardware address of tap inteface. */
- struct ifaddrs* addrs = nullptr;
- if (getifaddrs(&addrs) < 0)
- {
- ec = boost::system::error_code(errno, boost::system::system_category());
- return;
- }
- boost::shared_ptr<struct ifaddrs> paddrs(addrs, freeifaddrs);
- for (struct ifaddrs* ifa = addrs; ifa != NULL; ifa = ifa->ifa_next)
- {
- const std::string if_name = name();
- if ((ifa->ifa_addr->sa_family == AF_LINK) && !std::memcmp(ifa->ifa_name, if_name.c_str(), if_name.length()))
- {
- struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifa->ifa_addr;
- if (sdl->sdl_type == IFT_ETHER)
- {
- osi::ethernet_address _ethernet_address;
- std::memcpy(_ethernet_address.data().data(), LLADDR(sdl), ethernet_address().data().size());
- set_ethernet_address(_ethernet_address);
- break;
- }
- }
- }
- #endif
- update_mtu_from_device();
- if (descriptor().assign(device.release(), ec))
- {
- return;
- }
- }
- void posix_tap_adapter::open(const std::string& _name)
- {
- boost::system::error_code ec;
- open(_name, ec);
- if (ec)
- {
- throw boost::system::system_error(ec);
- }
- }
- void posix_tap_adapter::destroy_device()
- {
- boost::system::error_code ec;
- destroy_device(ec);
- if (ec)
- {
- throw boost::system::system_error(ec);
- }
- }
- void posix_tap_adapter::destroy_device(boost::system::error_code& ec)
- {
- #if defined(MACINTOSH) || defined(BSD)
- descriptor_handler socket = open_socket(AF_INET, ec);
- if (!socket.valid())
- {
- return;
- }
- struct ifreq ifr {};
- strncpy(ifr.ifr_name, name().c_str(), IFNAMSIZ);
- // Destroy the virtual tap device
- if (ioctl(socket.native_handle(), SIOCIFDESTROY, &ifr) < 0)
- {
- ec = boost::system::error_code(errno, boost::system::system_category());
- }
- #else
- static_cast<void>(ec);
- #endif
- }
- void posix_tap_adapter::set_connected_state(bool connected)
- {
- descriptor_handler socket = open_socket(AF_INET);
- struct ifreq netifr {};
- strncpy(netifr.ifr_name, name().c_str(), IFNAMSIZ);
- // Set the interface UP
- if (::ioctl(socket.native_handle(), SIOCGIFFLAGS, static_cast<void*>(&netifr)) < 0)
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- if (connected)
- {
- #ifdef MACINTOSH
- netifr.ifr_flags |= IFF_UP;
- #else
- netifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
- #endif
- }
- else
- {
- #ifdef MACINTOSH
- // Mac OS X: set_connected_state(false) seems to confuse the TAP
- // so do nothing for the moment.
- return;
- #else
- netifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
- #endif
- }
- if (::ioctl(socket.native_handle(), SIOCSIFFLAGS, static_cast<void*>(&netifr)) < 0)
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- }
- ip_network_address_list posix_tap_adapter::get_ip_addresses()
- {
- ip_network_address_list result;
- struct ifaddrs* addrs = nullptr;
- if (::getifaddrs(&addrs) < 0)
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- boost::shared_ptr<struct ifaddrs> paddrs(addrs, ::freeifaddrs);
- for (struct ifaddrs* ifa = paddrs.get(); ifa != nullptr; ifa = ifa->ifa_next)
- {
- const std::string ifname(ifa->ifa_name);
- if ((ifa->ifa_addr) && (ifname == name()))
- {
- if (ifa->ifa_addr->sa_family == AF_INET)
- {
- struct sockaddr_in* sai = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
- boost::asio::ip::address_v4::bytes_type bytes;
- memcpy(bytes.data(), &sai->sin_addr, bytes.size());
- boost::asio::ip::address_v4 address(bytes);
- unsigned int prefix_len = sizeof(in_addr) * 8;
- if (ifa->ifa_netmask)
- {
- struct sockaddr_in* sain = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_netmask);
- prefix_len = netmask_to_prefix_len(sain->sin_addr);
- }
- result.push_back(ipv4_network_address{ address, prefix_len });
- }
- else if (ifa->ifa_addr->sa_family == AF_INET6)
- {
- struct sockaddr_in6* sai = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
- boost::asio::ip::address_v6::bytes_type bytes;
- memcpy(bytes.data(), &sai->sin6_addr, bytes.size());
- boost::asio::ip::address_v6 address(bytes);
- unsigned int prefix_len = sizeof(in6_addr) * 8;
- if (ifa->ifa_netmask)
- {
- struct sockaddr_in6* sain = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_netmask);
- prefix_len = netmask_to_prefix_len(sain->sin6_addr);
- }
- result.push_back(ipv6_network_address{ address, prefix_len });
- }
- }
- }
- return result;
- }
- void posix_tap_adapter::configure(const configuration_type& configuration)
- {
- if (configuration.ipv4.network_address)
- {
- if (layer() == tap_adapter_layer::ethernet)
- {
- set_ip_address_v4(*configuration.ipv4.network_address);
- }
- else
- {
- if (configuration.ipv4.remote_address)
- {
- set_remote_ip_address_v4(*configuration.ipv4.network_address, *configuration.ipv4.remote_address);
- }
- }
- }
- if (configuration.ipv6.network_address)
- {
- set_ip_address_v6(*configuration.ipv6.network_address);
- }
- if (configuration.mtu > 0)
- {
- set_device_mtu(configuration.mtu);
- }
- }
- void posix_tap_adapter::update_mtu_from_device()
- {
- descriptor_handler socket = open_socket(AF_INET);
- struct ifreq netifr {};
- strncpy(netifr.ifr_name, name().c_str(), IFNAMSIZ);
- if (::ioctl(socket.native_handle(), SIOCGIFMTU, (void*)&netifr) >= 0)
- {
- set_mtu(netifr.ifr_mtu);
- }
- else
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- }
- void posix_tap_adapter::set_device_mtu(size_t _mtu)
- {
- descriptor_handler socket = open_socket(AF_INET);
- struct ifreq netifr {};
- strncpy(netifr.ifr_name, name().c_str(), IFNAMSIZ);
- netifr.ifr_mtu = _mtu;
- if (::ioctl(socket.native_handle(), SIOCSIFMTU, (void*)&netifr) < 0)
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- }
- void posix_tap_adapter::set_ip_address_v4(const ipv4_network_address& network_address)
- {
- const boost::asio::ip::address_v4& address = network_address.address();
- const unsigned int prefix_len = network_address.prefix_length();
- assert(prefix_len < 32);
- descriptor_handler socket = open_socket(AF_INET);
- ifreq ifr_a {};
- std::strncpy(ifr_a.ifr_name, name().c_str(), IFNAMSIZ - 1);
- sockaddr_in* ifr_a_addr = reinterpret_cast<sockaddr_in*>(&ifr_a.ifr_addr);
- ifr_a_addr->sin_family = AF_INET;
- #ifdef BSD
- ifr_a_addr->sin_len = sizeof(sockaddr_in);
- #endif
- std::memcpy(&ifr_a_addr->sin_addr.s_addr, address.to_bytes().data(), address.to_bytes().size());
- if (::ioctl(socket.native_handle(), SIOCSIFADDR, &ifr_a) < 0)
- {
- if (errno == EEXIST)
- {
- // The address is already set. We ignore this.
- }
- else
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- }
- if (prefix_len > 0)
- {
- ifreq ifr_n {};
- std::strncpy(ifr_n.ifr_name, name().c_str(), IFNAMSIZ - 1);
- sockaddr_in* ifr_n_addr = reinterpret_cast<sockaddr_in*>(&ifr_n.ifr_addr);
- ifr_n_addr->sin_family = AF_INET;
- #ifdef BSD
- ifr_n_addr->sin_len = sizeof(sockaddr_in);
- #endif
- ifr_n_addr->sin_addr.s_addr = htonl((0xFFFFFFFF >> (32 - prefix_len)) << (32 - prefix_len));
- if (::ioctl(socket.native_handle(), SIOCSIFNETMASK, &ifr_n) < 0)
- {
- if (errno == EEXIST)
- {
- // The netmask is already set. We ignore this.
- }
- else
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- }
- }
- }
- void posix_tap_adapter::set_ip_address_v6(const ipv6_network_address& network_address)
- {
- const boost::asio::ip::address_v6& address = network_address.address();
- const unsigned int prefix_len = network_address.prefix_length();
- assert(prefix_len < 128);
- descriptor_handler socket = open_socket(AF_INET6);
- #ifdef LINUX
- const unsigned int if_index = ::if_nametoindex(name().c_str());
- if (if_index == 0)
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- in6_ifreq ifr {};
- std::memcpy(&ifr.ifr6_addr, address.to_bytes().data(), address.to_bytes().size());
- ifr.ifr6_prefixlen = prefix_len;
- ifr.ifr6_ifindex = if_index;
- if (::ioctl(socket.native_handle(), SIOCSIFADDR, &ifr) < 0)
- #elif defined(MACINTOSH) || defined(BSD)
- in6_aliasreq iar {};
- std::memcpy(iar.ifra_name, name().c_str(), name().length());
- reinterpret_cast<sockaddr_in6*>(&iar.ifra_addr)->sin6_family = AF_INET6;
- reinterpret_cast<sockaddr_in6*>(&iar.ifra_prefixmask)->sin6_family = AF_INET6;
- std::memcpy(&reinterpret_cast<sockaddr_in6*>(&iar.ifra_addr)->sin6_addr, address.to_bytes().data(), address.to_bytes().size());
- std::memset(reinterpret_cast<sockaddr_in6*>(&iar.ifra_prefixmask)->sin6_addr.s6_addr, 0xFF, prefix_len / 8);
- reinterpret_cast<sockaddr_in6*>(&iar.ifra_prefixmask)->sin6_addr.s6_addr[prefix_len / 8] = (0xFF << (8 - (prefix_len % 8)));
- iar.ifra_lifetime.ia6t_pltime = 0xFFFFFFFF;
- iar.ifra_lifetime.ia6t_vltime = 0xFFFFFFFF;
- #ifdef SIN6_LEN
- reinterpret_cast<sockaddr_in6*>(&iar.ifra_addr)->sin6_len = sizeof(sockaddr_in6);
- reinterpret_cast<sockaddr_in6*>(&iar.ifra_prefixmask)->sin6_len = sizeof(sockaddr_in6);
- #endif
- if (::ioctl(socket.native_handle(), SIOCAIFADDR_IN6, &iar) < 0)
- #endif
- {
- if (errno == EEXIST)
- {
- // The address is already set. We ignore this.
- }
- else
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- }
- }
- void posix_tap_adapter::set_remote_ip_address_v4(const ipv4_network_address& network_address, const boost::asio::ip::address_v4& remote_address)
- {
- if (layer() != tap_adapter_layer::ip)
- {
- throw boost::system::system_error(make_error_code(asiotap_error::invalid_tap_adapter_layer));
- }
- #ifdef MACINTOSH
- // The TUN adapter for Mac OSX has a weird behavior regarding routes and ioctl.
- // For some reason, on Mac, setting up the IP address using ioctl() doesn't work for TUN devices.
- m_route_manager.ifconfig(name(), network_address, remote_address);
- // OSX apparently does not create a route even though ifconfig indicates that the netmask is understood.
- // We must create it ourselves.
- m_route_manager.set_route(posix_route_manager::route_action::add, name(), network_address);
- #else
- set_ip_address_v4(network_address);
- descriptor_handler socket = open_socket(AF_INET);
- ifreq ifr_d {};
- std::strncpy(ifr_d.ifr_name, name().c_str(), IFNAMSIZ - 1);
- sockaddr_in* ifr_dst_addr = reinterpret_cast<sockaddr_in*>(&ifr_d.ifr_dstaddr);
- ifr_dst_addr->sin_family = AF_INET;
- #ifdef BSD
- ifr_dst_addr->sin_len = sizeof(sockaddr_in);
- #endif
- std::memcpy(&ifr_dst_addr->sin_addr.s_addr, remote_address.to_bytes().data(), remote_address.to_bytes().size());
- if (::ioctl(socket.native_handle(), SIOCSIFDSTADDR, &ifr_d) < 0)
- {
- if (errno == EEXIST)
- {
- // The address is already set. We ignore this.
- }
- else
- {
- throw boost::system::system_error(errno, boost::system::system_category());
- }
- }
- #endif
- }
- }