From 4bd1eb80a195ce1a64e33f5fc9d5c58bf9f30c8d Mon Sep 17 00:00:00 2001 From: Andrea Claudi Date: Thu, 4 Jun 2020 21:43:50 +0200 Subject: [PATCH] tc: f_flower: add options support for vxlan Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1830485 Upstream Status: unknown commit 93c8d5f72f8ce Conflicts: on a removed line due to missing commit 93c8d5f72f8ce ("tc: f_flower: add options support for vxlan") commit 93c8d5f72f8ce4b98c68508e85457f83933302c0 Author: Xin Long Date: Mon Apr 27 18:27:50 2020 +0800 tc: f_flower: add options support for vxlan This patch is to add TCA_FLOWER_KEY_ENC_OPTS_VXLAN's parse and print to implement vxlan options support in m_tunnel_key, like Commit 56155d4df86d ("tc: f_flower: add geneve option match support to flower") for geneve options support. Option is expressed a 32bit number for gbp only, and vxlan doesn't support multiple options. With this patch, users can add and dump vxlan options like: # ip link add name vxlan1 type vxlan dstport 0 external # tc qdisc add dev vxlan1 ingress # tc filter add dev vxlan1 protocol ip parent ffff: \ flower \ enc_src_ip 10.0.99.192 \ enc_dst_ip 10.0.99.193 \ enc_key_id 11 \ vxlan_opts 65793/4008635966 \ ip_proto udp \ action mirred egress redirect dev eth1 # tc -s filter show dev vxlan1 parent ffff: filter protocol ip pref 49152 flower chain 0 handle 0x1 eth_type ipv4 ip_proto udp enc_dst_ip 10.0.99.193 enc_src_ip 10.0.99.192 enc_key_id 11 vxlan_opts 65793/4008635966 not_in_hw action order 1: mirred (Egress Redirect to device eth1) stolen index 3 ref 1 bind 1 Action statistics: Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 v1->v2: - get_u32 with base = 0 for gbp. v2->v3: - implement proper JSON array for opts. v3->v4: - keep the same format between input and output, json and non json. - print gbp as uint. Signed-off-by: Xin Long Signed-off-by: David Ahern --- man/man8/tc-flower.8 | 12 ++++ tc/f_flower.c | 130 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 126 insertions(+), 16 deletions(-) diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 index adff41e39b006..0efacbfdf9a95 100644 --- a/man/man8/tc-flower.8 +++ b/man/man8/tc-flower.8 @@ -81,7 +81,11 @@ flower \- flow based traffic control filter .IR TOS " | " .B enc_ttl .IR TTL " | " +{ .B geneve_opts +| +.B vxlan_opts +} .IR OPTIONS " | " .BR ip_flags .IR IP_FLAGS @@ -290,6 +294,8 @@ bits is assumed. .BI enc_ttl " NUMBER" .TQ .BI geneve_opts " OPTIONS" +.TQ +.BI vxlan_opts " OPTIONS" Match on IP tunnel metadata. Key id .I NUMBER is a 32 bit tunnel key id (e.g. VNI for VXLAN tunnel). @@ -310,6 +316,12 @@ 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. +vxlan_opts +.I OPTIONS +doesn't support multiple options, and it consists of a key followed by a slash +and corresponding mask. If the mask is missing, \fBtc\fR assumes a full-length +match. The option can be described in the form GBP/GBP_MASK, where GBP is +represented as a 32bit number. .TP .BI ip_flags " IP_FLAGS" .I IP_FLAGS diff --git a/tc/f_flower.c b/tc/f_flower.c index 70d40d3b2f2bf..09079cd2c2280 100644 --- a/tc/f_flower.c +++ b/tc/f_flower.c @@ -81,6 +81,7 @@ static void explain(void) " enc_tos MASKED-IP_TOS |\n" " enc_ttl MASKED-IP_TTL |\n" " geneve_opts MASKED-OPTIONS |\n" + " vxlan_opts MASKED-OPTIONS |\n" " ip_flags IP-FLAGS | \n" " enc_dst_port [ port_number ] }\n" " FILTERID := X:Y:Z\n" @@ -648,7 +649,7 @@ 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) +static int flower_parse_geneve_opt(char *str, struct nlmsghdr *n) { struct rtattr *nest; char *token; @@ -718,14 +719,33 @@ static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n) return 0; } -static int flower_parse_enc_opt_part(char *str, struct nlmsghdr *n) +static int flower_parse_vxlan_opt(char *str, struct nlmsghdr *n) +{ + struct rtattr *nest; + __u32 gbp; + int err; + + nest = addattr_nest(n, MAX_MSG, + TCA_FLOWER_KEY_ENC_OPTS_VXLAN | NLA_F_NESTED); + + err = get_u32(&gbp, str, 0); + if (err) + return err; + addattr32(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, gbp); + + addattr_nest_end(n, nest); + + return 0; +} + +static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n) { char *token; int err; token = strsep(&str, ","); while (token) { - err = flower_parse_geneve_opts(token, n); + err = flower_parse_geneve_opt(token, n); if (err) return err; @@ -755,7 +775,7 @@ static int flower_check_enc_opt_key(char *key) return 0; } -static int flower_parse_enc_opts(char *str, struct nlmsghdr *n) +static int flower_parse_enc_opts_geneve(char *str, struct nlmsghdr *n) { char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX]; int data_len, key_len, mask_len, err; @@ -807,13 +827,50 @@ static int flower_parse_enc_opts(char *str, struct nlmsghdr *n) mask[mask_len - 1] = '\0'; nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS); - err = flower_parse_enc_opt_part(key, n); + err = flower_parse_geneve_opts(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); + err = flower_parse_geneve_opts(mask, n); + if (err) + return err; + addattr_nest_end(n, nest); + + return 0; +} + +static int flower_parse_enc_opts_vxlan(char *str, struct nlmsghdr *n) +{ + char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX]; + struct rtattr *nest; + char *slash; + int err; + + slash = strchr(str, '/'); + if (slash) { + *slash++ = '\0'; + if (strlen(slash) > XATTR_SIZE_MAX) + return -1; + strcpy(mask, slash); + } else { + strcpy(mask, "0xffffffff"); + } + + if (strlen(str) > XATTR_SIZE_MAX) + return -1; + strcpy(key, str); + + nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS | NLA_F_NESTED); + err = flower_parse_vxlan_opt(str, n); + if (err) + return err; + addattr_nest_end(n, nest); + + nest = addattr_nest(n, MAX_MSG, + TCA_FLOWER_KEY_ENC_OPTS_MASK | NLA_F_NESTED); + err = flower_parse_vxlan_opt(mask, n); if (err) return err; addattr_nest_end(n, nest); @@ -1275,11 +1332,18 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, } } else if (matches(*argv, "geneve_opts") == 0) { NEXT_ARG(); - ret = flower_parse_enc_opts(*argv, n); + ret = flower_parse_enc_opts_geneve(*argv, n); if (ret < 0) { fprintf(stderr, "Illegal \"geneve_opts\"\n"); return -1; } + } else if (matches(*argv, "vxlan_opts") == 0) { + NEXT_ARG(); + ret = flower_parse_enc_opts_vxlan(*argv, n); + if (ret < 0) { + fprintf(stderr, "Illegal \"vxlan_opts\"\n"); + return -1; + } } else if (matches(*argv, "action") == 0) { NEXT_ARG(); ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n); @@ -1643,10 +1707,29 @@ static void flower_print_geneve_opts(const char *name, struct rtattr *attr, close_json_array(PRINT_JSON, name); } -static void flower_print_geneve_parts(const char *name, struct rtattr *attr, - char *key, char *mask) +static void flower_print_vxlan_opts(const char *name, struct rtattr *attr, + char *strbuf) +{ + struct rtattr *tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX + 1]; + struct rtattr *i = RTA_DATA(attr); + int rem = RTA_PAYLOAD(attr); + __u32 gbp; + + parse_rtattr(tb, TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX, i, rem); + gbp = rta_getattr_u32(tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP]); + + open_json_array(PRINT_JSON, name); + open_json_object(NULL); + print_uint(PRINT_JSON, "gbp", NULL, gbp); + close_json_object(); + close_json_array(PRINT_JSON, name); + + sprintf(strbuf, "%u", gbp); +} + +static void flower_print_enc_parts(const char *name, const char *namefrm, + struct rtattr *attr, char *key, char *mask) { - char *namefrm = "\n geneve_opt %s"; char *key_token, *mask_token, *out; int len; @@ -1687,14 +1770,29 @@ static void flower_print_enc_opts(const char *name, struct rtattr *attr, 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); + if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE]) { + flower_print_geneve_opts("geneve_opt_key", + key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], key); + + if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE]) + flower_print_geneve_opts("geneve_opt_mask", + msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], msk); + + flower_print_enc_parts(name, " geneve_opts %s", attr, key, + msk); + } else if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN]) { + flower_print_vxlan_opts("vxlan_opt_key", + key_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN], key); + + if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN]) + flower_print_vxlan_opts("vxlan_opt_mask", + msk_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN], msk); + + flower_print_enc_parts(name, " vxlan_opts %s", attr, key, + msk); + } free(msk); err_key_free: -- 2.26.2