summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
+