Merge branch 'tun-vip'

Beside some OS X love, this merge introduces virtual IP and route installation
support on the pfkey/pfroute kernel interfaces.

Each virtual IP gets installed on a dedicated TUN device. As Linux-like source
routes are not supported, routes for the negotiated traffic selectors get
installed using the TUN device.

To prevent IKE packets from using those routes, special exclude routes get
installed to the IKE gateway. This works for most road-warrior deployments, but
certainly does not for some more exotic configurations, such as those using
virtual-IP-to-host. Mobility is not yet supported, either.
This commit is contained in:
Martin Willi 2013-05-06 17:04:36 +02:00
commit a8849e0713
20 changed files with 1163 additions and 386 deletions

1
.gitignore vendored
View File

@ -33,3 +33,4 @@ apidoc/
*.diff
*.tar.bz2
*.tar.gz
.DS_Store

View File

@ -216,7 +216,7 @@ sad_failure:
METHOD(kernel_ipsec_t, query_sa, status_t,
private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes,
u_int64_t *packets)
u_int64_t *packets, u_int32_t *time)
{
return NOT_SUPPORTED;
}

View File

@ -86,7 +86,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
METHOD(kernel_ipsec_t, query_sa, status_t,
private_kernel_android_ipsec_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, mark_t mark,
u_int64_t *bytes, u_int64_t *packets)
u_int64_t *bytes, u_int64_t *packets, u_int32_t *time)
{
return NOT_SUPPORTED;
}

View File

@ -71,7 +71,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
METHOD(kernel_ipsec_t, query_sa, status_t,
private_load_tester_ipsec_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, mark_t mark,
u_int64_t *bytes, u_int64_t *packets)
u_int64_t *bytes, u_int64_t *packets, u_int32_t *time)
{
return NOT_SUPPORTED;
}

View File

@ -162,23 +162,26 @@ METHOD(socket_t, receiver, status_t,
FD_ZERO(&rfds);
if (this->ipv4)
if (this->ipv4 != -1)
{
FD_SET(this->ipv4, &rfds);
max_fd = max(max_fd, this->ipv4);
}
if (this->ipv4_natt)
if (this->ipv4_natt != -1)
{
FD_SET(this->ipv4_natt, &rfds);
max_fd = max(max_fd, this->ipv4_natt);
}
if (this->ipv6)
if (this->ipv6 != -1)
{
FD_SET(this->ipv6, &rfds);
max_fd = max(max_fd, this->ipv6);
}
if (this->ipv6_natt)
if (this->ipv6_natt != -1)
{
FD_SET(this->ipv6_natt, &rfds);
max_fd = max(max_fd, this->ipv6_natt);
}
max_fd = max(max(this->ipv4, this->ipv4_natt), max(this->ipv6, this->ipv6_natt));
DBG2(DBG_NET, "waiting for data on sockets");
oldstate = thread_cancelability(TRUE);
@ -326,7 +329,7 @@ METHOD(socket_t, receiver, status_t,
METHOD(socket_t, sender, status_t,
private_socket_default_socket_t *this, packet_t *packet)
{
int sport, skt, family;
int sport, skt = -1, family;
ssize_t bytes_sent;
chunk_t data;
host_t *src, *dst;
@ -376,9 +379,10 @@ METHOD(socket_t, sender, status_t,
return FAILED;
}
}
else
if (skt == -1)
{
DBG1(DBG_NET, "unable to locate a send socket for port %d", sport);
DBG1(DBG_NET, "no socket found to send IPv%d packet from port %d",
family == AF_INET ? 4 : 6, sport);
return FAILED;
}
@ -537,20 +541,20 @@ static int open_socket(private_socket_default_socket_t *this,
pktinfo = IPV6_RECVPKTINFO;
break;
default:
return 0;
return -1;
}
skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
if (skt < 0)
{
DBG1(DBG_NET, "could not open socket: %s", strerror(errno));
return 0;
return -1;
}
if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
{
DBG1(DBG_NET, "unable to set SO_REUSEADDR on socket: %s", strerror(errno));
close(skt);
return 0;
return -1;
}
/* bind the socket */
@ -558,7 +562,7 @@ static int open_socket(private_socket_default_socket_t *this,
{
DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
close(skt);
return 0;
return -1;
}
/* retrieve randomly allocated port if needed */
@ -568,7 +572,7 @@ static int open_socket(private_socket_default_socket_t *this,
{
DBG1(DBG_NET, "unable to determine port: %s", strerror(errno));
close(skt);
return 0;
return -1;
}
switch (family)
{
@ -588,7 +592,7 @@ static int open_socket(private_socket_default_socket_t *this,
{
DBG1(DBG_NET, "unable to set IP_PKTINFO on socket: %s", strerror(errno));
close(skt);
return 0;
return -1;
}
}
@ -610,22 +614,43 @@ static int open_socket(private_socket_default_socket_t *this,
return skt;
}
/**
* Open a socket pair (normal an NAT traversal) for a given address family
*/
static void open_socketpair(private_socket_default_socket_t *this, int family,
int *skt, int *skt_natt, char *label)
{
*skt = open_socket(this, family, &this->port);
if (*skt == -1)
{
DBG1(DBG_NET, "could not open %s socket, %s disabled", label, label);
}
else
{
*skt_natt = open_socket(this, family, &this->natt);
if (*skt_natt == -1)
{
DBG1(DBG_NET, "could not open %s NAT-T socket", label);
}
}
}
METHOD(socket_t, destroy, void,
private_socket_default_socket_t *this)
{
if (this->ipv4)
if (this->ipv4 != -1)
{
close(this->ipv4);
}
if (this->ipv4_natt)
if (this->ipv4_natt != -1)
{
close(this->ipv4_natt);
}
if (this->ipv6)
if (this->ipv6 != -1)
{
close(this->ipv6);
}
if (this->ipv6_natt)
if (this->ipv6_natt != -1)
{
close(this->ipv6_natt);
}
@ -667,36 +692,17 @@ socket_default_socket_t *socket_default_socket_create()
}
/* we allocate IPv6 sockets first as that will reserve randomly allocated
* ports also for IPv4 */
this->ipv6 = open_socket(this, AF_INET6, &this->port);
if (this->ipv6 == 0)
{
DBG1(DBG_NET, "could not open IPv6 socket, IPv6 disabled");
}
else
{
this->ipv6_natt = open_socket(this, AF_INET6, &this->natt);
if (this->ipv6_natt == 0)
{
DBG1(DBG_NET, "could not open IPv6 NAT-T socket");
}
}
* ports also for IPv4. On OS X, we have to do it the other way round
* for the same effect. */
#ifdef __APPLE__
open_socketpair(this, AF_INET, &this->ipv4, &this->ipv4_natt, "IPv4");
open_socketpair(this, AF_INET6, &this->ipv6, &this->ipv6_natt, "IPv6");
#else /* !__APPLE__ */
open_socketpair(this, AF_INET6, &this->ipv6, &this->ipv6_natt, "IPv6");
open_socketpair(this, AF_INET, &this->ipv4, &this->ipv4_natt, "IPv4");
#endif /* __APPLE__ */
this->ipv4 = open_socket(this, AF_INET, &this->port);
if (this->ipv4 == 0)
{
DBG1(DBG_NET, "could not open IPv4 socket, IPv4 disabled");
}
else
{
this->ipv4_natt = open_socket(this, AF_INET, &this->natt);
if (this->ipv4_natt == 0)
{
DBG1(DBG_NET, "could not open IPv4 NAT-T socket");
}
}
if (!this->ipv4 && !this->ipv6)
if (this->ipv4 == -1 && this->ipv6 == -1)
{
DBG1(DBG_NET, "could not create any sockets");
destroy(this);

View File

@ -424,6 +424,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
{
status_t status = FAILED;
u_int64_t bytes, packets;
u_int32_t time;
if (inbound)
{
@ -432,13 +433,17 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
this->other_addr, this->my_addr, this->my_spi,
proto_ike2ip(this->protocol), this->mark_in,
&bytes, &packets);
&bytes, &packets, &time);
if (status == SUCCESS)
{
if (bytes > this->my_usebytes)
{
this->my_usebytes = bytes;
this->my_usepackets = packets;
if (time)
{
this->my_usetime = time;
}
return SUCCESS;
}
return FAILED;
@ -452,13 +457,17 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
this->my_addr, this->other_addr, this->other_spi,
proto_ike2ip(this->protocol), this->mark_out,
&bytes, &packets);
&bytes, &packets, &time);
if (status == SUCCESS)
{
if (bytes > this->other_usebytes)
{
this->other_usebytes = bytes;
this->other_usepackets = packets;
if (time)
{
this->other_usetime = time;
}
return SUCCESS;
}
return FAILED;
@ -471,7 +480,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
/**
* updates the cached usetime
*/
static void update_usetime(private_child_sa_t *this, bool inbound)
static bool update_usetime(private_child_sa_t *this, bool inbound)
{
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
@ -511,7 +520,7 @@ static void update_usetime(private_child_sa_t *this, bool inbound)
if (last_use == 0)
{
return;
return FALSE;
}
if (inbound)
{
@ -521,18 +530,26 @@ static void update_usetime(private_child_sa_t *this, bool inbound)
{
this->other_usetime = last_use;
}
return TRUE;
}
METHOD(child_sa_t, get_usestats, void,
private_child_sa_t *this, bool inbound,
time_t *time, u_int64_t *bytes, u_int64_t *packets)
{
if (update_usebytes(this, inbound) != FAILED)
if ((!bytes && !packets) || update_usebytes(this, inbound) != FAILED)
{
/* there was traffic since last update or the kernel interface
* does not support querying the number of usebytes.
*/
update_usetime(this, inbound);
if (time)
{
if (!update_usetime(this, inbound) && !bytes && !packets)
{
/* if policy query did not yield a usetime, query SAs instead */
update_usebytes(this, inbound);
}
}
}
if (time)
{
@ -668,21 +685,18 @@ METHOD(child_sa_t, install, status_t,
lifetime->time.rekey = 0;
}
if (this->mode == MODE_BEET || this->mode == MODE_TRANSPORT)
/* BEET requires the bound address from the traffic selectors.
* TODO: We add just the first traffic selector for now, as the
* kernel accepts a single TS per SA only */
if (inbound)
{
/* BEET requires the bound address from the traffic selectors.
* TODO: We add just the first traffic selector for now, as the
* kernel accepts a single TS per SA only */
if (inbound)
{
my_ts->get_first(my_ts, (void**)&dst_ts);
other_ts->get_first(other_ts, (void**)&src_ts);
}
else
{
my_ts->get_first(my_ts, (void**)&src_ts);
other_ts->get_first(other_ts, (void**)&dst_ts);
}
my_ts->get_first(my_ts, (void**)&dst_ts);
other_ts->get_first(other_ts, (void**)&src_ts);
}
else
{
my_ts->get_first(my_ts, (void**)&src_ts);
other_ts->get_first(other_ts, (void**)&dst_ts);
}
status = hydra->kernel_interface->add_sa(hydra->kernel_interface,

View File

@ -208,14 +208,14 @@ METHOD(kernel_interface_t, update_sa, status_t,
METHOD(kernel_interface_t, query_sa, status_t,
private_kernel_interface_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, mark_t mark,
u_int64_t *bytes, u_int64_t *packets)
u_int64_t *bytes, u_int64_t *packets, u_int32_t *time)
{
if (!this->ipsec)
{
return NOT_SUPPORTED;
}
return this->ipsec->query_sa(this->ipsec, src, dst, spi, protocol, mark,
bytes, packets);
bytes, packets, time);
}
METHOD(kernel_interface_t, del_sa, status_t,
@ -415,7 +415,8 @@ METHOD(kernel_interface_t, all_interfaces_usable, bool,
}
METHOD(kernel_interface_t, get_address_by_ts, status_t,
private_kernel_interface_t *this, traffic_selector_t *ts, host_t **ip)
private_kernel_interface_t *this, traffic_selector_t *ts,
host_t **ip, bool *vip)
{
enumerator_t *addrs;
host_t *host;
@ -446,18 +447,41 @@ METHOD(kernel_interface_t, get_address_by_ts, status_t,
}
host->destroy(host);
addrs = create_address_enumerator(this, ADDR_TYPE_ALL);
addrs = create_address_enumerator(this, ADDR_TYPE_VIRTUAL);
while (addrs->enumerate(addrs, (void**)&host))
{
if (ts->includes(ts, host))
{
found = TRUE;
*ip = host->clone(host);
if (vip)
{
*vip = TRUE;
}
break;
}
}
addrs->destroy(addrs);
if (!found)
{
addrs = create_address_enumerator(this, ADDR_TYPE_REGULAR);
while (addrs->enumerate(addrs, (void**)&host))
{
if (ts->includes(ts, host))
{
found = TRUE;
*ip = host->clone(host);
if (vip)
{
*vip = FALSE;
}
break;
}
}
addrs->destroy(addrs);
}
if (!found)
{
DBG2(DBG_KNL, "no local address found in traffic selector %R", ts);

View File

@ -65,6 +65,8 @@ typedef enum kernel_feature_t kernel_feature_t;
enum kernel_feature_t {
/** IPsec can process ESPv3 (RFC 4303) TFC padded packets */
KERNEL_ESP_V3_TFC = (1<<0),
/** Networking requires an "exclude" route for IKE/ESP packets */
KERNEL_REQUIRE_EXCLUDE_ROUTE = (1<<1),
};
/**
@ -195,11 +197,12 @@ struct kernel_interface_t {
* @param mark optional mark for this SA
* @param[out] bytes the number of bytes processed by SA
* @param[out] packets number of packets processed by SA
* @param[out] time last time of SA use
* @return SUCCESS if operation completed
*/
status_t (*query_sa) (kernel_interface_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, mark_t mark,
u_int64_t *bytes, u_int64_t *packets);
u_int64_t *bytes, u_int64_t *packets, u_int32_t *time);
/**
* Delete a previously installed SA from the SAD.
@ -451,10 +454,11 @@ struct kernel_interface_t {
*
* @param ts traffic selector
* @param ip returned IP address (has to be destroyed)
* @param vip set to TRUE if returned address is a virtual IP
* @return SUCCESS if address found
*/
status_t (*get_address_by_ts)(kernel_interface_t *this,
traffic_selector_t *ts, host_t **ip);
traffic_selector_t *ts, host_t **ip, bool *vip);
/**
* Register an ipsec kernel interface constructor on the manager.

View File

@ -155,11 +155,12 @@ struct kernel_ipsec_t {
* @param mark optional mark for this SA
* @param[out] bytes the number of bytes processed by SA
* @param[out] packets number of packets processed by SA
* @param[out] time last time of SA use
* @return SUCCESS if operation completed
*/
status_t (*query_sa) (kernel_ipsec_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, mark_t mark,
u_int64_t *bytes, u_int64_t *packets);
u_int64_t *bytes, u_int64_t *packets, u_int32_t *time);
/**
* Delete a previusly installed SA from the SAD.

View File

@ -35,17 +35,17 @@ typedef enum kernel_address_type_t kernel_address_type_t;
*/
enum kernel_address_type_t {
/** normal addresses (on regular, up, non-ignored) interfaces */
ADDR_TYPE_REGULAR = 0,
ADDR_TYPE_REGULAR = (1 << 0),
/** addresses on down interfaces */
ADDR_TYPE_DOWN = (1 << 0),
ADDR_TYPE_DOWN = (1 << 1),
/** addresses on ignored interfaces */
ADDR_TYPE_IGNORED = (1 << 1),
ADDR_TYPE_IGNORED = (1 << 2),
/** addresses on loopback interfaces */
ADDR_TYPE_LOOPBACK = (1 << 2),
ADDR_TYPE_LOOPBACK = (1 << 3),
/** virtual IP addresses */
ADDR_TYPE_VIRTUAL = (1 << 3),
ADDR_TYPE_VIRTUAL = (1 << 4),
/** to enumerate all available addresses */
ADDR_TYPE_ALL = (1 << 4) - 1,
ADDR_TYPE_ALL = (1 << 5) - 1,
};
/**

View File

@ -1911,7 +1911,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
METHOD(kernel_ipsec_t, query_sa, status_t,
private_kernel_klips_ipsec_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, mark_t mark,
u_int64_t *bytes, u_int64_t *packets)
u_int64_t *bytes, u_int64_t *packets, u_int32_t *time)
{
return NOT_SUPPORTED; /* TODO */
}
@ -2118,7 +2118,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
this->install_routes)
{
hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface,
src_ts, &route->src_ip);
src_ts, &route->src_ip, NULL);
}
if (!route->src_ip)

View File

@ -1595,7 +1595,7 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this,
METHOD(kernel_ipsec_t, query_sa, status_t,
private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, mark_t mark,
u_int64_t *bytes, u_int64_t *packets)
u_int64_t *bytes, u_int64_t *packets, u_int32_t *time)
{
netlink_buf_t request;
struct nlmsghdr *out = NULL, *hdr;
@ -1680,6 +1680,12 @@ METHOD(kernel_ipsec_t, query_sa, status_t,
{
*packets = sa->curlft.packets;
}
if (time)
{ /* curlft contains an "use" time, but that contains a timestamp
* of the first use, not the last. Last use time must be queried
* on the policy on Linux */
*time = 0;
}
status = SUCCESS;
}
memwipe(out, len);
@ -2102,7 +2108,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
);
if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface,
fwd->dst_ts, &route->src_ip) == SUCCESS)
fwd->dst_ts, &route->src_ip, NULL) == SUCCESS)
{
/* get the nexthop to src (src as we are in POLICY_FWD) */
route->gateway = hydra->kernel_interface->get_nexthop(
@ -2638,13 +2644,7 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
this->replay_bmp = (this->replay_window + sizeof(u_int32_t) * 8 - 1) /
(sizeof(u_int32_t) * 8);
if (streq(hydra->daemon, "pluto"))
{ /* no routes for pluto, they are installed via updown script */
this->install_routes = FALSE;
/* no policy history for pluto */
this->policy_history = FALSE;
}
else if (streq(hydra->daemon, "starter"))
if (streq(hydra->daemon, "starter"))
{ /* starter has no threads, so we do not register for kernel events */
register_for_events = FALSE;
}

View File

@ -1165,6 +1165,10 @@ static bool filter_addresses(address_enumerator_t *data,
{ /* skip virtual interfaces added by us */
return FALSE;
}
if (!(data->which & ADDR_TYPE_REGULAR) && !(*in)->refcount)
{ /* address is regular, but not requested */
return FALSE;
}
if ((*in)->scope >= RT_SCOPE_LINK)
{ /* skip addresses with a unusable scope */
return FALSE;
@ -1209,9 +1213,12 @@ static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in,
METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
private_kernel_netlink_net_t *this, kernel_address_type_t which)
{
address_enumerator_t *data = malloc_thing(address_enumerator_t);
data->this = this;
data->which = which;
address_enumerator_t *data;
INIT(data,
.this = this,
.which = which,
);
this->lock->read_lock(this->lock);
return enumerator_create_nested(

View File

@ -179,6 +179,11 @@ struct private_kernel_pfkey_ipsec_t
*/
linked_list_t *policies;
/**
* List of exclude routes (exclude_route_t)
*/
linked_list_t *excludes;
/**
* Hash table of IPsec SAs using policies (ipsec_sa_t)
*/
@ -210,6 +215,33 @@ struct private_kernel_pfkey_ipsec_t
int seq;
};
typedef struct exclude_route_t exclude_route_t;
/**
* Exclude route definition
*/
struct exclude_route_t {
/** destination address of exclude */
host_t *dst;
/** source address for route */
host_t *src;
/** nexthop exclude has been installed */
host_t *gtw;
/** references to this route */
int refs;
};
/**
* clean up a route exclude entry
*/
static void exclude_route_destroy(exclude_route_t *this)
{
this->dst->destroy(this->dst);
this->src->destroy(this->src);
this->gtw->destroy(this->gtw);
free(this);
}
typedef struct route_entry_t route_entry_t;
/**
@ -230,6 +262,9 @@ struct route_entry_t {
/** destination net prefixlen */
u_int8_t prefixlen;
/** reference to exclude route, if any */
exclude_route_t *exclude;
};
/**
@ -251,6 +286,7 @@ static bool route_entry_equals(route_entry_t *a, route_entry_t *b)
{
return a->if_name && b->if_name && streq(a->if_name, b->if_name) &&
a->src_ip->ip_equals(a->src_ip, b->src_ip) &&
a->gateway && b->gateway &&
a->gateway->ip_equals(a->gateway, b->gateway) &&
chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen;
}
@ -339,7 +375,7 @@ static void ipsec_sa_destroy(private_kernel_pfkey_ipsec_t *this,
}
typedef struct policy_sa_t policy_sa_t;
typedef struct policy_sa_fwd_t policy_sa_fwd_t;
typedef struct policy_sa_in_t policy_sa_in_t;
/**
* Mapping between a policy and an IPsec SA.
@ -356,10 +392,10 @@ struct policy_sa_t {
};
/**
* For forward policies we also cache the traffic selectors in order to install
* For input policies we also cache the traffic selectors in order to install
* the route.
*/
struct policy_sa_fwd_t {
struct policy_sa_in_t {
/** Generic interface */
policy_sa_t generic;
@ -371,7 +407,7 @@ struct policy_sa_fwd_t {
};
/**
* Create a policy_sa(_fwd)_t object
* Create a policy_sa(_in)_t object
*/
static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this,
policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst,
@ -379,14 +415,14 @@ static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this,
{
policy_sa_t *policy;
if (dir == POLICY_FWD)
if (dir == POLICY_IN)
{
policy_sa_fwd_t *fwd;
INIT(fwd,
policy_sa_in_t *in;
INIT(in,
.src_ts = src_ts->clone(src_ts),
.dst_ts = dst_ts->clone(dst_ts),
);
policy = &fwd->generic;
policy = &in->generic;
}
else
{
@ -398,16 +434,16 @@ static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this,
}
/**
* Destroy a policy_sa(_fwd)_t object
* Destroy a policy_sa(_in)_t object
*/
static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir,
private_kernel_pfkey_ipsec_t *this)
{
if (*dir == POLICY_FWD)
if (*dir == POLICY_IN)
{
policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)policy;
fwd->src_ts->destroy(fwd->src_ts);
fwd->dst_ts->destroy(fwd->dst_ts);
policy_sa_in_t *in = (policy_sa_in_t*)policy;
in->src_ts->destroy(in->src_ts);
in->dst_ts->destroy(in->dst_ts);
}
ipsec_sa_destroy(this, policy->sa);
free(policy);
@ -1090,7 +1126,7 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket
}
if (msg->sadb_msg_seq != this->seq)
{
DBG1(DBG_KNL, "received PF_KEY message with unexpected sequence "
DBG2(DBG_KNL, "received PF_KEY message with unexpected sequence "
"number, was %d expected %d", msg->sadb_msg_seq,
this->seq);
if (msg->sadb_msg_seq == 0)
@ -1768,7 +1804,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
METHOD(kernel_ipsec_t, query_sa, status_t,
private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, mark_t mark,
u_int64_t *bytes, u_int64_t *packets)
u_int64_t *bytes, u_int64_t *packets, u_int32_t *time)
{
unsigned char request[PFKEY_BUFFER_SIZE];
struct sadb_msg *msg, *out;
@ -1826,6 +1862,18 @@ METHOD(kernel_ipsec_t, query_sa, status_t,
/* not supported by PF_KEY */
*packets = 0;
}
if (time)
{
#ifdef __APPLE__
/* OS X uses the "last" time of use in usetime */
*time = response.lft_current->sadb_lifetime_usetime;
#else /* !__APPLE__ */
/* on Linux, sadb_lifetime_usetime is set to the "first" time of use,
* which is actually correct according to PF_KEY. We have to query
* policies for the last usetime. */
*time = 0;
#endif /* !__APPLE__ */
}
free(out);
return SUCCESS;
@ -1914,6 +1962,209 @@ METHOD(kernel_ipsec_t, flush_sas, status_t,
return SUCCESS;
}
/**
* Add an explicit exclude route to a routing entry
*/
static void add_exclude_route(private_kernel_pfkey_ipsec_t *this,
route_entry_t *route, host_t *src, host_t *dst)
{
enumerator_t *enumerator;
exclude_route_t *exclude;
host_t *gtw;
enumerator = this->excludes->create_enumerator(this->excludes);
while (enumerator->enumerate(enumerator, &exclude))
{
if (dst->ip_equals(dst, exclude->dst))
{
route->exclude = exclude;
exclude->refs++;
}
}
enumerator->destroy(enumerator);
if (!route->exclude)
{
DBG2(DBG_KNL, "installing new exclude route for %H src %H", dst, src);
gtw = hydra->kernel_interface->get_nexthop(hydra->kernel_interface,
dst, NULL);
if (gtw)
{
if (hydra->kernel_interface->add_route(hydra->kernel_interface,
dst->get_address(dst),
dst->get_family(dst) == AF_INET ? 32 : 128,
gtw, src, NULL) == SUCCESS)
{
INIT(exclude,
.dst = dst->clone(dst),
.src = src->clone(src),
.gtw = gtw->clone(gtw),
.refs = 1,
);
route->exclude = exclude;
this->excludes->insert_last(this->excludes, exclude);
}
else
{
DBG1(DBG_KNL, "installing exclude route for %H failed", dst);
}
gtw->destroy(gtw);
}
else
{
DBG1(DBG_KNL, "gateway lookup for for %H failed", dst);
}
}
}
/**
* Remove an exclude route attached to a routing entry
*/
static void remove_exclude_route(private_kernel_pfkey_ipsec_t *this,
route_entry_t *route)
{
if (route->exclude)
{
enumerator_t *enumerator;
exclude_route_t *exclude;
bool removed = FALSE;
host_t *dst;
enumerator = this->excludes->create_enumerator(this->excludes);
while (enumerator->enumerate(enumerator, &exclude))
{
if (route->exclude == exclude)
{
if (--exclude->refs == 0)
{
this->excludes->remove_at(this->excludes, enumerator);
removed = TRUE;
break;
}
}
}
enumerator->destroy(enumerator);
if (removed)
{
dst = route->exclude->dst;
DBG2(DBG_KNL, "uninstalling exclude route for %H src %H",
dst, route->exclude->src);
if (hydra->kernel_interface->del_route(hydra->kernel_interface,
dst->get_address(dst),
dst->get_family(dst) == AF_INET ? 32 : 128,
route->exclude->gtw, route->exclude->src,
NULL) != SUCCESS)
{
DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst);
}
exclude_route_destroy(route->exclude);
}
route->exclude = NULL;
}
}
/**
* Try to install a route to the given inbound policy
*/
static bool install_route(private_kernel_pfkey_ipsec_t *this,
policy_entry_t *policy, policy_sa_in_t *in)
{
route_entry_t *route, *old;
host_t *host, *src, *dst;
bool is_virtual;
if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface,
in->dst_ts, &host, &is_virtual) != SUCCESS)
{
return FALSE;
}
/* switch src/dst, as we handle an IN policy */
src = in->generic.sa->dst;
dst = in->generic.sa->src;
INIT(route,
.prefixlen = policy->src.mask,
.src_ip = host,
.gateway = hydra->kernel_interface->get_nexthop(
hydra->kernel_interface, dst, src),
.dst_net = chunk_clone(policy->src.net->get_address(policy->src.net)),
);
/* if the IP is virtual, we install the route over the interface it has
* been installed on. Otherwise we use the interface we use for IKE, as
* this is required for example on Linux. */
if (is_virtual)
{
src = route->src_ip;
}
/* get interface for route, using source address */
if (!hydra->kernel_interface->get_interface(hydra->kernel_interface,
src, &route->if_name))
{
route_entry_destroy(route);
return FALSE;
}
if (policy->route)
{
old = policy->route;
if (route_entry_equals(old, route))
{ /* such a route already exists */
route_entry_destroy(route);
return TRUE;
}
/* uninstall previously installed route */
if (hydra->kernel_interface->del_route(hydra->kernel_interface,
old->dst_net, old->prefixlen, old->gateway,
old->src_ip, old->if_name) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with policy "
"%R === %R %N", in->src_ts, in->dst_ts,
policy_dir_names, policy->direction);
}
route_entry_destroy(old);
policy->route = NULL;
}
/* if remote traffic selector covers the IKE peer, add an exclude route */
if (hydra->kernel_interface->get_features(
hydra->kernel_interface) & KERNEL_REQUIRE_EXCLUDE_ROUTE)
{
if (in->src_ts->includes(in->src_ts, dst))
{
add_exclude_route(this, route, in->generic.sa->dst, dst);
}
}
DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s",
in->src_ts, route->gateway, route->src_ip, route->if_name);
switch (hydra->kernel_interface->add_route(hydra->kernel_interface,
route->dst_net, route->prefixlen, route->gateway,
route->src_ip, route->if_name))
{
case ALREADY_DONE:
/* route exists, do not uninstall */
remove_exclude_route(this, route);
route_entry_destroy(route);
return TRUE;
case SUCCESS:
/* cache the installed route */
policy->route = route;
return TRUE;
default:
DBG1(DBG_KNL, "installing route failed: %R via %H src %H dev %s",
in->src_ts, route->gateway, route->src_ip, route->if_name);
remove_exclude_route(this, route);
route_entry_destroy(route);
return FALSE;
}
}
/**
* Add or update a policy in the kernel.
*
@ -2027,83 +2278,10 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this,
* - we are in tunnel mode
* - routing is not disabled via strongswan.conf
*/
if (policy->direction == POLICY_FWD &&
if (policy->direction == POLICY_IN &&
ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes)
{
policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping;
route_entry_t *route;
INIT(route,
.prefixlen = policy->src.mask,
);
if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface,
fwd->dst_ts, &route->src_ip) == SUCCESS)
{
/* get the nexthop to src (src as we are in POLICY_FWD).*/
route->gateway = hydra->kernel_interface->get_nexthop(
hydra->kernel_interface, ipsec->src,
ipsec->dst);
route->dst_net = chunk_clone(policy->src.net->get_address(
policy->src.net));
/* install route via outgoing interface */
if (!hydra->kernel_interface->get_interface(hydra->kernel_interface,
ipsec->dst, &route->if_name))
{
this->mutex->unlock(this->mutex);
route_entry_destroy(route);
return SUCCESS;
}
if (policy->route)
{
route_entry_t *old = policy->route;
if (route_entry_equals(old, route))
{
this->mutex->unlock(this->mutex);
route_entry_destroy(route);
return SUCCESS;
}
/* uninstall previously installed route */
if (hydra->kernel_interface->del_route(hydra->kernel_interface,
old->dst_net, old->prefixlen, old->gateway,
old->src_ip, old->if_name) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with "
"policy %R === %R %N", fwd->src_ts,
fwd->dst_ts, policy_dir_names,
policy->direction);
}
route_entry_destroy(old);
policy->route = NULL;
}
DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s",
fwd->src_ts, route->gateway, route->src_ip, route->if_name);
switch (hydra->kernel_interface->add_route(
hydra->kernel_interface, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name))
{
default:
DBG1(DBG_KNL, "unable to install source route for %H",
route->src_ip);
/* FALL */
case ALREADY_DONE:
/* route exists, do not uninstall */
route_entry_destroy(route);
break;
case SUCCESS:
/* cache the installed route */
policy->route = route;
break;
}
}
else
{
free(route);
}
install_route(this, policy, (policy_sa_in_t*)mapping);
}
this->mutex->unlock(this->mutex);
return SUCCESS;
@ -2269,7 +2447,7 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
}
else if (response.lft_current == NULL)
{
DBG1(DBG_KNL, "unable to query policy %R === %R %N: kernel reports no "
DBG2(DBG_KNL, "unable to query policy %R === %R %N: kernel reports no "
"use time", src_ts, dst_ts, policy_dir_names, direction);
free(out);
return FAILED;
@ -2398,6 +2576,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
"policy %R === %R %N", src_ts, dst_ts,
policy_dir_names, direction);
}
remove_exclude_route(this, route);
}
this->policies->remove(this->policies, found, NULL);
@ -2548,8 +2727,10 @@ METHOD(kernel_ipsec_t, enable_udp_decap, bool,
return FALSE;
}
#else /* __APPLE__ */
if (sysctlbyname("net.inet.ipsec.esp_port", NULL, NULL, &port,
sizeof(port)) != 0)
int intport = port;
if (sysctlbyname("net.inet.ipsec.esp_port", NULL, NULL, &intport,
sizeof(intport)) != 0)
{
DBG1(DBG_KNL, "could not set net.inet.ipsec.esp_port to %d: %s",
port, strerror(errno));
@ -2575,6 +2756,7 @@ METHOD(kernel_ipsec_t, destroy, void,
(linked_list_invoke_t)policy_entry_destroy,
this);
this->policies->destroy(this->policies);
this->excludes->destroy(this->excludes);
this->sas->destroy(this->sas);
this->mutex->destroy(this->mutex);
this->mutex_pfkey->destroy(this->mutex_pfkey);
@ -2609,6 +2791,7 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create()
},
},
.policies = linked_list_create(),
.excludes = linked_list_create(),
.sas = hashtable_create((hashtable_hash_t)ipsec_sa_hash,
(hashtable_equals_t)ipsec_sa_equals, 32),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
@ -2618,11 +2801,7 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create()
hydra->daemon),
);
if (streq(hydra->daemon, "pluto"))
{ /* no routes for pluto, they are installed via updown script */
this->install_routes = FALSE;
}
else if (streq(hydra->daemon, "starter"))
if (streq(hydra->daemon, "starter"))
{ /* starter has no threads, so we do not register for kernel events */
register_for_events = FALSE;
}

View File

@ -16,6 +16,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <ifaddrs.h>
#include <net/route.h>
#include <unistd.h>
@ -26,8 +27,10 @@
#include <hydra.h>
#include <utils/debug.h>
#include <networking/host.h>
#include <networking/tun_device.h>
#include <threading/thread.h>
#include <threading/mutex.h>
#include <threading/condvar.h>
#include <threading/rwlock.h>
#include <collections/hashtable.h>
#include <collections/linked_list.h>
@ -40,9 +43,6 @@
/** delay before firing roam events (ms) */
#define ROAM_DELAY 100
/** buffer size for PF_ROUTE messages */
#define PFROUTE_BUFFER_SIZE 4096
typedef struct addr_entry_t addr_entry_t;
/**
@ -55,9 +55,6 @@ struct addr_entry_t {
/** virtual IP managed by us */
bool virtual;
/** Number of times this IP is used, if virtual */
u_int refcount;
};
/**
@ -197,25 +194,45 @@ struct private_kernel_pfroute_net_t
hashtable_t *addrs;
/**
* mutex to lock access to the PF_ROUTE socket
* List of tun devices we installed for virtual IPs
*/
mutex_t *mutex_pfroute;
linked_list_t *tuns;
/**
* mutex to communicate exclusively with PF_KEY
*/
mutex_t *mutex;
/**
* condvar to signal if PF_KEY query got a response
*/
condvar_t *condvar;
/**
* pid to send PF_ROUTE messages with
*/
pid_t pid;
/**
* PF_ROUTE socket to communicate with the kernel
*/
int socket;
/**
* PF_ROUTE socket to receive events
*/
int socket_events;
/**
* sequence number for messages sent to the kernel
*/
int seq;
/**
* Sequence number a query is waiting for
*/
int waiting_seq;
/**
* Allocated reply message from kernel
*/
struct rt_msghdr *reply;
/**
* time of last roam event
*/
@ -295,33 +312,91 @@ static void fire_roam_event(private_kernel_pfroute_net_t *this, bool address)
}
}
/**
* Data for enumerator over rtmsg sockaddrs
*/
typedef struct {
/** implements enumerator */
enumerator_t public;
/** copy of attribute bitfield */
int types;
/** bytes remaining in buffer */
int remaining;
/** next sockaddr to enumerate */
struct sockaddr *addr;
} rt_enumerator_t;
METHOD(enumerator_t, rt_enumerate, bool,
rt_enumerator_t *this, int *xtype, struct sockaddr **addr)
{
int i, type;
if (this->remaining < sizeof(this->addr->sa_len) ||
this->remaining < this->addr->sa_len)
{
return FALSE;
}
for (i = 0; i < RTAX_MAX; i++)
{
type = (1 << i);
if (this->types & type)
{
this->types &= ~type;
*addr = this->addr;
*xtype = i;
this->remaining -= this->addr->sa_len;
this->addr = (void*)this->addr + this->addr->sa_len;
return TRUE;
}
}
return FALSE;
}
/**
* Create a safe enumerator over sockaddrs in ifa/ifam/rt_msg
*/
static enumerator_t *create_rtmsg_enumerator(void *hdr, size_t hdrlen)
{
struct rt_msghdr *rthdr = hdr;
rt_enumerator_t *this;
INIT(this,
.public = {
.enumerate = (void*)_rt_enumerate,
.destroy = (void*)free,
},
.types = rthdr->rtm_addrs,
.remaining = rthdr->rtm_msglen - hdrlen,
.addr = hdr + hdrlen,
);
return &this->public;
}
/**
* Process an RTM_*ADDR message from the kernel
*/
static void process_addr(private_kernel_pfroute_net_t *this,
struct rt_msghdr *msg)
struct ifa_msghdr *ifa)
{
struct ifa_msghdr *ifa = (struct ifa_msghdr*)msg;
sockaddr_t *sockaddr = (sockaddr_t*)(ifa + 1);
struct sockaddr *sockaddr;
host_t *host = NULL;
enumerator_t *ifaces, *addrs;
iface_entry_t *iface;
addr_entry_t *addr;
bool found = FALSE, changed = FALSE, roam = FALSE;
int i;
enumerator_t *enumerator;
int type;
for (i = 1; i < (1 << RTAX_MAX); i <<= 1)
enumerator = create_rtmsg_enumerator(ifa, sizeof(*ifa));
while (enumerator->enumerate(enumerator, &type, &sockaddr))
{
if (ifa->ifam_addrs & i)
if (type == RTAX_IFA)
{
if (RTA_IFA & i)
{
host = host_create_from_sockaddr(sockaddr);
break;
}
sockaddr = (sockaddr_t*)((char*)sockaddr + sockaddr->sa_len);
host = host_create_from_sockaddr(sockaddr);
break;
}
}
enumerator->destroy(enumerator);
if (!host)
{
@ -352,21 +427,16 @@ static void process_addr(private_kernel_pfroute_net_t *this,
addr_map_entry_remove(addr, iface, this);
addr_entry_destroy(addr);
}
else if (ifa->ifam_type == RTM_NEWADDR && addr->virtual)
{
addr->refcount = 1;
}
}
}
addrs->destroy(addrs);
if (!found && ifa->ifam_type == RTM_NEWADDR)
{
INIT(addr,
.ip = host->clone(host),
);
changed = TRUE;
addr = malloc_thing(addr_entry_t);
addr->ip = host->clone(host);
addr->virtual = FALSE;
addr->refcount = 1;
iface->addrs->insert_last(iface->addrs, addr);
addr_map_entry_add(this, addr, iface);
if (iface->usable)
@ -392,16 +462,55 @@ static void process_addr(private_kernel_pfroute_net_t *this,
}
}
/**
* Re-initialize address list of an interface if it changes state
*/
static void repopulate_iface(private_kernel_pfroute_net_t *this,
iface_entry_t *iface)
{
struct ifaddrs *ifap, *ifa;
addr_entry_t *addr;
while (iface->addrs->remove_last(iface->addrs, (void**)&addr) == SUCCESS)
{
addr_map_entry_remove(addr, iface, this);
addr_entry_destroy(addr);
}
if (getifaddrs(&ifap) == 0)
{
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr && streq(ifa->ifa_name, iface->ifname))
{
switch (ifa->ifa_addr->sa_family)
{
case AF_INET:
case AF_INET6:
INIT(addr,
.ip = host_create_from_sockaddr(ifa->ifa_addr),
);
iface->addrs->insert_last(iface->addrs, addr);
addr_map_entry_add(this, addr, iface);
break;
default:
break;
}
}
}
freeifaddrs(ifap);
}
}
/**
* Process an RTM_IFINFO message from the kernel
*/
static void process_link(private_kernel_pfroute_net_t *this,
struct rt_msghdr *hdr)
struct if_msghdr *msg)
{
struct if_msghdr *msg = (struct if_msghdr*)hdr;
enumerator_t *enumerator;
iface_entry_t *iface;
bool roam = FALSE;
bool roam = FALSE, found = FALSE;;
this->lock->write_lock(this->lock);
enumerator = this->ifaces->create_enumerator(this->ifaces);
@ -423,10 +532,33 @@ static void process_link(private_kernel_pfroute_net_t *this,
}
}
iface->flags = msg->ifm_flags;
repopulate_iface(this, iface);
found = TRUE;
break;
}
}
enumerator->destroy(enumerator);
if (!found)
{
INIT(iface,
.ifindex = msg->ifm_index,
.flags = msg->ifm_flags,
.addrs = linked_list_create(),
);
if (if_indextoname(iface->ifindex, iface->ifname))
{
DBG1(DBG_KNL, "interface %s appeared", iface->ifname);
iface->usable = hydra->kernel_interface->is_interface_usable(
hydra->kernel_interface, iface->ifname);
repopulate_iface(this, iface);
this->ifaces->insert_last(this->ifaces, iface);
}
else
{
free(iface);
}
}
this->lock->unlock(this->lock);
if (roam)
@ -445,17 +577,23 @@ static void process_route(private_kernel_pfroute_net_t *this,
}
/**
* Receives events from kernel
* Receives PF_ROUTE messages from kernel
*/
static job_requeue_t receive_events(private_kernel_pfroute_net_t *this)
{
unsigned char buf[PFROUTE_BUFFER_SIZE];
struct rt_msghdr *msg = (struct rt_msghdr*)buf;
int len;
struct {
union {
struct rt_msghdr rtm;
struct if_msghdr ifm;
struct ifa_msghdr ifam;
};
char buf[sizeof(struct sockaddr_storage) * RTAX_MAX];
} msg;
int len, hdrlen;
bool oldstate;
oldstate = thread_cancelability(TRUE);
len = recvfrom(this->socket_events, buf, sizeof(buf), 0, NULL, 0);
len = recv(this->socket, &msg, sizeof(msg), 0);
thread_cancelability(oldstate);
if (len < 0)
@ -463,10 +601,7 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this)
switch (errno)
{
case EINTR:
/* interrupted, try again */
return JOB_REQUEUE_DIRECT;
case EAGAIN:
/* no data ready, select again */
return JOB_REQUEUE_DIRECT;
default:
DBG1(DBG_KNL, "unable to receive from PF_ROUTE event socket");
@ -475,30 +610,67 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this)
}
}
if (len < sizeof(msg->rtm_msglen) || len < msg->rtm_msglen ||
msg->rtm_version != RTM_VERSION)
if (len < offsetof(struct rt_msghdr, rtm_flags) || len < msg.rtm.rtm_msglen)
{
DBG2(DBG_KNL, "received corrupted PF_ROUTE message");
DBG1(DBG_KNL, "received invalid PF_ROUTE message");
return JOB_REQUEUE_DIRECT;
}
switch (msg->rtm_type)
if (msg.rtm.rtm_version != RTM_VERSION)
{
DBG1(DBG_KNL, "received PF_ROUTE message with unsupported version: %d",
msg.rtm.rtm_version);
return JOB_REQUEUE_DIRECT;
}
switch (msg.rtm.rtm_type)
{
case RTM_NEWADDR:
case RTM_DELADDR:
process_addr(this, msg);
hdrlen = sizeof(msg.ifam);
break;
case RTM_IFINFO:
/*case RTM_IFANNOUNCE <- what about this*/
process_link(this, msg);
hdrlen = sizeof(msg.ifm);
break;
case RTM_ADD:
case RTM_DELETE:
process_route(this, msg);
case RTM_GET:
hdrlen = sizeof(msg.rtm);
break;
default:
return JOB_REQUEUE_DIRECT;
}
if (msg.rtm.rtm_msglen < hdrlen)
{
DBG1(DBG_KNL, "ignoring short PF_ROUTE message");
return JOB_REQUEUE_DIRECT;
}
switch (msg.rtm.rtm_type)
{
case RTM_NEWADDR:
case RTM_DELADDR:
process_addr(this, &msg.ifam);
break;
case RTM_IFINFO:
process_link(this, &msg.ifm);
break;
case RTM_ADD:
case RTM_DELETE:
process_route(this, &msg.rtm);
break;
default:
break;
}
this->mutex->lock(this->mutex);
if (msg.rtm.rtm_pid == this->pid && msg.rtm.rtm_seq == this->waiting_seq)
{
/* seems like the message someone is waiting for, deliver */
this->reply = realloc(this->reply, msg.rtm.rtm_msglen);
memcpy(this->reply, &msg, msg.rtm.rtm_msglen);
}
/* signal on any event, add_ip()/del_ip() might wait for it */
this->condvar->broadcast(this->condvar);
this->mutex->unlock(this->mutex);
return JOB_REQUEUE_DIRECT;
}
@ -530,6 +702,10 @@ static bool filter_addresses(address_enumerator_t *data,
{ /* skip virtual interfaces added by us */
return FALSE;
}
if (!(data->which & ADDR_TYPE_REGULAR) && !(*in)->virtual)
{ /* address is regular, but not requested */
return FALSE;
}
ip = (*in)->ip;
if (ip->get_family(ip) == AF_INET6)
{
@ -578,9 +754,12 @@ static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in,
METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
private_kernel_pfroute_net_t *this, kernel_address_type_t which)
{
address_enumerator_t *data = malloc_thing(address_enumerator_t);
data->this = this;
data->which = which;
address_enumerator_t *data;
INIT(data,
.this = this,
.which = which,
);
this->lock->read_lock(this->lock);
return enumerator_create_nested(
@ -591,6 +770,12 @@ METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
(void*)address_enumerator_destroy);
}
METHOD(kernel_net_t, get_features, kernel_feature_t,
private_kernel_pfroute_net_t *this)
{
return KERNEL_REQUIRE_EXCLUDE_ROUTE;
}
METHOD(kernel_net_t, get_interface_name, bool,
private_kernel_pfroute_net_t *this, host_t* ip, char **name)
{
@ -633,38 +818,352 @@ METHOD(kernel_net_t, get_source_addr, host_t*,
return NULL;
}
METHOD(kernel_net_t, get_nexthop, host_t*,
private_kernel_pfroute_net_t *this, host_t *dest, host_t *src)
{
return NULL;
}
METHOD(kernel_net_t, add_ip, status_t,
private_kernel_pfroute_net_t *this, host_t *virtual_ip, int prefix,
char *iface)
private_kernel_pfroute_net_t *this, host_t *vip, int prefix,
char *ifname)
{
return FAILED;
enumerator_t *ifaces, *addrs;
iface_entry_t *iface;
addr_entry_t *addr;
tun_device_t *tun;
bool timeout = FALSE;
tun = tun_device_create(NULL);
if (!tun)
{
return FAILED;
}
if (prefix == -1)
{
prefix = vip->get_address(vip).len * 8;
}
if (!tun->set_address(tun, vip, prefix) || !tun->up(tun))
{
tun->destroy(tun);
return FAILED;
}
/* wait until address appears */
this->mutex->lock(this->mutex);
while (!timeout && !get_interface_name(this, vip, NULL))
{
timeout = this->condvar->timed_wait(this->condvar, this->mutex, 1000);
}
this->mutex->unlock(this->mutex);
if (timeout)
{
DBG1(DBG_KNL, "virtual IP %H did not appear on %s",
vip, tun->get_name(tun));
tun->destroy(tun);
return FAILED;
}
this->lock->write_lock(this->lock);
this->tuns->insert_last(this->tuns, tun);
ifaces = this->ifaces->create_enumerator(this->ifaces);
while (ifaces->enumerate(ifaces, &iface))
{
if (streq(iface->ifname, tun->get_name(tun)))
{
addrs = iface->addrs->create_enumerator(iface->addrs);
while (addrs->enumerate(addrs, &addr))
{
if (addr->ip->ip_equals(addr->ip, vip))
{
addr->virtual = TRUE;
}
}
addrs->destroy(addrs);
}
}
ifaces->destroy(ifaces);
this->lock->unlock(this->lock);
return SUCCESS;
}
METHOD(kernel_net_t, del_ip, status_t,
private_kernel_pfroute_net_t *this, host_t *virtual_ip, int prefix,
private_kernel_pfroute_net_t *this, host_t *vip, int prefix,
bool wait)
{
return FAILED;
enumerator_t *enumerator;
tun_device_t *tun;
host_t *addr;
bool timeout = FALSE, found = FALSE;
this->lock->write_lock(this->lock);
enumerator = this->tuns->create_enumerator(this->tuns);
while (enumerator->enumerate(enumerator, &tun))
{
addr = tun->get_address(tun, NULL);
if (addr && addr->ip_equals(addr, vip))
{
this->tuns->remove_at(this->tuns, enumerator);
tun->destroy(tun);
found = TRUE;
break;
}
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
if (!found)
{
return NOT_FOUND;
}
/* wait until address disappears */
if (wait)
{
this->mutex->lock(this->mutex);
while (!timeout && get_interface_name(this, vip, NULL))
{
timeout = this->condvar->timed_wait(this->condvar, this->mutex, 1000);
}
this->mutex->unlock(this->mutex);
if (timeout)
{
DBG1(DBG_KNL, "virtual IP %H did not disappear from tun", vip);
return FAILED;
}
}
return SUCCESS;
}
/**
* Append a sockaddr_in/in6 of given type to routing message
*/
static void add_rt_addr(struct rt_msghdr *hdr, int type, host_t *addr)
{
if (addr)
{
int len;
len = *addr->get_sockaddr_len(addr);
memcpy((char*)hdr + hdr->rtm_msglen, addr->get_sockaddr(addr), len);
hdr->rtm_msglen += len;
hdr->rtm_addrs |= type;
}
}
/**
* Append a subnet mask sockaddr using the given prefix to routing message
*/
static void add_rt_mask(struct rt_msghdr *hdr, int type, int family, int prefix)
{
host_t *mask;
mask = host_create_netmask(family, prefix);
if (mask)
{
add_rt_addr(hdr, type, mask);
mask->destroy(mask);
}
}
/**
* Append an interface name sockaddr_dl to routing message
*/
static void add_rt_ifname(struct rt_msghdr *hdr, int type, char *name)
{
struct sockaddr_dl sdl = {
.sdl_len = sizeof(struct sockaddr_dl),
.sdl_family = AF_LINK,
.sdl_nlen = strlen(name),
};
if (strlen(name) <= sizeof(sdl.sdl_data))
{
memcpy(sdl.sdl_data, name, sdl.sdl_nlen);
memcpy((char*)hdr + hdr->rtm_msglen, &sdl, sdl.sdl_len);
hdr->rtm_msglen += sdl.sdl_len;
hdr->rtm_addrs |= type;
}
}
/**
* Add or remove a route
*/
static status_t manage_route(private_kernel_pfroute_net_t *this, int op,
chunk_t dst_net, u_int8_t prefixlen,
host_t *gateway, char *if_name)
{
struct {
struct rt_msghdr hdr;
char buf[sizeof(struct sockaddr_storage) * RTAX_MAX];
} msg = {
.hdr = {
.rtm_version = RTM_VERSION,
.rtm_type = op,
.rtm_flags = RTF_UP | RTF_STATIC,
.rtm_pid = this->pid,
.rtm_seq = ++this->seq,
},
};
host_t *dst;
int type;
if (prefixlen == 0 && dst_net.len)
{
status_t status;
chunk_t half;
half = chunk_clonea(dst_net);
half.ptr[0] |= 0x80;
prefixlen = 1;
status = manage_route(this, op, half, prefixlen, gateway, if_name);
if (status != SUCCESS)
{
return status;
}
}
dst = host_create_from_chunk(AF_UNSPEC, dst_net, 0);
if (!dst)
{
return FAILED;
}
if ((dst->get_family(dst) == AF_INET && prefixlen == 32) ||
(dst->get_family(dst) == AF_INET6 && prefixlen == 128))
{
msg.hdr.rtm_flags |= RTF_HOST | RTF_GATEWAY;
}
msg.hdr.rtm_msglen = sizeof(struct rt_msghdr);
for (type = 0; type < RTAX_MAX; type++)
{
switch (type)
{
case RTAX_DST:
add_rt_addr(&msg.hdr, RTA_DST, dst);
break;
case RTAX_NETMASK:
if (!(msg.hdr.rtm_flags & RTF_HOST))
{
add_rt_mask(&msg.hdr, RTA_NETMASK,
dst->get_family(dst), prefixlen);
}
break;
case RTAX_GATEWAY:
/* interface name seems to replace gateway on OS X */
if (if_name)
{
add_rt_ifname(&msg.hdr, RTA_GATEWAY, if_name);
}
else if (gateway)
{
add_rt_addr(&msg.hdr, RTA_GATEWAY, gateway);
}
break;
default:
break;
}
}
dst->destroy(dst);
if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) != msg.hdr.rtm_msglen)
{
DBG1(DBG_KNL, "%s PF_ROUTE route failed: %s",
op == RTM_ADD ? "adding" : "deleting", strerror(errno));
return FAILED;
}
return SUCCESS;
}
METHOD(kernel_net_t, add_route, status_t,
private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
{
return FAILED;
return manage_route(this, RTM_ADD, dst_net, prefixlen, gateway, if_name);
}
METHOD(kernel_net_t, del_route, status_t,
private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
{
return FAILED;
return manage_route(this, RTM_DELETE, dst_net, prefixlen, gateway, if_name);
}
METHOD(kernel_net_t, get_nexthop, host_t*,
private_kernel_pfroute_net_t *this, host_t *dest, host_t *src)
{
struct {
struct rt_msghdr hdr;
char buf[sizeof(struct sockaddr_storage) * RTAX_MAX];
} msg = {
.hdr = {
.rtm_version = RTM_VERSION,
.rtm_type = RTM_GET,
.rtm_pid = this->pid,
.rtm_seq = ++this->seq,
},
};
host_t *hop = NULL;
enumerator_t *enumerator;
struct sockaddr *addr;
int type;
msg.hdr.rtm_msglen = sizeof(struct rt_msghdr);
for (type = 0; type < RTAX_MAX; type++)
{
switch (type)
{
case RTAX_DST:
add_rt_addr(&msg.hdr, RTA_DST, dest);
break;
case RTAX_IFA:
add_rt_addr(&msg.hdr, RTA_IFA, src);
break;
default:
break;
}
}
this->mutex->lock(this->mutex);
while (this->waiting_seq)
{
this->condvar->wait(this->condvar, this->mutex);
}
this->waiting_seq = msg.hdr.rtm_seq;
if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) == msg.hdr.rtm_msglen)
{
while (TRUE)
{
if (this->condvar->timed_wait(this->condvar, this->mutex, 1000))
{ /* timed out? */
break;
}
if (this->reply->rtm_msglen < sizeof(*this->reply) ||
msg.hdr.rtm_seq != this->reply->rtm_seq)
{
continue;
}
enumerator = create_rtmsg_enumerator(this->reply,
sizeof(*this->reply));
while (enumerator->enumerate(enumerator, &type, &addr))
{
if (type == RTAX_GATEWAY)
{
hop = host_create_from_sockaddr(addr);
break;
}
}
enumerator->destroy(enumerator);
break;
}
}
else
{
DBG1(DBG_KNL, "PF_ROUTE lookup failed: %s", strerror(errno));
}
/* signal completion of query to a waiting thread */
this->waiting_seq = 0;
this->condvar->signal(this->condvar);
this->mutex->unlock(this->mutex);
return hop;
}
/**
@ -711,22 +1210,22 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this)
if (!iface)
{
iface = malloc_thing(iface_entry_t);
INIT(iface,
.ifindex = if_nametoindex(ifa->ifa_name),
.flags = ifa->ifa_flags,
.addrs = linked_list_create(),
.usable = hydra->kernel_interface->is_interface_usable(
hydra->kernel_interface, ifa->ifa_name),
);
memcpy(iface->ifname, ifa->ifa_name, IFNAMSIZ);
iface->ifindex = if_nametoindex(ifa->ifa_name);
iface->flags = ifa->ifa_flags;
iface->addrs = linked_list_create();
iface->usable = hydra->kernel_interface->is_interface_usable(
hydra->kernel_interface, ifa->ifa_name);
this->ifaces->insert_last(this->ifaces, iface);
}
if (ifa->ifa_addr->sa_family != AF_LINK)
{
addr = malloc_thing(addr_entry_t);
addr->ip = host_create_from_sockaddr(ifa->ifa_addr);
addr->virtual = FALSE;
addr->refcount = 1;
INIT(addr,
.ip = host_create_from_sockaddr(ifa->ifa_addr),
);
iface->addrs->insert_last(iface->addrs, addr);
addr_map_entry_add(this, addr, iface);
}
@ -760,14 +1259,10 @@ METHOD(kernel_net_t, destroy, void,
enumerator_t *enumerator;
addr_entry_t *addr;
if (this->socket > 0)
if (this->socket != -1)
{
close(this->socket);
}
if (this->socket_events)
{
close(this->socket_events);
}
enumerator = this->addrs->create_enumerator(this->addrs);
while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
{
@ -776,8 +1271,11 @@ METHOD(kernel_net_t, destroy, void,
enumerator->destroy(enumerator);
this->addrs->destroy(this->addrs);
this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
this->tuns->destroy(this->tuns);
this->lock->destroy(this->lock);
this->mutex_pfroute->destroy(this->mutex_pfroute);
this->mutex->destroy(this->mutex);
this->condvar->destroy(this->condvar);
free(this->reply);
free(this);
}
@ -787,11 +1285,11 @@ METHOD(kernel_net_t, destroy, void,
kernel_pfroute_net_t *kernel_pfroute_net_create()
{
private_kernel_pfroute_net_t *this;
bool register_for_events = TRUE;
INIT(this,
.public = {
.interface = {
.get_features = _get_features,
.get_interface = _get_interface_name,
.create_address_enumerator = _create_address_enumerator,
.get_source_addr = _get_source_addr,
@ -803,45 +1301,42 @@ kernel_pfroute_net_t *kernel_pfroute_net_create()
.destroy = _destroy,
},
},
.pid = getpid(),
.ifaces = linked_list_create(),
.addrs = hashtable_create(
(hashtable_hash_t)addr_map_entry_hash,
(hashtable_equals_t)addr_map_entry_equals, 16),
.tuns = linked_list_create(),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
.mutex_pfroute = mutex_create(MUTEX_TYPE_DEFAULT),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
);
if (streq(hydra->daemon, "starter"))
{ /* starter has no threads, so we do not register for kernel events */
register_for_events = FALSE;
}
/* create a PF_ROUTE socket to communicate with the kernel */
this->socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if (this->socket < 0)
if (this->socket == -1)
{
DBG1(DBG_KNL, "unable to create PF_ROUTE socket");
destroy(this);
return NULL;
}
if (register_for_events)
if (streq(hydra->daemon, "starter"))
{
/* create a PF_ROUTE socket to receive events */
this->socket_events = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if (this->socket_events < 0)
/* starter has no threads, so we do not register for kernel events */
if (shutdown(this->socket, SHUT_RD) != 0)
{
DBG1(DBG_KNL, "unable to create PF_ROUTE event socket");
destroy(this);
return NULL;
DBG1(DBG_KNL, "closing read end of PF_ROUTE socket failed: %s",
strerror(errno));
}
}
else
{
lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create_with_prio(
(callback_job_cb_t)receive_events, this, NULL,
(callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
}
if (init_address_list(this) != SUCCESS)
{
DBG1(DBG_KNL, "unable to get interface list");

View File

@ -54,6 +54,15 @@ struct private_host_t {
socklen_t socklen;
};
/**
* Update the sockaddr internal sa_len option, if available
*/
static inline void update_sa_len(private_host_t *this)
{
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
this->address.sa_len = this->socklen;
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
}
METHOD(host_t, get_sockaddr, sockaddr_t*,
private_host_t *this)
@ -102,7 +111,7 @@ int host_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
{
snprintf(buffer, sizeof(buffer), "(null)");
}
else if (is_anyaddr(this) && !spec->plus)
else if (is_anyaddr(this) && !spec->plus && !spec->hash)
{
snprintf(buffer, sizeof(buffer), "%%any%s",
this->address.sa_family == AF_INET6 ? "6" : "");
@ -264,26 +273,6 @@ static bool ip_equals(private_host_t *this, private_host_t *other)
return FALSE;
}
/**
* Implements host_t.get_differences
*/
static host_diff_t get_differences(host_t *this, host_t *other)
{
host_diff_t ret = HOST_DIFF_NONE;
if (!this->ip_equals(this, other))
{
ret |= HOST_DIFF_ADDR;
}
if (this->get_port(this) != other->get_port(other))
{
ret |= HOST_DIFF_PORT;
}
return ret;
}
/**
* Implements host_t.equals
*/
@ -332,7 +321,6 @@ static private_host_t *host_create_empty(void)
.get_address = _get_address,
.get_port = _get_port,
.set_port = _set_port,
.get_differences = get_differences,
.ip_equals = (bool (*)(host_t *,host_t *))ip_equals,
.equals = (bool (*)(host_t *,host_t *)) equals,
.is_anyaddr = _is_anyaddr,
@ -440,6 +428,7 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr)
memcpy(&this->address4, (struct sockaddr_in*)sockaddr,
sizeof(struct sockaddr_in));
this->socklen = sizeof(struct sockaddr_in);
update_sa_len(this);
return &this->public;
}
case AF_INET6:
@ -447,6 +436,7 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr)
memcpy(&this->address6, (struct sockaddr_in6*)sockaddr,
sizeof(struct sockaddr_in6));
this->socklen = sizeof(struct sockaddr_in6);
update_sa_len(this);
return &this->public;
}
default:
@ -529,6 +519,7 @@ host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port)
this->socklen = sizeof(struct sockaddr_in6);
break;
}
update_sa_len(this);
return &this->public;
}
@ -567,6 +558,55 @@ host_t *host_create_from_subnet(char *string, int *bits)
return net;
}
/*
* See header.
*/
host_t *host_create_netmask(int family, int netbits)
{
private_host_t *this;
int bits, bytes, len = 0;
char *target;
switch (family)
{
case AF_INET:
if (netbits < 0 || netbits > 32)
{
return NULL;
}
this = host_create_empty();
this->socklen = sizeof(struct sockaddr_in);
target = (char*)&this->address4.sin_addr;
len = 4;
break;
case AF_INET6:
if (netbits < 0 || netbits > 128)
{
return NULL;
}
this = host_create_empty();
this->socklen = sizeof(struct sockaddr_in6);
target = (char*)&this->address6.sin6_addr;
len = 16;
break;
default:
return NULL;
}
memset(&this->address_max, 0, sizeof(struct sockaddr_storage));
this->address.sa_family = family;
update_sa_len(this);
bytes = (netbits + 7) / 8;
bits = (bytes * 8) - netbits;
memset(target, 0xff, bytes);
memset(target + bytes, 0x00, len - bytes);
target[bytes - 1] = bits ? (u_int8_t)(0xff << bits) : 0xff;
return &this->public;
}
/*
* Described in header.
*/
@ -582,11 +622,13 @@ host_t *host_create_any(int family)
case AF_INET:
{
this->socklen = sizeof(struct sockaddr_in);
update_sa_len(this);
return &(this->public);
}
case AF_INET6:
{
this->socklen = sizeof(struct sockaddr_in6);
update_sa_len(this);
return &this->public;
}
default:

View File

@ -36,16 +36,6 @@ typedef struct host_t host_t;
#include <utils/chunk.h>
/**
* Differences between two hosts. They differ in
* address, port, or both.
*/
enum host_diff_t {
HOST_DIFF_NONE = 0,
HOST_DIFF_ADDR = 1,
HOST_DIFF_PORT = 2,
};
/**
* Representates a Host
*
@ -136,14 +126,6 @@ struct host_t {
*/
bool (*equals) (host_t *this, host_t *other);
/**
* Compare two hosts and return the differences.
*
* @param other the other to compare
* @return differences in a combination of host_diff_t's
*/
host_diff_t (*get_differences) (host_t *this, host_t *other);
/**
* Destroy this host object.
*/
@ -209,6 +191,14 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr);
*/
host_t *host_create_from_subnet(char *string, int *bits);
/**
* Create a netmask host having the first netbits bits set.
*
* @param netbits number of leading bits set in the host
* @return netmask host
*/
host_t *host_create_netmask(int family, int netbits);
/**
* Create a host without an address, a "any" host.
*

View File

@ -73,63 +73,28 @@ struct private_tun_device_t {
* The current MTU
*/
int mtu;
/**
* Associated address
*/
host_t *address;
/**
* Netmask for address
*/
u_int8_t netmask;
};
/**
* Set the sockaddr_t from the given netmask
*/
static void set_netmask(struct ifreq *ifr, int family, u_int8_t netmask)
{
int len, bytes, bits;
char *target;
switch (family)
{
case AF_INET:
{
struct sockaddr_in *addr = (struct sockaddr_in*)&ifr->ifr_addr;
target = (char*)&addr->sin_addr;
len = 4;
break;
}
case AF_INET6:
{
struct sockaddr_in6 *addr = (struct sockaddr_in6*)&ifr->ifr_addr;
target = (char*)&addr->sin6_addr;
len = 16;
break;
}
default:
return;
}
ifr->ifr_addr.sa_family = family;
bytes = (netmask + 7) / 8;
bits = (bytes * 8) - netmask;
memset(target, 0xff, bytes);
memset(target + bytes, 0x00, len - bytes);
target[bytes - 1] = bits ? (u_int8_t)(0xff << bits) : 0xff;
}
METHOD(tun_device_t, set_address, bool,
private_tun_device_t *this, host_t *addr, u_int8_t netmask)
{
struct ifreq ifr;
int family;
family = addr->get_family(addr);
if ((netmask > 32 && family == AF_INET) || netmask > 128)
{
DBG1(DBG_LIB, "failed to set address on %s: invalid netmask",
this->if_name);
return FALSE;
}
host_t *mask;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr), sizeof(sockaddr_t));
memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr),
*addr->get_sockaddr_len(addr));
if (ioctl(this->sock, SIOCSIFADDR, &ifr) < 0)
{
@ -146,7 +111,15 @@ METHOD(tun_device_t, set_address, bool,
}
#endif /* __APPLE__ */
set_netmask(&ifr, family, netmask);
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)
{
@ -154,9 +127,21 @@ METHOD(tun_device_t, set_address, bool,
this->if_name, strerror(errno));
return FALSE;
}
this->address = addr->clone(addr);
this->netmask = netmask;
return TRUE;
}
METHOD(tun_device_t, get_address, host_t*,
private_tun_device_t *this, u_int8_t *netmask)
{
if (netmask && this->address)
{
*netmask = this->netmask;
}
return this->address;
}
METHOD(tun_device_t, up, bool,
private_tun_device_t *this)
{
@ -229,6 +214,12 @@ METHOD(tun_device_t, get_name, char*,
return this->if_name;
}
METHOD(tun_device_t, get_fd, int,
private_tun_device_t *this)
{
return this->tunfd;
}
METHOD(tun_device_t, write_packet, bool,
private_tun_device_t *this, chunk_t packet)
{
@ -308,6 +299,7 @@ METHOD(tun_device_t, destroy, void,
{
close(this->sock);
}
DESTROY_IF(this->address);
free(this);
}
@ -435,7 +427,9 @@ tun_device_t *tun_device_create(const char *name_tmpl)
.get_mtu = _get_mtu,
.set_mtu = _set_mtu,
.get_name = _get_name,
.get_fd = _get_fd,
.set_address = _set_address,
.get_address = _get_address,
.up = _up,
.destroy = _destroy,
},

View File

@ -65,6 +65,14 @@ struct tun_device_t {
*/
bool (*set_address)(tun_device_t *this, host_t *addr, u_int8_t netmask);
/**
* Get the IP address previously assigned to using set_address().
*
* @param netmask pointer receiving the configured netmask, or NULL
* @return address previously set, NULL if none
*/
host_t* (*get_address)(tun_device_t *this, u_int8_t *netmask);
/**
* Bring the TUN device up
*
@ -94,6 +102,13 @@ struct tun_device_t {
*/
char *(*get_name)(tun_device_t *this);
/**
* Get the underlying tun file descriptor.
*
* @return file descriptor of this tun device
*/
int (*get_fd)(tun_device_t *this);
/**
* Destroy a tun_device_t
*/

View File

@ -21,6 +21,11 @@
#ifndef THREADING_SEMAPHORE_H_
#define THREADING_SEMAPHORE_H_
#ifdef __APPLE__
/* Mach uses a semaphore_create() call, use a different name for ours */
#define semaphore_create(x) strongswan_semaphore_create(x)
#endif /* __APPLE__ */
typedef struct semaphore_t semaphore_t;
/**