Merge branch 'tun-device-ipv6'
Adds support for IPv6 addresses on TUN devices, in particular, to support virtual IPv6 addresses on FreeBSD/macOS. Closes strongswan/strongswan#2465
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 Tobias Brunner
|
* Copyright (C) 2009-2024 Tobias Brunner
|
||||||
*
|
*
|
||||||
* Copyright (C) secunet Security Networks AG
|
* Copyright (C) secunet Security Networks AG
|
||||||
*
|
*
|
||||||
@@ -1443,6 +1443,35 @@ static void add_rt_ifname(struct rt_msghdr *hdr, int type, char *name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append an interface address sockaddr_dl to routing message
|
||||||
|
*/
|
||||||
|
static void add_rt_ifaddr(struct rt_msghdr *hdr, int type, char *name)
|
||||||
|
{
|
||||||
|
struct ifaddrs *ifap, *ifa;
|
||||||
|
struct sockaddr_dl *sdl;
|
||||||
|
|
||||||
|
if (getifaddrs(&ifap) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
|
||||||
|
{
|
||||||
|
if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_LINK ||
|
||||||
|
!streq(name, ifa->ifa_name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sdl = (struct sockaddr_dl*)ifa->ifa_addr;
|
||||||
|
memcpy((char*)hdr + hdr->rtm_msglen, sdl, sdl->sdl_len);
|
||||||
|
hdr->rtm_msglen += SA_LEN(sdl->sdl_len);
|
||||||
|
hdr->rtm_addrs |= type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add or remove a route
|
* Add or remove a route
|
||||||
*/
|
*/
|
||||||
@@ -1519,6 +1548,10 @@ static status_t manage_route(private_kernel_pfroute_net_t *this, int op,
|
|||||||
{
|
{
|
||||||
add_rt_addr(&msg.hdr, RTA_GATEWAY, gateway);
|
add_rt_addr(&msg.hdr, RTA_GATEWAY, gateway);
|
||||||
}
|
}
|
||||||
|
else if (if_name)
|
||||||
|
{
|
||||||
|
add_rt_ifaddr(&msg.hdr, RTA_GATEWAY, if_name);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 Tobias Brunner
|
* Copyright (C) 2012-2014 Tobias Brunner
|
||||||
* Copyright (C) 2012 Giuliano Grassi
|
* Copyright (C) 2012 Giuliano Grassi
|
||||||
* Copyright (C) 2012 Ralf Sager
|
* Copyright (C) 2012 Ralf Sager
|
||||||
* Copyright (C) 2012 Martin Willi
|
* Copyright (C) 2012 Martin Willi
|
||||||
@@ -59,12 +59,11 @@ tun_device_t *tun_device_create(const char *name_tmpl)
|
|||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/if_tun.h>
|
#include <linux/if_tun.h>
|
||||||
#elif __FreeBSD__ >= 10
|
#include <linux/ipv6.h>
|
||||||
|
#else
|
||||||
#include <net/if_tun.h>
|
#include <net/if_tun.h>
|
||||||
#include <net/if_var.h>
|
#include <net/if_var.h>
|
||||||
#include <netinet/in_var.h>
|
#include <netinet/in_var.h>
|
||||||
#else
|
|
||||||
#include <net/if_tun.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TUN_DEFAULT_MTU 1500
|
#define TUN_DEFAULT_MTU 1500
|
||||||
@@ -93,6 +92,11 @@ struct private_tun_device_t {
|
|||||||
*/
|
*/
|
||||||
int sock;
|
int sock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket used for ioctl() to set IPv6 interface addr, ...
|
||||||
|
*/
|
||||||
|
int sock_v6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current MTU
|
* The current MTU
|
||||||
*/
|
*/
|
||||||
@@ -109,13 +113,83 @@ struct private_tun_device_t {
|
|||||||
uint8_t netmask;
|
uint8_t netmask;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
#ifdef __linux__
|
||||||
* FreeBSD 10 deprecated the SIOCSIFADDR etc. commands.
|
|
||||||
*/
|
|
||||||
#if __FreeBSD__ >= 10
|
|
||||||
|
|
||||||
static bool set_address_and_mask(struct in_aliasreq *ifra, host_t *addr,
|
/**
|
||||||
uint8_t netmask)
|
* Set an IPv4 address and netmask
|
||||||
|
*/
|
||||||
|
static bool set_address_v4(private_tun_device_t *this, host_t *addr,
|
||||||
|
uint8_t netmask)
|
||||||
|
{
|
||||||
|
struct ifreq ifr = {0};
|
||||||
|
host_t *mask;
|
||||||
|
|
||||||
|
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
||||||
|
memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr),
|
||||||
|
*addr->get_sockaddr_len(addr));
|
||||||
|
|
||||||
|
if (ioctl(this->sock, SIOCSIFADDR, &ifr) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "failed to set address on %s: %s",
|
||||||
|
this->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
mask = host_create_netmask(addr->get_family(addr), netmask);
|
||||||
|
if (!mask)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "invalid netmask: %d", netmask);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
memcpy(&ifr.ifr_addr, mask->get_sockaddr(mask),
|
||||||
|
*mask->get_sockaddr_len(mask));
|
||||||
|
mask->destroy(mask);
|
||||||
|
|
||||||
|
if (ioctl(this->sock, SIOCSIFNETMASK, &ifr) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "failed to set netmask on %s: %s",
|
||||||
|
this->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an IPv6 address and netmask
|
||||||
|
*/
|
||||||
|
static bool set_address_v6(private_tun_device_t *this, host_t *addr,
|
||||||
|
uint8_t netmask)
|
||||||
|
{
|
||||||
|
struct in6_ifreq ifr6 = {0};
|
||||||
|
struct ifreq ifr = {0};
|
||||||
|
|
||||||
|
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
||||||
|
if (ioctl(this->sock_v6, SIOCGIFINDEX, &ifr) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "failed to determine index of %s: %s",
|
||||||
|
this->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&ifr6.ifr6_addr, addr->get_sockaddr(addr),
|
||||||
|
*addr->get_sockaddr_len(addr));
|
||||||
|
ifr6.ifr6_prefixlen = netmask;
|
||||||
|
ifr6.ifr6_ifindex = ifr.ifr_ifindex;
|
||||||
|
if (ioctl(this->sock_v6, SIOCSIFADDR, &ifr6) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "failed to set address on %s: %s",
|
||||||
|
this->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* __linux__ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply address and mask for IPv4
|
||||||
|
*/
|
||||||
|
static bool set_address_and_mask_v4(struct in_aliasreq *ifra, host_t *addr,
|
||||||
|
uint8_t netmask)
|
||||||
{
|
{
|
||||||
host_t *mask;
|
host_t *mask;
|
||||||
|
|
||||||
@@ -138,20 +212,18 @@ static bool set_address_and_mask(struct in_aliasreq *ifra, host_t *addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the address using the more flexible SIOCAIFADDR/SIOCDIFADDR commands
|
* Set the address for IPv4
|
||||||
* on FreeBSD 10 an newer.
|
|
||||||
*/
|
*/
|
||||||
static bool set_address_impl(private_tun_device_t *this, host_t *addr,
|
static bool set_address_v4(private_tun_device_t *this, host_t *addr,
|
||||||
uint8_t netmask)
|
uint8_t netmask)
|
||||||
{
|
{
|
||||||
struct in_aliasreq ifra;
|
struct in_aliasreq ifra = {0};
|
||||||
|
|
||||||
memset(&ifra, 0, sizeof(ifra));
|
|
||||||
strncpy(ifra.ifra_name, this->if_name, IFNAMSIZ);
|
strncpy(ifra.ifra_name, this->if_name, IFNAMSIZ);
|
||||||
|
|
||||||
if (this->address)
|
if (this->address)
|
||||||
{ /* remove the existing address first */
|
{ /* remove the existing address first */
|
||||||
if (!set_address_and_mask(&ifra, this->address, this->netmask))
|
if (!set_address_and_mask_v4(&ifra, this->address, this->netmask))
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -162,7 +234,7 @@ static bool set_address_impl(private_tun_device_t *this, host_t *addr,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!set_address_and_mask(&ifra, addr, netmask))
|
if (!set_address_and_mask_v4(&ifra, addr, netmask))
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -175,36 +247,19 @@ static bool set_address_impl(private_tun_device_t *this, host_t *addr,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* __FreeBSD__ */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the address using the classic SIOCSIFADDR etc. commands on other systems.
|
* Apply address and mask for IPv6
|
||||||
*/
|
*/
|
||||||
static bool set_address_impl(private_tun_device_t *this, host_t *addr,
|
static bool set_address_and_mask_v6(struct in6_aliasreq *ifra, host_t *addr,
|
||||||
uint8_t netmask)
|
uint8_t netmask)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
|
||||||
host_t *mask;
|
host_t *mask;
|
||||||
|
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
memcpy(&ifra->ifra_addr, addr->get_sockaddr(addr),
|
||||||
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
*addr->get_sockaddr_len(addr));
|
||||||
memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr),
|
/* set the same address as destination address */
|
||||||
|
memcpy(&ifra->ifra_dstaddr, addr->get_sockaddr(addr),
|
||||||
*addr->get_sockaddr_len(addr));
|
*addr->get_sockaddr_len(addr));
|
||||||
|
|
||||||
if (ioctl(this->sock, SIOCSIFADDR, &ifr) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_LIB, "failed to set address on %s: %s",
|
|
||||||
this->if_name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
#ifdef __APPLE__
|
|
||||||
if (ioctl(this->sock, SIOCSIFDSTADDR, &ifr) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_LIB, "failed to set dest address on %s: %s",
|
|
||||||
this->if_name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
#endif /* __APPLE__ */
|
|
||||||
|
|
||||||
mask = host_create_netmask(addr->get_family(addr), netmask);
|
mask = host_create_netmask(addr->get_family(addr), netmask);
|
||||||
if (!mask)
|
if (!mask)
|
||||||
@@ -212,27 +267,71 @@ static bool set_address_impl(private_tun_device_t *this, host_t *addr,
|
|||||||
DBG1(DBG_LIB, "invalid netmask: %d", netmask);
|
DBG1(DBG_LIB, "invalid netmask: %d", netmask);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
memcpy(&ifr.ifr_addr, mask->get_sockaddr(mask),
|
memcpy(&ifra->ifra_prefixmask, mask->get_sockaddr(mask),
|
||||||
*mask->get_sockaddr_len(mask));
|
*mask->get_sockaddr_len(mask));
|
||||||
mask->destroy(mask);
|
mask->destroy(mask);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
if (ioctl(this->sock, SIOCSIFNETMASK, &ifr) < 0)
|
/**
|
||||||
|
* Set the address for IPv6
|
||||||
|
*/
|
||||||
|
static bool set_address_v6(private_tun_device_t *this, host_t *addr,
|
||||||
|
uint8_t netmask)
|
||||||
|
{
|
||||||
|
struct in6_aliasreq ifra = {
|
||||||
|
.ifra_lifetime = { 0, 0, 0xffffffff, 0xffffffff },
|
||||||
|
};
|
||||||
|
|
||||||
|
strncpy(ifra.ifra_name, this->if_name, IFNAMSIZ);
|
||||||
|
|
||||||
|
if (this->address)
|
||||||
|
{ /* remove the existing address first */
|
||||||
|
if (!set_address_and_mask_v6(&ifra, this->address, this->netmask))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (ioctl(this->sock_v6, SIOCDIFADDR_IN6, &ifra) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "failed to remove existing address on %s: %s",
|
||||||
|
this->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!set_address_and_mask_v6(&ifra, addr, netmask))
|
||||||
{
|
{
|
||||||
DBG1(DBG_LIB, "failed to set netmask on %s: %s",
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (ioctl(this->sock_v6, SIOCAIFADDR_IN6, &ifra) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "failed to add address on %s: %s",
|
||||||
this->if_name, strerror(errno));
|
this->if_name, strerror(errno));
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __FreeBSD__ */
|
#endif /* __linux__ */
|
||||||
|
|
||||||
METHOD(tun_device_t, set_address, bool,
|
METHOD(tun_device_t, set_address, bool,
|
||||||
private_tun_device_t *this, host_t *addr, uint8_t netmask)
|
private_tun_device_t *this, host_t *addr, uint8_t netmask)
|
||||||
{
|
{
|
||||||
if (!set_address_impl(this, addr, netmask))
|
switch (addr->get_family(addr))
|
||||||
{
|
{
|
||||||
return FALSE;
|
case AF_INET:
|
||||||
|
if (!set_address_v4(this, addr, netmask))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
if (!set_address_v6(this, addr, netmask))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
DESTROY_IF(this->address);
|
DESTROY_IF(this->address);
|
||||||
this->address = addr->clone(addr);
|
this->address = addr->clone(addr);
|
||||||
@@ -253,9 +352,8 @@ METHOD(tun_device_t, get_address, host_t*,
|
|||||||
METHOD(tun_device_t, up, bool,
|
METHOD(tun_device_t, up, bool,
|
||||||
private_tun_device_t *this)
|
private_tun_device_t *this)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
struct ifreq ifr = {0};
|
||||||
|
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
|
||||||
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
||||||
|
|
||||||
if (ioctl(this->sock, SIOCGIFFLAGS, &ifr) < 0)
|
if (ioctl(this->sock, SIOCGIFFLAGS, &ifr) < 0)
|
||||||
@@ -279,9 +377,8 @@ METHOD(tun_device_t, up, bool,
|
|||||||
METHOD(tun_device_t, set_mtu, bool,
|
METHOD(tun_device_t, set_mtu, bool,
|
||||||
private_tun_device_t *this, int mtu)
|
private_tun_device_t *this, int mtu)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
struct ifreq ifr = {0};
|
||||||
|
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
|
||||||
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
||||||
ifr.ifr_mtu = mtu;
|
ifr.ifr_mtu = mtu;
|
||||||
|
|
||||||
@@ -298,14 +395,13 @@ METHOD(tun_device_t, set_mtu, bool,
|
|||||||
METHOD(tun_device_t, get_mtu, int,
|
METHOD(tun_device_t, get_mtu, int,
|
||||||
private_tun_device_t *this)
|
private_tun_device_t *this)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
struct ifreq ifr = {0};
|
||||||
|
|
||||||
if (this->mtu > 0)
|
if (this->mtu > 0)
|
||||||
{
|
{
|
||||||
return this->mtu;
|
return this->mtu;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
|
||||||
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
||||||
this->mtu = TUN_DEFAULT_MTU;
|
this->mtu = TUN_DEFAULT_MTU;
|
||||||
|
|
||||||
@@ -390,9 +486,8 @@ METHOD(tun_device_t, destroy, void,
|
|||||||
/* tun(4) says the following: "These network interfaces persist until
|
/* tun(4) says the following: "These network interfaces persist until
|
||||||
* the if_tun.ko module is unloaded, or until removed with the
|
* the if_tun.ko module is unloaded, or until removed with the
|
||||||
* ifconfig(8) command." So simply closing the FD is not enough. */
|
* ifconfig(8) command." So simply closing the FD is not enough. */
|
||||||
struct ifreq ifr;
|
struct ifreq ifr = {0};
|
||||||
|
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
|
||||||
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
|
||||||
if (ioctl(this->sock, SIOCIFDESTROY, &ifr) < 0)
|
if (ioctl(this->sock, SIOCIFDESTROY, &ifr) < 0)
|
||||||
{
|
{
|
||||||
@@ -416,13 +511,10 @@ static bool init_tun(private_tun_device_t *this, const char *name_tmpl)
|
|||||||
{
|
{
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
||||||
struct ctl_info info;
|
struct ctl_info info = {0};
|
||||||
struct sockaddr_ctl addr;
|
struct sockaddr_ctl addr = {0};
|
||||||
socklen_t size = IFNAMSIZ;
|
socklen_t size = IFNAMSIZ;
|
||||||
|
|
||||||
memset(&info, 0, sizeof(info));
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
|
|
||||||
this->tunfd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
|
this->tunfd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
|
||||||
if (this->tunfd < 0)
|
if (this->tunfd < 0)
|
||||||
{
|
{
|
||||||
@@ -464,7 +556,7 @@ static bool init_tun(private_tun_device_t *this, const char *name_tmpl)
|
|||||||
|
|
||||||
#elif defined(IFF_TUN)
|
#elif defined(IFF_TUN)
|
||||||
|
|
||||||
struct ifreq ifr;
|
struct ifreq ifr = {0};
|
||||||
|
|
||||||
strncpy(this->if_name, name_tmpl ?: "tun%d", IFNAMSIZ-1);
|
strncpy(this->if_name, name_tmpl ?: "tun%d", IFNAMSIZ-1);
|
||||||
this->if_name[IFNAMSIZ-1] = '\0';
|
this->if_name[IFNAMSIZ-1] = '\0';
|
||||||
@@ -476,8 +568,6 @@ static bool init_tun(private_tun_device_t *this, const char *name_tmpl)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
|
||||||
|
|
||||||
/* TUN device, no packet info */
|
/* TUN device, no packet info */
|
||||||
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
||||||
|
|
||||||
@@ -576,6 +666,14 @@ tun_device_t *tun_device_create(const char *name_tmpl)
|
|||||||
destroy(this);
|
destroy(this);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* FIXME: allow this to fail? (just ignore IPv6 addrs later?) */
|
||||||
|
this->sock_v6 = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||||
|
if (this->sock_v6 < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "failed to open IPv6 socket to configure TUN device");
|
||||||
|
destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return &this->public;
|
return &this->public;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user