]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - ip/iproute_lwtunnel.c
iproute_lwtunnel: add options support for vxlan metadata
[mirror_iproute2.git] / ip / iproute_lwtunnel.c
index 03217b8f08f86fd2b385c6c8c64bb87936a58709..bbd0ace78eea4e463b0ae993acb1073545d1b782 100644 (file)
@@ -229,6 +229,8 @@ static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
        struct rtattr *tb[SEG6_LOCAL_MAX + 1];
        int action;
 
+       SPRINT_BUF(b1);
+
        parse_rtattr_nested(tb, SEG6_LOCAL_MAX, encap);
 
        if (!tb[SEG6_LOCAL_ACTION])
@@ -246,8 +248,9 @@ static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
        }
 
        if (tb[SEG6_LOCAL_TABLE])
-               print_uint(PRINT_ANY, "table",
-                          "table %u ", rta_getattr_u32(tb[SEG6_LOCAL_TABLE]));
+               print_string(PRINT_ANY, "table", "table %s ",
+                            rtnl_rttable_n2a(rta_getattr_u32(tb[SEG6_LOCAL_TABLE]),
+                            b1, sizeof(b1)));
 
        if (tb[SEG6_LOCAL_NH4]) {
                print_string(PRINT_ANY, "nh4",
@@ -291,6 +294,76 @@ static void print_encap_mpls(FILE *fp, struct rtattr *encap)
                        rta_getattr_u8(tb[MPLS_IPTUNNEL_TTL]));
 }
 
+static void lwtunnel_print_geneve_opts(struct rtattr *attr)
+{
+       struct rtattr *tb[LWTUNNEL_IP_OPT_GENEVE_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       int rem = RTA_PAYLOAD(attr);
+       char *name = "geneve_opts";
+       int data_len, offset = 0;
+       char data[rem * 2 + 1];
+       __u16 class;
+       __u8 type;
+
+       print_nl();
+       print_string(PRINT_FP, name, "\t%s ", name);
+       open_json_array(PRINT_JSON, name);
+
+       while (rem) {
+               parse_rtattr(tb, LWTUNNEL_IP_OPT_GENEVE_MAX, i, rem);
+               class = rta_getattr_be16(tb[LWTUNNEL_IP_OPT_GENEVE_CLASS]);
+               type = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_GENEVE_TYPE]);
+               data_len = RTA_PAYLOAD(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]);
+               hexstring_n2a(RTA_DATA(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]),
+                             data_len, data, sizeof(data));
+               offset += data_len + 20;
+               rem -= data_len + 20;
+               i = RTA_DATA(attr) + offset;
+
+               open_json_object(NULL);
+               print_uint(PRINT_ANY, "class", "%u", class);
+               print_uint(PRINT_ANY, "type", ":%u", type);
+               if (rem)
+                       print_string(PRINT_ANY, "data", ":%s,", data);
+               else
+                       print_string(PRINT_ANY, "data", ":%s ", data);
+               close_json_object();
+       }
+
+       close_json_array(PRINT_JSON, name);
+}
+
+static void lwtunnel_print_vxlan_opts(struct rtattr *attr)
+{
+       struct rtattr *tb[LWTUNNEL_IP_OPT_VXLAN_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       int rem = RTA_PAYLOAD(attr);
+       char *name = "vxlan_opts";
+       __u32 gbp;
+
+       parse_rtattr(tb, LWTUNNEL_IP_OPT_VXLAN_MAX, i, rem);
+       gbp = rta_getattr_u32(tb[LWTUNNEL_IP_OPT_VXLAN_GBP]);
+
+       print_nl();
+       print_string(PRINT_FP, name, "\t%s ", name);
+       open_json_array(PRINT_JSON, name);
+       open_json_object(NULL);
+       print_uint(PRINT_ANY, "gbp", "%u ", gbp);
+       close_json_object();
+       close_json_array(PRINT_JSON, name);
+}
+
+static void lwtunnel_print_opts(struct rtattr *attr)
+{
+       struct rtattr *tb_opt[LWTUNNEL_IP_OPTS_MAX + 1];
+
+       parse_rtattr_nested(tb_opt, LWTUNNEL_IP_OPTS_MAX, attr);
+       if (tb_opt[LWTUNNEL_IP_OPTS_GENEVE])
+               lwtunnel_print_geneve_opts(tb_opt[LWTUNNEL_IP_OPTS_GENEVE]);
+       else if (tb_opt[LWTUNNEL_IP_OPTS_VXLAN])
+               lwtunnel_print_vxlan_opts(tb_opt[LWTUNNEL_IP_OPTS_VXLAN]);
+}
+
 static void print_encap_ip(FILE *fp, struct rtattr *encap)
 {
        struct rtattr *tb[LWTUNNEL_IP_MAX+1];
@@ -329,6 +402,9 @@ static void print_encap_ip(FILE *fp, struct rtattr *encap)
                if (flags & TUNNEL_SEQ)
                        print_bool(PRINT_ANY, "seq", "seq ", true);
        }
+
+       if (tb[LWTUNNEL_IP_OPTS])
+               lwtunnel_print_opts(tb[LWTUNNEL_IP_OPTS]);
 }
 
 static void print_encap_ila(FILE *fp, struct rtattr *encap)
@@ -401,6 +477,9 @@ static void print_encap_ip6(FILE *fp, struct rtattr *encap)
                if (flags & TUNNEL_SEQ)
                        print_bool(PRINT_ANY, "seq", "seq ", true);
        }
+
+       if (tb[LWTUNNEL_IP6_OPTS])
+               lwtunnel_print_opts(tb[LWTUNNEL_IP6_OPTS]);
 }
 
 static void print_encap_bpf(FILE *fp, struct rtattr *encap)
@@ -654,7 +733,7 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
                        NEXT_ARG();
                        if (table_ok++)
                                duparg2("table", *argv);
-                       get_u32(&table, *argv, 0);
+                       rtnl_rttable_a2n(&table, *argv);
                        ret = rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table);
                } else if (strcmp(*argv, "nh4") == 0) {
                        NEXT_ARG();
@@ -795,11 +874,113 @@ static int parse_encap_mpls(struct rtattr *rta, size_t len,
        return 0;
 }
 
+static int lwtunnel_parse_geneve_opt(char *str, size_t len, struct rtattr *rta)
+{
+       struct rtattr *nest;
+       char *token;
+       int i, err;
+
+       nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_GENEVE | NLA_F_NESTED);
+       i = 1;
+       token = strsep(&str, ":");
+       while (token) {
+               switch (i) {
+               case LWTUNNEL_IP_OPT_GENEVE_CLASS:
+               {
+                       __be16 opt_class;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_be16(&opt_class, token, 0);
+                       if (err)
+                               return err;
+
+                       rta_addattr16(rta, len, i, opt_class);
+                       break;
+               }
+               case LWTUNNEL_IP_OPT_GENEVE_TYPE:
+               {
+                       __u8 opt_type;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_u8(&opt_type, token, 0);
+                       if (err)
+                               return err;
+
+                       rta_addattr8(rta, len, i, opt_type);
+                       break;
+               }
+               case LWTUNNEL_IP_OPT_GENEVE_DATA:
+               {
+                       size_t token_len = strlen(token);
+                       __u8 *opts;
+
+                       if (!token_len)
+                               break;
+                       opts = malloc(token_len / 2);
+                       if (!opts)
+                               return -1;
+                       if (hex2mem(token, opts, token_len / 2) < 0) {
+                               free(opts);
+                               return -1;
+                       }
+                       rta_addattr_l(rta, len, i, opts, token_len / 2);
+                       free(opts);
+
+                       break;
+               }
+               default:
+                       fprintf(stderr, "Unknown \"geneve_opts\" type\n");
+                       return -1;
+               }
+
+               token = strsep(&str, ":");
+               i++;
+       }
+       rta_nest_end(rta, nest);
+
+       return 0;
+}
+
+static int lwtunnel_parse_geneve_opts(char *str, size_t len, struct rtattr *rta)
+{
+       char *token;
+       int err;
+
+       token = strsep(&str, ",");
+       while (token) {
+               err = lwtunnel_parse_geneve_opt(token, len, rta);
+               if (err)
+                       return err;
+
+               token = strsep(&str, ",");
+       }
+
+       return 0;
+}
+
+static int lwtunnel_parse_vxlan_opts(char *str, size_t len, struct rtattr *rta)
+{
+       struct rtattr *nest;
+       __u32 gbp;
+       int err;
+
+       nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_VXLAN | NLA_F_NESTED);
+       err = get_u32(&gbp, str, 0);
+       if (err)
+               return err;
+       rta_addattr32(rta, len, LWTUNNEL_IP_OPT_VXLAN_GBP, gbp);
+
+       rta_nest_end(rta, nest);
+       return 0;
+}
+
 static int parse_encap_ip(struct rtattr *rta, size_t len,
                          int *argcp, char ***argvp)
 {
        int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
-       int key_ok = 0, csum_ok = 0, seq_ok = 0;
+       int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0;
        char **argv = *argvp;
        int argc = *argcp;
        int ret = 0;
@@ -851,6 +1032,36 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
                        if (get_u8(&ttl, *argv, 0))
                                invarg("\"ttl\" value is invalid\n", *argv);
                        ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
+               } else if (strcmp(*argv, "geneve_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_geneve_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"geneve_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
+               } else if (strcmp(*argv, "vxlan_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_vxlan_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"vxlan_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
                } else if (strcmp(*argv, "key") == 0) {
                        if (key_ok++)
                                duparg2("key", *argv);
@@ -966,7 +1177,7 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
                           int *argcp, char ***argvp)
 {
        int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
-       int key_ok = 0, csum_ok = 0, seq_ok = 0;
+       int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0;
        char **argv = *argvp;
        int argc = *argcp;
        int ret = 0;
@@ -1020,6 +1231,36 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
                                       *argv);
                        ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT,
                                           hoplimit);
+               } else if (strcmp(*argv, "geneve_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_geneve_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"geneve_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
+               } else if (strcmp(*argv, "vxlan_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_vxlan_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"vxlan_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
                } else if (strcmp(*argv, "key") == 0) {
                        if (key_ok++)
                                duparg2("key", *argv);
@@ -1111,7 +1352,8 @@ static int parse_encap_bpf(struct rtattr *rta, size_t len, int *argcp,
        return 0;
 }
 
-int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
+int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
+                   int encap_attr, int encap_type_attr)
 {
        struct rtattr *nest;
        int argc = *argcp;
@@ -1131,7 +1373,7 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
                exit(-1);
        }
 
-       nest = rta_nest(rta, len, RTA_ENCAP);
+       nest = rta_nest(rta, len, encap_attr);
        switch (type) {
        case LWTUNNEL_ENCAP_MPLS:
                ret = parse_encap_mpls(rta, len, &argc, &argv);
@@ -1164,7 +1406,7 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
 
        rta_nest_end(rta, nest);
 
-       ret = rta_addattr16(rta, len, RTA_ENCAP_TYPE, type);
+       ret = rta_addattr16(rta, len, encap_type_attr, type);
 
        *argcp = argc;
        *argvp = argv;