Merge branch 'throw-type-routes'
Implements simpler routes for passthrough policies on Linux, which basically act as fallbacks on routes in other routing tables. This way they require less information (e.g. no interface or source IP) and can be installed earlier and are not affected by updates. Closes strongswan/strongswan#165. Fixes #3118.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -370,55 +370,6 @@ struct private_kernel_netlink_ipsec_t {
|
||||
kernel_ipsec_manage_policy_t *data);
|
||||
};
|
||||
|
||||
typedef struct route_entry_t route_entry_t;
|
||||
|
||||
/**
|
||||
* Installed routing entry
|
||||
*/
|
||||
struct route_entry_t {
|
||||
/** Name of the interface the route is bound to */
|
||||
char *if_name;
|
||||
|
||||
/** Source ip of the route */
|
||||
host_t *src_ip;
|
||||
|
||||
/** Gateway for this route */
|
||||
host_t *gateway;
|
||||
|
||||
/** Destination net */
|
||||
chunk_t dst_net;
|
||||
|
||||
/** Destination net prefixlen */
|
||||
uint8_t prefixlen;
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy a route_entry_t object
|
||||
*/
|
||||
static void route_entry_destroy(route_entry_t *this)
|
||||
{
|
||||
free(this->if_name);
|
||||
this->src_ip->destroy(this->src_ip);
|
||||
DESTROY_IF(this->gateway);
|
||||
chunk_free(&this->dst_net);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two route_entry_t objects
|
||||
*/
|
||||
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->src_ip->ip_equals(a->src_ip, b->src_ip) &&
|
||||
chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen)
|
||||
{
|
||||
return (!a->gateway && !b->gateway) || (a->gateway && b->gateway &&
|
||||
a->gateway->ip_equals(a->gateway, b->gateway));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
typedef struct ipsec_sa_t ipsec_sa_t;
|
||||
|
||||
/**
|
||||
@@ -2614,89 +2565,97 @@ 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,
|
||||
&route->src_ip, NULL) == SUCCESS)
|
||||
&route->src_ip, NULL) != SUCCESS)
|
||||
{
|
||||
if (!ipsec->dst->is_anyaddr(ipsec->dst))
|
||||
if (!route->pass)
|
||||
{
|
||||
route->gateway = charon->kernel->get_nexthop(charon->kernel,
|
||||
ipsec->dst, -1, ipsec->src,
|
||||
&route->if_name);
|
||||
free(route);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{ /* for shunt policies */
|
||||
iface = xfrm2host(policy->sel.family, &policy->sel.daddr, 0);
|
||||
route->gateway = charon->kernel->get_nexthop(charon->kernel,
|
||||
iface, policy->sel.prefixlen_d,
|
||||
route->src_ip, &route->if_name);
|
||||
iface->destroy(iface);
|
||||
}
|
||||
route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
|
||||
memcpy(route->dst_net.ptr, &policy->sel.daddr, route->dst_net.len);
|
||||
/* allow blank source IP for passthrough policies */
|
||||
route->src_ip = host_create_any(policy->sel.family);
|
||||
}
|
||||
|
||||
/* get the interface to install the route for, if we haven't one yet.
|
||||
* If we have a local address, use it. Otherwise (for shunt policies)
|
||||
* use the route's source address. */
|
||||
if (!route->if_name)
|
||||
{
|
||||
iface = ipsec->src;
|
||||
if (iface->is_anyaddr(iface))
|
||||
{
|
||||
iface = route->src_ip;
|
||||
}
|
||||
if (!charon->kernel->get_interface(charon->kernel, iface,
|
||||
&route->if_name))
|
||||
{
|
||||
route_entry_destroy(route);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (policy->route)
|
||||
{
|
||||
route_entry_t *old = policy->route;
|
||||
if (route_entry_equals(old, route))
|
||||
{
|
||||
route_entry_destroy(route);
|
||||
return;
|
||||
}
|
||||
/* 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)
|
||||
{
|
||||
DBG1(DBG_KNL, "error uninstalling route installed with policy "
|
||||
"%R === %R %N", out->src_ts, out->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", out->dst_ts,
|
||||
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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (!ipsec->dst->is_anyaddr(ipsec->dst))
|
||||
{
|
||||
route->gateway = charon->kernel->get_nexthop(charon->kernel,
|
||||
ipsec->dst, -1, ipsec->src,
|
||||
&route->if_name);
|
||||
}
|
||||
else
|
||||
{ /* for shunt policies */
|
||||
iface = xfrm2host(policy->sel.family, &policy->sel.daddr, 0);
|
||||
route->gateway = charon->kernel->get_nexthop(charon->kernel,
|
||||
iface, policy->sel.prefixlen_d,
|
||||
route->src_ip, &route->if_name);
|
||||
iface->destroy(iface);
|
||||
}
|
||||
route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
|
||||
memcpy(route->dst_net.ptr, &policy->sel.daddr, route->dst_net.len);
|
||||
|
||||
/* get the interface to install the route for, if we haven't one yet.
|
||||
* If we have a local address, use it. Otherwise (for shunt policies)
|
||||
* use the route's source address. */
|
||||
if (!route->if_name)
|
||||
{
|
||||
free(route);
|
||||
iface = ipsec->src;
|
||||
if (iface->is_anyaddr(iface))
|
||||
{
|
||||
iface = route->src_ip;
|
||||
}
|
||||
if (!charon->kernel->get_interface(charon->kernel, iface,
|
||||
&route->if_name) &&
|
||||
!route->pass)
|
||||
{ /* don't require an interface for passthrough policies */
|
||||
route_entry_destroy(route);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (policy->route)
|
||||
{
|
||||
route_entry_t *old = policy->route;
|
||||
if (route_entry_equals(old, route))
|
||||
{
|
||||
route_entry_destroy(route);
|
||||
return;
|
||||
}
|
||||
/* uninstall previously installed route */
|
||||
if (charon->kernel->del_route(charon->kernel, old->dst_net,
|
||||
old->prefixlen, old->gateway,
|
||||
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,
|
||||
policy->direction);
|
||||
}
|
||||
route_entry_destroy(old);
|
||||
policy->route = NULL;
|
||||
}
|
||||
|
||||
DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", out->dst_ts,
|
||||
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->pass))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3207,7 +3166,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,
|
||||
|
||||
@@ -266,81 +266,6 @@ static bool addr_map_entry_match(addr_map_entry_t *a, addr_map_entry_t *b)
|
||||
return a->ip->ip_equals(a->ip, b->ip);
|
||||
}
|
||||
|
||||
typedef struct route_entry_t route_entry_t;
|
||||
|
||||
/**
|
||||
* Installed routing entry
|
||||
*/
|
||||
struct route_entry_t {
|
||||
/** Name of the interface the route is bound to */
|
||||
char *if_name;
|
||||
|
||||
/** Source ip of the route */
|
||||
host_t *src_ip;
|
||||
|
||||
/** Gateway for this route */
|
||||
host_t *gateway;
|
||||
|
||||
/** Destination net */
|
||||
chunk_t dst_net;
|
||||
|
||||
/** Destination net prefixlen */
|
||||
uint8_t prefixlen;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone a route_entry_t object.
|
||||
*/
|
||||
static route_entry_t *route_entry_clone(route_entry_t *this)
|
||||
{
|
||||
route_entry_t *route;
|
||||
|
||||
INIT(route,
|
||||
.if_name = strdup(this->if_name),
|
||||
.src_ip = this->src_ip->clone(this->src_ip),
|
||||
.gateway = this->gateway ? this->gateway->clone(this->gateway) : NULL,
|
||||
.dst_net = chunk_clone(this->dst_net),
|
||||
.prefixlen = this->prefixlen,
|
||||
);
|
||||
return route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a route_entry_t object
|
||||
*/
|
||||
static void route_entry_destroy(route_entry_t *this)
|
||||
{
|
||||
free(this->if_name);
|
||||
DESTROY_IF(this->src_ip);
|
||||
DESTROY_IF(this->gateway);
|
||||
chunk_free(&this->dst_net);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a route_entry_t object
|
||||
*/
|
||||
static u_int route_entry_hash(route_entry_t *this)
|
||||
{
|
||||
return chunk_hash_inc(chunk_from_thing(this->prefixlen),
|
||||
chunk_hash(this->dst_net));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two route_entry_t objects
|
||||
*/
|
||||
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->src_ip->ip_equals(a->src_ip, b->src_ip) &&
|
||||
chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen)
|
||||
{
|
||||
return (!a->gateway && !b->gateway) || (a->gateway && b->gateway &&
|
||||
a->gateway->ip_equals(a->gateway, b->gateway));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
typedef struct net_change_t net_change_t;
|
||||
|
||||
/**
|
||||
@@ -544,7 +469,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 +505,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 || !route->if_name)
|
||||
{ /* 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 +527,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 +2561,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 +2582,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 +2599,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 +2620,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 +2704,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 +2715,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 +2749,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 +2763,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 +2774,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 +2808,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 +3059,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);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (C) 2014 Martin Willi
|
||||
* Copyright (C) 2014 revosec AG
|
||||
*
|
||||
* Copyright (C) 2008-2019 Tobias Brunner
|
||||
* Copyright (C) 2008-2020 Tobias Brunner
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -767,3 +767,63 @@ void *netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len)
|
||||
}
|
||||
return RTA_DATA(rta);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
void route_entry_destroy(route_entry_t *this)
|
||||
{
|
||||
free(this->if_name);
|
||||
DESTROY_IF(this->src_ip);
|
||||
DESTROY_IF(this->gateway);
|
||||
chunk_free(&this->dst_net);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
route_entry_t *route_entry_clone(const route_entry_t *this)
|
||||
{
|
||||
route_entry_t *route;
|
||||
|
||||
INIT(route,
|
||||
.if_name = strdupnull(this->if_name),
|
||||
.src_ip = this->src_ip ? this->src_ip->clone(this->src_ip) : NULL,
|
||||
.gateway = this->gateway ? this->gateway->clone(this->gateway) : NULL,
|
||||
.dst_net = chunk_clone(this->dst_net),
|
||||
.prefixlen = this->prefixlen,
|
||||
.pass = this->pass,
|
||||
);
|
||||
return route;
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
u_int route_entry_hash(const route_entry_t *this)
|
||||
{
|
||||
return chunk_hash_inc(chunk_from_thing(this->prefixlen),
|
||||
chunk_hash(this->dst_net));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two IP addresses, also accept it if both are NULL
|
||||
*/
|
||||
static bool addrs_null_or_equal(host_t *a, host_t *b)
|
||||
{
|
||||
return (!a && !b) || (a && b && a->ip_equals(a, b));
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
bool route_entry_equals(const route_entry_t *a, const route_entry_t *b)
|
||||
{
|
||||
return streq(a->if_name, b->if_name) &&
|
||||
a->pass == b->pass &&
|
||||
a->prefixlen == b->prefixlen &&
|
||||
chunk_equals(a->dst_net, b->dst_net) &&
|
||||
addrs_null_or_equal(a->src_ip, b->src_ip) &&
|
||||
addrs_null_or_equal(a->gateway, b->gateway);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2019 Tobias Brunner
|
||||
* Copyright (C) 2008-2020 Tobias Brunner
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -130,4 +130,50 @@ void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len);
|
||||
*/
|
||||
u_int netlink_get_buflen();
|
||||
|
||||
/**
|
||||
* Information about an installed route.
|
||||
*/
|
||||
struct route_entry_t {
|
||||
|
||||
/** Destination net */
|
||||
chunk_t dst_net;
|
||||
|
||||
/** Destination net prefix length */
|
||||
uint8_t prefixlen;
|
||||
|
||||
/** Name of the interface the route is bound to (optional) */
|
||||
char *if_name;
|
||||
|
||||
/** Source IP of the route (virtual IP or %any) */
|
||||
host_t *src_ip;
|
||||
|
||||
/** Gateway for this route (optional) */
|
||||
host_t *gateway;
|
||||
|
||||
/** Whether the route was installed for a passthrough policy */
|
||||
bool pass;
|
||||
};
|
||||
|
||||
typedef struct route_entry_t route_entry_t;
|
||||
|
||||
/**
|
||||
* Destroy a route entry.
|
||||
*/
|
||||
void route_entry_destroy(route_entry_t *this);
|
||||
|
||||
/**
|
||||
* Clone a route entry.
|
||||
*/
|
||||
route_entry_t *route_entry_clone(const route_entry_t *this);
|
||||
|
||||
/**
|
||||
* Hash a route entry (note that this only hashes the destination).
|
||||
*/
|
||||
u_int route_entry_hash(const route_entry_t *this);
|
||||
|
||||
/**
|
||||
* Compare two route entries.
|
||||
*/
|
||||
bool route_entry_equals(const route_entry_t *a, const route_entry_t *b);
|
||||
|
||||
#endif /* KERNEL_NETLINK_SHARED_H_ */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user