mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 12:12:15 +02:00
Windows core : added layer 2 support.
We use the DHCP model where the connecting client talks directly to the server-side DHCP server to configure the TAP adapter.
This commit is contained in:
parent
0f40e47f9c
commit
bd4a994b6d
@ -383,6 +383,7 @@ namespace openvpn {
|
||||
#elif defined(OPENVPN_PLATFORM_WIN) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
{
|
||||
TunWin::ClientConfig::Ptr tunconf = TunWin::ClientConfig::new_obj();
|
||||
tunconf->tun_prop.layer = cp->layer;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
if (tun_mtu)
|
||||
|
90
openvpn/ip/dhcp.hpp
Normal file
90
openvpn/ip/dhcp.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
// 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-2015 OpenVPN Technologies, 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/>.
|
||||
|
||||
#ifndef OPENVPN_IP_DHCP_H
|
||||
#define OPENVPN_IP_DHCP_H
|
||||
|
||||
#include <openvpn/ip/eth.hpp>
|
||||
#include <openvpn/ip/ip.hpp>
|
||||
#include <openvpn/ip/udp.hpp>
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
namespace openvpn {
|
||||
struct DHCP {
|
||||
enum {
|
||||
/* DHCP Option types */
|
||||
DHCP_PAD = 0,
|
||||
DHCP_NETMASK = 1,
|
||||
DHCP_ROUTER = 3,
|
||||
DHCP_DNS = 6,
|
||||
DHCP_MSG_TYPE = 53 /* message type (u8) */,
|
||||
DHCP_END = 255,
|
||||
|
||||
/* DHCP Messages types */
|
||||
DHCPDISCOVER = 1,
|
||||
DHCPOFFER = 2,
|
||||
DHCPREQUEST = 3,
|
||||
DHCPDECLINE = 4,
|
||||
DHCPACK = 5,
|
||||
DHCPNAK = 6,
|
||||
DHCPRELEASE = 7,
|
||||
DHCPINFORM = 8,
|
||||
|
||||
/* DHCP UDP port numbers */
|
||||
BOOTPS_PORT = 67,
|
||||
BOOTPC_PORT = 68,
|
||||
|
||||
/* DHCP message op */
|
||||
BOOTREQUEST = 1,
|
||||
BOOTREPLY = 2,
|
||||
};
|
||||
|
||||
std::uint8_t op; /* message op */
|
||||
std::uint8_t htype; /* hardware address type (e.g. '1' = 10Mb Ethernet) */
|
||||
std::uint8_t hlen; /* hardware address length (e.g. '6' for 10Mb Ethernet) */
|
||||
std::uint8_t hops; /* client sets to 0, may be used by relay agents */
|
||||
std::uint32_t xid; /* transaction ID, chosen by client */
|
||||
std::uint16_t secs; /* seconds since request process began, set by client */
|
||||
std::uint16_t flags;
|
||||
std::uint32_t ciaddr; /* client IP address, client sets if known */
|
||||
std::uint32_t yiaddr; /* 'your' IP address -- server's response to client */
|
||||
std::uint32_t siaddr; /* server IP address */
|
||||
std::uint32_t giaddr; /* relay agent IP address */
|
||||
std::uint8_t chaddr[16]; /* client hardware address */
|
||||
std::uint8_t sname[64]; /* optional server host name */
|
||||
std::uint8_t file[128]; /* boot file name */
|
||||
std::uint32_t magic; /* must be 0x63825363 (network order) */
|
||||
};
|
||||
|
||||
struct DHCPPacket {
|
||||
EthHeader eth;
|
||||
IPHeader ip;
|
||||
UDPHeader udp;
|
||||
DHCP dhcp;
|
||||
std::uint8_t options[];
|
||||
};
|
||||
}
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
42
openvpn/ip/eth.hpp
Normal file
42
openvpn/ip/eth.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
// 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-2015 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Define the Ethernet header
|
||||
|
||||
#ifndef OPENVPN_IP_ETH_H
|
||||
#define OPENVPN_IP_ETH_H
|
||||
|
||||
#include <cstdint> // for std::uint32_t, uint16_t, uint8_t
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
namespace openvpn {
|
||||
struct EthHeader {
|
||||
std::uint8_t dest_mac[6];
|
||||
std::uint8_t src_mac[6];
|
||||
std::uint16_t ethertype;
|
||||
};
|
||||
}
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
83
openvpn/ip/udp.hpp
Normal file
83
openvpn/ip/udp.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
// 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-2015 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Define the UDP header
|
||||
|
||||
#ifndef OPENVPN_IP_UDP_H
|
||||
#define OPENVPN_IP_UDP_H
|
||||
|
||||
#include <openvpn/ip/ip.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
struct UDPHeader {
|
||||
std::uint16_t source;
|
||||
std::uint16_t dest;
|
||||
std::uint16_t len;
|
||||
std::uint16_t check;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
inline std::uint16_t udp_checksum (const std::uint8_t *buf,
|
||||
const unsigned int len_udp,
|
||||
const std::uint8_t *src_addr,
|
||||
const std::uint8_t *dest_addr)
|
||||
{
|
||||
std::uint32_t sum = 0;
|
||||
|
||||
/* make 16 bit words out of every two adjacent 8 bit words and */
|
||||
/* calculate the sum of all 16 bit words */
|
||||
for (unsigned int i = 0; i < len_udp; i += 2)
|
||||
{
|
||||
std::uint16_t word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0);
|
||||
sum += word16;
|
||||
}
|
||||
|
||||
/* add the UDP pseudo header which contains the IP source and destination addresses */
|
||||
for (unsigned int i = 0; i < 4; i += 2)
|
||||
{
|
||||
std::uint16_t word16 =((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
|
||||
sum += word16;
|
||||
}
|
||||
for (unsigned int i = 0; i < 4; i += 2)
|
||||
{
|
||||
std::uint16_t word16 =((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
|
||||
sum += word16;
|
||||
}
|
||||
|
||||
/* the protocol number and the length of the UDP packet */
|
||||
sum += (std::uint16_t)IPHeader::UDP + (std::uint16_t)len_udp;
|
||||
|
||||
/* keep only the last 16 bits of the 32 bit calculated sum and add the carries */
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
|
||||
/* take the one's complement of sum */
|
||||
return std::uint16_t(~sum);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -610,14 +610,15 @@ namespace openvpn {
|
||||
std::ostringstream out;
|
||||
|
||||
const bool server = ssl_factory->mode().is_server();
|
||||
const unsigned int l2extra = (layer() == Layer::OSI_LAYER_2 ? 32 : 0);
|
||||
|
||||
out << "V4";
|
||||
|
||||
out << ",dev-type " << layer.dev_type();
|
||||
out << ",link-mtu " << tun_mtu + link_mtu_adjust();
|
||||
out << ",tun-mtu " << tun_mtu;
|
||||
out << ",link-mtu " << tun_mtu + link_mtu_adjust() + l2extra;
|
||||
out << ",tun-mtu " << tun_mtu + l2extra;
|
||||
out << ",proto " << protocol.str_client(true);
|
||||
|
||||
|
||||
{
|
||||
const char *compstr = comp_ctx.options_string();
|
||||
if (compstr)
|
||||
|
@ -48,6 +48,13 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional callback that indicates OSI layer, should be 2 or 3.
|
||||
// Defaults to 3.
|
||||
virtual bool tun_builder_set_layer(int layer)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback to set address of remote server
|
||||
// Never called more than once per tun_builder session.
|
||||
virtual bool tun_builder_set_remote_address(const std::string& address, bool ipv6)
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/http/urlparse.hpp>
|
||||
#include <openvpn/tun/layer.hpp>
|
||||
|
||||
#ifdef HAVE_JSONCPP
|
||||
#include <openvpn/common/jsonhelper.hpp>
|
||||
@ -550,6 +551,12 @@ namespace openvpn {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_layer(int layer) override
|
||||
{
|
||||
this->layer = Layer::from_value(layer);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_mtu(int mtu) override
|
||||
{
|
||||
this->mtu = mtu;
|
||||
@ -604,6 +611,18 @@ namespace openvpn {
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset_tunnel_addresses()
|
||||
{
|
||||
tunnel_addresses.clear();
|
||||
tunnel_address_index_ipv4 = -1;
|
||||
tunnel_address_index_ipv6 = -1;
|
||||
}
|
||||
|
||||
void reset_dns_servers()
|
||||
{
|
||||
dns_servers.clear();
|
||||
}
|
||||
|
||||
const RouteAddress* vpn_ipv4() const
|
||||
{
|
||||
if (tunnel_address_index_ipv4 >= 0)
|
||||
@ -635,6 +654,7 @@ namespace openvpn {
|
||||
|
||||
void validate() const
|
||||
{
|
||||
validate_layer("root");
|
||||
validate_mtu("root");
|
||||
remote_address.validate("remote_address");
|
||||
validate_list(tunnel_addresses, "tunnel_addresses");
|
||||
@ -654,6 +674,7 @@ namespace openvpn {
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Session Name: " << session_name << std::endl;
|
||||
os << "Layer: " << layer.str() << std::endl;
|
||||
if (mtu)
|
||||
os << "MTU: " << mtu << std::endl;
|
||||
os << "Remote Address: " << remote_address.to_string() << std::endl;
|
||||
@ -684,6 +705,7 @@ namespace openvpn {
|
||||
Json::Value root(Json::objectValue);
|
||||
root["session_name"] = Json::Value(session_name);
|
||||
root["mtu"] = Json::Value(mtu);
|
||||
root["layer"] = Json::Value(layer.value());
|
||||
if (remote_address.defined())
|
||||
root["remote_address"] = remote_address.to_json();
|
||||
json::from_vector(root, tunnel_addresses, "tunnel_addresses");
|
||||
@ -712,6 +734,7 @@ namespace openvpn {
|
||||
TunBuilderCapture::Ptr tbc(new TunBuilderCapture);
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, tbc->session_name, "session_name", title);
|
||||
tbc->layer = Layer::from_value(json::get_int(root, "layer", title));
|
||||
json::to_int(root, tbc->mtu, "mtu", title);
|
||||
tbc->remote_address.from_json(root["remote_address"], "remote_address");
|
||||
json::to_vector(root, tbc->tunnel_addresses, "tunnel_addresses", title);
|
||||
@ -736,6 +759,7 @@ namespace openvpn {
|
||||
// builder data
|
||||
std::string session_name;
|
||||
int mtu = 0;
|
||||
Layer layer{Layer::OSI_LAYER_3}; // OSI layer
|
||||
RemoteAddress remote_address; // real address of server
|
||||
std::vector<RouteAddress> tunnel_addresses; // local tunnel addresses
|
||||
int tunnel_address_index_ipv4 = -1; // index into tunnel_addresses for IPv4 entry (or -1 if undef)
|
||||
@ -801,6 +825,11 @@ namespace openvpn {
|
||||
OPENVPN_THROW_EXCEPTION(title << ".mtu : MTU out of range: " << mtu);
|
||||
}
|
||||
|
||||
void validate_layer(const std::string& title) const
|
||||
{
|
||||
if (!layer.defined())
|
||||
OPENVPN_THROW_EXCEPTION(title << ": layer undefined");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
314
openvpn/tun/client/dhcp_capture.hpp
Normal file
314
openvpn/tun/client/dhcp_capture.hpp
Normal file
@ -0,0 +1,314 @@
|
||||
// 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-2015 OpenVPN Technologies, 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/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_CLIENT_DHCP_CAPTURE_H
|
||||
#define OPENVPN_TUN_CLIENT_DHCP_CAPTURE_H
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/addr/ipv4.hpp>
|
||||
#include <openvpn/ip/dhcp.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class DHCPCapture
|
||||
{
|
||||
public:
|
||||
// We take a TunBuilderCapture object with previously pushed
|
||||
// options and augment it with additional options sniffed
|
||||
// from the DHCP reply.
|
||||
DHCPCapture(const TunBuilderCapture::Ptr& props_arg)
|
||||
: props(props_arg)
|
||||
{
|
||||
if (props->vpn_ipv4() || props->vpn_ipv4())
|
||||
OPENVPN_LOG("NOTE: pushed ifconfig directive is ignored in layer 2 mode");
|
||||
if (!props->dns_servers.empty())
|
||||
OPENVPN_LOG("NOTE: pushed DNS servers are ignored in layer 2 mode");
|
||||
reset();
|
||||
}
|
||||
|
||||
// returns true when router addr and DNS servers are captured
|
||||
bool mod_reply(Buffer& buf)
|
||||
{
|
||||
if (buf.size() < sizeof(DHCPPacket))
|
||||
return false;
|
||||
|
||||
DHCPPacket* dhcp = (DHCPPacket*)buf.data();
|
||||
if (dhcp->ip.protocol == IPHeader::UDP
|
||||
&& dhcp->udp.source == htons(DHCP::BOOTPS_PORT)
|
||||
&& dhcp->udp.dest == htons(DHCP::BOOTPC_PORT)
|
||||
&& dhcp->dhcp.op == DHCP::BOOTREPLY)
|
||||
{
|
||||
const unsigned int optlen = buf.size() - sizeof(DHCPPacket);
|
||||
const int message_type = dhcp_message_type(dhcp, optlen);
|
||||
if (message_type == DHCP::DHCPACK || message_type == DHCP::DHCPOFFER)
|
||||
{
|
||||
/* get host IP address/netmask */
|
||||
const IPv4::Addr host = IPv4::Addr::from_uint32_net(dhcp->dhcp.yiaddr);
|
||||
const IPv4::Addr netmask = get_netmask(dhcp, optlen);
|
||||
|
||||
/* get the router IP address while padding out all DHCP router options */
|
||||
const IPv4::Addr router = extract_router(dhcp, optlen);
|
||||
|
||||
/* get DNS server addresses */
|
||||
const std::vector<IPv4::Addr> dns_servers = get_dns(dhcp, optlen);
|
||||
|
||||
/* recompute the UDP checksum */
|
||||
dhcp->udp.check = 0;
|
||||
dhcp->udp.check = htons(udp_checksum((uint8_t *)&dhcp->udp,
|
||||
sizeof(UDPHeader) + sizeof(DHCP) + optlen,
|
||||
(uint8_t *)&dhcp->ip.saddr,
|
||||
(uint8_t *)&dhcp->ip.daddr));
|
||||
|
||||
/* only capture the extracted Router address if DHCPACK */
|
||||
if (message_type == DHCP::DHCPACK && !configured)
|
||||
{
|
||||
bool complete = true;
|
||||
if (host.unspecified())
|
||||
{
|
||||
OPENVPN_LOG("NOTE: failed to obtain host address via DHCP");
|
||||
complete = false;
|
||||
}
|
||||
if (netmask.unspecified())
|
||||
{
|
||||
OPENVPN_LOG("NOTE: failed to obtain netmask via DHCP");
|
||||
complete = false;
|
||||
}
|
||||
if (router.unspecified())
|
||||
{
|
||||
OPENVPN_LOG("NOTE: failed to obtain router via DHCP");
|
||||
complete = false;
|
||||
}
|
||||
if (complete)
|
||||
{
|
||||
reset();
|
||||
props->tun_builder_add_address(host.to_string(), netmask.prefix_len(), router.to_string(), false, false);
|
||||
if (dns_servers.empty())
|
||||
OPENVPN_LOG("NOTE: failed to obtain DNS servers via DHCP");
|
||||
else
|
||||
{
|
||||
for (const auto &a : dns_servers)
|
||||
props->tun_builder_add_dns_server(a.to_string(), false);
|
||||
}
|
||||
}
|
||||
return configured = complete;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const TunBuilderCapture& get_props() const
|
||||
{
|
||||
return *props;
|
||||
}
|
||||
|
||||
private:
|
||||
void reset()
|
||||
{
|
||||
props->reset_tunnel_addresses();
|
||||
props->reset_dns_servers();
|
||||
}
|
||||
|
||||
static int dhcp_message_type(const DHCPPacket* dhcp, const unsigned int optlen)
|
||||
{
|
||||
const std::uint8_t* p = dhcp->options;
|
||||
for (unsigned int i = 0; i < optlen; ++i)
|
||||
{
|
||||
const std::uint8_t type = p[i];
|
||||
const unsigned int room = optlen - i;
|
||||
|
||||
if (type == DHCP::DHCP_END) /* didn't find what we were looking for */
|
||||
return -1;
|
||||
else if (type == DHCP::DHCP_PAD) /* no-operation */
|
||||
;
|
||||
else if (type == DHCP::DHCP_MSG_TYPE) /* what we are looking for */
|
||||
{
|
||||
if (room >= 3)
|
||||
{
|
||||
if (p[i+1] == 1) /* option length should be 1 */
|
||||
return p[i+2]; /* return message type */
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else /* some other option */
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
i += (len + 1); /* advance to next option */
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static IPv4::Addr extract_router(DHCPPacket* dhcp, const unsigned int optlen)
|
||||
{
|
||||
std::uint8_t* p = dhcp->options;
|
||||
IPv4::Addr ret = IPv4::Addr::from_zero();
|
||||
|
||||
for (unsigned int i = 0; i < optlen; )
|
||||
{
|
||||
const std::uint8_t type = p[i];
|
||||
const unsigned int room = optlen - i;
|
||||
|
||||
if (type == DHCP::DHCP_END)
|
||||
break;
|
||||
else if (type == DHCP::DHCP_PAD)
|
||||
++i;
|
||||
else if (type == DHCP::DHCP_ROUTER)
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
if (len <= (room-2))
|
||||
{
|
||||
/* get router IP address */
|
||||
if (ret.unspecified() && len >= 4 && (len & 3) == 0)
|
||||
ret = IPv4::Addr::from_bytes_net(p + i + 2);
|
||||
|
||||
/* delete the router option */
|
||||
std::uint8_t *dest = p + i;
|
||||
const unsigned int owlen = len + 2; /* len of data to overwrite */
|
||||
std::uint8_t *src = dest + owlen;
|
||||
std::uint8_t *end = p + optlen;
|
||||
const int movlen = end - src;
|
||||
if (movlen > 0)
|
||||
std::memmove(dest, src, movlen); /* overwrite router option */
|
||||
std::memset(end - owlen, DHCP::DHCP_PAD, owlen); /* pad tail */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else /* some other option */
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
i += (len + 2); /* advance to next option */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IPv4::Addr get_netmask(const DHCPPacket* dhcp, const unsigned int optlen)
|
||||
{
|
||||
const std::uint8_t* p = dhcp->options;
|
||||
IPv4::Addr ret = IPv4::Addr::from_zero();
|
||||
|
||||
for (unsigned int i = 0; i < optlen; )
|
||||
{
|
||||
const std::uint8_t type = p[i];
|
||||
const unsigned int room = optlen - i;
|
||||
|
||||
if (type == DHCP::DHCP_END)
|
||||
break;
|
||||
else if (type == DHCP::DHCP_PAD)
|
||||
++i;
|
||||
else if (type == DHCP::DHCP_NETMASK)
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
if (len <= (room-2) && len == 4)
|
||||
return IPv4::Addr::from_bytes_net(p + i + 2);
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else /* some other option */
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
i += (len + 2); /* advance to next option */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<IPv4::Addr> get_dns(const DHCPPacket* dhcp, const unsigned int optlen)
|
||||
{
|
||||
const std::uint8_t* p = dhcp->options;
|
||||
std::vector<IPv4::Addr> ret;
|
||||
|
||||
for (unsigned int i = 0; i < optlen; )
|
||||
{
|
||||
const std::uint8_t type = p[i];
|
||||
const unsigned int room = optlen - i;
|
||||
|
||||
if (type == DHCP::DHCP_END)
|
||||
break;
|
||||
else if (type == DHCP::DHCP_PAD)
|
||||
++i;
|
||||
else if (type == DHCP::DHCP_DNS)
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
if (len <= (room-2) && (len & 3) == 0)
|
||||
{
|
||||
/* get DNS addresses */
|
||||
for (unsigned int j = 0; j < len; j += 4)
|
||||
ret.push_back(IPv4::Addr::from_bytes_net(p + i + j + 2));
|
||||
|
||||
i += (len + 2); /* advance to next option */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else /* some other option */
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
i += (len + 2); /* advance to next option */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
TunBuilderCapture::Ptr props;
|
||||
bool configured = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -36,6 +36,7 @@
|
||||
#include <openvpn/client/remotelist.hpp>
|
||||
#include <openvpn/client/ipverflags.hpp>
|
||||
#include <openvpn/tun/client/emuexr.hpp>
|
||||
#include <openvpn/tun/layer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class TunProp {
|
||||
@ -56,11 +57,10 @@ namespace openvpn {
|
||||
|
||||
struct Config
|
||||
{
|
||||
Config() : mtu(0), google_dns_fallback(false), remote_bypass(false) {}
|
||||
|
||||
std::string session_name;
|
||||
int mtu;
|
||||
bool google_dns_fallback;
|
||||
int mtu = 0;
|
||||
bool google_dns_fallback = false;
|
||||
Layer layer{Layer::OSI_LAYER_3};
|
||||
|
||||
// If remote_bypass is true, obtain cached remote IPs from
|
||||
// remote_list, and preconfigure exclude route rules for them.
|
||||
@ -72,7 +72,7 @@ namespace openvpn {
|
||||
// servers in the remote list after the routing configuration
|
||||
// for the initial connection has taken effect.
|
||||
RemoteList::Ptr remote_list;
|
||||
bool remote_bypass;
|
||||
bool remote_bypass = false;
|
||||
};
|
||||
|
||||
struct State : public RC<thread_unsafe_refcount>
|
||||
@ -100,7 +100,15 @@ namespace openvpn {
|
||||
eer = eer_factory->new_obj();
|
||||
|
||||
// do ifconfig
|
||||
const IP::Addr::VersionMask ip_ver_flags = tun_ifconfig(tb, state, opt);
|
||||
IP::Addr::VersionMask ip_ver_flags = tun_ifconfig(tb, state, opt);
|
||||
|
||||
// with layer 2, either IPv4 or IPv6 might be supported
|
||||
if (config.layer() == Layer::OSI_LAYER_2)
|
||||
ip_ver_flags |= (IP::Addr::V4_MASK|IP::Addr::V6_MASK);
|
||||
|
||||
// verify IPv4/IPv6
|
||||
if (!ip_ver_flags)
|
||||
throw tun_prop_error("one of ifconfig or ifconfig-ipv6 must be specified");
|
||||
|
||||
// get IP version and redirect-gateway flags
|
||||
IPVerFlags ipv(opt, ip_ver_flags);
|
||||
@ -135,7 +143,7 @@ namespace openvpn {
|
||||
OPENVPN_LOG("Google DNS fallback enabled");
|
||||
add_google_dns(tb);
|
||||
}
|
||||
else if (stats)
|
||||
else if (stats && (config.layer() != Layer::OSI_LAYER_2))
|
||||
stats->error(Error::REROUTE_GW_NO_DNS);
|
||||
}
|
||||
|
||||
@ -144,6 +152,10 @@ namespace openvpn {
|
||||
server_addr.version() == IP::Addr::V6))
|
||||
throw tun_prop_error("tun_builder_set_remote_address failed");
|
||||
|
||||
// set layer
|
||||
if (!tb->tun_builder_set_layer(config.layer.value()))
|
||||
throw tun_prop_error("tun_builder_set_layer failed");
|
||||
|
||||
// set MTU
|
||||
if (config.mtu)
|
||||
{
|
||||
@ -175,7 +187,9 @@ namespace openvpn {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IP::Addr::VersionMask tun_ifconfig(TunBuilderBase* tb, State* state, const OptionList& opt)
|
||||
static IP::Addr::VersionMask tun_ifconfig(TunBuilderBase* tb,
|
||||
State* state,
|
||||
const OptionList& opt)
|
||||
{
|
||||
enum Topology {
|
||||
NET30,
|
||||
@ -270,8 +284,6 @@ namespace openvpn {
|
||||
ip_ver_flags |= IP::Addr::V6_MASK;
|
||||
}
|
||||
|
||||
if (!ip_ver_flags)
|
||||
throw tun_prop_error("one of ifconfig or ifconfig-ipv6 must be specified");
|
||||
return ip_ver_flags;
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,11 @@ namespace openvpn {
|
||||
explicit Layer(const Type t) : type_(t) {}
|
||||
Type operator()() const { return type_; }
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return type_ != NONE;
|
||||
}
|
||||
|
||||
const char *dev_type() const
|
||||
{
|
||||
switch (type_)
|
||||
@ -68,6 +73,21 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
int value() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case NONE:
|
||||
return 0;
|
||||
case OSI_LAYER_2:
|
||||
return 2;
|
||||
case OSI_LAYER_3:
|
||||
return 3;
|
||||
default:
|
||||
throw Exception("Layer: unrecognized layer type");
|
||||
}
|
||||
}
|
||||
|
||||
static Layer from_str(const std::string& str)
|
||||
{
|
||||
if (str == "OSI_LAYER_3")
|
||||
@ -80,6 +100,18 @@ namespace openvpn {
|
||||
throw Exception("Layer: unrecognized layer string");
|
||||
}
|
||||
|
||||
static Layer from_value(const int value)
|
||||
{
|
||||
if (value == 3)
|
||||
return Layer(OSI_LAYER_3);
|
||||
else if (value == 2)
|
||||
return Layer(OSI_LAYER_2);
|
||||
else if (value == 0)
|
||||
return Layer(NONE);
|
||||
else
|
||||
throw Exception("Layer: unrecognized layer value");
|
||||
}
|
||||
|
||||
bool operator==(const Layer& other) const
|
||||
{
|
||||
return type_ == other.type_;
|
||||
|
@ -47,6 +47,12 @@ namespace openvpn {
|
||||
Stop* stop,
|
||||
std::ostream& os) = 0;
|
||||
|
||||
virtual bool l2_ready(const TunBuilderCapture& pull) = 0;
|
||||
|
||||
virtual void l2_finish(const TunBuilderCapture& pull,
|
||||
Stop* stop,
|
||||
std::ostream& os) = 0;
|
||||
|
||||
virtual void confirm()
|
||||
{
|
||||
}
|
||||
|
@ -31,8 +31,10 @@
|
||||
#include <openvpn/common/format.hpp>
|
||||
#include <openvpn/common/scoped_asio_stream.hpp>
|
||||
#include <openvpn/common/cleanup.hpp>
|
||||
#include <openvpn/time/asiotimer.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/client/dhcp_capture.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/persist/tunwrapasio.hpp>
|
||||
#include <openvpn/tun/tunio.hpp>
|
||||
@ -120,6 +122,11 @@ namespace openvpn {
|
||||
if (disconnected)
|
||||
tun_persist.reset();
|
||||
}
|
||||
|
||||
virtual bool layer_2_supported() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Client : public TunClient
|
||||
@ -172,7 +179,7 @@ namespace openvpn {
|
||||
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
|
||||
|
||||
// create new tun setup object
|
||||
TunWin::SetupBase::Ptr tun_setup(config->new_setup_obj(io_context));
|
||||
tun_setup = config->new_setup_obj(io_context);
|
||||
|
||||
// open/config TAP
|
||||
HANDLE th;
|
||||
@ -200,6 +207,10 @@ namespace openvpn {
|
||||
|
||||
// assert ownership over TAP device handle
|
||||
tun_setup->confirm();
|
||||
|
||||
// if layer 2, set up to capture DHCP messages over the tunnel
|
||||
if (config->tun_prop.layer() == Layer::OSI_LAYER_2)
|
||||
dhcp_capture.reset(new DHCPCapture(po));
|
||||
}
|
||||
|
||||
// configure tun interface packet forwarding
|
||||
@ -212,8 +223,8 @@ namespace openvpn {
|
||||
));
|
||||
impl->start(config->n_parallel);
|
||||
|
||||
// signal that we are connected
|
||||
parent.tun_connected();
|
||||
if (!dhcp_capture)
|
||||
parent.tun_connected(); // signal that we are connected
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@ -272,15 +283,23 @@ namespace openvpn {
|
||||
: io_context(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg),
|
||||
halt(false),
|
||||
state(new TunProp::State())
|
||||
state(new TunProp::State()),
|
||||
l2_timer(io_context_arg),
|
||||
halt(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool send(Buffer& buf)
|
||||
{
|
||||
if (impl)
|
||||
return impl->write(buf);
|
||||
{
|
||||
if (dhcp_capture && dhcp_capture->mod_reply(buf))
|
||||
{
|
||||
OPENVPN_LOG("DHCP PROPS:" << std::endl << dhcp_capture->get_props().to_string());
|
||||
layer_2_schedule_timer(1);
|
||||
}
|
||||
return impl->write(buf);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#ifdef OPENVPN_DEBUG_TAPWIN
|
||||
@ -311,6 +330,8 @@ namespace openvpn {
|
||||
{
|
||||
halt = true;
|
||||
|
||||
l2_timer.cancel();
|
||||
|
||||
// stop tun
|
||||
if (impl)
|
||||
impl->stop();
|
||||
@ -336,13 +357,51 @@ namespace openvpn {
|
||||
Util::tap_process_logging(h);
|
||||
}
|
||||
|
||||
void layer_2_schedule_timer(const unsigned int seconds)
|
||||
{
|
||||
l2_timer.expires_at(Time::now() + Time::Duration::seconds(seconds));
|
||||
l2_timer.async_wait([self=Ptr(this)](const asio::error_code& error)
|
||||
{
|
||||
if (!error && !self->halt)
|
||||
self->layer_2_timer_callback();
|
||||
});
|
||||
}
|
||||
|
||||
// Normally called once per second by l2_timer while we are waiting
|
||||
// for layer 2 DHCP handshake to complete.
|
||||
void layer_2_timer_callback()
|
||||
{
|
||||
if (dhcp_capture && tun_setup)
|
||||
{
|
||||
if (tun_setup->l2_ready(dhcp_capture->get_props()))
|
||||
{
|
||||
std::ostringstream os;
|
||||
tun_setup->l2_finish(dhcp_capture->get_props(), config->stop, os);
|
||||
OPENVPN_LOG_STRING(os.str());
|
||||
parent.tun_connected();
|
||||
dhcp_capture.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG("L2: Waiting for DHCP handshake...");
|
||||
layer_2_schedule_timer(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asio::io_context& io_context;
|
||||
TunPersist::Ptr tun_persist; // contains the TAP device HANDLE
|
||||
ClientConfig::Ptr config;
|
||||
TunClientParent& parent;
|
||||
TunImpl::Ptr impl;
|
||||
bool halt;
|
||||
TunProp::State::Ptr state;
|
||||
TunWin::SetupBase::Ptr tun_setup;
|
||||
|
||||
// Layer 2 DHCP stuff
|
||||
std::unique_ptr<DHCPCapture> dhcp_capture;
|
||||
AsioTimer l2_timer;
|
||||
|
||||
bool halt;
|
||||
};
|
||||
|
||||
inline TunClient::Ptr ClientConfig::new_tun_client_obj(asio::io_context& io_context,
|
||||
|
@ -27,13 +27,16 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <thread>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/arraysize.hpp>
|
||||
#include <openvpn/time/time.hpp>
|
||||
#include <openvpn/error/excode.hpp>
|
||||
#include <openvpn/win/scoped_handle.hpp>
|
||||
#include <openvpn/win/cmd.hpp>
|
||||
@ -54,6 +57,7 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
// Set up the TAP device
|
||||
virtual HANDLE establish(const TunBuilderCapture& pull,
|
||||
const std::wstring& openvpn_app_path,
|
||||
Stop* stop,
|
||||
@ -87,8 +91,17 @@ namespace openvpn {
|
||||
remove_cmds.reset(new ActionList());
|
||||
|
||||
// populate add/remove lists with actions
|
||||
adapter_config(th(), openvpn_app_path, tap, pull, *add_cmds, *remove_cmds, os);
|
||||
|
||||
switch (pull.layer())
|
||||
{
|
||||
case Layer::OSI_LAYER_3:
|
||||
adapter_config(th(), openvpn_app_path, tap, pull, false, *add_cmds, *remove_cmds, os);
|
||||
break;
|
||||
case Layer::OSI_LAYER_2:
|
||||
adapter_config_l2(th(), openvpn_app_path, tap, pull, *add_cmds, *remove_cmds, os);
|
||||
break;
|
||||
default:
|
||||
throw tun_win_setup("layer undefined");
|
||||
}
|
||||
// execute the add actions
|
||||
add_cmds->execute(os);
|
||||
|
||||
@ -96,11 +109,72 @@ namespace openvpn {
|
||||
// enable the remove actions
|
||||
remove_cmds->enable_destroy(true);
|
||||
|
||||
// if layer 2, save state
|
||||
if (pull.layer() == Layer::OSI_LAYER_2)
|
||||
l2_state.reset(new L2State(tap, openvpn_app_path));
|
||||
|
||||
return th.release();
|
||||
}
|
||||
|
||||
// In layer 2 mode, return true route_delay seconds after
|
||||
// the adapter properties matches the data given in pull.
|
||||
// This method is usually called once per second until it
|
||||
// returns true.
|
||||
virtual bool l2_ready(const TunBuilderCapture& pull) override
|
||||
{
|
||||
const unsigned int route_delay = 5;
|
||||
if (l2_state)
|
||||
{
|
||||
if (l2_state->props_ready.defined())
|
||||
{
|
||||
if (Time::now() >= l2_state->props_ready)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const Util::IPNetmask4 vpn_addr(pull, "VPN IP");
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (ai.is_up(l2_state->tap.index, vpn_addr))
|
||||
l2_state->props_ready = Time::now() + Time::Duration::seconds(route_delay);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finish the layer 2 configuration, should be called
|
||||
// after l2_ready() returns true.
|
||||
virtual void l2_finish(const TunBuilderCapture& pull,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
std::unique_ptr<L2State> l2s(std::move(l2_state));
|
||||
if (l2s)
|
||||
{
|
||||
Win::ScopedHANDLE nh;
|
||||
ActionList::Ptr add_cmds(new ActionList());
|
||||
adapter_config(nh(), l2s->openvpn_app_path, l2s->tap, pull, true, *add_cmds, *remove_cmds, os);
|
||||
add_cmds->execute(os);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void destroy(std::ostream& os) override // defined by DestructorBase
|
||||
{
|
||||
// l2_state
|
||||
l2_state.reset();
|
||||
|
||||
// l2_thread
|
||||
if (l2_thread)
|
||||
{
|
||||
try {
|
||||
l2_thread->join();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
l2_thread.reset();
|
||||
}
|
||||
|
||||
// remove_cmds
|
||||
if (remove_cmds)
|
||||
{
|
||||
remove_cmds->destroy(os);
|
||||
@ -115,12 +189,27 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
private:
|
||||
struct L2State
|
||||
{
|
||||
L2State(const Util::TapNameGuidPair& tap_arg,
|
||||
const std::wstring& openvpn_app_path_arg)
|
||||
: tap(tap_arg),
|
||||
openvpn_app_path(openvpn_app_path_arg)
|
||||
{
|
||||
}
|
||||
|
||||
Util::TapNameGuidPair tap;
|
||||
std::wstring openvpn_app_path;
|
||||
Time props_ready;
|
||||
};
|
||||
|
||||
#if _WIN32_WINNT >= 0x0600
|
||||
// Configure TAP adapter on Vista and higher
|
||||
void adapter_config(HANDLE th,
|
||||
const std::wstring& openvpn_app_path,
|
||||
const Util::TapNameGuidPair& tap,
|
||||
const TunBuilderCapture& pull,
|
||||
const bool l2_post,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
std::ostream& os)
|
||||
@ -139,7 +228,8 @@ namespace openvpn {
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// set TAP media status to CONNECTED
|
||||
Util::tap_set_media_status(th, true);
|
||||
if (!l2_post)
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// try to delete any stale routes on interface left over from previous session
|
||||
create.add(new Util::ActionDeleteAllRoutesOnInterface(tap.index));
|
||||
@ -156,7 +246,7 @@ namespace openvpn {
|
||||
// Usage: delete address [name=]<string> [[address=]<IPv4 address>]
|
||||
// [[gateway=]<IPv4 address>|all]
|
||||
// [[store=]active|persistent]
|
||||
if (local4)
|
||||
if (local4 && !l2_post)
|
||||
{
|
||||
// Process ifconfig and topology
|
||||
const std::string netmask = IPv4::Addr::netmask_from_prefix_len(local4->prefix_length).to_string();
|
||||
@ -193,7 +283,7 @@ namespace openvpn {
|
||||
// [[store=]active|persistent]
|
||||
//Usage: delete address [interface=]<string> [address=]<IPv6 address>
|
||||
// [[store=]active|persistent]
|
||||
if (local6 && !pull.block_ipv6)
|
||||
if (local6 && !pull.block_ipv6 && !l2_post)
|
||||
{
|
||||
create.add(new WinCmd("netsh interface ipv6 set address " + tap_index_name + ' ' + local6->address + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ipv6 delete address " + tap_index_name + ' ' + local6->address + " store=active"));
|
||||
@ -351,7 +441,7 @@ namespace openvpn {
|
||||
continue;
|
||||
const std::string proto = ds.ipv6 ? "ipv6" : "ip";
|
||||
const int idx = indices[bool(ds.ipv6)]++;
|
||||
if (add_netsh_rules)
|
||||
if (add_netsh_rules && !l2_post)
|
||||
{
|
||||
if (idx)
|
||||
create.add(new WinCmd("netsh interface " + proto + " add " + dns_servers_cmd + " " + tap_index_name + ' ' + ds.address + " " + to_string(idx+1) + validate_cmd));
|
||||
@ -460,6 +550,7 @@ namespace openvpn {
|
||||
const std::wstring& openvpn_app_path,
|
||||
const Util::TapNameGuidPair& tap,
|
||||
const TunBuilderCapture& pull,
|
||||
const bool l2_post,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
std::ostream& os)
|
||||
@ -473,74 +564,80 @@ namespace openvpn {
|
||||
// set local4 to point to IPv4 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
|
||||
// Make sure the TAP adapter is set for DHCP
|
||||
{
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (!ai.is_dhcp_enabled(tap.index))
|
||||
{
|
||||
os << "TAP: DHCP is disabled, attempting to enable" << std::endl;
|
||||
ActionList::Ptr cmds(new ActionList());
|
||||
cmds->add(new Util::ActionEnableDHCP(tap));
|
||||
cmds->execute(os);
|
||||
}
|
||||
}
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
// This section skipped on layer 2 post-config
|
||||
if (!l2_post)
|
||||
{
|
||||
// Process ifconfig and topology
|
||||
const std::string netmask = IPv4::Addr::netmask_from_prefix_len(local4->prefix_length).to_string();
|
||||
const IP::Addr localaddr = IP::Addr::from_string(local4->address);
|
||||
if (local4->net30)
|
||||
Util::tap_configure_topology_net30(th, localaddr, local4->prefix_length);
|
||||
else
|
||||
Util::tap_configure_topology_subnet(th, localaddr, local4->prefix_length);
|
||||
// Make sure the TAP adapter is set for DHCP
|
||||
{
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (!ai.is_dhcp_enabled(tap.index))
|
||||
{
|
||||
os << "TAP: DHCP is disabled, attempting to enable" << std::endl;
|
||||
ActionList::Ptr cmds(new ActionList());
|
||||
cmds->add(new Util::ActionEnableDHCP(tap));
|
||||
cmds->execute(os);
|
||||
}
|
||||
}
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
// Process ifconfig and topology
|
||||
const std::string netmask = IPv4::Addr::netmask_from_prefix_len(local4->prefix_length).to_string();
|
||||
const IP::Addr localaddr = IP::Addr::from_string(local4->address);
|
||||
if (local4->net30)
|
||||
Util::tap_configure_topology_net30(th, localaddr, local4->prefix_length);
|
||||
else
|
||||
Util::tap_configure_topology_subnet(th, localaddr, local4->prefix_length);
|
||||
}
|
||||
|
||||
// On pre-Vista, set up TAP adapter DHCP masquerade for
|
||||
// configuring adapter properties.
|
||||
{
|
||||
os << "TAP: configure DHCP masquerade" << std::endl;
|
||||
Util::TAPDHCPMasquerade dhmasq;
|
||||
dhmasq.init_from_capture(pull);
|
||||
dhmasq.ioctl(th);
|
||||
}
|
||||
|
||||
// set TAP media status to CONNECTED
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// ARP
|
||||
Util::flush_arp(tap.index, os);
|
||||
|
||||
// DHCP release/renew
|
||||
{
|
||||
const Util::InterfaceInfoList ii;
|
||||
Util::dhcp_release(ii, tap.index, os);
|
||||
Util::dhcp_renew(ii, tap.index, os);
|
||||
}
|
||||
|
||||
// Wait for TAP adapter to come up
|
||||
{
|
||||
bool succeed = false;
|
||||
const Util::IPNetmask4 vpn_addr(pull, "VPN IP");
|
||||
for (int i = 1; i <= 30; ++i)
|
||||
{
|
||||
os << '[' << i << "] waiting for TAP adapter to receive DHCP settings..." << std::endl;
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (ai.is_up(tap.index, vpn_addr))
|
||||
{
|
||||
succeed = true;
|
||||
break;
|
||||
}
|
||||
::Sleep(1000);
|
||||
}
|
||||
if (!succeed)
|
||||
throw tun_win_setup("TAP adapter DHCP handshake failed");
|
||||
}
|
||||
|
||||
// Pre route-add sleep
|
||||
os << "Sleeping 5 seconds prior to adding routes..." << std::endl;
|
||||
::Sleep(5000);
|
||||
}
|
||||
|
||||
// On pre-Vista, set up TAP adapter DHCP masquerade for
|
||||
// configuring adapter properties.
|
||||
{
|
||||
os << "TAP: configure DHCP masquerade" << std::endl;
|
||||
Util::TAPDHCPMasquerade dhmasq;
|
||||
dhmasq.init_from_capture(pull);
|
||||
dhmasq.ioctl(th);
|
||||
}
|
||||
|
||||
// set TAP media status to CONNECTED
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// ARP
|
||||
Util::flush_arp(tap.index, os);
|
||||
|
||||
// DHCP release/renew
|
||||
{
|
||||
const Util::InterfaceInfoList ii;
|
||||
Util::dhcp_release(ii, tap.index, os);
|
||||
Util::dhcp_renew(ii, tap.index, os);
|
||||
}
|
||||
|
||||
// Wait for TAP adapter to come up
|
||||
{
|
||||
bool succeed = false;
|
||||
const Util::IPNetmask4 vpn_addr(pull, "VPN IP");
|
||||
for (int i = 1; i <= 30; ++i)
|
||||
{
|
||||
os << '[' << i << "] waiting for TAP adapter to receive DHCP settings..." << std::endl;
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (ai.is_up(tap.index, vpn_addr))
|
||||
{
|
||||
succeed = true;
|
||||
break;
|
||||
}
|
||||
::Sleep(1000);
|
||||
}
|
||||
if (!succeed)
|
||||
throw tun_win_setup("TAP adapter DHCP handshake failed");
|
||||
}
|
||||
|
||||
// Process routes
|
||||
os << "Sleeping 5 seconds prior to adding routes..." << std::endl;
|
||||
::Sleep(5000);
|
||||
for (auto &route : pull.add_routes)
|
||||
{
|
||||
if (!route.ipv6)
|
||||
@ -605,10 +702,52 @@ namespace openvpn {
|
||||
}
|
||||
#endif
|
||||
|
||||
void adapter_config_l2(HANDLE th,
|
||||
const std::wstring& openvpn_app_path,
|
||||
const Util::TapNameGuidPair& tap,
|
||||
const TunBuilderCapture& pull,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
std::ostream& os)
|
||||
{
|
||||
// Make sure the TAP adapter is set for DHCP
|
||||
{
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (!ai.is_dhcp_enabled(tap.index))
|
||||
{
|
||||
os << "TAP: DHCP is disabled, attempting to enable" << std::endl;
|
||||
ActionList::Ptr cmds(new ActionList());
|
||||
cmds->add(new Util::ActionEnableDHCP(tap));
|
||||
cmds->execute(os);
|
||||
}
|
||||
}
|
||||
|
||||
// set TAP media status to CONNECTED
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// ARP
|
||||
Util::flush_arp(tap.index, os);
|
||||
|
||||
// We must do DHCP release/renew in a background thread
|
||||
// so the foreground can forward the DHCP negotiation packets
|
||||
// over the tunnel.
|
||||
l2_thread.reset(new std::thread([this, logwrap=Log::Context::Wrapper(), tap]() {
|
||||
Log::Context logctx(logwrap);
|
||||
std::ostringstream os;
|
||||
const Util::InterfaceInfoList ii;
|
||||
Util::dhcp_release(ii, tap.index, os);
|
||||
Util::dhcp_renew(ii, tap.index, os);
|
||||
OPENVPN_LOG_STRING(os.str());
|
||||
}));
|
||||
}
|
||||
|
||||
#if _WIN32_WINNT >= 0x0600 // Vista+
|
||||
TunWin::WFPContext::Ptr wfp{new TunWin::WFPContext};
|
||||
#endif
|
||||
|
||||
std::unique_ptr<std::thread> l2_thread;
|
||||
std::unique_ptr<L2State> l2_state;
|
||||
|
||||
ActionList::Ptr remove_cmds;
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user