From: Simon Horman
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 : \
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
---
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 (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(, 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(, 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, ":", );
+ 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, ":", );
+ 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, ",", );
+ while (token) {
+ ret = tunnel_key_parse_geneve_opt(token, n);
+ if (ret)
+ return