From 9dd748cd49d15b7e90a7a65de53d431a2c515c86 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 31 Jan 2019 17:13:07 +0100
Subject: [PATCH] tc: f_flower: add geneve option match support to flower
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1654761
Upstream Status: iproute2.git commit 56155d4df86d4
commit 56155d4df86d489c4207444c8a90ce4e0e22e49f
Author: Pieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com>
Date: Fri Sep 28 16:03:39 2018 +0200
tc: f_flower: add geneve option match support to flower
Allow matching on options in Geneve tunnel headers.
The options can be described in the form
CLASS:TYPE:DATA/CLASS_MASK:TYPE_MASK:DATA_MASK, where CLASS is
represented as a 16bit hexadecimal value, TYPE as an 8bit
hexadecimal value and DATA as a variable length hexadecimal value.
e.g.
# ip link add name geneve0 type geneve dstport 0 external
# tc qdisc add dev geneve0 ingress
# tc filter add dev geneve0 protocol ip parent ffff: \
flower \
enc_src_ip 10.0.99.192 \
enc_dst_ip 10.0.99.193 \
enc_key_id 11 \
geneve_opts 0102:80:1122334421314151/ffff:ff:ffffffffffffffff \
ip_proto udp \
action mirred egress redirect dev eth1
Signed-off-by: Pieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
---
man/man8/tc-flower.8 | 13 ++-
tc/f_flower.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 294 insertions(+), 1 deletion(-)
diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8
index f917f24..276b527 100644
--- a/man/man8/tc-flower.8
+++ b/man/man8/tc-flower.8
@@ -74,6 +74,8 @@ flower \- flow based traffic control filter
.IR TOS " | "
.B enc_ttl
.IR TTL " | "
+.B geneve_opts
+.IR OPTIONS " | "
.BR ip_flags
.IR IP_FLAGS
.SH DESCRIPTION
@@ -260,6 +262,8 @@ bits is assumed.
.BI enc_tos " NUMBER"
.TQ
.BI enc_ttl " NUMBER"
+.TQ
+.BI geneve_opts " OPTIONS"
Match on IP tunnel metadata. Key id
.I NUMBER
is a 32 bit tunnel key id (e.g. VNI for VXLAN tunnel).
@@ -272,7 +276,14 @@ is a 16 bit UDP dst port. Tos
.I NUMBER
is an 8 bit tos (dscp+ecn) value, ttl
.I NUMBER
-is an 8 bit time-to-live value.
+is an 8 bit time-to-live value. geneve_opts
+.I OPTIONS
+must be a valid list of comma-separated geneve options where each option
+consists of a key optionally followed by a slash and corresponding mask. If
+the masks is missing, \fBtc\fR assumes a full-length match. The options can
+be described in the form CLASS:TYPE:DATA/CLASS_MASK:TYPE_MASK:DATA_MASK,
+where CLASS is represented as a 16bit hexadecimal value, TYPE as an 8bit
+hexadecimal value and DATA as a variable length hexadecimal value.
.TP
.BI ip_flags " IP_FLAGS"
.I IP_FLAGS
diff --git a/tc/f_flower.c b/tc/f_flower.c
index cd102f2..43102c8 100644
--- a/tc/f_flower.c
+++ b/tc/f_flower.c
@@ -76,6 +76,7 @@ static void explain(void)
" enc_key_id [ KEY-ID ] |\n"
" enc_tos MASKED-IP_TOS |\n"
" enc_ttl MASKED-IP_TTL |\n"
+ " geneve_opts MASKED-OPTIONS |\n"
" ip_flags IP-FLAGS | \n"
" enc_dst_port [ port_number ] }\n"
" FILTERID := X:Y:Z\n"
@@ -580,6 +581,179 @@ static int flower_parse_enc_port(char *str, int type, struct nlmsghdr *n)
return 0;
}
+static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n)
+{
+ struct rtattr *nest;
+ char *token;
+ int i, err;
+
+ nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
+
+ i = 1;
+ token = strsep(&str, ":");
+ while (token) {
+ switch (i) {
+ case TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS:
+ {
+ __be16 opt_class;
+
+ if (!strlen(token))
+ break;
+ err = get_be16(&opt_class, token, 16);
+ if (err)
+ return err;
+
+ addattr16(n, MAX_MSG, i, opt_class);
+ break;
+ }
+ case TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE:
+ {
+ __u8 opt_type;
+
+ if (!strlen(token))
+ break;
+ err = get_u8(&opt_type, token, 16);
+ if (err)
+ return err;
+
+ addattr8(n, MAX_MSG, i, opt_type);
+ break;
+ }
+ case TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA:
+ {
+ size_t token_len = strlen(token);
+ __u8 *opts;
+
+ if (!token_len)
+ break;
+ opts = malloc(token_len / 2);
+ if (!opts)
+ return -1;
+ if (hex2mem(token, opts, token_len / 2) < 0) {
+ free(opts);
+ return -1;
+ }
+ addattr_l(n, MAX_MSG, i, opts, token_len / 2);
+ free(opts);
+
+ break;
+ }
+ default:
+ fprintf(stderr, "Unknown \"geneve_opts\" type\n");
+ return -1;
+ }
+
+ token = strsep(&str, ":");
+ i++;
+ }
+ addattr_nest_end(n, nest);
+
+ return 0;
+}
+
+static int flower_parse_enc_opt_part(char *str, struct nlmsghdr *n)
+{
+ char *token;
+ int err;
+
+ token = strsep(&str, ",");
+ while (token) {
+ err = flower_parse_geneve_opts(token, n);
+ if (err)
+ return err;
+
+ token = strsep(&str, ",");
+ }
+
+ return 0;
+}
+
+static int flower_check_enc_opt_key(char *key)
+{
+ int key_len, col_cnt = 0;
+
+ key_len = strlen(key);
+ while ((key = strchr(key, ':'))) {
+ if (strlen(key) == key_len)
+ return -1;
+
+ key_len = strlen(key) - 1;
+ col_cnt++;
+ key++;
+ }
+
+ if (col_cnt != 2 || !key_len)
+ return -1;
+
+ return 0;
+}
+
+static int flower_parse_enc_opts(char *str, struct nlmsghdr *n)
+{
+ char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
+ int data_len, key_len, mask_len, err;
+ char *token, *slash;
+ struct rtattr *nest;
+
+ key_len = 0;
+ mask_len = 0;
+ token = strsep(&str, ",");
+ while (token) {
+ slash = strchr(token, '/');
+ if (slash)
+ *slash = '\0';
+
+ if ((key_len + strlen(token) > XATTR_SIZE_MAX) ||
+ flower_check_enc_opt_key(token))
+ return -1;
+
+ strcpy(&key[key_len], token);
+ key_len += strlen(token) + 1;
+ key[key_len - 1] = ',';
+
+ if (!slash) {
+ /* Pad out mask when not provided */
+ if (mask_len + strlen(token) > XATTR_SIZE_MAX)
+ return -1;
+
+ data_len = strlen(rindex(token, ':'));
+ sprintf(&mask[mask_len], "ffff:ff:");
+ mask_len += 8;
+ memset(&mask[mask_len], 'f', data_len - 1);
+ mask_len += data_len;
+ mask[mask_len - 1] = ',';
+ token = strsep(&str, ",");
+ continue;
+ }
+
+ if (mask_len + strlen(slash + 1) > XATTR_SIZE_MAX)
+ return -1;
+
+ strcpy(&mask[mask_len], slash + 1);
+ mask_len += strlen(slash + 1) + 1;
+ mask[mask_len - 1] = ',';
+
+ *slash = '/';
+ token = strsep(&str, ",");
+ }
+ key[key_len - 1] = '\0';
+ mask[mask_len - 1] = '\0';
+
+ nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS);
+ err = flower_parse_enc_opt_part(key, n);
+ if (err)
+ return err;
+ addattr_nest_end(n, nest);
+
+ nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS_MASK);
+ err = flower_parse_enc_opt_part(mask, n);
+ if (err)
+ return err;
+ addattr_nest_end(n, nest);
+
+ return 0;
+}
+
static int flower_parse_opt(struct filter_util *qu, char *handle,
int argc, char **argv, struct nlmsghdr *n)
{
@@ -994,6 +1168,13 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
fprintf(stderr, "Illegal \"enc_ttl\"\n");
return -1;
}
+ } else if (matches(*argv, "geneve_opts") == 0) {
+ NEXT_ARG();
+ ret = flower_parse_enc_opts(*argv, n);
+ if (ret < 0) {
+ fprintf(stderr, "Illegal \"geneve_opts\"\n");
+ return -1;
+ }
} else if (matches(*argv, "action") == 0) {
NEXT_ARG();
ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n);
@@ -1291,6 +1472,105 @@ static void flower_print_key_id(const char *name, struct rtattr *attr)
print_uint(PRINT_ANY, name, namefrm, rta_getattr_be32(attr));
}
+static void flower_print_geneve_opts(const char *name, struct rtattr *attr,
+ char *strbuf)
+{
+ struct rtattr *tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1];
+ int ii, data_len, offset = 0, slen = 0;
+ struct rtattr *i = RTA_DATA(attr);
+ int rem = RTA_PAYLOAD(attr);
+ __u8 type, data_r[rem];
+ char data[rem * 2 + 1];
+ __u16 class;
+
+ open_json_array(PRINT_JSON, name);
+ while (rem) {
+ parse_rtattr(tb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX, i, rem);
+ class = rta_getattr_be16(tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]);
+ type = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]);
+ data_len = RTA_PAYLOAD(tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]);
+ hexstring_n2a(RTA_DATA(tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]),
+ data_len, data, sizeof(data));
+ hex2mem(data, data_r, data_len);
+ offset += data_len + 20;
+ rem -= data_len + 20;
+ i = RTA_DATA(attr) + offset;
+
+ open_json_object(NULL);
+ print_uint(PRINT_JSON, "class", NULL, class);
+ print_uint(PRINT_JSON, "type", NULL, type);
+ open_json_array(PRINT_JSON, "data");
+ for (ii = 0; ii < data_len; ii++)
+ print_uint(PRINT_JSON, NULL, NULL, data_r[ii]);
+ close_json_array(PRINT_JSON, "data");
+ close_json_object();
+
+ slen += sprintf(strbuf + slen, "%04x:%02x:%s",
+ class, type, data);
+ if (rem)
+ slen += sprintf(strbuf + slen, ",");
+ }
+ close_json_array(PRINT_JSON, name);
+}
+
+static void flower_print_geneve_parts(const char *name, struct rtattr *attr,
+ char *key, char *mask)
+{
+ char *namefrm = "\n geneve_opt %s";
+ char *key_token, *mask_token, *out;
+ int len;
+
+ out = malloc(RTA_PAYLOAD(attr) * 4 + 3);
+ if (!out)
+ return;
+
+ len = 0;
+ key_token = strsep(&key, ",");
+ mask_token = strsep(&mask, ",");
+ while (key_token) {
+ len += sprintf(&out[len], "%s/%s,", key_token, mask_token);
+ mask_token = strsep(&mask, ",");
+ key_token = strsep(&key, ",");
+ }
+
+ out[len - 1] = '\0';
+ print_string(PRINT_FP, name, namefrm, out);
+ free(out);
+}
+
+static void flower_print_enc_opts(const char *name, struct rtattr *attr,
+ struct rtattr *mask_attr)
+{
+ struct rtattr *key_tb[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1];
+ struct rtattr *msk_tb[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1];
+ char *key, *msk;
+
+ if (!attr)
+ return;
+
+ key = malloc(RTA_PAYLOAD(attr) * 2 + 1);
+ if (!key)
+ return;
+
+ msk = malloc(RTA_PAYLOAD(attr) * 2 + 1);
+ if (!msk)
+ goto err_key_free;
+
+ parse_rtattr_nested(key_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, attr);
+ flower_print_geneve_opts("geneve_opt_key",
+ key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], key);
+
+ parse_rtattr_nested(msk_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, mask_attr);
+ flower_print_geneve_opts("geneve_opt_mask",
+ msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], msk);
+
+ flower_print_geneve_parts(name, attr, key, msk);
+
+ free(msk);
+err_key_free:
+ free(key);
+}
+
static void flower_print_masked_u8(const char *name, struct rtattr *attr,
struct rtattr *mask_attr,
const char *(*value_to_str)(__u8 value))
@@ -1489,6 +1769,8 @@ static int flower_print_opt(struct filter_util *qu, FILE *f,
tb[TCA_FLOWER_KEY_ENC_IP_TOS_MASK]);
flower_print_ip_attr("enc_ttl", tb[TCA_FLOWER_KEY_ENC_IP_TTL],
tb[TCA_FLOWER_KEY_ENC_IP_TTL_MASK]);
+ flower_print_enc_opts("enc_opt", tb[TCA_FLOWER_KEY_ENC_OPTS],
+ tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]);
flower_print_matching_flags("ip_flags", FLOWER_IP_FLAGS,
tb[TCA_FLOWER_KEY_FLAGS],
--
1.8.3.1