diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 index adff41e..0efacbf 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 70d40d3..09079cd 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: