Merge branch 'fwmarks'
Allows setting a mark on outbound packets and the routing rule installed by charon. With those settings it is possible to setup tunnels with kernel-libipsec where the remote peer is part of the remote traffic selector. The following example settings in strongswan.conf show how this can be configured: charon { plugins { kernel-netlink { fwmark = !0x42 } socket-default { fwmark = 0x42 } kernel-libipsec { allow_peer_ts = yes } } } To make it work it is necessary to set net.ipv4.conf.all.rp_filter appropriately, otherwise the kernel drops the packets. References #380.
This commit is contained in:
commit
1ff63f153e
|
@ -623,6 +623,18 @@ Number of ipsecN devices
|
|||
.BR charon.plugins.kernel-klips.ipsec_dev_mtu " [0]"
|
||||
Set MTU of ipsecN device
|
||||
.TP
|
||||
.BR charon.plugins.kernel-libipsec.allow_peer_ts " [no]"
|
||||
Allow that the remote traffic selector equals the IKE peer. The route installed
|
||||
for such traffic (via TUN device) usually prevents further IKE traffic. The
|
||||
fwmark options for the \fIkernel-netlink\fR and \fIsocket-default\fR plugins can
|
||||
be used to circumvent that problem.
|
||||
to
|
||||
.TP
|
||||
.BR charon.plugins.kernel-netlink.fwmark
|
||||
Firewall mark to set on the routing rule that directs traffic to our own routing
|
||||
table. The format is [!]mark[/mask], where the optional exclamation mark inverts
|
||||
the meaning (i.e. the rule only applies to packets that don't match the mark).
|
||||
.TP
|
||||
.BR charon.plugins.kernel-netlink.roam_events " [yes]"
|
||||
Whether to trigger roam events when interfaces, addresses or routes change
|
||||
.TP
|
||||
|
@ -656,6 +668,9 @@ is appended to this prefix to make it unique. The result has to be a valid
|
|||
interface name according to the rules defined by resolvconf. Also, it should
|
||||
have a high priority according to the order defined in interface-order(5).
|
||||
.TP
|
||||
.BR charon.plugins.socket-default.fwmark
|
||||
Firewall mark to set on outbound packets.
|
||||
.TP
|
||||
.BR charon.plugins.socket-default.set_source " [yes]"
|
||||
Set source address on outbound packets, if possible.
|
||||
.TP
|
||||
|
|
|
@ -50,6 +50,11 @@ struct private_kernel_libipsec_ipsec_t {
|
|||
* List of exclude routes (exclude_route_t)
|
||||
*/
|
||||
linked_list_t *excludes;
|
||||
|
||||
/**
|
||||
* Whether the remote TS may equal the IKE peer
|
||||
*/
|
||||
bool allow_peer_ts;
|
||||
};
|
||||
|
||||
typedef struct exclude_route_t exclude_route_t;
|
||||
|
@ -465,7 +470,7 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this,
|
|||
policy->route = NULL;
|
||||
}
|
||||
|
||||
if (dst_ts->is_host(dst_ts, dst))
|
||||
if (!this->allow_peer_ts && dst_ts->is_host(dst_ts, dst))
|
||||
{
|
||||
DBG1(DBG_KNL, "can't install route for %R === %R %N, conflicts with "
|
||||
"IKE traffic", src_ts, dst_ts, policy_dir_names,
|
||||
|
@ -475,7 +480,7 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this,
|
|||
return FALSE;
|
||||
}
|
||||
/* if remote traffic selector covers the IKE peer, add an exclude route */
|
||||
if (dst_ts->includes(dst_ts, dst))
|
||||
if (!this->allow_peer_ts && dst_ts->includes(dst_ts, dst))
|
||||
{
|
||||
/* add exclude route for peer */
|
||||
add_exclude_route(this, route, src, dst);
|
||||
|
@ -518,11 +523,6 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
|
|||
policy_entry_t *policy, *found = NULL;
|
||||
status_t status;
|
||||
|
||||
if (type != POLICY_IPSEC)
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
status = ipsec->policies->add_policy(ipsec->policies, src, dst, src_ts,
|
||||
dst_ts, direction, type, sa, mark, priority);
|
||||
if (status != SUCCESS)
|
||||
|
@ -694,6 +694,8 @@ kernel_libipsec_ipsec_t *kernel_libipsec_ipsec_create()
|
|||
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.policies = linked_list_create(),
|
||||
.excludes = linked_list_create(),
|
||||
.allow_peer_ts = lib->settings->get_bool(lib->settings,
|
||||
"%s.plugins.kernel-libipsec.allow_peer_ts", FALSE, hydra->daemon),
|
||||
);
|
||||
|
||||
ipsec->events->register_listener(ipsec->events, &this->ipsec_listener);
|
||||
|
|
|
@ -611,6 +611,24 @@ static int open_socket(private_socket_default_socket_t *this,
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
#ifdef SO_MARK
|
||||
{ /* set optional MARK on socket (requires CAP_NET_ADMIN) */
|
||||
char *fwmark;
|
||||
mark_t mark;
|
||||
|
||||
fwmark = lib->settings->get_str(lib->settings,
|
||||
"%s.plugins.socket-default.fwmark", NULL, charon->name);
|
||||
if (fwmark && mark_from_string(fwmark, &mark))
|
||||
{
|
||||
if (setsockopt(skt, SOL_SOCKET, SO_MARK, &mark.value,
|
||||
sizeof(mark.value)) < 0)
|
||||
{
|
||||
DBG1(DBG_NET, "unable to set SO_MARK on socket: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface,
|
||||
skt, family))
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/fib_rules.h>
|
||||
|
||||
#include "kernel_netlink_net.h"
|
||||
#include "kernel_netlink_shared.h"
|
||||
|
@ -2096,6 +2097,8 @@ static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type,
|
|||
struct nlmsghdr *hdr;
|
||||
struct rtmsg *msg;
|
||||
chunk_t chunk;
|
||||
char *fwmark;
|
||||
mark_t mark;
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
hdr = (struct nlmsghdr*)request;
|
||||
|
@ -2117,6 +2120,23 @@ static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type,
|
|||
chunk = chunk_from_thing(prio);
|
||||
netlink_add_attribute(hdr, RTA_PRIORITY, chunk, sizeof(request));
|
||||
|
||||
fwmark = lib->settings->get_str(lib->settings,
|
||||
"%s.plugins.kernel-netlink.fwmark", NULL, hydra->daemon);
|
||||
if (fwmark)
|
||||
{
|
||||
if (fwmark[0] == '!')
|
||||
{
|
||||
msg->rtm_flags |= FIB_RULE_INVERT;
|
||||
fwmark++;
|
||||
}
|
||||
if (mark_from_string(fwmark, &mark))
|
||||
{
|
||||
chunk = chunk_from_thing(mark.value);
|
||||
netlink_add_attribute(hdr, FRA_FWMARK, chunk, sizeof(request));
|
||||
chunk = chunk_from_thing(mark.mask);
|
||||
netlink_add_attribute(hdr, FRA_FWMASK, chunk, sizeof(request));
|
||||
}
|
||||
}
|
||||
return this->socket->send_ack(this->socket, hdr);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Tobias Brunner
|
||||
* Copyright (C) 2012-2013 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
@ -36,3 +36,38 @@ ENUM(ipcomp_transform_names, IPCOMP_NONE, IPCOMP_LZJH,
|
|||
"IPCOMP_LZS",
|
||||
"IPCOMP_LZJH"
|
||||
);
|
||||
|
||||
/*
|
||||
* See header
|
||||
*/
|
||||
bool mark_from_string(const char *value, mark_t *mark)
|
||||
{
|
||||
char *endptr;
|
||||
|
||||
if (!value)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
mark->value = strtoul(value, &endptr, 0);
|
||||
if (*endptr)
|
||||
{
|
||||
if (*endptr != '/')
|
||||
{
|
||||
DBG1(DBG_APP, "invalid mark value: %s", value);
|
||||
return FALSE;
|
||||
}
|
||||
mark->mask = strtoul(endptr+1, &endptr, 0);
|
||||
if (*endptr)
|
||||
{
|
||||
DBG1(DBG_LIB, "invalid mark mask: %s", endptr);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mark->mask = 0xffffffff;
|
||||
}
|
||||
/* apply the mask to ensure the value is in range */
|
||||
mark->value &= mark->mask;
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Tobias Brunner
|
||||
* Copyright (C) 2012-2013 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
@ -169,4 +169,13 @@ struct mark_t {
|
|||
*/
|
||||
#define MARK_REQID (0xFFFFFFFF)
|
||||
|
||||
/**
|
||||
* Try to parse a mark_t from the given string of the form <mark>[/<mask>].
|
||||
*
|
||||
* @param value string to parse
|
||||
* @param mark mark to fill
|
||||
* @return TRUE if parsing was successful
|
||||
*/
|
||||
bool mark_from_string(const char *value, mark_t *mark);
|
||||
|
||||
#endif /** IPSEC_TYPES_H_ @}*/
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <library.h>
|
||||
#include <utils/utils.h>
|
||||
#include <ipsec/ipsec_types.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
|
@ -442,6 +443,50 @@ START_TEST(test_time_delta_printf_hook)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/*******************************************************************************
|
||||
* mark_from_string
|
||||
*/
|
||||
|
||||
static struct {
|
||||
char *s;
|
||||
bool ok;
|
||||
mark_t m;
|
||||
} mark_data[] = {
|
||||
{NULL, FALSE, { 0 }},
|
||||
{"", TRUE, { 0, 0xffffffff }},
|
||||
{"/", TRUE, { 0, 0 }},
|
||||
{"42", TRUE, { 42, 0xffffffff }},
|
||||
{"0x42", TRUE, { 0x42, 0xffffffff }},
|
||||
{"x", FALSE, { 0 }},
|
||||
{"42/", TRUE, { 0, 0 }},
|
||||
{"42/0", TRUE, { 0, 0 }},
|
||||
{"42/x", FALSE, { 0 }},
|
||||
{"42/42", TRUE, { 42, 42 }},
|
||||
{"42/0xff", TRUE, { 42, 0xff }},
|
||||
{"0x42/0xff", TRUE, { 0x42, 0xff }},
|
||||
{"/0xff", TRUE, { 0, 0xff }},
|
||||
{"/x", FALSE, { 0 }},
|
||||
{"x/x", FALSE, { 0 }},
|
||||
{"0xffffffff/0x0000ffff", TRUE, { 0x0000ffff, 0x0000ffff }},
|
||||
{"0xffffffff/0xffffffff", TRUE, { 0xffffffff, 0xffffffff }},
|
||||
};
|
||||
|
||||
START_TEST(test_mark_from_string)
|
||||
{
|
||||
mark_t mark;
|
||||
|
||||
if (mark_from_string(mark_data[_i].s, &mark))
|
||||
{
|
||||
ck_assert_int_eq(mark.value, mark_data[_i].m.value);
|
||||
ck_assert_int_eq(mark.mask, mark_data[_i].m.mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
ck_assert(!mark_data[_i].ok);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *utils_suite_create()
|
||||
{
|
||||
Suite *s;
|
||||
|
@ -496,5 +541,9 @@ Suite *utils_suite_create()
|
|||
tcase_add_loop_test(tc, test_time_delta_printf_hook, 0, countof(time_delta_data));
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("mark_from_string");
|
||||
tcase_add_loop_test(tc, test_mark_from_string, 0, countof(mark_data));
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -375,47 +375,6 @@ static void handle_firewall(const char *label, starter_end_t *end,
|
|||
}
|
||||
}
|
||||
|
||||
static bool handle_mark(char *value, mark_t *mark)
|
||||
{
|
||||
char *sep, *endptr;
|
||||
|
||||
sep = strchr(value, '/');
|
||||
if (sep)
|
||||
{
|
||||
*sep = '\0';
|
||||
mark->mask = strtoul(sep+1, &endptr, 0);
|
||||
if (*endptr != '\0')
|
||||
{
|
||||
DBG1(DBG_APP, "# invalid mark mask: %s", sep+1);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mark->mask = 0xffffffff;
|
||||
}
|
||||
if (value == '\0')
|
||||
{
|
||||
mark->value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mark->value = strtoul(value, &endptr, 0);
|
||||
if (*endptr != '\0')
|
||||
{
|
||||
DBG1(DBG_APP, "# invalid mark value: %s", value);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (sep)
|
||||
{ /* restore the original text in case also= is used */
|
||||
*sep = '/';
|
||||
}
|
||||
/* apply the mask to ensure the value is in range */
|
||||
mark->value &= mark->mask;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse a conn section
|
||||
*/
|
||||
|
@ -522,7 +481,7 @@ static void load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg
|
|||
KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_COMPRESS)
|
||||
break;
|
||||
case KW_MARK:
|
||||
if (!handle_mark(kw->value, &conn->mark_in))
|
||||
if (!mark_from_string(kw->value, &conn->mark_in))
|
||||
{
|
||||
cfg->err++;
|
||||
break;
|
||||
|
@ -530,13 +489,13 @@ static void load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg
|
|||
conn->mark_out = conn->mark_in;
|
||||
break;
|
||||
case KW_MARK_IN:
|
||||
if (!handle_mark(kw->value, &conn->mark_in))
|
||||
if (!mark_from_string(kw->value, &conn->mark_in))
|
||||
{
|
||||
cfg->err++;
|
||||
}
|
||||
break;
|
||||
case KW_MARK_OUT:
|
||||
if (!handle_mark(kw->value, &conn->mark_out))
|
||||
if (!mark_from_string(kw->value, &conn->mark_out))
|
||||
{
|
||||
cfg->err++;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue