From d75736d332f6aa0fcd12352e2d2a5c1aa65c6464 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 31 Jan 2019 17:13:07 +0100 Subject: [PATCH] tc: m_tunnel_key: Add tunnel option support to act_tunnel_key Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1654761 Upstream Status: iproute2.git commit 6217917a38268 Conflicts: Context change due to previous backport of tos and ttl support. commit 6217917a382682d8e8a7ecdeb0c6626f701a0933 Author: Simon Horman Date: Thu Jul 5 17:12:00 2018 -0700 tc: m_tunnel_key: Add tunnel option support to act_tunnel_key Allow setting tunnel options using the act_tunnel_key action. Options are expressed as class:type:data and multiple options may be listed using a comma delimiter. # ip link add name geneve0 type geneve dstport 0 external # tc qdisc add dev eth0 ingress # tc filter add dev eth0 protocol ip parent ffff: \ flower indev eth0 \ ip_proto udp \ action tunnel_key \ set src_ip 10.0.99.192 \ dst_ip 10.0.99.193 \ dst_port 6081 \ id 11 \ geneve_opts 0102:80:00800022,0102:80:00800022 \ action mirred egress redirect dev geneve0 Signed-off-by: Simon Horman Signed-off-by: Pieter Jansen van Vuuren Reviewed-by: Jakub Kicinski Signed-off-by: David Ahern --- man/man8/tc-tunnel_key.8 | 12 +++- tc/m_tunnel_key.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 1 deletion(-) diff --git a/man/man8/tc-tunnel_key.8 b/man/man8/tc-tunnel_key.8 index 71cee5b..1e09362 100644 --- a/man/man8/tc-tunnel_key.8 +++ b/man/man8/tc-tunnel_key.8 @@ -66,7 +66,9 @@ and .B dst_ip options. .B dst_port -is optional. +and +.B geneve_opts +are optional. .RS .TP .B id @@ -81,6 +83,14 @@ Outer header destination IP address (IPv4 or IPv6) .B dst_port Outer header destination UDP port .TP +.B geneve_opts +Geneve variable length options. +.B geneve_opts +is specified in the form CLASS:TYPE:DATA, where CLASS is represented as a +16bit hexadecimal value, TYPE as an 8bit hexadecimal value and DATA as a +variable length hexadecimal value. Additionally multiple options may be +listed using a comma delimiter. +.TP .B tos Outer header TOS .TP diff --git a/tc/m_tunnel_key.c b/tc/m_tunnel_key.c index 8d0a8d1..e9e71e4 100644 --- a/tc/m_tunnel_key.c +++ b/tc/m_tunnel_key.c @@ -29,6 +29,7 @@ static void explain(void) "src_ip (mandatory)\n" "dst_ip (mandatory)\n" "dst_port \n" + "geneve_opts \n" "csum | nocsum (default is \"csum\")\n"); } @@ -81,6 +82,114 @@ static int tunnel_key_parse_dst_port(char *str, int type, struct nlmsghdr *n) return 0; } +static int tunnel_key_parse_be16(char *str, int base, int type, + struct nlmsghdr *n) +{ + int ret; + __be16 value; + + ret = get_be16(&value, str, base); + if (ret) + return ret; + + addattr16(n, MAX_MSG, type, value); + + return 0; +} + +static int tunnel_key_parse_u8(char *str, int base, int type, + struct nlmsghdr *n) +{ + int ret; + __u8 value; + + ret = get_u8(&value, str, base); + if (ret) + return ret; + + addattr8(n, MAX_MSG, type, value); + + return 0; +} + +static int tunnel_key_parse_geneve_opt(char *str, struct nlmsghdr *n) +{ + char *token, *saveptr = NULL; + struct rtattr *nest; + int i, ret; + + nest = addattr_nest(n, MAX_MSG, TCA_TUNNEL_KEY_ENC_OPTS_GENEVE); + + token = strtok_r(str, ":", &saveptr); + i = 1; + while (token) { + switch (i) { + case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS: + { + ret = tunnel_key_parse_be16(token, 16, i, n); + if (ret) + return ret; + break; + } + case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE: + { + ret = tunnel_key_parse_u8(token, 16, i, n); + if (ret) + return ret; + break; + } + case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA: + { + size_t token_len = strlen(token); + uint8_t *opts; + + 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: + return -1; + } + + token = strtok_r(NULL, ":", &saveptr); + i++; + } + + addattr_nest_end(n, nest); + + return 0; +} + +static int tunnel_key_parse_geneve_opts(char *str, struct nlmsghdr *n) +{ + char *token, *saveptr = NULL; + struct rtattr *nest; + int ret; + + nest = addattr_nest(n, MAX_MSG, TCA_TUNNEL_KEY_ENC_OPTS); + + token = strtok_r(str, ",", &saveptr); + while (token) { + ret = tunnel_key_parse_geneve_opt(token, n); + if (ret) + return ret; + + token = strtok_r(NULL, ",", &saveptr); + } + + addattr_nest_end(n, nest); + + return 0; +} + static int tunnel_key_parse_tos_ttl(char *str, int type, struct nlmsghdr *n) { int ret; @@ -173,6 +282,13 @@ static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p, fprintf(stderr, "Illegal \"dst port\"\n"); return -1; } + } else if (matches(*argv, "geneve_opts") == 0) { + NEXT_ARG(); + + if (tunnel_key_parse_geneve_opts(*argv, n)) { + fprintf(stderr, "Illegal \"geneve_opts\"\n"); + return -1; + } } else if (matches(*argv, "tos") == 0) { NEXT_ARG(); ret = tunnel_key_parse_tos_ttl(*argv, @@ -292,6 +408,65 @@ static void tunnel_key_print_flag(FILE *f, const char *name_on, rta_getattr_u8(attr) ? name_on : name_off); } +static void tunnel_key_print_geneve_options(const char *name, + struct rtattr *attr) +{ + struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1]; + struct rtattr *i = RTA_DATA(attr); + int ii, data_len = 0, offset = 0; + int rem = RTA_PAYLOAD(attr); + char strbuf[rem * 2 + 1]; + char data[rem * 2 + 1]; + uint8_t data_r[rem]; + uint16_t clss; + uint8_t type; + + open_json_array(PRINT_JSON, name); + print_string(PRINT_FP, name, "\n\t%s ", "geneve_opt"); + + while (rem) { + parse_rtattr(tb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX, i, rem); + clss = rta_getattr_be16(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS]); + type = rta_getattr_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE]); + data_len = RTA_PAYLOAD(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]); + hexstring_n2a(RTA_DATA(tb[TCA_TUNNEL_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, clss); + 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(); + + sprintf(strbuf, "%04x:%02x:%s", clss, type, data); + if (rem) + print_string(PRINT_FP, NULL, "%s,", strbuf); + else + print_string(PRINT_FP, NULL, "%s", strbuf); + } + + close_json_array(PRINT_JSON, name); +} + +static void tunnel_key_print_key_opt(const char *name, struct rtattr *attr) +{ + struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPTS_MAX + 1]; + + if (!attr) + return; + + parse_rtattr_nested(tb, TCA_TUNNEL_KEY_ENC_OPTS_MAX, attr); + tunnel_key_print_geneve_options(name, + tb[TCA_TUNNEL_KEY_ENC_OPTS_GENEVE]); +} + static void tunnel_key_print_tos_ttl(FILE *f, char *name, struct rtattr *attr) { @@ -346,6 +521,8 @@ static int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg) tb[TCA_TUNNEL_KEY_ENC_KEY_ID]); tunnel_key_print_dst_port(f, "dst_port", tb[TCA_TUNNEL_KEY_ENC_DST_PORT]); + tunnel_key_print_key_opt("geneve_opts", + tb[TCA_TUNNEL_KEY_ENC_OPTS]); tunnel_key_print_flag(f, "nocsum", "csum", tb[TCA_TUNNEL_KEY_NO_CSUM]); tunnel_key_print_tos_ttl(f, "tos", -- 1.8.3.1