From: Simon Horman <simon.hor...@netronome.com> 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 <simon.hor...@netronome.com> Signed-off-by: Pieter Jansen van Vuuren <pieter.jansenvanvuu...@netronome.com> Reviewed-by: Jakub Kicinski <jakub.kicin...@netronome.com> --- 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 e979a74715cb..7d4b30e41faf 100644 --- a/man/man8/tc-tunnel_key.8 +++ b/man/man8/tc-tunnel_key.8 @@ -64,7 +64,9 @@ and .B dst_ip options. .B dst_port -is optional. +and +.B geneve_opts +are optional. .RS .TP .B id @@ -79,6 +81,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 .RB [ no ] csum Controlls outer UDP checksum. When set to .B csum diff --git a/tc/m_tunnel_key.c b/tc/m_tunnel_key.c index 0fa461549ad9..5a0e3fc3c48f 100644 --- a/tc/m_tunnel_key.c +++ b/tc/m_tunnel_key.c @@ -29,6 +29,7 @@ static void explain(void) "src_ip <IP> (mandatory)\n" "dst_ip <IP> (mandatory)\n" "dst_port <UDP_PORT>\n" + "geneve_opts <OPTIONS>\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 parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { @@ -157,6 +266,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, "csum") == 0) { csum = 1; } else if (matches(*argv, "nocsum") == 0) { @@ -260,6 +376,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 int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg) { struct rtattr *tb[TCA_TUNNEL_KEY_MAX + 1]; @@ -297,6 +472,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]); break; -- 2.17.1