summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2020-06-12 00:59:57 +0200
committerChristian Poessinger <christian@poessinger.com>2020-06-12 01:00:19 +0200
commit1014d39807a14d266d35db5a94eae2d26e6ea336 (patch)
tree6f48c09d5852f1a7419ef8257354ee0e48faf100
parentfd286871448acc36f8d727c07405ba8ff91d1d2b (diff)
downloadvyos-strongswan-1014d39807a14d266d35db5a94eae2d26e6ea336.tar.gz
vyos-strongswan-1014d39807a14d266d35db5a94eae2d26e6ea336.zip
dmvpn: add required patches for FRR NHRP implementation
Patches are not active. To activate bth patches add their corresponding file name to debian/patches/series. From FRR docs: nhrpd needs tight integration with IKE daemon for various reasons. Currently only strongSwan is supported as IKE daemon. nhrpd connects to strongSwan using VICI protocol based on UNIX socket (hardcoded now as /var/run/charon.vici). strongSwan currently needs few patches applied. Please check out bot git - https://git.alpinelinux.org/user/tteras/strongswan/log/?h=tteras-release - https://git.alpinelinux.org/user/tteras/strongswan/log/?h=tteras repositories for the patches.
-rw-r--r--debian/patches/1006-vici-add-deprecated-async-parameter.patch49
-rw-r--r--debian/patches/1007-support-gre-key-in-ikev1.patch508
2 files changed, 557 insertions, 0 deletions
diff --git a/debian/patches/1006-vici-add-deprecated-async-parameter.patch b/debian/patches/1006-vici-add-deprecated-async-parameter.patch
new file mode 100644
index 000000000..90ae658d4
--- /dev/null
+++ b/debian/patches/1006-vici-add-deprecated-async-parameter.patch
@@ -0,0 +1,49 @@
+From 0c33419af4403597aac1f75e8d044150ecc2f03c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
+Date: Mon, 21 Sep 2015 13:42:15 +0300
+Subject: [PATCH] vici: add (deprecated) async parameter
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This is obsoleted by the new "timeout=-1" option that achieves
+the same. Only for compatibility with old versions of quagga-nhrp.
+
+Signed-off-by: Timo Teräs <timo.teras@iki.fi>
+---
+ src/libcharon/plugins/vici/vici_control.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c
+index 9c6b86741..718d14b3c 100644
+--- a/src/libcharon/plugins/vici/vici_control.c
++++ b/src/libcharon/plugins/vici/vici_control.c
+@@ -197,7 +197,7 @@ CALLBACK(initiate, vici_message_t*,
+ host_t *my_host = NULL, *other_host = NULL;
+ char *child, *ike, *my_host_str, *other_host_str;
+ int timeout;
+- bool limits;
++ bool limits, async;
+ controller_cb_t log_cb = NULL;
+ log_info_t log = {
+ .dispatcher = this->dispatcher,
+@@ -208,6 +208,7 @@ CALLBACK(initiate, vici_message_t*,
+ ike = request->get_str(request, NULL, "ike");
+ timeout = request->get_int(request, 0, "timeout");
+ limits = request->get_bool(request, FALSE, "init-limits");
++ async = request->get_bool(request, FALSE, "async");
+ log.level = request->get_int(request, 1, "loglevel");
+ my_host_str = request->get_str(request, NULL, "my-host");
+ other_host_str = request->get_str(request, NULL, "other-host");
+@@ -216,7 +217,7 @@ CALLBACK(initiate, vici_message_t*,
+ {
+ return send_reply(this, "missing configuration name");
+ }
+- if (timeout >= 0)
++ if (timeout >= 0 && !async)
+ {
+ log_cb = (controller_cb_t)log_vici;
+ }
+--
+2.20.1
+
diff --git a/debian/patches/1007-support-gre-key-in-ikev1.patch b/debian/patches/1007-support-gre-key-in-ikev1.patch
new file mode 100644
index 000000000..5b9d0d184
--- /dev/null
+++ b/debian/patches/1007-support-gre-key-in-ikev1.patch
@@ -0,0 +1,508 @@
+From 0f09d2cb000f9037a2b73ff5caa797b4caee492a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
+Date: Mon, 21 Sep 2015 13:42:18 +0300
+Subject: [PATCH] support gre key in ikev1
+
+this implements gre key negotiation in ikev1 similarly to the
+ipsec-tools patch in alpine.
+
+the from/to port pair is internally used as gre key for gre
+protocol traffic selectors. since from/to pairs 0/0xffff and
+0xffff/0 have special meaning, the gre keys 0xffff and 0xffff0000
+will not work.
+
+this is not standard compliant, and should probably not be upstreamed
+or used widely, but it is applied for interoperability with alpine
+racoon for the time being.
+---
+ src/libcharon/encoding/payloads/id_payload.c | 68 ++++++++++++++-----
+ src/libcharon/encoding/payloads/id_payload.h | 6 +-
+ .../kernel_netlink/kernel_netlink_ipsec.c | 40 ++++++++---
+ src/libcharon/plugins/stroke/stroke_config.c | 5 ++
+ src/libcharon/plugins/unity/unity_narrow.c | 2 +-
+ src/libcharon/plugins/vici/vici_config.c | 9 ++-
+ src/libcharon/sa/ikev1/tasks/quick_mode.c | 16 +++--
+ .../selectors/traffic_selector.c | 34 +++++++++-
+ .../selectors/traffic_selector.h | 31 +++++++++
+ 9 files changed, 171 insertions(+), 40 deletions(-)
+
+diff --git a/src/libcharon/encoding/payloads/id_payload.c b/src/libcharon/encoding/payloads/id_payload.c
+index b2f1adbbc..6b44d0cf6 100644
+--- a/src/libcharon/encoding/payloads/id_payload.c
++++ b/src/libcharon/encoding/payloads/id_payload.c
+@@ -245,18 +245,20 @@ METHOD(id_payload_t, get_identification, identification_t*,
+ * Create a traffic selector from an range ID
+ */
+ static traffic_selector_t *get_ts_from_range(private_id_payload_t *this,
+- ts_type_t type)
++ ts_type_t type,
++ uint16_t from_port, uint16_t to_port)
+ {
+ return traffic_selector_create_from_bytes(this->protocol_id, type,
+- chunk_create(this->id_data.ptr, this->id_data.len / 2), this->port,
+- chunk_skip(this->id_data, this->id_data.len / 2), this->port ?: 65535);
++ chunk_create(this->id_data.ptr, this->id_data.len / 2), from_port,
++ chunk_skip(this->id_data, this->id_data.len / 2), to_port);
+ }
+
+ /**
+ * Create a traffic selector from an subnet ID
+ */
+ static traffic_selector_t *get_ts_from_subnet(private_id_payload_t *this,
+- ts_type_t type)
++ ts_type_t type,
++ uint16_t from_port, uint16_t to_port)
+ {
+ traffic_selector_t *ts;
+ chunk_t net, netmask;
+@@ -269,7 +271,7 @@ static traffic_selector_t *get_ts_from_subnet(private_id_payload_t *this,
+ netmask.ptr[i] = (netmask.ptr[i] ^ 0xFF) | net.ptr[i];
+ }
+ ts = traffic_selector_create_from_bytes(this->protocol_id, type,
+- net, this->port, netmask, this->port ?: 65535);
++ net, from_port, netmask, to_port);
+ chunk_free(&netmask);
+ return ts;
+ }
+@@ -278,51 +280,76 @@ static traffic_selector_t *get_ts_from_subnet(private_id_payload_t *this,
+ * Create a traffic selector from an IP ID
+ */
+ static traffic_selector_t *get_ts_from_ip(private_id_payload_t *this,
+- ts_type_t type)
++ ts_type_t type,
++ uint16_t from_port, uint16_t to_port)
+ {
+ return traffic_selector_create_from_bytes(this->protocol_id, type,
+- this->id_data, this->port, this->id_data, this->port ?: 65535);
++ this->id_data, from_port, this->id_data, to_port);
+ }
+
+ METHOD(id_payload_t, get_ts, traffic_selector_t*,
+- private_id_payload_t *this)
++ private_id_payload_t *this, id_payload_t *other_, bool initiator)
+ {
++ private_id_payload_t *other = (private_id_payload_t *) other_;
++ uint16_t from_port, to_port;
++
++ if (other && this->protocol_id == IPPROTO_GRE && other->protocol_id == IPPROTO_GRE)
++ {
++ if (initiator)
++ {
++ from_port = this->port;
++ to_port = other->port;
++ }
++ else
++ {
++ from_port = other->port;
++ to_port = this->port;
++ }
++ if (from_port == 0 && to_port == 0)
++ to_port = 0xffff;
++ }
++ else
++ {
++ from_port = this->port;
++ to_port = this->port ?: 0xffff;
++ }
++
+ switch (this->id_type)
+ {
+ case ID_IPV4_ADDR_SUBNET:
+ if (this->id_data.len == 8)
+ {
+- return get_ts_from_subnet(this, TS_IPV4_ADDR_RANGE);
++ return get_ts_from_subnet(this, TS_IPV4_ADDR_RANGE, from_port, to_port);
+ }
+ break;
+ case ID_IPV6_ADDR_SUBNET:
+ if (this->id_data.len == 32)
+ {
+- return get_ts_from_subnet(this, TS_IPV6_ADDR_RANGE);
++ return get_ts_from_subnet(this, TS_IPV6_ADDR_RANGE, from_port, to_port);
+ }
+ break;
+ case ID_IPV4_ADDR_RANGE:
+ if (this->id_data.len == 8)
+ {
+- return get_ts_from_range(this, TS_IPV4_ADDR_RANGE);
++ return get_ts_from_range(this, TS_IPV4_ADDR_RANGE, from_port, to_port);
+ }
+ break;
+ case ID_IPV6_ADDR_RANGE:
+ if (this->id_data.len == 32)
+ {
+- return get_ts_from_range(this, TS_IPV6_ADDR_RANGE);
++ return get_ts_from_range(this, TS_IPV6_ADDR_RANGE, from_port, to_port);
+ }
+ break;
+ case ID_IPV4_ADDR:
+ if (this->id_data.len == 4)
+ {
+- return get_ts_from_ip(this, TS_IPV4_ADDR_RANGE);
++ return get_ts_from_ip(this, TS_IPV4_ADDR_RANGE, from_port, to_port);
+ }
+ break;
+ case ID_IPV6_ADDR:
+ if (this->id_data.len == 16)
+ {
+- return get_ts_from_ip(this, TS_IPV6_ADDR_RANGE);
++ return get_ts_from_ip(this, TS_IPV6_ADDR_RANGE, from_port, to_port);
+ }
+ break;
+ default:
+@@ -397,7 +424,7 @@ id_payload_t *id_payload_create_from_identification(payload_type_t type,
+ /*
+ * Described in header.
+ */
+-id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts)
++id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts, bool initiator)
+ {
+ private_id_payload_t *this;
+ uint8_t mask;
+@@ -460,8 +487,17 @@ id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts)
+ ts->get_from_address(ts), ts->get_to_address(ts));
+ net->destroy(net);
+ }
+- this->port = ts->get_from_port(ts);
+ this->protocol_id = ts->get_protocol(ts);
++ if (initiator || this->protocol_id != IPPROTO_GRE)
++ {
++ this->port = ts->get_from_port(ts);
++ }
++ else
++ {
++ this->port = ts->get_to_port(ts);
++ if (this->port == 0xffff && ts->get_from_port(ts) == 0)
++ this->port = 0;
++ }
+ this->payload_length += this->id_data.len;
+
+ return &this->public;
+diff --git a/src/libcharon/encoding/payloads/id_payload.h b/src/libcharon/encoding/payloads/id_payload.h
+index 283780624..fafeca8bc 100644
+--- a/src/libcharon/encoding/payloads/id_payload.h
++++ b/src/libcharon/encoding/payloads/id_payload.h
+@@ -48,11 +48,11 @@ struct id_payload_t {
+ identification_t *(*get_identification) (id_payload_t *this);
+
+ /**
+- * Creates a traffic selector form a ID_ADDR_SUBNET/RANGE identity.
++ * Creates a traffic selector form a ID_ADDR_SUBNET/RANGE identity pair.
+ *
+ * @return traffic selector, NULL on failure
+ */
+- traffic_selector_t* (*get_ts)(id_payload_t *this);
++ traffic_selector_t* (*get_ts)(id_payload_t *this, id_payload_t *other, bool initiator);
+
+ /**
+ * Get encoded payload without fixed payload header (used for IKEv1).
+@@ -91,6 +91,6 @@ id_payload_t *id_payload_create_from_identification(payload_type_t type,
+ * @param ts traffic selector
+ * @return PLV1_ID id_paylad_t object.
+ */
+-id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts);
++id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts, bool initiator);
+
+ #endif /** ID_PAYLOAD_H_ @}*/
+diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+index 40fff7e05..0743f7a95 100644
+--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
++++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+@@ -869,7 +869,18 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src,
+ ts2subnet(src, &sel.saddr, &sel.prefixlen_s);
+ ts2ports(dst, &sel.dport, &sel.dport_mask);
+ ts2ports(src, &sel.sport, &sel.sport_mask);
+- if ((sel.proto == IPPROTO_ICMP || sel.proto == IPPROTO_ICMPV6) &&
++ if (sel.proto == IPPROTO_GRE)
++ {
++ sel.sport = htons(src->get_from_port(src));
++ sel.dport = htons(src->get_to_port(src));
++ sel.sport_mask = ~0;
++ sel.dport_mask = ~0;
++ if (sel.sport == htons(0) && sel.dport == htons(0xffff))
++ {
++ sel.sport = sel.dport = sel.sport_mask = sel.dport_mask = 0;
++ }
++ }
++ else if ((sel.proto == IPPROTO_ICMP || sel.proto == IPPROTO_ICMPV6) &&
+ (sel.dport || sel.sport))
+ {
+ /* the kernel expects the ICMP type and code in the source and
+@@ -893,7 +904,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src)
+ {
+ u_char *addr;
+ uint8_t prefixlen;
+- uint16_t port = 0;
++ uint16_t from_port = 0, to_port = 65535;
+ host_t *host = NULL;
+
+ if (src)
+@@ -902,7 +913,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src)
+ prefixlen = sel->prefixlen_s;
+ if (sel->sport_mask)
+ {
+- port = ntohs(sel->sport);
++ from_port = to_port = ntohs(sel->sport);
+ }
+ }
+ else
+@@ -911,14 +922,27 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src)
+ prefixlen = sel->prefixlen_d;
+ if (sel->dport_mask)
+ {
+- port = ntohs(sel->dport);
++ from_port = to_port = ntohs(sel->dport);
++ }
++ }
++ if (sel->proto == IPPROTO_GRE)
++ {
++ if (sel->sport_mask)
++ {
++ from_port = ntohs(sel->sport);
++ to_port = ntohs(sel->dport);
++ }
++ else
++ {
++ from_port = 0;
++ to_port = 0xffff;
+ }
+ }
+- if (sel->proto == IPPROTO_ICMP || sel->proto == IPPROTO_ICMPV6)
++ else if (sel->proto == IPPROTO_ICMP || sel->proto == IPPROTO_ICMPV6)
+ { /* convert ICMP[v6] message type and code as supplied by the kernel in
+ * source and destination ports (both in network order) */
+- port = (sel->sport >> 8) | (sel->dport & 0xff00);
+- port = ntohs(port);
++ from_port = (sel->sport >> 8) | (sel->dport & 0xff00);
++ from_port = to_port = ntohs(from_port);
+ }
+ /* The Linux 2.6 kernel does not set the selector's family field,
+ * so as a kludge we additionally test the prefix length.
+@@ -935,7 +959,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src)
+ if (host)
+ {
+ return traffic_selector_create_from_subnet(host, prefixlen,
+- sel->proto, port, port ?: 65535);
++ sel->proto, from_port, to_port);
+ }
+ return NULL;
+ }
+diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c
+index 8cdb5ef48..a81949c09 100644
+--- a/src/libcharon/plugins/stroke/stroke_config.c
++++ b/src/libcharon/plugins/stroke/stroke_config.c
+@@ -936,6 +936,11 @@ static bool parse_protoport(char *token, uint16_t *from_port,
+ *from_port = 0xffff;
+ *to_port = 0;
+ }
++ else if (*port && *protocol == IPPROTO_GRE)
++ {
++ p = strtol(port, &endptr, 0);
++ traffic_selector_split_grekey(p, from_port, to_port);
++ }
+ else if (*port)
+ {
+ svc = getservbyname(port, NULL);
+diff --git a/src/libcharon/plugins/unity/unity_narrow.c b/src/libcharon/plugins/unity/unity_narrow.c
+index afbd6cc7e..911fe70c6 100644
+--- a/src/libcharon/plugins/unity/unity_narrow.c
++++ b/src/libcharon/plugins/unity/unity_narrow.c
+@@ -248,7 +248,7 @@ METHOD(listener_t, message, bool,
+ if (!first)
+ {
+ id_payload = (id_payload_t*)payload;
+- tsr = id_payload->get_ts(id_payload);
++ tsr = id_payload->get_ts(id_payload, NULL, FALSE);
+ break;
+ }
+ first = FALSE;
+diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c
+index f0fd8a989..9f9dcfa45 100644
+--- a/src/libcharon/plugins/vici/vici_config.c
++++ b/src/libcharon/plugins/vici/vici_config.c
+@@ -691,8 +691,13 @@ CALLBACK(parse_ts, bool,
+ }
+ else if (*port && !streq(port, "any"))
+ {
+- svc = getservbyname(port, NULL);
+- if (svc)
++ if (proto == IPPROTO_GRE)
++ {
++ p = strtol(port, &end, 0);
++ if (*end) return FALSE;
++ traffic_selector_split_grekey(p, &from, &to);
++ }
++ else if ((svc = getservbyname(port, NULL)) != NULL)
+ {
+ from = to = ntohs(svc->s_port);
+ }
+diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
+index b0a42b8bd..4ef4bf324 100644
+--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
++++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
+@@ -567,9 +567,9 @@ static void add_ts(private_quick_mode_t *this, message_t *message)
+ {
+ id_payload_t *id_payload;
+
+- id_payload = id_payload_create_from_ts(this->tsi);
++ id_payload = id_payload_create_from_ts(this->tsi, TRUE);
+ message->add_payload(message, &id_payload->payload_interface);
+- id_payload = id_payload_create_from_ts(this->tsr);
++ id_payload = id_payload_create_from_ts(this->tsr, FALSE);
+ message->add_payload(message, &id_payload->payload_interface);
+ }
+
+@@ -580,7 +580,7 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
+ {
+ traffic_selector_t *tsi = NULL, *tsr = NULL;
+ enumerator_t *enumerator;
+- id_payload_t *id_payload;
++ id_payload_t *idi = NULL, *idr = NULL;
+ payload_t *payload;
+ host_t *hsi, *hsr;
+ bool first = TRUE;
+@@ -590,20 +590,22 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
+ {
+ if (payload->get_type(payload) == PLV1_ID)
+ {
+- id_payload = (id_payload_t*)payload;
+-
+ if (first)
+ {
+- tsi = id_payload->get_ts(id_payload);
++ idi = (id_payload_t*)payload;
+ first = FALSE;
+ }
+ else
+ {
+- tsr = id_payload->get_ts(id_payload);
++ idr = (id_payload_t*)payload;
+ break;
+ }
+ }
+ }
++ if (idi && idr) {
++ tsi = idi->get_ts(idi, idr, TRUE);
++ tsr = idr->get_ts(idr, idi, FALSE);
++ }
+ enumerator->destroy(enumerator);
+
+ /* create host2host selectors if ID payloads missing */
+diff --git a/src/libstrongswan/selectors/traffic_selector.c b/src/libstrongswan/selectors/traffic_selector.c
+index cfd2b029d..0b9697d5e 100644
+--- a/src/libstrongswan/selectors/traffic_selector.c
++++ b/src/libstrongswan/selectors/traffic_selector.c
+@@ -198,6 +198,14 @@ 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_grekey(printf_hook_data_t *data, uint16_t from_port, uint16_t to_port)
++{
++ return print_in_hook(data, "%d", traffic_selector_grekey(from_port, to_port));
++}
++
+ /**
+ * Described in header.
+ */
+@@ -302,8 +310,11 @@ int traffic_selector_printf_hook(printf_hook_data_t *data,
+ if (has_ports)
+ {
+ written += print_in_hook(data, "/");
+-
+- if (this->from_port == this->to_port)
++ if (this->protocol == IPPROTO_GRE)
++ {
++ written += print_grekey(data, this->from_port, this->to_port);
++ }
++ else if (this->from_port == this->to_port)
+ {
+ struct servent *serv;
+
+@@ -377,7 +388,24 @@ METHOD(traffic_selector_t, get_subset, traffic_selector_t*,
+ /* select protocol, which is not zero */
+ protocol = max(this->protocol, other->protocol);
+
+- if ((is_opaque(this) && is_opaque(other)) ||
++ if (this->protocol == IPPROTO_GRE)
++ {
++ if (is_any(this))
++ {
++ from_port = other->from_port;
++ to_port = other->to_port;
++ }
++ else if (is_any(other) ||
++ (this->from_port == other->from_port &&
++ this->to_port == other->to_port))
++ {
++ from_port = this->from_port;
++ to_port = this->to_port;
++ }
++ else
++ return NULL;
++ }
++ else if ((is_opaque(this) && is_opaque(other)) ||
+ (is_opaque(this) && is_any(other)) ||
+ (is_opaque(other) && is_any(this)))
+ {
+diff --git a/src/libstrongswan/selectors/traffic_selector.h b/src/libstrongswan/selectors/traffic_selector.h
+index 03f7a6d8c..b27ca4ad1 100644
+--- a/src/libstrongswan/selectors/traffic_selector.h
++++ b/src/libstrongswan/selectors/traffic_selector.h
+@@ -120,6 +120,9 @@ struct traffic_selector_t {
+ * 8 bits and the code in the least significant 8 bits. Use the utility
+ * functions to extract them.
+ *
++ * If the protocol is GRE, the high 16-bits of the 32-bit GRE key is stored
++ * in the from port. Use the utility function to merge and split them.
++ *
+ * @return port
+ */
+ uint16_t (*get_from_port)(traffic_selector_t *this);
+@@ -134,6 +137,9 @@ struct traffic_selector_t {
+ * 8 bits and the code in the least significant 8 bits. Use the utility
+ * functions to extract them.
+ *
++ * If the protocol is GRE, the low 16-bits of the 32-bit GRE key is stored
++ * in the to port. Use the utility function to merge and split them.
++ *
+ * @return port
+ */
+ uint16_t (*get_to_port)(traffic_selector_t *this);
+@@ -277,6 +283,31 @@ static inline uint8_t traffic_selector_icmp_code(uint16_t port)
+ int traffic_selector_cmp(traffic_selector_t *a, traffic_selector_t *b,
+ void *opts);
+
++/**
++ * Reconstruct the 32-bit GRE KEY in host order from a from/to ports.
++ *
++ * @param from_port port number in host order
++ * @param to_port port number in host order
++ * @return GRE KEY in host order
++ */
++static inline uint32_t traffic_selector_grekey(uint16_t from_port, uint16_t to_port)
++{
++ return (from_port << 16) | to_port;
++}
++
++/**
++ * Split 32-bit GRE KEY in host order to from/to ports.
++ *
++ * @param grekey grekey in host order
++ * @param from_port from port in host order
++ * @param to_port to port in host order
++ */
++static inline void traffic_selector_split_grekey(uint32_t grekey, uint16_t *from_port, uint16_t *to_port)
++{
++ *from_port = grekey >> 16;
++ *to_port = grekey & 0xffff;
++}
++
+ /**
+ * Create a new traffic selector using human readable params.
+ *
+--
+2.20.1
+