mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Squashed 'Sources/OpenVPNAdapter/Libraries/Vendors/openvpn/' changes from cc90cde57..6608878d5
6608878d5 [OVPN3-341] implement mssfix support 1bf3fc0e4 win: update project files f8d209435 travis: update to default osx image: xcode9.4 31eb246a8 travis.yml: align deps version to lib-version 996f86635 RunContext: fixed rebase issue that added two "default: signal_rearm();" clauses aebea6456 build script: minor changes to Cityhash inclusion 1d754072c modstat: make update_file_mod_time_nanoseconds() a no-op on non-Linux 7974c9867 Fixed some breakage caused by recent endian/ffs commits a0dd7fe8b endian.hpp: break out endian compile-time tests to endian_platform.hpp c8bdf5a34 ffs.hpp: support additional numeric types dcb0c9452 BufferType: append() argument can now be a flexible buffer type 2009a8a25 Added AsioTimerSafe 39e71b7dd event_loop_wait_barrier: use a longer default timeout when running under valgrind 8b7e08e9b string::contains_non_space_ctrl: consider ASCII char 127 (DEL) to be a control char e43024d7c RunContext: rearm non-terminating signals 6ab379323 write_binary_atomic: remove temporary file on move failure 55dc653cd path: added is_contained() 02bf235c6 Reverted previous commit: "ReplyParser: added undefined status" 84dbc5b9b Allow test/cli.cpp to be used with NetCfg Tunbuilder client 80fed2c55 Allow updating auth-token during session ad7da751e don't print time in debug message and use OPENVPN_LOG_PROTO_VERBOSE 981407994 tls-crypt-v2: implement abstract metadata parser be38bbeb8 tls-crypt-v2: test/ssl/proto.cpp - extend protocol test 60fcf374f tls-crypt-v2: implement WKc appending/unwrapping logic 51f4a3a29 tls-crypt-v2: introduce CONTROL_HARD_RESET_V3 packet type 156a6e58b tls-crypt-v2: implement client key parser and renderer 54a97b381 ssl: add support for encoding/decoding PEM format f090fcda4 tls-crypt: make HMAC API more generic d87f5bbc0 OpenSSL: init library 2ea88a93b Add Remote endpoint information to protect_socket call 0a081ee17 [OVPN3-315] cli/go: add option to compile SITNL component 5bbfb57c0 [OVPN3-315] TunLinux::Client: allow user to select netlink at compile time e8458a68e [OVPN3-315] GW: add netlink support 4e77edb9e [OVPN3-315] TunLinux: add Netlink implementation for Tun setup methods 68508fe56 bigmutex: include missing extern.hpp header a7b923e1e Fix logic inversion from commit 2de9aebc 923e10d13 runcontext: arrange members to allow inheritance 2de9aebc7 Replace deprecated mbedtls_sha1 with mbedtls_sha1_ret e9c0bd00b Remove unused private field ee17c33c2 Add virtual deconstructor to TransportClientParent fab64ba0f Fix clang warning about unused attributes and missing overrides 2624d9ddf Also parse dhcp-option DNS6 as DNS server for compatibility with OpenVPN 2 6d12c9cc2 Refuse external pki with non RSA keys 4a25059f5 test/ovpncli: Don't override PROF env variable f241c4c5f scripts: Add tool to update copyright years 27beeb03d Update lz4 version to 1.8.3 17e356858 Define DASIO_HAS_STD_STRING_VIEW on Android build b107fd994 Remove unsupported platforms from Android build 6a200f72e Ensure all Android components are always installed fbcd374a4 [OVPN3-327] OpenSSL: ensure >TLS1.0 is negotiated by default d9b1f78b6 JSON: #define OPENVPN_JSON_INTERNAL when internal JSON library is used 39290f19d Fix build issues with #if macro on big-endian hardware d4f62d9ed Fix instantiating a new URL instead of parsing the URL git-subtree-dir: Sources/OpenVPNAdapter/Libraries/Vendors/openvpn git-subtree-split: 6608878d57eec1c64c16c5a13ee65b2cf0418ca1
This commit is contained in:
@@ -0,0 +1,917 @@
|
||||
// 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-2018 OpenVPN Inc.
|
||||
// Copyright (C) 2018 Antonio Quartulli <antonio@openvpn.net>
|
||||
//
|
||||
// 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/addr/ipv4.hpp>
|
||||
#include <openvpn/addr/ipv6.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
|
||||
|
||||
#ifdef DEBUG_RTNL
|
||||
#define OPENVPN_LOG_RTNL(_x) OPENVPN_LOG(_x)
|
||||
#else
|
||||
#define OPENVPN_LOG_RTNL(_x)
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunNetlink {
|
||||
|
||||
#define SNDBUF_SIZE (1024 * 2)
|
||||
#define RCVBUF_SIZE (1024 * 4)
|
||||
|
||||
#define SITNL_ADDATTR(_msg, _max_size, _attr, _data, _size) \
|
||||
{ \
|
||||
if (sitnl_addattr(_msg, _max_size, _attr, _data, _size) < 0)\
|
||||
{ \
|
||||
goto err; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define NLMSG_TAIL(nmsg) \
|
||||
((struct rtattr *)(((uint8_t *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
|
||||
|
||||
/* this class contains only static members */
|
||||
class SITNL
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Link state request message
|
||||
*/
|
||||
struct sitnl_link_req
|
||||
{
|
||||
struct nlmsghdr n;
|
||||
struct ifinfomsg i;
|
||||
char buf[256];
|
||||
};
|
||||
|
||||
/**
|
||||
* Address request message
|
||||
*/
|
||||
struct sitnl_addr_req
|
||||
{
|
||||
struct nlmsghdr n;
|
||||
struct ifaddrmsg i;
|
||||
char buf[256];
|
||||
};
|
||||
|
||||
/**
|
||||
* Route request message
|
||||
*/
|
||||
struct sitnl_route_req
|
||||
{
|
||||
struct nlmsghdr n;
|
||||
struct rtmsg r;
|
||||
char buf[256];
|
||||
};
|
||||
|
||||
typedef int (*sitnl_parse_reply_cb)(struct nlmsghdr *msg, void *arg);
|
||||
|
||||
/**
|
||||
* Helper function used to easily add attributes to a rtnl message
|
||||
*/
|
||||
static int
|
||||
sitnl_addattr(struct nlmsghdr *n, int maxlen, int type, const void *data,
|
||||
int alen)
|
||||
{
|
||||
int len = RTA_LENGTH(alen);
|
||||
struct rtattr *rta;
|
||||
|
||||
if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: message exceeded bound of " << maxlen);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
rta = NLMSG_TAIL(n);
|
||||
rta->rta_type = type;
|
||||
rta->rta_len = len;
|
||||
|
||||
if (!data)
|
||||
{
|
||||
memset(RTA_DATA(rta), 0, alen);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(RTA_DATA(rta), data, alen);
|
||||
}
|
||||
|
||||
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open RTNL socket
|
||||
*/
|
||||
static int
|
||||
sitnl_socket(void)
|
||||
{
|
||||
int sndbuf = SNDBUF_SIZE;
|
||||
int rcvbuf = RCVBUF_SIZE;
|
||||
int fd;
|
||||
|
||||
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (fd < 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": cannot open netlink socket");
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": SO_SNDBUF");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) < 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": SO_RCVBUF");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind socket to Netlink subsystem
|
||||
*/
|
||||
static int
|
||||
sitnl_bind(int fd, uint32_t groups)
|
||||
{
|
||||
socklen_t addr_len;
|
||||
struct sockaddr_nl local = { };
|
||||
|
||||
local.nl_family = AF_NETLINK;
|
||||
local.nl_groups = groups;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": cannot bind netlink socket");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
addr_len = sizeof(local);
|
||||
if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": cannot getsockname");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (addr_len != sizeof(local))
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": wrong address length " << addr_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (local.nl_family != AF_NETLINK)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": wrong address family " << local.nl_family);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Netlink message and run callback on reply (if specified)
|
||||
*/
|
||||
static int
|
||||
sitnl_send(struct nlmsghdr *payload, pid_t peer, unsigned int groups,
|
||||
sitnl_parse_reply_cb cb, void *arg_cb)
|
||||
{
|
||||
int len, rem_len, fd, ret, rcv_len;
|
||||
struct sockaddr_nl nladdr = { };
|
||||
struct nlmsgerr *err;
|
||||
struct nlmsghdr *h;
|
||||
unsigned int seq;
|
||||
char buf[1024 * 16];
|
||||
struct iovec iov =
|
||||
{
|
||||
.iov_base = payload,
|
||||
.iov_len = payload->nlmsg_len,
|
||||
};
|
||||
struct msghdr nlmsg =
|
||||
{
|
||||
.msg_name = &nladdr,
|
||||
.msg_namelen = sizeof(nladdr),
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
|
||||
nladdr.nl_family = AF_NETLINK;
|
||||
nladdr.nl_pid = peer;
|
||||
nladdr.nl_groups = groups;
|
||||
|
||||
payload->nlmsg_seq = seq = time(NULL);
|
||||
|
||||
/* no need to send reply */
|
||||
if (!cb)
|
||||
{
|
||||
payload->nlmsg_flags |= NLM_F_ACK;
|
||||
}
|
||||
|
||||
fd = sitnl_socket();
|
||||
if (fd < 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": can't open rtnl socket");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ret = sitnl_bind(fd, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": can't bind rtnl socket");
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = sendmsg(fd, &nlmsg, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: error on sendmsg()");
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* prepare buffer to store RTNL replies */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
iov.iov_base = buf;
|
||||
|
||||
while (1)
|
||||
{
|
||||
/*
|
||||
* iov_len is modified by recvmsg(), therefore has to be initialized before
|
||||
* using it again
|
||||
*/
|
||||
OPENVPN_LOG_RTNL(__func__ << ": checking for received messages");
|
||||
iov.iov_len = sizeof(buf);
|
||||
rcv_len = recvmsg(fd, &nlmsg, 0);
|
||||
OPENVPN_LOG_RTNL(__func__ << ": rtnl: received " << rcv_len << " bytes");
|
||||
if (rcv_len < 0)
|
||||
{
|
||||
if ((errno == EINTR) || (errno == EAGAIN))
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": interrupted call");
|
||||
continue;
|
||||
}
|
||||
OPENVPN_LOG(__func__ << ": rtnl: error on recvmsg()");
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rcv_len == 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: socket reached unexpected EOF");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nlmsg.msg_namelen != sizeof(nladdr))
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": sender address length: "
|
||||
<< nlmsg.msg_namelen << " (expected " << sizeof(nladdr)
|
||||
<< ")");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
h = (struct nlmsghdr *)buf;
|
||||
while (rcv_len >= (int)sizeof(*h))
|
||||
{
|
||||
len = h->nlmsg_len;
|
||||
rem_len = len - sizeof(*h);
|
||||
|
||||
if ((rem_len < 0) || (len > rcv_len))
|
||||
{
|
||||
if (nlmsg.msg_flags & MSG_TRUNC)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": truncated message");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
OPENVPN_LOG(__func__ << ": malformed message: len=" << len);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (h->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
err = (struct nlmsgerr *)NLMSG_DATA(h);
|
||||
if (rem_len < (int)sizeof(struct nlmsgerr))
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": ERROR truncated");
|
||||
ret = -EIO;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!err->error)
|
||||
{
|
||||
ret = 0;
|
||||
if (cb)
|
||||
ret = cb(h, arg_cb);
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: generic error: "
|
||||
<< strerror(-err->error)
|
||||
<< " (" << err->error << ")");
|
||||
ret = err->error;
|
||||
}
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cb)
|
||||
{
|
||||
ret = cb(h, arg_cb);
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": RTNL: unexpected reply");
|
||||
}
|
||||
|
||||
rcv_len -= NLMSG_ALIGN(len);
|
||||
h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
|
||||
}
|
||||
|
||||
if (nlmsg.msg_flags & MSG_TRUNC)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": message truncated");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rcv_len)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: " << rcv_len
|
||||
<< " not parsed bytes");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* store the route entry resulting from the query */
|
||||
typedef struct
|
||||
{
|
||||
sa_family_t family;
|
||||
IP::Addr gw;
|
||||
std::string iface;
|
||||
} route_res_t;
|
||||
|
||||
static int
|
||||
sitnl_route_save(struct nlmsghdr *n, void *arg)
|
||||
{
|
||||
route_res_t *res = (route_res_t *)arg;
|
||||
struct rtmsg *r = (struct rtmsg *)NLMSG_DATA(n);
|
||||
struct rtattr *rta = RTM_RTA(r);
|
||||
int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
|
||||
int ifindex = 0;
|
||||
|
||||
while (RTA_OK(rta, len))
|
||||
{
|
||||
switch (rta->rta_type)
|
||||
{
|
||||
case RTA_OIF:
|
||||
/* route interface */
|
||||
ifindex = *(unsigned int *)RTA_DATA(rta);
|
||||
break;
|
||||
case RTA_DST:
|
||||
/* route prefix */
|
||||
RTA_DATA(rta);
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
/* GW for the route */
|
||||
{
|
||||
const unsigned char *bytestr = (unsigned char *)RTA_DATA(rta);
|
||||
switch (res->family)
|
||||
{
|
||||
case AF_INET:
|
||||
res->gw = IP::Addr::from_ipv4(IPv4::Addr::from_bytes_net(bytestr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
res->gw = IP::Addr::from_ipv6(IPv6::Addr::from_byte_string(bytestr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rta = RTA_NEXT(rta, len);
|
||||
}
|
||||
|
||||
if (ifindex > 0)
|
||||
{
|
||||
char iface[IFNAMSIZ];
|
||||
if (!if_indextoname(ifindex, iface))
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: can't get ifname for index "
|
||||
<< ifindex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res->iface = iface;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sitnl_route_best_gw(const IP::Route& route, IP::Addr& best_gw,
|
||||
std::string& best_iface)
|
||||
{
|
||||
struct sitnl_route_req req = { };
|
||||
route_res_t res;
|
||||
int ret = -EINVAL;
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
|
||||
req.n.nlmsg_type = RTM_GETROUTE;
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST;
|
||||
|
||||
res.family = req.r.rtm_family = route.addr.family();
|
||||
req.r.rtm_dst_len = route.prefix_len;
|
||||
|
||||
if (route.addr.family() == AF_INET)
|
||||
{
|
||||
req.n.nlmsg_flags |= NLM_F_DUMP;
|
||||
}
|
||||
|
||||
{
|
||||
unsigned char bytestr[IP::Addr::V6_SIZE / 8];
|
||||
route.addr.to_byte_string_variable(bytestr);
|
||||
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), RTA_DST, bytestr,
|
||||
route.addr.size_bytes());
|
||||
}
|
||||
|
||||
ret = sitnl_send(&req.n, 0, 0, sitnl_route_save, &res);
|
||||
if (ret >= 0)
|
||||
{
|
||||
/* save result in output variables */
|
||||
best_gw = std::move(res.gw);
|
||||
best_iface = std::move(res.iface);
|
||||
|
||||
OPENVPN_LOG(__func__ << " result: via " << best_gw << " dev " << best_iface);
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": failed to retrieve route, err=" << ret);
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
sitnl_addr_set(const int cmd, const uint32_t flags, const std::string& iface,
|
||||
const IP::Addr& local, const IP::Addr& remote, int prefixlen,
|
||||
const IP::Addr& broadcast)
|
||||
{
|
||||
struct sitnl_addr_req req = { };
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (iface.empty())
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": passed empty interface");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (local.unspecified())
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": passed zero IP address");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
|
||||
req.n.nlmsg_type = cmd;
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST | flags;
|
||||
|
||||
req.i.ifa_family = local.family();
|
||||
req.i.ifa_index = if_nametoindex(iface.c_str());
|
||||
if (req.i.ifa_index == 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": cannot get ifindex for " << iface << " "
|
||||
<< strerror(errno));
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* if no prefixlen has been specified, assume host address */
|
||||
if (prefixlen == 0)
|
||||
{
|
||||
prefixlen = local.size();
|
||||
}
|
||||
req.i.ifa_prefixlen = prefixlen;
|
||||
|
||||
{
|
||||
unsigned char bytestr[IP::Addr::V6_SIZE / 8];
|
||||
|
||||
local.to_byte_string_variable(bytestr);
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), IFA_LOCAL, bytestr, local.size_bytes());
|
||||
|
||||
if (remote.specified())
|
||||
{
|
||||
remote.to_byte_string_variable(bytestr);
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), IFA_ADDRESS, bytestr, remote.size_bytes());
|
||||
}
|
||||
|
||||
if (broadcast.specified())
|
||||
{
|
||||
broadcast.to_byte_string_variable(bytestr);
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), IFA_BROADCAST, bytestr, broadcast.size_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
ret = sitnl_send(&req.n, 0, 0, NULL, NULL);
|
||||
if ((ret < 0) && (errno == EEXIST))
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
sitnl_addr_ptp_add(const std::string& iface, const IP::Addr& local,
|
||||
const IP::Addr& remote)
|
||||
{
|
||||
return sitnl_addr_set(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, iface,
|
||||
local, remote, 0,
|
||||
IP::Addr::from_zero(local.version()));
|
||||
}
|
||||
|
||||
static int
|
||||
sitnl_addr_ptp_del(const std::string& iface, const IP::Addr& local)
|
||||
{
|
||||
return sitnl_addr_set(RTM_DELADDR, 0, iface, local,
|
||||
IP::Addr::from_zero(local.version()),
|
||||
0, IP::Addr::from_zero(local.version()));
|
||||
}
|
||||
|
||||
static int
|
||||
sitnl_route_set(const int cmd, const uint32_t flags,
|
||||
const std::string& iface, const IP::Route& route,
|
||||
const IP::Addr& gw, const enum rt_class_t table,
|
||||
const int metric, const enum rt_scope_t scope,
|
||||
const int protocol, const int type)
|
||||
{
|
||||
struct sitnl_route_req req = { };
|
||||
int ret = -1;
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
|
||||
req.n.nlmsg_type = cmd;
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST | flags;
|
||||
|
||||
req.r.rtm_family = route.addr.family();
|
||||
req.r.rtm_scope = scope;
|
||||
req.r.rtm_protocol = protocol;
|
||||
req.r.rtm_type = type;
|
||||
req.r.rtm_dst_len = route.prefix_len;
|
||||
|
||||
if (table < 256)
|
||||
{
|
||||
req.r.rtm_table = table;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.r.rtm_table = RT_TABLE_UNSPEC;
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), RTA_TABLE, &table, 4);
|
||||
}
|
||||
|
||||
{
|
||||
unsigned char bytestr[IP::Addr::V6_SIZE / 8];
|
||||
|
||||
route.addr.to_byte_string_variable(bytestr);
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), RTA_DST, bytestr, route.addr.size_bytes());
|
||||
|
||||
if (gw.specified())
|
||||
{
|
||||
gw.to_byte_string_variable(bytestr);
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), RTA_GATEWAY, bytestr, gw.size_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
if (!iface.empty())
|
||||
{
|
||||
int ifindex = if_nametoindex(iface.c_str());
|
||||
if (ifindex == 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: cannot get ifindex for " << iface);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), RTA_OIF, &ifindex, 4);
|
||||
}
|
||||
|
||||
if (metric > 0)
|
||||
{
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), RTA_PRIORITY, &metric, 4);
|
||||
}
|
||||
|
||||
ret = sitnl_send(&req.n, 0, 0, NULL, NULL);
|
||||
if ((ret < 0) && (errno == EEXIST))
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
sitnl_addr_add(const std::string& iface, const IP::Addr& addr,
|
||||
int prefixlen, const IP::Addr& broadcast)
|
||||
{
|
||||
return sitnl_addr_set(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, iface,
|
||||
addr, IP::Addr::from_zero(addr.version()),
|
||||
prefixlen, broadcast);
|
||||
}
|
||||
|
||||
static int
|
||||
sitnl_addr_del(const std::string& iface, const IP::Addr& addr, int prefixlen)
|
||||
{
|
||||
return sitnl_addr_set(RTM_DELADDR, 0, iface, addr,
|
||||
IP::Addr::from_zero(addr.version()), prefixlen,
|
||||
IP::Addr::from_zero(addr.version()));
|
||||
}
|
||||
|
||||
static int
|
||||
sitnl_route_add(const IP::Route& route, const IP::Addr& gw,
|
||||
const std::string& iface, const uint32_t table,
|
||||
const int metric)
|
||||
{
|
||||
return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE, iface,
|
||||
route, gw,
|
||||
(enum rt_class_t)(!table ? RT_TABLE_MAIN : table),
|
||||
metric, RT_SCOPE_UNIVERSE, RTPROT_BOOT, RTN_UNICAST);
|
||||
}
|
||||
|
||||
static int
|
||||
sitnl_route_del(const IP::Route& route, const IP::Addr& gw,
|
||||
const std::string& iface, const uint32_t table,
|
||||
const int metric)
|
||||
{
|
||||
return sitnl_route_set(RTM_DELROUTE, 0, iface, route, gw,
|
||||
(enum rt_class_t)(!table ? RT_TABLE_MAIN : table),
|
||||
metric, RT_SCOPE_NOWHERE,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static int
|
||||
net_route_best_gw(const IP::Route6& route, IPv6::Addr& best_gw6,
|
||||
std::string& best_iface)
|
||||
{
|
||||
IP::Addr best_gw;
|
||||
int ret;
|
||||
|
||||
OPENVPN_LOG(__func__ << " query IPv6: " << route);
|
||||
|
||||
ret = sitnl_route_best_gw(IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len),
|
||||
best_gw, best_iface);
|
||||
if (ret >= 0)
|
||||
{
|
||||
best_gw6 = best_gw.to_ipv6();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
net_route_best_gw(const IP::Route4& route, IPv4::Addr &best_gw4,
|
||||
std::string& best_iface)
|
||||
{
|
||||
IP::Addr best_gw;
|
||||
int ret;
|
||||
|
||||
OPENVPN_LOG(__func__ << " query IPv4: " << route);
|
||||
|
||||
ret = sitnl_route_best_gw(IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len),
|
||||
best_gw, best_iface);
|
||||
if (ret >= 0)
|
||||
{
|
||||
best_gw4 = best_gw.to_ipv4();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
net_iface_up(std::string& iface, bool up)
|
||||
{
|
||||
struct sitnl_link_req req = { };
|
||||
int ifindex;
|
||||
|
||||
if (iface.empty())
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": passed empty interface");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ifindex = if_nametoindex(iface.c_str());
|
||||
if (ifindex == 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: cannot get ifindex for " << iface
|
||||
<< ": " << strerror(errno));
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST;
|
||||
req.n.nlmsg_type = RTM_NEWLINK;
|
||||
|
||||
req.i.ifi_family = AF_PACKET;
|
||||
req.i.ifi_index = ifindex;
|
||||
req.i.ifi_change |= IFF_UP;
|
||||
if (up)
|
||||
{
|
||||
req.i.ifi_flags |= IFF_UP;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.i.ifi_flags &= ~IFF_UP;
|
||||
}
|
||||
|
||||
OPENVPN_LOG(__func__ << ": set " << iface << " " << (up ? "up" : "down"));
|
||||
|
||||
return sitnl_send(&req.n, 0, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
net_iface_mtu_set(std::string& iface, uint32_t mtu)
|
||||
{
|
||||
struct sitnl_link_req req = { };
|
||||
int ifindex;
|
||||
|
||||
if (iface.empty())
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": passed empty interface");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ifindex = if_nametoindex(iface.c_str());
|
||||
if (ifindex == 0)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: cannot get ifindex for " << iface);
|
||||
return -1;
|
||||
}
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST;
|
||||
req.n.nlmsg_type = RTM_NEWLINK;
|
||||
|
||||
req.i.ifi_family = AF_PACKET;
|
||||
req.i.ifi_index = ifindex;
|
||||
|
||||
SITNL_ADDATTR(&req.n, sizeof(req), IFLA_MTU, &mtu, 4);
|
||||
|
||||
OPENVPN_LOG(__func__ << ": mtu " << mtu << " for " << iface);
|
||||
|
||||
err:
|
||||
return sitnl_send(&req.n, 0, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
net_addr_add(const std::string& iface, const IPv4::Addr& addr,
|
||||
const int prefixlen, const IPv4::Addr& broadcast)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << addr << "/" << prefixlen << " brd "
|
||||
<< broadcast << " dev " << iface);
|
||||
|
||||
return sitnl_addr_add(iface, IP::Addr::from_ipv4(addr), prefixlen,
|
||||
IP::Addr::from_ipv4(broadcast));
|
||||
}
|
||||
|
||||
static int
|
||||
net_addr_add(const std::string& iface, const IPv6::Addr& addr,
|
||||
const int prefixlen)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << addr << "/" << prefixlen << " dev " << iface);
|
||||
|
||||
return sitnl_addr_add(iface, IP::Addr::from_ipv6(addr), prefixlen,
|
||||
IP::Addr::from_zero(IP::Addr::V6));
|
||||
}
|
||||
|
||||
static int
|
||||
net_addr_del(const std::string& iface, const IPv4::Addr& addr,
|
||||
const int prefixlen)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << addr << "/" << prefixlen << " dev " << iface);
|
||||
|
||||
return sitnl_addr_del(iface, IP::Addr::from_ipv4(addr), prefixlen);
|
||||
}
|
||||
|
||||
static int
|
||||
net_addr_del(const std::string& iface, const IPv6::Addr& addr,
|
||||
const int prefixlen)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << addr << "/" << prefixlen << " dev " << iface);
|
||||
|
||||
return sitnl_addr_del(iface, IP::Addr::from_ipv6(addr), prefixlen);
|
||||
}
|
||||
|
||||
static int
|
||||
net_addr_ptp_add(const std::string& iface, const IPv4::Addr& local,
|
||||
const IPv4::Addr& remote)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << local << " peer " << remote << " dev " << iface);
|
||||
|
||||
return sitnl_addr_ptp_add(iface, IP::Addr::from_ipv4(local),
|
||||
IP::Addr::from_ipv4(remote));
|
||||
}
|
||||
|
||||
static int
|
||||
net_addr_ptp_del(const std::string& iface, const IPv4::Addr& local,
|
||||
const IPv4::Addr& remote)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << local << " dev " << iface);
|
||||
|
||||
return sitnl_addr_ptp_del(iface, IP::Addr::from_ipv4(local));
|
||||
}
|
||||
|
||||
static int
|
||||
net_route_add(const IP::Route4& route, const IPv4::Addr& gw,
|
||||
const std::string& iface, const uint32_t table,
|
||||
const int metric)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << route << " via " << gw << " dev " << iface
|
||||
<< " table " << table << " metric " << metric);
|
||||
|
||||
return sitnl_route_add(IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len),
|
||||
IP::Addr::from_ipv4(gw), iface, table, metric);
|
||||
}
|
||||
|
||||
static int
|
||||
net_route_add(const IP::Route6& route, const IPv6::Addr& gw,
|
||||
const std::string& iface, const uint32_t table,
|
||||
const int metric)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << route << " via " << gw << " dev " << iface
|
||||
<< " table " << table << " metric " << metric);
|
||||
|
||||
return sitnl_route_add(IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len),
|
||||
IP::Addr::from_ipv6(gw), iface, table, metric);
|
||||
}
|
||||
|
||||
static int
|
||||
net_route_del(const IP::Route4& route, const IPv4::Addr& gw,
|
||||
const std::string& iface, const uint32_t table,
|
||||
const int metric)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << route << " via " << gw << " dev " << iface
|
||||
<< " table " << table << " metric " << metric);
|
||||
|
||||
return sitnl_route_del(IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len),
|
||||
IP::Addr::from_ipv4(gw), iface, table, metric);
|
||||
}
|
||||
|
||||
static int
|
||||
net_route_del(const IP::Route6& route, const IPv6::Addr& gw,
|
||||
const std::string& iface, const uint32_t table,
|
||||
const int metric)
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": " << route << " via " << gw << " dev " << iface
|
||||
<< " table " << table << " metric " << metric);
|
||||
|
||||
return sitnl_route_del(IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len),
|
||||
IP::Addr::from_ipv6(gw), iface, table, metric);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,15 @@
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/tunio.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
|
||||
// check if Netlink has been selected at compile time
|
||||
#ifdef OPENVPN_USE_SITNL
|
||||
#include <openvpn/tun/linux/client/tunnetlink.hpp>
|
||||
#define TUN_LINUX TunNetlink
|
||||
#else
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
#define TUN_LINUX TunLinux
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunLinux {
|
||||
@@ -114,7 +122,7 @@ namespace openvpn {
|
||||
if (tun_setup_factory)
|
||||
return tun_setup_factory->new_setup_obj();
|
||||
else
|
||||
return new TunLinux::Setup();
|
||||
return new TUN_LINUX::Setup();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -129,7 +137,7 @@ namespace openvpn {
|
||||
typedef Tun<Client*> TunImpl;
|
||||
|
||||
public:
|
||||
virtual void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&)
|
||||
virtual void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
@@ -184,7 +192,7 @@ namespace openvpn {
|
||||
tun_setup = config->new_setup_obj();
|
||||
|
||||
// create config object for tun setup layer
|
||||
Setup::Config tsconf;
|
||||
TUN_LINUX::Setup::Config tsconf;
|
||||
tsconf.layer = config->tun_prop.layer;
|
||||
tsconf.dev_name = config->dev_name;
|
||||
tsconf.txqueuelen = config->txqueuelen;
|
||||
@@ -228,12 +236,12 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool tun_send(BufferAllocated& buf)
|
||||
virtual bool tun_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual std::string tun_name() const
|
||||
virtual std::string tun_name() const override
|
||||
{
|
||||
if (impl)
|
||||
return impl->name();
|
||||
@@ -241,7 +249,7 @@ namespace openvpn {
|
||||
return "UNDEF_TUN";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip4() const
|
||||
virtual std::string vpn_ip4() const override
|
||||
{
|
||||
if (state->vpn_ip4_addr.specified())
|
||||
return state->vpn_ip4_addr.to_string();
|
||||
@@ -249,7 +257,7 @@ namespace openvpn {
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip6() const
|
||||
virtual std::string vpn_ip6() const override
|
||||
{
|
||||
if (state->vpn_ip6_addr.specified())
|
||||
return state->vpn_ip6_addr.to_string();
|
||||
@@ -273,11 +281,11 @@ namespace openvpn {
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual void set_disconnect()
|
||||
virtual void set_disconnect() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
virtual void stop() override { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,821 @@
|
||||
// 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-2018 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <openvpn/netconf/linux/gwnetlink.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/tun/linux/client/sitnl.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunNetlink {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_linux_error);
|
||||
OPENVPN_EXCEPTION(tun_open_error);
|
||||
OPENVPN_EXCEPTION(tun_layer_error);
|
||||
OPENVPN_EXCEPTION(tun_ioctl_error);
|
||||
OPENVPN_EXCEPTION(tun_fcntl_error);
|
||||
OPENVPN_EXCEPTION(tun_name_error);
|
||||
OPENVPN_EXCEPTION(tun_tx_queue_len_error);
|
||||
OPENVPN_EXCEPTION(tun_ifconfig_error);
|
||||
|
||||
struct NetlinkLinkSet : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkLinkSet> Ptr;
|
||||
|
||||
NetlinkLinkSet() {}
|
||||
|
||||
NetlinkLinkSet(std::string dev_arg, bool up_arg, int mtu_arg)
|
||||
: dev(dev_arg),
|
||||
up(up_arg),
|
||||
mtu(mtu_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkLinkSet* copy() const
|
||||
{
|
||||
NetlinkLinkSet *ret = new NetlinkLinkSet;
|
||||
ret->dev = dev;
|
||||
ret->up = up;
|
||||
ret->mtu = mtu;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkLinkSet with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = SITNL::net_iface_mtu_set(dev, mtu);
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkLinkSet " << dev << " mtu " << mtu
|
||||
<< ": " << ret << std::endl;
|
||||
}
|
||||
|
||||
ret = SITNL::net_iface_up(dev, up);
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkLinkSet " << dev << " up " << up
|
||||
<< ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink iface " << dev << " link set " << up << " mtu " << mtu;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string dev;
|
||||
bool up;
|
||||
int mtu;
|
||||
};
|
||||
|
||||
struct NetlinkAddr4 : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkAddr4> Ptr;
|
||||
|
||||
NetlinkAddr4() {}
|
||||
|
||||
NetlinkAddr4(std::string dev_arg, IPv4::Addr& addr_arg, int prefixlen_arg,
|
||||
IPv4::Addr& broadcast_arg, bool add_arg)
|
||||
: dev(dev_arg),
|
||||
addr(addr_arg),
|
||||
prefixlen(prefixlen_arg),
|
||||
broadcast(broadcast_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkAddr4* copy() const
|
||||
{
|
||||
NetlinkAddr4 *ret = new NetlinkAddr4;
|
||||
ret->dev = dev;
|
||||
ret->addr = addr;
|
||||
ret->prefixlen = prefixlen;
|
||||
ret->broadcast = broadcast;
|
||||
ret->add = add;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkAddr4 with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_addr_add(dev, addr, prefixlen, broadcast);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_addr_del(dev, addr, prefixlen);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkAddr4(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink iface " << dev << " " << (add ? "add" : "del") << " "
|
||||
<< addr.to_string() << "/" << prefixlen << " broadcast "
|
||||
<< broadcast.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string dev;
|
||||
IPv4::Addr addr;
|
||||
int prefixlen;
|
||||
IPv4::Addr broadcast;
|
||||
bool add;
|
||||
};
|
||||
|
||||
struct NetlinkAddr6 : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkAddr6> Ptr;
|
||||
|
||||
NetlinkAddr6() {}
|
||||
|
||||
NetlinkAddr6(std::string dev_arg, IPv6::Addr& addr_arg, int prefixlen_arg,
|
||||
bool add_arg)
|
||||
: dev(dev_arg),
|
||||
addr(addr_arg),
|
||||
prefixlen(prefixlen_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkAddr6* copy() const
|
||||
{
|
||||
NetlinkAddr6 *ret = new NetlinkAddr6;
|
||||
ret->dev = dev;
|
||||
ret->addr = addr;
|
||||
ret->prefixlen = prefixlen;
|
||||
ret->add = add;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkAddr6 with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_addr_add(dev, addr, prefixlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_addr_del(dev, addr, prefixlen);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkAddr6(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink iface " << dev << " " << (add ? "add" : "del") << " "
|
||||
<< addr.to_string() << "/" << prefixlen;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string dev;
|
||||
IPv6::Addr addr;
|
||||
int prefixlen;
|
||||
bool add;
|
||||
};
|
||||
|
||||
struct NetlinkAddr4PtP : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkAddr4PtP> Ptr;
|
||||
|
||||
NetlinkAddr4PtP() {}
|
||||
|
||||
NetlinkAddr4PtP(std::string dev_arg, IPv4::Addr local_arg,
|
||||
IPv4::Addr remote_arg, bool add_arg)
|
||||
: dev(dev_arg),
|
||||
local(local_arg),
|
||||
remote(remote_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkAddr4PtP* copy() const
|
||||
{
|
||||
NetlinkAddr4PtP *ret = new NetlinkAddr4PtP;
|
||||
ret->dev = dev;
|
||||
ret->local = local;
|
||||
ret->remote = remote;
|
||||
ret->add = add;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkAddr4PtP with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_addr_ptp_add(dev, local, remote);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_addr_ptp_del(dev, local, remote);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkAddr4PtP(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
return "netlink iface " + dev + " " + (add ? "add" : "del") + " ptp "
|
||||
+ local.to_string() + " remote " + remote.to_string();
|
||||
}
|
||||
|
||||
std::string dev;
|
||||
IPv4::Addr local;
|
||||
IPv4::Addr remote;
|
||||
bool add;
|
||||
};
|
||||
|
||||
struct NetlinkRoute4 : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkRoute4> Ptr;
|
||||
|
||||
NetlinkRoute4() {}
|
||||
|
||||
NetlinkRoute4(IPv4::Addr& dst_arg, int prefixlen_arg, IPv4::Addr& gw_arg,
|
||||
std::string dev_arg, bool add_arg)
|
||||
: route(dst_arg, prefixlen_arg),
|
||||
gw(gw_arg),
|
||||
dev(dev_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkRoute4* copy() const
|
||||
{
|
||||
NetlinkRoute4 *ret = new NetlinkRoute4;
|
||||
ret->route = route;
|
||||
ret->gw = gw;
|
||||
ret->dev = dev;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkRoute4 with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_route_add(route, gw, dev, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_route_del(route, gw, dev, 0, 0);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkRoute4(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink route " << (add ? "add" : "del") << " dev " << dev << " "
|
||||
<< route << " via " << gw.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
IP::Route4 route;
|
||||
IPv4::Addr gw;
|
||||
std::string dev;
|
||||
bool add;
|
||||
};
|
||||
|
||||
struct NetlinkRoute6 : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkRoute6> Ptr;
|
||||
|
||||
NetlinkRoute6() {}
|
||||
|
||||
NetlinkRoute6(IPv6::Addr& dst_arg, int prefixlen_arg, IPv6::Addr& gw_arg,
|
||||
std::string dev_arg, bool add_arg)
|
||||
: route(dst_arg, prefixlen_arg),
|
||||
gw(gw_arg),
|
||||
dev(dev_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkRoute6* copy() const
|
||||
{
|
||||
NetlinkRoute6 *ret = new NetlinkRoute6;
|
||||
ret->route = route;
|
||||
ret->gw = gw;
|
||||
ret->dev = dev;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkRoute6 with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_route_add(route, gw, dev, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_route_del(route, gw, dev, 0, 0);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkRoute6(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink route " << (add ? "add" : "del") << " dev " << dev << " "
|
||||
<< route << " via " << gw.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
IP::Route6 route;
|
||||
IPv6::Addr gw;
|
||||
std::string dev;
|
||||
bool add;
|
||||
};
|
||||
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_ADD_SYS=(1<<1),
|
||||
R_ADD_DCO=(1<<2),
|
||||
R_ADD_ALL=R_ADD_SYS|R_ADD_DCO,
|
||||
};
|
||||
|
||||
/*inline IPv4::Addr cvt_pnr_ip_v4(const std::string& hexaddr)
|
||||
{
|
||||
BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO);
|
||||
parse_hex(v, hexaddr);
|
||||
if (v.size() != 4)
|
||||
throw tun_linux_error("bad hex address");
|
||||
IPv4::Addr ret = IPv4::Addr::from_bytes(v.data());
|
||||
return IP::Addr::from_ipv4(ret);
|
||||
}*/
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 2001:db8:1::/48 via 2001:db8:1::1
|
||||
NetlinkRoute6::Ptr add(new NetlinkRoute6);
|
||||
add->route.addr = net;
|
||||
add->route.prefix_len = prefix_len;
|
||||
add->gw = IPv6::Addr::from_string(gateway_str);
|
||||
add->dev = dev;
|
||||
add->add = true;
|
||||
|
||||
create = add;
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
NetlinkRoute6::Ptr del(add->copy());
|
||||
del->add = false;
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 192.0.2.128/25 via 192.0.2.1
|
||||
NetlinkRoute4::Ptr add(new NetlinkRoute4);
|
||||
add->route.addr = net;
|
||||
add->route.prefix_len = prefix_len;
|
||||
add->gw = IPv4::Addr::from_string(gateway_str);
|
||||
add->dev = dev;
|
||||
add->add = true;
|
||||
|
||||
create = add;
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
NetlinkRoute4::Ptr del(add->copy());
|
||||
del->add = false;
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,// add interface route to rtvec if defined
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, dev, flags, rtvec, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
inline void iface_up(const std::string& iface_name,
|
||||
const int mtu,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
{
|
||||
NetlinkLinkSet::Ptr add(new NetlinkLinkSet);
|
||||
add->dev = iface_name;
|
||||
add->up = true;
|
||||
add->mtu = mtu;
|
||||
|
||||
create.add(add);
|
||||
// for the destroy command, copy the add command but replace "up" with "down"
|
||||
NetlinkLinkSet::Ptr del(add->copy());
|
||||
del->up = false;
|
||||
destroy.add(del);
|
||||
}
|
||||
}
|
||||
|
||||
inline void iface_config(const std::string& iface_name,
|
||||
int unit,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
NetlinkAddr4::Ptr add(new NetlinkAddr4);
|
||||
add->addr = IPv4::Addr::from_string(local4->address);
|
||||
add->prefixlen = local4->prefix_length;
|
||||
add->broadcast = IPv4::Addr::from_string(local4->address)
|
||||
| ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length);
|
||||
add->dev = iface_name;
|
||||
add->add = true;
|
||||
// if (unit >= 0)
|
||||
// {
|
||||
// add->argv.push_back("label");
|
||||
// add->argv.push_back(iface_name + ':' + openvpn::to_string(unit));
|
||||
// }
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
NetlinkAddr4::Ptr del(add->copy());
|
||||
del->add = false;
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, R_ADD_DCO, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
NetlinkAddr6::Ptr add(new NetlinkAddr6);
|
||||
add->addr = IPv6::Addr::from_string(local6->address);
|
||||
add->prefixlen = local6->prefix_length;
|
||||
add->dev = iface_name;
|
||||
add->add = true;
|
||||
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
NetlinkAddr6::Ptr del(add->copy());
|
||||
del->add = false;
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local6->address, local6->prefix_length, local6->address, iface_name, R_ADD_DCO|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
const LinuxGW46Netlink gw;
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add bypass route
|
||||
if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
class Setup : public TunBuilderSetup::Base
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
struct Config : public TunBuilderSetup::Config
|
||||
{
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
std::string dev_name;
|
||||
int txqueuelen;
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["iface_name"] = Json::Value(iface_name);
|
||||
root["layer"] = Json::Value(layer.str());
|
||||
root["dev_name"] = Json::Value(dev_name);
|
||||
root["txqueuelen"] = Json::Value(txqueuelen);
|
||||
return root;
|
||||
};
|
||||
|
||||
virtual void from_json(const Json::Value& root, const std::string& title) override
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, iface_name, "iface_name", title);
|
||||
layer = Layer::from_str(json::get_string(root, "layer", title));
|
||||
json::to_string(root, dev_name, "dev_name", title);
|
||||
json::to_int(root, txqueuelen, "txqueuelen", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
virtual void destroy(std::ostream &os) override
|
||||
{
|
||||
// remove added routes
|
||||
if (remove_cmds)
|
||||
remove_cmds->execute(std::cout);
|
||||
}
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
// get configuration
|
||||
Config *conf = dynamic_cast<Config *>(config);
|
||||
if (!conf)
|
||||
throw tun_linux_error("missing config");
|
||||
|
||||
static const char node[] = "/dev/net/tun";
|
||||
ScopedFD fd(open(node, O_RDWR));
|
||||
if (!fd.defined())
|
||||
OPENVPN_THROW(tun_open_error, "error opening tun device " << node << ": " << errinfo(errno));
|
||||
|
||||
struct ifreq ifr;
|
||||
std::memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_ONE_QUEUE;
|
||||
ifr.ifr_flags |= IFF_NO_PI;
|
||||
if (conf->layer() == Layer::OSI_LAYER_3)
|
||||
ifr.ifr_flags |= IFF_TUN;
|
||||
else if (conf->layer() == Layer::OSI_LAYER_2)
|
||||
ifr.ifr_flags |= IFF_TAP;
|
||||
else
|
||||
throw tun_layer_error("unknown OSI layer");
|
||||
|
||||
open_unit(conf->dev_name, ifr, fd);
|
||||
|
||||
if (fcntl (fd(), F_SETFL, O_NONBLOCK) < 0)
|
||||
throw tun_fcntl_error(errinfo(errno));
|
||||
|
||||
// Set the TX send queue size
|
||||
if (conf->txqueuelen)
|
||||
{
|
||||
struct ifreq netifr;
|
||||
ScopedFD ctl_fd(socket (AF_INET, SOCK_DGRAM, 0));
|
||||
|
||||
if (ctl_fd.defined())
|
||||
{
|
||||
std::memset(&netifr, 0, sizeof(netifr));
|
||||
strcpy (netifr.ifr_name, ifr.ifr_name);
|
||||
netifr.ifr_qlen = conf->txqueuelen;
|
||||
if (ioctl (ctl_fd(), SIOCSIFTXQLEN, (void *) &netifr) < 0)
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
else
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
|
||||
conf->iface_name = ifr.ifr_name;
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
remove_cmds.reset(new ActionListReversed()); // remove commands executed in reversed order
|
||||
|
||||
// configure tun properties
|
||||
tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds);
|
||||
|
||||
// execute commands to bring up interface
|
||||
add_cmds->execute(std::cout);
|
||||
|
||||
return fd.release();
|
||||
}
|
||||
|
||||
private:
|
||||
void open_unit(const std::string& name, struct ifreq& ifr, ScopedFD& fd)
|
||||
{
|
||||
if (!name.empty())
|
||||
{
|
||||
const int max_units = 256;
|
||||
for (int unit = 0; unit < max_units; ++unit)
|
||||
{
|
||||
std::string n = name;
|
||||
if (unit)
|
||||
n += openvpn::to_string(unit);
|
||||
if (n.length() < IFNAMSIZ)
|
||||
::strcpy (ifr.ifr_name, n.c_str());
|
||||
else
|
||||
throw tun_name_error();
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) == 0)
|
||||
return;
|
||||
}
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' : " << errinfo(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionListReversed::Ptr remove_cmds;
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
@@ -364,7 +364,7 @@ namespace openvpn {
|
||||
#endif
|
||||
};
|
||||
|
||||
virtual void destroy(std::ostream &os)
|
||||
virtual void destroy(std::ostream &os) override
|
||||
{
|
||||
// remove added routes
|
||||
if (remove_cmds)
|
||||
|
||||
Reference in New Issue
Block a user