From 30238c949e7c1ba2df4f8adfaefdc205f7ddb98f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zoran=20Peri=C4=8Di=C4=87?= Date: Sun, 21 Jan 2024 03:11:32 +0100 Subject: [PATCH 4/4] Support GRE key in selectors with kernel-netlink. Implementation use two 2-byte port fields (from/to range) to store key similar to ICMP. --- .../kernel_netlink/kernel_netlink_ipsec.c | 19 +++++++++++++ .../plugins/load_tester/load_tester_config.c | 22 ++++++++++++++- src/libcharon/plugins/stroke/stroke_config.c | 22 ++++++++++++++- src/libcharon/plugins/vici/vici_config.c | 27 ++++++++++++++++++- .../selectors/traffic_selector.c | 20 ++++++++++++++ .../selectors/traffic_selector.h | 12 +++++++++ src/starter/confread.c | 24 ++++++++++++++++- src/swanctl/swanctl.opt | 3 +++ 8 files changed, 145 insertions(+), 4 deletions(-) diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index d951aa0737..dd279c24aa 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -893,6 +893,7 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src, { struct xfrm_selector sel; uint16_t port; + uint32_t gre_key; memset(&sel, 0, sizeof(sel)); sel.family = (src->get_type(src) == TS_IPV4_ADDR_RANGE) ? AF_INET : AF_INET6; @@ -913,6 +914,24 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src, sel.dport = htons(traffic_selector_icmp_code(port)); sel.dport_mask = sel.dport ? ~0 : 0; } + if (sel.proto == IPPROTO_GRE) + { + /* the kernel expects the GRE key in the source and destination + * port fields, respectively. */ + gre_key = htons(traffic_selector_gre_key(dst->get_from_port(dst), dst->get_to_port(dst))); + if ( gre_key != 0 ) + { + sel.sport = gre_key >> 16; + sel.sport_mask = ~0; + sel.dport = gre_key & 0xffff; + sel.dport_mask = ~0; + } else { + sel.sport = 0; + sel.sport_mask = 0; + sel.dport = 0; + sel.dport_mask = 0; + } + } sel.ifindex = interface ? if_nametoindex(interface) : 0; sel.user = 0; diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index 2a440aa630..bc9a1e40b9 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -507,7 +507,27 @@ static bool parse_protoport(char *token, uint16_t *from_port, *protocol = (uint8_t)p; } } - if (streq(port, "%any")) + if (*protocol == IPPROTO_GRE) + { + if (*port && !streq(port, "%any")) + { + p = strtol(port, &endptr, 0); + if (p < 0 || p > 0xffffffff) + { + return FALSE; + } + *from_port = (p >> 16) & 0xffff; + *to_port = p & 0xffff; + if (*endptr) + { + return FALSE; + } + } else { + *from_port = 0; + *to_port = 0; + } + } + else if (streq(port, "%any")) { *from_port = 0; *to_port = 0xffff; diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index b6e700fa9c..017e6b4766 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -926,7 +926,27 @@ static bool parse_protoport(char *token, uint16_t *from_port, *protocol = (uint8_t)p; } } - if (streq(port, "%any")) + if (*protocol == IPPROTO_GRE) + { + if (*port && !streq(port, "%any")) + { + p = strtol(port, &endptr, 0); + if (p < 0 || p > 0xffffffff) + { + return FALSE; + } + *from_port = (p >> 16) & 0xffff; + *to_port = p & 0xffff; + if (*endptr) + return FALSE; + } + else + { + *from_port = 0; + *to_port = 0; + } + } + else if (streq(port, "%any")) { *from_port = 0; *to_port = 0xffff; diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 6ea239f0a1..96c2d1276e 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -749,7 +749,27 @@ CALLBACK(parse_ts, bool, proto = (uint8_t)p; } } - if (streq(port, "opaque")) + if (proto == IPPROTO_GRE) + { + if (*port && !streq(port, "any")) + { + p = strtol(port, &end, 0); + if (p < 0 || p > 0xffffffff) + { + return FALSE; + } + from = (p >> 16) & 0xffff; + to = p & 0xffff; + if (*end) + { + return FALSE; + } + } else { + from = 0; + to = 0; + } + } + else if (streq(port, "opaque")) { from = 0xffff; to = 0; @@ -786,6 +806,11 @@ CALLBACK(parse_ts, bool, } } } + else if (proto == IPPROTO_GRE) + { + from = 0; + to = 0; + } if (streq(buf, "dynamic")) { ts = traffic_selector_create_dynamic(proto, from, to); diff --git a/src/libstrongswan/selectors/traffic_selector.c b/src/libstrongswan/selectors/traffic_selector.c index 4022c45c13..ae07e02995 100644 --- a/src/libstrongswan/selectors/traffic_selector.c +++ b/src/libstrongswan/selectors/traffic_selector.c @@ -205,6 +205,18 @@ static int print_icmp(printf_hook_data_t *data, uint16_t port) return print_in_hook(data, "%d", type); } +/** + * Print GRE key + */ +static int print_gre(printf_hook_data_t *data, uint16_t from_port, uint16_t to_port) +{ + uint32_t gre_key; + + gre_key = traffic_selector_gre_key(from_port, to_port); + + return print_in_hook(data, "%d", gre_key); +} + /** * Described in header. */ @@ -319,6 +331,10 @@ int traffic_selector_printf_hook(printf_hook_data_t *data, { written += print_icmp(data, this->from_port); } + else if (this->protocol == IPPROTO_GRE) + { + written += print_gre(data, this->from_port, this->to_port); + } else { serv = getservbyport(htons(this->from_port), serv_proto); @@ -332,6 +348,10 @@ int traffic_selector_printf_hook(printf_hook_data_t *data, } } } + else if (this->protocol == IPPROTO_GRE) + { + written += print_gre(data, this->from_port, this->to_port); + } else if (is_opaque(this)) { written += print_in_hook(data, "OPAQUE"); diff --git a/src/libstrongswan/selectors/traffic_selector.h b/src/libstrongswan/selectors/traffic_selector.h index 367b4fff94..b7010e4a73 100644 --- a/src/libstrongswan/selectors/traffic_selector.h +++ b/src/libstrongswan/selectors/traffic_selector.h @@ -272,6 +272,18 @@ static inline uint8_t traffic_selector_icmp_code(uint16_t port) return port & 0xff; } +/** + * Extract the GRE key from a source and destination port in host order + * + * @param from_port port number in host order + * @param to_port port number in host order + * @return GRE key + */ +static inline uint8_t traffic_selector_gre_key(uint16_t from_port, uint16_t to_port) +{ + return (from_port & 0xffff) << 16 | (to_port & 0xffff); +} + /** * Compare two traffic selectors, usable as sort function * diff --git a/src/starter/confread.c b/src/starter/confread.c index 5065bc369f..039b6f402b 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -325,7 +325,29 @@ static void kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token, end->protocol = (uint8_t)p; } } - if (streq(port, "%any")) + if (end->protocol == IPPROTO_GRE) + { + if (*port && !streq(port, "%any")) + { + p = strtol(port, &endptr, 0); + if (p < 0 || p > 0xffffffff) + { + DBG1(DBG_APP, "# bad GRE key: %s=%s", key, port); + goto err; + } + end->from_port = (p >> 16) & 0xffff; + end->to_port = p & 0xffff; + if (*endptr) + { + DBG1(DBG_APP, "# bad GRE key: %s=%s", key, port); + goto err; + } + } else { + end->from_port = 0; + end->to_port = 0; + } + } + else if (streq(port, "%any")) { end->from_port = 0; end->to_port = 0xffff; diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 8336735fff..be185bc056 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -805,6 +805,9 @@ connections..children..local_ts = dynamic equal to 256, with the type in the most significant 8 bits and the code in the least significant 8 bits. + If protocol is restricted to GRE, port restriction specifies GRE key + in 32 bit numeric form eg. dynamic[gre/100]. + When IKEv1 is used only the first selector is interpreted, except if the Cisco Unity extension plugin is used. This is due to a limitation of the IKEv1 protocol, which only allows a single pair of selectors per CHILD_SA. -- 2.51.0