diff --git a/man/strongswan.conf.5.in b/man/strongswan.conf.5.in index 1df58a7ee..e8dbe63f8 100644 --- a/man/strongswan.conf.5.in +++ b/man/strongswan.conf.5.in @@ -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 diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c index 9d95aa7fd..d0744e300 100644 --- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c +++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c @@ -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); diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c index 3aa09bef8..ea976dfe9 100644 --- a/src/libcharon/plugins/socket_default/socket_default_socket.c +++ b/src/libcharon/plugins/socket_default/socket_default_socket.c @@ -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)) diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index 1b9e0f031..04dc22c00 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -44,6 +44,7 @@ #include #include #include +#include #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); } diff --git a/src/libstrongswan/ipsec/ipsec_types.c b/src/libstrongswan/ipsec/ipsec_types.c index e4e927313..4bbd918a0 100644 --- a/src/libstrongswan/ipsec/ipsec_types.c +++ b/src/libstrongswan/ipsec/ipsec_types.c @@ -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; +} diff --git a/src/libstrongswan/ipsec/ipsec_types.h b/src/libstrongswan/ipsec/ipsec_types.h index 32e55bc50..43f72ccaa 100644 --- a/src/libstrongswan/ipsec/ipsec_types.h +++ b/src/libstrongswan/ipsec/ipsec_types.h @@ -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 [/]. + * + * @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_ @}*/ diff --git a/src/libstrongswan/tests/test_utils.c b/src/libstrongswan/tests/test_utils.c index 6ab60a6fd..506b3a174 100644 --- a/src/libstrongswan/tests/test_utils.c +++ b/src/libstrongswan/tests/test_utils.c @@ -17,6 +17,7 @@ #include #include +#include #include @@ -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; } diff --git a/src/starter/confread.c b/src/starter/confread.c index cec9399e3..19178a2c3 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -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++; }