]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - ip/iproute_lwtunnel.c
vdpa: add .gitignore
[mirror_iproute2.git] / ip / iproute_lwtunnel.c
index 5ee37f715eca628b3ee73b67b1e15159c3d9c8da..566fc7ea340dae02aef67076011e9c1eea46645e 100644 (file)
 
 #include <linux/seg6.h>
 #include <linux/seg6_iptunnel.h>
+#include <linux/rpl.h>
+#include <linux/rpl_iptunnel.h>
 #include <linux/seg6_hmac.h>
 #include <linux/seg6_local.h>
-#include <net/if.h>
+#include <linux/if_tunnel.h>
 
 static const char *format_encap_type(int type)
 {
@@ -50,6 +52,8 @@ static const char *format_encap_type(int type)
                return "seg6";
        case LWTUNNEL_ENCAP_SEG6_LOCAL:
                return "seg6local";
+       case LWTUNNEL_ENCAP_RPL:
+               return "rpl";
        default:
                return "unknown";
        }
@@ -84,6 +88,8 @@ static int read_encap_type(const char *name)
                return LWTUNNEL_ENCAP_SEG6;
        else if (strcmp(name, "seg6local") == 0)
                return LWTUNNEL_ENCAP_SEG6_LOCAL;
+       else if (strcmp(name, "rpl") == 0)
+               return LWTUNNEL_ENCAP_RPL;
        else if (strcmp(name, "help") == 0)
                encap_type_usage();
 
@@ -115,7 +121,7 @@ static void print_srh(FILE *fp, struct ipv6_sr_hdr *srh)
 
                tlv = (struct sr6_tlv_hmac *)((char *)srh + offset);
                print_0xhex(PRINT_ANY, "hmac",
-                           "hmac 0x%X ", ntohl(tlv->hmackeyid));
+                           "hmac %llX ", ntohl(tlv->hmackeyid));
        }
 }
 
@@ -162,6 +168,42 @@ static void print_encap_seg6(FILE *fp, struct rtattr *encap)
        print_srh(fp, tuninfo->srh);
 }
 
+static void print_rpl_srh(FILE *fp, struct ipv6_rpl_sr_hdr *srh)
+{
+       int i;
+
+       if (is_json_context())
+               open_json_array(PRINT_JSON, "segs");
+       else
+               fprintf(fp, "segs %d [ ", srh->segments_left);
+
+       for (i = srh->segments_left - 1; i >= 0; i--) {
+               print_color_string(PRINT_ANY, COLOR_INET6,
+                                  NULL, "%s ",
+                                  rt_addr_n2a(AF_INET6, 16, &srh->rpl_segaddr[i]));
+       }
+
+       if (is_json_context())
+               close_json_array(PRINT_JSON, NULL);
+       else
+               fprintf(fp, "] ");
+}
+
+static void print_encap_rpl(FILE *fp, struct rtattr *encap)
+{
+       struct rtattr *tb[RPL_IPTUNNEL_MAX + 1];
+       struct ipv6_rpl_sr_hdr *srh;
+
+       parse_rtattr_nested(tb, RPL_IPTUNNEL_MAX, encap);
+
+       if (!tb[RPL_IPTUNNEL_SRH])
+               return;
+
+       srh = RTA_DATA(tb[RPL_IPTUNNEL_SRH]);
+
+       print_rpl_srh(fp, srh);
+}
+
 static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
        [SEG6_LOCAL_ACTION_END]                 = "End",
        [SEG6_LOCAL_ACTION_END_X]               = "End.X",
@@ -229,6 +271,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 +290,14 @@ 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_VRFTABLE])
+               print_string(PRINT_ANY, "vrftable", "vrftable %s ",
+                            rtnl_rttable_n2a(rta_getattr_u32(tb[SEG6_LOCAL_VRFTABLE]),
+                            b1, sizeof(b1)));
 
        if (tb[SEG6_LOCAL_NH4]) {
                print_string(PRINT_ANY, "nh4",
@@ -291,9 +341,114 @@ 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_erspan_opts(struct rtattr *attr)
+{
+       struct rtattr *tb[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       char *name = "erspan_opts";
+       __u8 ver, hwid, dir;
+       __u32 idx;
+
+       parse_rtattr(tb, LWTUNNEL_IP_OPT_ERSPAN_MAX, i, RTA_PAYLOAD(attr));
+       ver = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_VER]);
+       if (ver == 1) {
+               idx = rta_getattr_be32(tb[LWTUNNEL_IP_OPT_ERSPAN_INDEX]);
+               dir = 0;
+               hwid = 0;
+       } else {
+               idx = 0;
+               dir = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_DIR]);
+               hwid = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_HWID]);
+       }
+
+       print_nl();
+       print_string(PRINT_FP, name, "\t%s ", name);
+       open_json_array(PRINT_JSON, name);
+       open_json_object(NULL);
+       print_uint(PRINT_ANY, "ver", "%u", ver);
+       print_uint(PRINT_ANY, "index", ":%u", idx);
+       print_uint(PRINT_ANY, "dir", ":%u", dir);
+       print_uint(PRINT_ANY, "hwid", ":%u ", hwid);
+       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]);
+       else if (tb_opt[LWTUNNEL_IP_OPTS_ERSPAN])
+               lwtunnel_print_erspan_opts(tb_opt[LWTUNNEL_IP_OPTS_ERSPAN]);
+}
+
 static void print_encap_ip(FILE *fp, struct rtattr *encap)
 {
        struct rtattr *tb[LWTUNNEL_IP_MAX+1];
+       __u16 flags;
 
        parse_rtattr_nested(tb, LWTUNNEL_IP_MAX, encap);
 
@@ -318,6 +473,19 @@ static void print_encap_ip(FILE *fp, struct rtattr *encap)
        if (tb[LWTUNNEL_IP_TOS])
                print_uint(PRINT_ANY, "tos",
                           "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS]));
+
+       if (tb[LWTUNNEL_IP_FLAGS]) {
+               flags = rta_getattr_u16(tb[LWTUNNEL_IP_FLAGS]);
+               if (flags & TUNNEL_KEY)
+                       print_bool(PRINT_ANY, "key", "key ", true);
+               if (flags & TUNNEL_CSUM)
+                       print_bool(PRINT_ANY, "csum", "csum ", true);
+               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)
@@ -354,6 +522,7 @@ static void print_encap_ila(FILE *fp, struct rtattr *encap)
 static void print_encap_ip6(FILE *fp, struct rtattr *encap)
 {
        struct rtattr *tb[LWTUNNEL_IP6_MAX+1];
+       __u16 flags;
 
        parse_rtattr_nested(tb, LWTUNNEL_IP6_MAX, encap);
 
@@ -379,6 +548,19 @@ static void print_encap_ip6(FILE *fp, struct rtattr *encap)
        if (tb[LWTUNNEL_IP6_TC])
                print_uint(PRINT_ANY, "tc",
                           "tc %u ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC]));
+
+       if (tb[LWTUNNEL_IP6_FLAGS]) {
+               flags = rta_getattr_u16(tb[LWTUNNEL_IP6_FLAGS]);
+               if (flags & TUNNEL_KEY)
+                       print_bool(PRINT_ANY, "key", "key ", true);
+               if (flags & TUNNEL_CSUM)
+                       print_bool(PRINT_ANY, "csum", "csum ", true);
+               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)
@@ -432,6 +614,9 @@ void lwt_print_encap(FILE *fp, struct rtattr *encap_type,
        case LWTUNNEL_ENCAP_SEG6_LOCAL:
                print_encap_seg6local(fp, encap);
                break;
+       case LWTUNNEL_ENCAP_RPL:
+               print_encap_rpl(fp, encap);
+               break;
        }
 }
 
@@ -494,10 +679,11 @@ static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp,
        struct seg6_iptunnel_encap *tuninfo;
        struct ipv6_sr_hdr *srh;
        char **argv = *argvp;
-       char segbuf[1024];
+       char segbuf[1024] = "";
        int argc = *argcp;
        int encap = -1;
        __u32 hmac = 0;
+       int ret = 0;
        int srhlen;
 
        while (argc > 0) {
@@ -539,16 +725,92 @@ static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp,
        memcpy(tuninfo->srh, srh, srhlen);
 
        if (rta_addattr_l(rta, len, SEG6_IPTUNNEL_SRH, tuninfo,
-                         sizeof(*tuninfo) + srhlen))
-               return -1;
+                         sizeof(*tuninfo) + srhlen)) {
+               ret = -1;
+               goto out;
+       }
 
+       *argcp = argc + 1;
+       *argvp = argv - 1;
+
+out:
        free(tuninfo);
        free(srh);
 
+       return ret;
+}
+
+static struct ipv6_rpl_sr_hdr *parse_rpl_srh(char *segbuf)
+{
+       struct ipv6_rpl_sr_hdr *srh;
+       int nsegs = 0;
+       int srhlen;
+       char *s;
+       int i;
+
+       s = segbuf;
+       for (i = 0; *s; *s++ == ',' ? i++ : *s);
+       nsegs = i + 1;
+
+       srhlen = 8 + 16 * nsegs;
+
+       srh = calloc(1, srhlen);
+
+       srh->hdrlen = (srhlen >> 3) - 1;
+       srh->type = 3;
+       srh->segments_left = nsegs;
+
+       for (s = strtok(segbuf, ","); s; s = strtok(NULL, ",")) {
+               inet_prefix addr;
+
+               get_addr(&addr, s, AF_INET6);
+               memcpy(&srh->rpl_segaddr[i], addr.data, sizeof(struct in6_addr));
+               i--;
+       }
+
+       return srh;
+}
+
+static int parse_encap_rpl(struct rtattr *rta, size_t len, int *argcp,
+                          char ***argvp)
+{
+       struct ipv6_rpl_sr_hdr *srh;
+       char **argv = *argvp;
+       char segbuf[1024] = "";
+       int argc = *argcp;
+       int segs_ok = 0;
+       int ret = 0;
+       int srhlen;
+
+       while (argc > 0) {
+               if (strcmp(*argv, "segs") == 0) {
+                       NEXT_ARG();
+                       if (segs_ok++)
+                               duparg2("segs", *argv);
+
+                       strlcpy(segbuf, *argv, 1024);
+               } else {
+                       break;
+               }
+               argc--; argv++;
+       }
+
+       srh = parse_rpl_srh(segbuf);
+       srhlen = (srh->hdrlen + 1) << 3;
+
+       if (rta_addattr_l(rta, len, RPL_IPTUNNEL_SRH, srh,
+                         srhlen)) {
+               ret = -1;
+               goto out;
+       }
+
        *argcp = argc + 1;
        *argvp = argv - 1;
 
-       return 0;
+out:
+       free(srh);
+
+       return ret;
 }
 
 struct lwt_x {
@@ -603,9 +865,10 @@ static int lwt_parse_bpf(struct rtattr *rta, size_t len,
 static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
                                 char ***argvp)
 {
-       int segs_ok = 0, hmac_ok = 0, table_ok = 0, nh4_ok = 0, nh6_ok = 0;
-       int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0, bpf_ok = 0;
-       __u32 action = 0, table, iif, oif;
+       int segs_ok = 0, hmac_ok = 0, table_ok = 0, vrftable_ok = 0;
+       int nh4_ok = 0, nh6_ok = 0, iif_ok = 0, oif_ok = 0;
+       __u32 action = 0, table, vrftable, iif, oif;
+       int action_ok = 0, srh_ok = 0, bpf_ok = 0;
        struct ipv6_sr_hdr *srh;
        char **argv = *argvp;
        int argc = *argcp;
@@ -628,8 +891,17 @@ 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);
+                       if (rtnl_rttable_a2n(&table, *argv))
+                               invarg("invalid table id\n", *argv);
                        ret = rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table);
+               } else if (strcmp(*argv, "vrftable") == 0) {
+                       NEXT_ARG();
+                       if (vrftable_ok++)
+                               duparg2("vrftable", *argv);
+                       if (rtnl_rttable_a2n(&vrftable, *argv))
+                               invarg("invalid vrf table id\n", *argv);
+                       ret = rta_addattr32(rta, len, SEG6_LOCAL_VRFTABLE,
+                                           vrftable);
                } else if (strcmp(*argv, "nh4") == 0) {
                        NEXT_ARG();
                        if (nh4_ok++)
@@ -769,13 +1041,193 @@ 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 lwtunnel_parse_erspan_opts(char *str, size_t len, struct rtattr *rta)
+{
+       struct rtattr *nest;
+       char *token;
+       int i, err;
+
+       nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_ERSPAN | NLA_F_NESTED);
+       i = 1;
+       token = strsep(&str, ":");
+       while (token) {
+               switch (i) {
+               case LWTUNNEL_IP_OPT_ERSPAN_VER:
+               {
+                       __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_ERSPAN_INDEX:
+               {
+                       __be32 opt_class;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_be32(&opt_class, token, 0);
+                       if (err)
+                               return err;
+
+                       rta_addattr32(rta, len, i, opt_class);
+                       break;
+               }
+               case LWTUNNEL_IP_OPT_ERSPAN_DIR:
+               {
+                       __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_ERSPAN_HWID:
+               {
+                       __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;
+               }
+               default:
+                       fprintf(stderr, "Unknown \"geneve_opts\" type\n");
+                       return -1;
+               }
+
+               token = strsep(&str, ":");
+               i++;
+       }
+
+       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, opts_ok = 0;
        char **argv = *argvp;
        int argc = *argcp;
        int ret = 0;
+       __u16 flags = 0;
 
        while (argc > 0) {
                if (strcmp(*argv, "id") == 0) {
@@ -823,6 +1275,63 @@ 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, "erspan_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_erspan_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"erspan_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
+               } else if (strcmp(*argv, "key") == 0) {
+                       if (key_ok++)
+                               duparg2("key", *argv);
+                       flags |= TUNNEL_KEY;
+               } else if (strcmp(*argv, "csum") == 0) {
+                       if (csum_ok++)
+                               duparg2("csum", *argv);
+                       flags |= TUNNEL_CSUM;
+               } else if (strcmp(*argv, "seq") == 0) {
+                       if (seq_ok++)
+                               duparg2("seq", *argv);
+                       flags |= TUNNEL_SEQ;
                } else {
                        break;
                }
@@ -831,6 +1340,9 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
                argc--; argv++;
        }
 
+       if (flags)
+               ret = rta_addattr16(rta, len,  LWTUNNEL_IP_FLAGS, flags);
+
        /* argv is currently the first unparsed argument,
         * but the lwt_parse_encap() caller will move to the next,
         * so step back
@@ -856,7 +1368,7 @@ static int parse_encap_ila(struct rtattr *rta, size_t len,
 
        argc--; argv++;
 
-       if (rta_addattr64(rta, 1024, ILA_ATTR_LOCATOR, locator))
+       if (rta_addattr64(rta, len, ILA_ATTR_LOCATOR, locator))
                return -1;
 
        while (argc > 0) {
@@ -870,7 +1382,7 @@ static int parse_encap_ila(struct rtattr *rta, size_t len,
                                invarg("\"csum-mode\" value is invalid\n",
                                       *argv);
 
-                       ret = rta_addattr8(rta, 1024, ILA_ATTR_CSUM_MODE,
+                       ret = rta_addattr8(rta, len, ILA_ATTR_CSUM_MODE,
                                           (__u8)csum_mode);
 
                        argc--; argv++;
@@ -884,7 +1396,7 @@ static int parse_encap_ila(struct rtattr *rta, size_t len,
                                invarg("\"ident-type\" value is invalid\n",
                                       *argv);
 
-                       ret = rta_addattr8(rta, 1024, ILA_ATTR_IDENT_TYPE,
+                       ret = rta_addattr8(rta, len, ILA_ATTR_IDENT_TYPE,
                                           (__u8)ident_type);
 
                        argc--; argv++;
@@ -898,7 +1410,7 @@ static int parse_encap_ila(struct rtattr *rta, size_t len,
                                invarg("\"hook-type\" value is invalid\n",
                                       *argv);
 
-                       ret = rta_addattr8(rta, 1024, ILA_ATTR_HOOK_TYPE,
+                       ret = rta_addattr8(rta, len, ILA_ATTR_HOOK_TYPE,
                                           (__u8)hook_type);
 
                        argc--; argv++;
@@ -923,9 +1435,11 @@ 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, opts_ok = 0;
        char **argv = *argvp;
        int argc = *argcp;
        int ret = 0;
+       __u16 flags = 0;
 
        while (argc > 0) {
                if (strcmp(*argv, "id") == 0) {
@@ -975,6 +1489,63 @@ 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, "erspan_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_erspan_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"erspan_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
+               } else if (strcmp(*argv, "key") == 0) {
+                       if (key_ok++)
+                               duparg2("key", *argv);
+                       flags |= TUNNEL_KEY;
+               } else if (strcmp(*argv, "csum") == 0) {
+                       if (csum_ok++)
+                               duparg2("csum", *argv);
+                       flags |= TUNNEL_CSUM;
+               } else if (strcmp(*argv, "seq") == 0) {
+                       if (seq_ok++)
+                               duparg2("seq", *argv);
+                       flags |= TUNNEL_SEQ;
                } else {
                        break;
                }
@@ -983,6 +1554,9 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
                argc--; argv++;
        }
 
+       if (flags)
+               ret = rta_addattr16(rta, len,  LWTUNNEL_IP6_FLAGS, flags);
+
        /* argv is currently the first unparsed argument,
         * but the lwt_parse_encap() caller will move to the next,
         * so step back
@@ -1030,7 +1604,7 @@ static int parse_encap_bpf(struct rtattr *rta, size_t len, int *argcp,
                        if (get_unsigned(&headroom, *argv, 0) || headroom == 0)
                                invarg("headroom is invalid\n", *argv);
                        if (!headroom_set)
-                               rta_addattr32(rta, 1024, LWT_BPF_XMIT_HEADROOM,
+                               rta_addattr32(rta, len, LWT_BPF_XMIT_HEADROOM,
                                              headroom);
                        headroom_set = 1;
                } else if (strcmp(*argv, "help") == 0) {
@@ -1051,7 +1625,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;
@@ -1071,7 +1646,7 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
                exit(-1);
        }
 
-       nest = rta_nest(rta, 1024, RTA_ENCAP);
+       nest = rta_nest(rta, len, encap_attr);
        switch (type) {
        case LWTUNNEL_ENCAP_MPLS:
                ret = parse_encap_mpls(rta, len, &argc, &argv);
@@ -1095,6 +1670,9 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
        case LWTUNNEL_ENCAP_SEG6_LOCAL:
                ret = parse_encap_seg6local(rta, len, &argc, &argv);
                break;
+       case LWTUNNEL_ENCAP_RPL:
+               ret = parse_encap_rpl(rta, len, &argc, &argv);
+               break;
        default:
                fprintf(stderr, "Error: unsupported encap type\n");
                break;
@@ -1104,7 +1682,7 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
 
        rta_nest_end(rta, nest);
 
-       ret = rta_addattr16(rta, 1024, RTA_ENCAP_TYPE, type);
+       ret = rta_addattr16(rta, len, encap_type_attr, type);
 
        *argcp = argc;
        *argvp = argv;