kernel-netlink: Implement passthrough type routes and use them on Linux

Enables us to ignore any future kernel features for routes unless
we actually need to consider them for the source IP routes.

Also enables us to actually really skip IPsec processing for those networks
(because even the routes don't touch those packets). It's more what
users expect.

Co-authored-by: Tobias Brunner <tobias@strongswan.org>
This commit is contained in:
Noel Kuntze 2020-02-09 14:52:32 +01:00 committed by Tobias Brunner
parent 4958acc0c2
commit 09f4bccfea
11 changed files with 124 additions and 80 deletions

View File

@ -256,14 +256,14 @@ METHOD(kernel_net_t, del_ip, status_t,
METHOD(kernel_net_t, add_route, status_t,
private_android_net_t *this, chunk_t dst_net, uint8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
host_t *gateway, host_t *src_ip, char *if_name, bool pass)
{
return NOT_SUPPORTED;
}
METHOD(kernel_net_t, del_route, status_t,
private_android_net_t *this, chunk_t dst_net, uint8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
host_t *gateway, host_t *src_ip, char *if_name, bool pass)
{
return NOT_SUPPORTED;
}

View File

@ -614,26 +614,28 @@ METHOD(kernel_interface_t, del_ip, status_t,
METHOD(kernel_interface_t, add_route, status_t,
private_kernel_interface_t *this, chunk_t dst_net,
uint8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name)
uint8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name,
bool pass)
{
if (!this->net)
{
return NOT_SUPPORTED;
}
return this->net->add_route(this->net, dst_net, prefixlen, gateway,
src_ip, if_name);
src_ip, if_name, pass);
}
METHOD(kernel_interface_t, del_route, status_t,
private_kernel_interface_t *this, chunk_t dst_net,
uint8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name)
uint8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name,
bool pass)
{
if (!this->net)
{
return NOT_SUPPORTED;
}
return this->net->del_route(this->net, dst_net, prefixlen, gateway,
src_ip, if_name);
src_ip, if_name, pass);
}
METHOD(kernel_interface_t, bypass_socket, bool,

View File

@ -375,12 +375,13 @@ struct kernel_interface_t {
* @param gateway gateway for this route
* @param src_ip source ip of the route
* @param if_name name of the interface the route is bound to
* @param pass TRUE if route is installed for passthrough policy
* @return SUCCESS if operation completed
* ALREADY_DONE if the route already exists
*/
status_t (*add_route) (kernel_interface_t *this, chunk_t dst_net,
uint8_t prefixlen, host_t *gateway, host_t *src_ip,
char *if_name);
char *if_name, bool pass);
/**
* Delete a route.
@ -390,11 +391,12 @@ struct kernel_interface_t {
* @param gateway gateway for this route
* @param src_ip source ip of the route
* @param if_name name of the interface the route is bound to
* @param pass TRUE if route was installed for passthrough policy
* @return SUCCESS if operation completed
*/
status_t (*del_route) (kernel_interface_t *this, chunk_t dst_net,
uint8_t prefixlen, host_t *gateway, host_t *src_ip,
char *if_name);
char *if_name, bool pass);
/**
* Set up a bypass policy for a given socket.

View File

@ -165,12 +165,13 @@ struct kernel_net_t {
* @param gateway gateway for this route
* @param src_ip source ip of the route
* @param if_name name of the interface the route is bound to
* @param pass TRUE if route is installed for passthrough policy
* @return SUCCESS if operation completed
* ALREADY_DONE if the route already exists
*/
status_t (*add_route) (kernel_net_t *this, chunk_t dst_net,
uint8_t prefixlen, host_t *gateway, host_t *src_ip,
char *if_name);
char *if_name, bool pass);
/**
* Delete a route.
@ -180,11 +181,12 @@ struct kernel_net_t {
* @param gateway gateway for this route
* @param src_ip source ip of the route
* @param if_name name of the interface the route is bound to
* @param pass TRUE if route was installed for passthrough policy
* @return SUCCESS if operation completed
*/
status_t (*del_route) (kernel_net_t *this, chunk_t dst_net,
uint8_t prefixlen, host_t *gateway, host_t *src_ip,
char *if_name);
char *if_name, bool pass);
/**
* Destroy the implementation.

View File

@ -715,14 +715,14 @@ static status_t manage_route(private_kernel_iph_net_t *this, bool add,
METHOD(kernel_net_t, add_route, status_t,
private_kernel_iph_net_t *this, chunk_t dst, uint8_t prefixlen,
host_t *gateway, host_t *src, char *name)
host_t *gateway, host_t *src, char *name, bool pass)
{
return manage_route(this, TRUE, dst, prefixlen, gateway, name);
}
METHOD(kernel_net_t, del_route, status_t,
private_kernel_iph_net_t *this, chunk_t dst, uint8_t prefixlen,
host_t *gateway, host_t *src, char *name)
host_t *gateway, host_t *src, char *name, bool pass)
{
return manage_route(this, FALSE, dst, prefixlen, gateway, name);
}

View File

@ -315,7 +315,7 @@ static void add_exclude_route(private_kernel_libipsec_ipsec_t *this,
if (charon->kernel->get_interface(charon->kernel, src, &if_name) &&
charon->kernel->add_route(charon->kernel, dst->get_address(dst),
dst->get_family(dst) == AF_INET ? 32 : 128,
gtw, src, if_name) == SUCCESS)
gtw, src, if_name, TRUE) == SUCCESS)
{
INIT(exclude,
.dst = dst->clone(dst),
@ -363,7 +363,7 @@ static void remove_exclude_route(private_kernel_libipsec_ipsec_t *this,
charon->kernel->del_route(charon->kernel, dst->get_address(dst),
dst->get_family(dst) == AF_INET ? 32 : 128,
route->exclude->gtw, route->exclude->src,
if_name) != SUCCESS)
if_name, TRUE) != SUCCESS)
{
DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst);
}
@ -449,8 +449,8 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this,
}
/* uninstall previously installed route */
if (charon->kernel->del_route(charon->kernel, old->dst_net,
old->prefixlen, old->gateway,
old->src_ip, old->if_name) != SUCCESS)
old->prefixlen, old->gateway, old->src_ip,
old->if_name, FALSE) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with policy "
"%R === %R %N", src_ts, dst_ts, policy_dir_names,
@ -481,7 +481,7 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this,
switch (charon->kernel->add_route(charon->kernel, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name))
route->src_ip, route->if_name, FALSE))
{
case ALREADY_DONE:
/* route exists, do not uninstall */
@ -586,8 +586,8 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
route_entry_t *route = policy->route;
if (charon->kernel->del_route(charon->kernel, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name) != SUCCESS)
route->prefixlen, route->gateway, route->src_ip,
route->if_name, FALSE) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with "
"policy %R === %R %N", id->src_ts, id->dst_ts,
@ -618,7 +618,7 @@ METHOD(kernel_ipsec_t, flush_policies, status_t,
charon->kernel->del_route(charon->kernel, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name);
route->src_ip, route->if_name, FALSE);
remove_exclude_route(this, route);
}
policy_entry_destroy(pol);

View File

@ -390,6 +390,9 @@ struct route_entry_t {
/** Destination net prefixlen */
uint8_t prefixlen;
/** Whether the route was installed for a passthrough policy */
bool pass;
};
/**
@ -410,6 +413,7 @@ static void route_entry_destroy(route_entry_t *this)
static bool route_entry_equals(route_entry_t *a, route_entry_t *b)
{
if (a->if_name && b->if_name && streq(a->if_name, b->if_name) &&
a->pass == b->pass &&
a->src_ip->ip_equals(a->src_ip, b->src_ip) &&
chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen)
{
@ -2614,6 +2618,7 @@ static void install_route(private_kernel_netlink_ipsec_t *this,
INIT(route,
.prefixlen = policy->sel.prefixlen_d,
.pass = mapping->type == POLICY_PASS,
);
if (charon->kernel->get_address_by_ts(charon->kernel, out->src_ts,
@ -2664,7 +2669,8 @@ static void install_route(private_kernel_netlink_ipsec_t *this,
/* uninstall previously installed route */
if (charon->kernel->del_route(charon->kernel, old->dst_net,
old->prefixlen, old->gateway,
old->src_ip, old->if_name) != SUCCESS)
old->src_ip, old->if_name,
old->pass) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with policy "
"%R === %R %N", out->src_ts, out->dst_ts, policy_dir_names,
@ -2678,7 +2684,8 @@ static void install_route(private_kernel_netlink_ipsec_t *this,
route->gateway, route->src_ip, route->if_name);
switch (charon->kernel->add_route(charon->kernel, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name))
route->src_ip, route->if_name,
route->pass))
{
default:
DBG1(DBG_KNL, "unable to install source route for %H",
@ -3207,7 +3214,8 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
route_entry_t *route = current->route;
if (charon->kernel->del_route(charon->kernel, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name) != SUCCESS)
route->src_ip, route->if_name,
route->pass) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with policy "
"%R === %R %N%s", id->src_ts, id->dst_ts, policy_dir_names,

View File

@ -286,6 +286,9 @@ struct route_entry_t {
/** Destination net prefixlen */
uint8_t prefixlen;
/** Whether the route was installed for a passthrough policy */
bool pass;
};
/**
@ -301,6 +304,7 @@ static route_entry_t *route_entry_clone(route_entry_t *this)
.gateway = this->gateway ? this->gateway->clone(this->gateway) : NULL,
.dst_net = chunk_clone(this->dst_net),
.prefixlen = this->prefixlen,
.pass = this->pass,
);
return route;
}
@ -332,6 +336,7 @@ static u_int route_entry_hash(route_entry_t *this)
static bool route_entry_equals(route_entry_t *a, route_entry_t *b)
{
if (a->if_name && b->if_name && streq(a->if_name, b->if_name) &&
a->pass == b->pass &&
a->src_ip->ip_equals(a->src_ip, b->src_ip) &&
chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen)
{
@ -544,7 +549,7 @@ struct private_kernel_netlink_net_t {
static status_t manage_srcroute(private_kernel_netlink_net_t *this,
int nlmsg_type, int flags, chunk_t dst_net,
uint8_t prefixlen, host_t *gateway,
host_t *src_ip, char *if_name);
host_t *src_ip, char *if_name, bool pass);
/**
* Clear the queued network changes.
@ -580,6 +585,10 @@ static job_requeue_t reinstall_routes(private_kernel_netlink_net_t *this)
net_change_t *change, lookup = {
.if_name = route->if_name,
};
if (route->pass)
{ /* no need to reinstall these, they don't reference interfaces */
continue;
}
/* check if a change for the outgoing interface is queued */
change = this->net_changes->get(this->net_changes, &lookup);
if (!change)
@ -598,7 +607,7 @@ static job_requeue_t reinstall_routes(private_kernel_netlink_net_t *this)
{
manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL,
route->dst_net, route->prefixlen, route->gateway,
route->src_ip, route->if_name);
route->src_ip, route->if_name, route->pass);
}
}
enumerator->destroy(enumerator);
@ -2632,7 +2641,7 @@ METHOD(kernel_net_t, del_ip, status_t,
static status_t manage_srcroute(private_kernel_netlink_net_t *this,
int nlmsg_type, int flags, chunk_t dst_net,
uint8_t prefixlen, host_t *gateway,
host_t *src_ip, char *if_name)
host_t *src_ip, char *if_name, bool pass)
{
netlink_buf_t request;
struct nlmsghdr *hdr;
@ -2653,12 +2662,12 @@ static status_t manage_srcroute(private_kernel_netlink_net_t *this,
half_net = chunk_alloca(dst_net.len);
memset(half_net.ptr, 0, half_net.len);
half_prefixlen = 1;
/* no throw routes in the main table */
status = manage_srcroute(this, nlmsg_type, flags, half_net,
half_prefixlen, gateway, src_ip, if_name);
half_prefixlen, gateway, src_ip, if_name, FALSE);
half_net.ptr[0] |= 0x80;
status |= manage_srcroute(this, nlmsg_type, flags, half_net,
half_prefixlen, gateway, src_ip, if_name);
half_prefixlen, gateway, src_ip, if_name, FALSE);
return status;
}
@ -2670,10 +2679,10 @@ static status_t manage_srcroute(private_kernel_netlink_net_t *this,
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
msg = NLMSG_DATA(hdr);
msg->rtm_family = src_ip->get_family(src_ip);
msg->rtm_family = (dst_net.len == 4) ? AF_INET : AF_INET6;
msg->rtm_dst_len = prefixlen;
msg->rtm_protocol = RTPROT_STATIC;
msg->rtm_type = RTN_UNICAST;
msg->rtm_type = pass ? RTN_THROW : RTN_UNICAST;
msg->rtm_scope = RT_SCOPE_UNIVERSE;
if (this->routing_table < 256)
@ -2691,42 +2700,48 @@ static status_t manage_srcroute(private_kernel_netlink_net_t *this,
#endif /* HAVE_RTA_TABLE */
}
netlink_add_attribute(hdr, RTA_DST, dst_net, sizeof(request));
chunk = src_ip->get_address(src_ip);
netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request));
if (gateway && gateway->get_family(gateway) == src_ip->get_family(src_ip))
{
chunk = gateway->get_address(gateway);
netlink_add_attribute(hdr, RTA_GATEWAY, chunk, sizeof(request));
}
ifindex = get_interface_index(this, if_name);
chunk.ptr = (char*)&ifindex;
chunk.len = sizeof(ifindex);
netlink_add_attribute(hdr, RTA_OIF, chunk, sizeof(request));
if (this->mtu || this->mss)
/* only when installing regular routes do we need all the parameters,
* deletes are done by destination net (except if metrics are used, which
* we don't support), for throw routes we don't need any of them either */
if (nlmsg_type == RTM_NEWROUTE && !pass)
{
chunk = chunk_alloca(RTA_LENGTH((sizeof(struct rtattr) +
sizeof(uint32_t)) * 2));
chunk.len = 0;
rta = (struct rtattr*)chunk.ptr;
if (this->mtu)
chunk = src_ip->get_address(src_ip);
netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request));
if (gateway && gateway->get_family(gateway) == src_ip->get_family(src_ip))
{
rta->rta_type = RTAX_MTU;
rta->rta_len = RTA_LENGTH(sizeof(uint32_t));
memcpy(RTA_DATA(rta), &this->mtu, sizeof(uint32_t));
chunk.len = rta->rta_len;
chunk = gateway->get_address(gateway);
netlink_add_attribute(hdr, RTA_GATEWAY, chunk, sizeof(request));
}
if (this->mss)
{
rta = (struct rtattr*)(chunk.ptr + RTA_ALIGN(chunk.len));
rta->rta_type = RTAX_ADVMSS;
rta->rta_len = RTA_LENGTH(sizeof(uint32_t));
memcpy(RTA_DATA(rta), &this->mss, sizeof(uint32_t));
chunk.len = RTA_ALIGN(chunk.len) + rta->rta_len;
}
netlink_add_attribute(hdr, RTA_METRICS, chunk, sizeof(request));
}
ifindex = get_interface_index(this, if_name);
chunk.ptr = (char*)&ifindex;
chunk.len = sizeof(ifindex);
netlink_add_attribute(hdr, RTA_OIF, chunk, sizeof(request));
if (this->mtu || this->mss)
{
chunk = chunk_alloca(RTA_LENGTH((sizeof(struct rtattr) +
sizeof(uint32_t)) * 2));
chunk.len = 0;
rta = (struct rtattr*)chunk.ptr;
if (this->mtu)
{
rta->rta_type = RTAX_MTU;
rta->rta_len = RTA_LENGTH(sizeof(uint32_t));
memcpy(RTA_DATA(rta), &this->mtu, sizeof(uint32_t));
chunk.len = rta->rta_len;
}
if (this->mss)
{
rta = (struct rtattr*)(chunk.ptr + RTA_ALIGN(chunk.len));
rta->rta_type = RTAX_ADVMSS;
rta->rta_len = RTA_LENGTH(sizeof(uint32_t));
memcpy(RTA_DATA(rta), &this->mss, sizeof(uint32_t));
chunk.len = RTA_ALIGN(chunk.len) + rta->rta_len;
}
netlink_add_attribute(hdr, RTA_METRICS, chunk, sizeof(request));
}
}
return this->socket->send_ack(this->socket, hdr);
}
@ -2769,7 +2784,7 @@ static bool route_with_dst(route_entry_lookup_t *a, route_entry_t *b)
METHOD(kernel_net_t, add_route, status_t,
private_kernel_netlink_net_t *this, chunk_t dst_net, uint8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
host_t *gateway, host_t *src_ip, char *if_name, bool pass)
{
status_t status;
route_entry_t *found;
@ -2780,10 +2795,16 @@ METHOD(kernel_net_t, add_route, status_t,
.gateway = gateway,
.src_ip = src_ip,
.if_name = if_name,
.pass = pass,
},
.this = this,
};
if (!this->routing_table)
{ /* treat these as regular routes if installing in the main table */
pass = lookup.route.pass = FALSE;
}
this->routes_lock->lock(this->routes_lock);
found = this->routes->get(this->routes, &lookup.route);
if (found)
@ -2808,7 +2829,8 @@ METHOD(kernel_net_t, add_route, status_t,
else
{
status = manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE,
dst_net, prefixlen, gateway, src_ip, if_name);
dst_net, prefixlen, gateway, src_ip, if_name,
pass);
}
if (status == SUCCESS)
{
@ -2821,7 +2843,7 @@ METHOD(kernel_net_t, add_route, status_t,
METHOD(kernel_net_t, del_route, status_t,
private_kernel_netlink_net_t *this, chunk_t dst_net, uint8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
host_t *gateway, host_t *src_ip, char *if_name, bool pass)
{
status_t status;
route_entry_t *found;
@ -2832,10 +2854,16 @@ METHOD(kernel_net_t, del_route, status_t,
.gateway = gateway,
.src_ip = src_ip,
.if_name = if_name,
.pass = pass,
},
.this = this,
};
if (!this->routing_table)
{ /* treat these as regular routes if installing in the main table */
pass = lookup.route.pass = FALSE;
}
this->routes_lock->lock(this->routes_lock);
found = this->routes->remove(this->routes, &lookup.route);
if (!found)
@ -2860,12 +2888,12 @@ METHOD(kernel_net_t, del_route, status_t,
{
status = manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE,
found->dst_net, found->prefixlen, found->gateway,
found->src_ip, found->if_name);
found->src_ip, found->if_name, found->pass);
}
else
{
status = manage_srcroute(this, RTM_DELROUTE, 0, dst_net, prefixlen,
gateway, src_ip, if_name);
gateway, src_ip, if_name, pass);
}
this->routes_lock->unlock(this->routes_lock);
return status;
@ -3111,7 +3139,8 @@ METHOD(kernel_net_t, destroy, void,
while (enumerator->enumerate(enumerator, NULL, (void**)&route))
{
manage_srcroute(this, RTM_DELROUTE, 0, route->dst_net, route->prefixlen,
route->gateway, route->src_ip, route->if_name);
route->gateway, route->src_ip, route->if_name,
route->pass);
route_entry_destroy(route);
}
enumerator->destroy(enumerator);

View File

@ -2332,7 +2332,7 @@ static void add_exclude_route(private_kernel_pfkey_ipsec_t *this,
charon->kernel->add_route(charon->kernel,
dst->get_address(dst),
dst->get_family(dst) == AF_INET ? 32 : 128,
gtw, src, if_name) == SUCCESS)
gtw, src, if_name, FALSE) == SUCCESS)
{
INIT(exclude,
.dst = dst->clone(dst),
@ -2399,7 +2399,7 @@ static void remove_exclude_route(private_kernel_pfkey_ipsec_t *this,
dst->get_address(dst),
dst->get_family(dst) == AF_INET ? 32 : 128,
route->exclude->gtw, route->exclude->src,
if_name) != SUCCESS)
if_name, FALSE) != SUCCESS)
{
DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst);
}
@ -2479,8 +2479,8 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
}
/* uninstall previously installed route */
if (charon->kernel->del_route(charon->kernel, old->dst_net,
old->prefixlen, old->gateway,
old->src_ip, old->if_name) != SUCCESS)
old->prefixlen, old->gateway,
old->src_ip, old->if_name, FALSE) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with policy "
"%R === %R %N", out->src_ts, out->dst_ts,
@ -2512,7 +2512,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this,
switch (charon->kernel->add_route(charon->kernel, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name))
route->src_ip, route->if_name, FALSE))
{
case ALREADY_DONE:
/* route exists, do not uninstall */
@ -3067,8 +3067,8 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
{
route_entry_t *route = policy->route;
if (charon->kernel->del_route(charon->kernel, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name) != SUCCESS)
route->prefixlen, route->gateway,
route->src_ip, route->if_name, FALSE) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with "
"policy %R === %R %N", id->src_ts, id->dst_ts,

View File

@ -1494,7 +1494,7 @@ static status_t manage_route(private_kernel_pfroute_net_t *this, int op,
METHOD(kernel_net_t, add_route, status_t,
private_kernel_pfroute_net_t *this, chunk_t dst_net, uint8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
host_t *gateway, host_t *src_ip, char *if_name, bool pass)
{
status_t status;
route_entry_t *found, route = {
@ -1523,7 +1523,7 @@ METHOD(kernel_net_t, add_route, status_t,
METHOD(kernel_net_t, del_route, status_t,
private_kernel_pfroute_net_t *this, chunk_t dst_net, uint8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
host_t *gateway, host_t *src_ip, char *if_name, bool pass)
{
status_t status;
route_entry_t *found, route = {

View File

@ -1402,7 +1402,8 @@ static bool uninstall_route(private_kernel_wfp_ipsec_t *this,
if (charon->kernel->get_interface(charon->kernel, src, &name))
{
res = charon->kernel->del_route(charon->kernel,
dst->get_address(dst), mask, gtw, src, name) == SUCCESS;
dst->get_address(dst), mask, gtw, src,
name, FALSE) == SUCCESS;
free(name);
}
route = this->routes->remove(this->routes, route);
@ -1446,8 +1447,8 @@ static bool install_route(private_kernel_wfp_ipsec_t *this,
{
if (charon->kernel->get_interface(charon->kernel, src, &name))
{
if (charon->kernel->add_route(charon->kernel,
dst->get_address(dst), mask, gtw, src, name) == SUCCESS)
if (charon->kernel->add_route(charon->kernel, dst->get_address(dst),
mask, gtw, src, name, FALSE) == SUCCESS)
{
INIT(route,
.dst = dst->clone(dst),