]> git.ipfire.org Git - thirdparty/libnftnl.git/commitdiff
tunnel: add support to geneve options
authorFernando Fernandez Mancera <fmancera@suse.de>
Thu, 21 Aug 2025 09:17:18 +0000 (11:17 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 27 Aug 2025 22:09:54 +0000 (00:09 +0200)
In addition, modifies the netlink parsing to loop through the nested
array of NFTA_TUNNEL_KEY_OPTS.

Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/libnftnl/object.h
src/obj/tunnel.c

index 6d0aab0d6430131b55d61099e7f7f321f46751a6..490e8b4ee0411691eb5ca162e36a73d01a267cdd 100644 (file)
@@ -119,9 +119,12 @@ enum {
 #define NFTNL_TUNNEL_TYPE      0
 #define NFTNL_TUNNEL_BASE      4
 
+#define NFTNL_TUNNEL_GENEVE_DATA_MAXLEN                127
+
 enum nftnl_tunnel_type {
        NFTNL_TUNNEL_TYPE_VXLAN,
        NFTNL_TUNNEL_TYPE_ERSPAN,
+       NFTNL_TUNNEL_TYPE_GENEVE,
 };
 
 enum {
@@ -137,6 +140,12 @@ enum {
        __NFTNL_TUNNEL_ERSPAN_MAX,
 };
 
+enum {
+       NFTNL_TUNNEL_GENEVE_CLASS       = NFTNL_TUNNEL_BASE,
+       NFTNL_TUNNEL_GENEVE_TYPE,
+       NFTNL_TUNNEL_GENEVE_DATA,
+};
+
 struct nftnl_tunnel_opt;
 struct nftnl_tunnel_opts;
 
index bed0342c239f3e549c0e82b90cbe768d4788f7f0..ea9cb021741ddd4aded1efe08ee295dfbb7498d6 100644 (file)
@@ -308,6 +308,12 @@ struct nftnl_tunnel_opt {
                                uint8_t         dir;
                        } v2;
                } erspan;
+               struct {
+                       uint16_t        geneve_class;
+                       uint8_t         type;
+                       uint8_t         data[NFTNL_TUNNEL_GENEVE_DATA_MAXLEN];
+                       uint32_t        data_len;
+               } geneve;
        };
 };
 
@@ -348,6 +354,19 @@ const void *nftnl_tunnel_opt_get_data(const struct nftnl_tunnel_opt *ne,
                        return &ne->vxlan.gbp;
                }
                break;
+       case NFTNL_TUNNEL_TYPE_GENEVE:
+               switch (attr) {
+               case NFTNL_TUNNEL_GENEVE_CLASS:
+                       *data_len = sizeof(uint16_t);
+                       return &ne->geneve.geneve_class;
+               case NFTNL_TUNNEL_GENEVE_TYPE:
+                       *data_len = sizeof(uint8_t);
+                       return &ne->geneve.type;
+               case NFTNL_TUNNEL_GENEVE_DATA:
+                       *data_len = ne->geneve.data_len;
+                       return &ne->geneve.data;
+               }
+               break;
        }
 
        errno = EOPNOTSUPP;
@@ -522,18 +541,25 @@ nftnl_obj_tunnel_parse_erspan(struct nftnl_tunnel_opts *opts, struct nlattr *att
        return 0;
 }
 
-static int nftnl_obj_tunnel_opts_cb(const struct nlattr *attr, void *data)
+static int nftnl_obj_tunnel_geneve_cb(const struct nlattr *attr, void *data)
 {
        const struct nlattr **tb = data;
        int type = mnl_attr_get_type(attr);
 
-       if (mnl_attr_type_valid(attr, NFTA_TUNNEL_KEY_OPTS_MAX) < 0)
+       if (mnl_attr_type_valid(attr, NFTA_TUNNEL_KEY_GENEVE_MAX) < 0)
                return MNL_CB_OK;
 
        switch (type) {
-       case NFTA_TUNNEL_KEY_OPTS_VXLAN:
-       case NFTA_TUNNEL_KEY_OPTS_ERSPAN:
-               if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+       case NFTA_TUNNEL_KEY_GENEVE_CLASS:
+               if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+                       abi_breakage();
+               break;
+       case NFTA_TUNNEL_KEY_GENEVE_TYPE:
+               if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+                       abi_breakage();
+               break;
+       case NFTA_TUNNEL_KEY_GENEVE_DATA:
+               if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
                        abi_breakage();
                break;
        }
@@ -542,31 +568,85 @@ static int nftnl_obj_tunnel_opts_cb(const struct nlattr *attr, void *data)
        return MNL_CB_OK;
 }
 
+static int
+nftnl_obj_tunnel_parse_geneve(struct nftnl_tunnel_opts *opts, struct nlattr *attr)
+{
+       struct nlattr *tb[NFTA_TUNNEL_KEY_GENEVE_MAX + 1] = {};
+       struct nftnl_tunnel_opt *opt;
+
+       if (mnl_attr_parse_nested(attr, nftnl_obj_tunnel_geneve_cb, tb) < 0)
+               return -1;
+
+       opt = nftnl_tunnel_opt_alloc(NFTNL_TUNNEL_TYPE_GENEVE);
+       if (!opt)
+               return -1;
+
+       if (tb[NFTA_TUNNEL_KEY_GENEVE_CLASS]) {
+               opt->geneve.geneve_class =
+                       ntohs(mnl_attr_get_u16(tb[NFTA_TUNNEL_KEY_GENEVE_CLASS]));
+               opt->flags |= (1 << NFTNL_TUNNEL_GENEVE_CLASS);
+       }
+
+       if (tb[NFTA_TUNNEL_KEY_GENEVE_TYPE]) {
+               opt->geneve.type =
+                       mnl_attr_get_u8(tb[NFTA_TUNNEL_KEY_GENEVE_TYPE]);
+               opt->flags |= (1 << NFTNL_TUNNEL_GENEVE_TYPE);
+       }
+
+       if (tb[NFTA_TUNNEL_KEY_GENEVE_DATA]) {
+               uint32_t len = mnl_attr_get_payload_len(tb[NFTA_TUNNEL_KEY_GENEVE_DATA]);
+
+               memcpy(opt->geneve.data,
+                      mnl_attr_get_payload(tb[NFTA_TUNNEL_KEY_GENEVE_DATA]),
+                      len);
+               opt->geneve.data_len = len;
+               opt->flags |= (1 << NFTNL_TUNNEL_GENEVE_DATA);
+       }
+
+       list_add_tail(&opt->list, &opts->opts_list);
+
+       return 0;
+}
+
 struct nftnl_tunnel_opts *nftnl_tunnel_opts_alloc(enum nftnl_tunnel_type type);
 
 static int
-nftnl_obj_tunnel_parse_opts(struct nftnl_obj *e, struct nlattr *attr,
+nftnl_obj_tunnel_parse_opts(struct nftnl_obj *e, struct nlattr *nest,
                            struct nftnl_obj_tunnel *tun)
 {
-       struct nlattr *tb[NFTA_TUNNEL_KEY_OPTS_MAX + 1] = {};
+       struct nlattr *attr;
        struct nftnl_tunnel_opts *opts = NULL;
        int err = 0;
 
-       if (mnl_attr_parse_nested(attr, nftnl_obj_tunnel_opts_cb, tb) < 0)
-               return -1;
+       mnl_attr_for_each_nested(attr, nest) {
+               if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+                       abi_breakage();
 
-       if (tb[NFTA_TUNNEL_KEY_OPTS_VXLAN]) {
-               opts = nftnl_tunnel_opts_alloc(NFTNL_TUNNEL_TYPE_VXLAN);
-               if (!opts)
-                       return -1;
+               switch(mnl_attr_get_type(attr)) {
+                       case NFTA_TUNNEL_KEY_OPTS_VXLAN:
+                               opts = nftnl_tunnel_opts_alloc(NFTNL_TUNNEL_TYPE_VXLAN);
+                               if (!opts)
+                                       return -1;
 
-               err = nftnl_obj_tunnel_parse_vxlan(opts, tb[NFTA_TUNNEL_KEY_OPTS_VXLAN]);
-       } else if (tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN]) {
-               opts = nftnl_tunnel_opts_alloc(NFTNL_TUNNEL_TYPE_ERSPAN);
-               if (!opts)
-                       return -1;
+                               err = nftnl_obj_tunnel_parse_vxlan(opts, attr);
+                       break;
+                       case NFTA_TUNNEL_KEY_OPTS_ERSPAN:
+                               opts = nftnl_tunnel_opts_alloc(NFTNL_TUNNEL_TYPE_ERSPAN);
+                               if (!opts)
+                                       return -1;
+
+                               err = nftnl_obj_tunnel_parse_erspan(opts, attr);
+                       break;
+                       case NFTA_TUNNEL_KEY_OPTS_GENEVE:
+                               if (!opts)
+                                       opts = nftnl_tunnel_opts_alloc(NFTNL_TUNNEL_TYPE_GENEVE);
 
-               err = nftnl_obj_tunnel_parse_erspan(opts, tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN]);
+                               if (!opts)
+                                       return -1;
+
+                               err = nftnl_obj_tunnel_parse_geneve(opts, attr);
+                       break;
+               }
        }
 
        if (opts) {
@@ -646,6 +726,7 @@ struct nftnl_tunnel_opts *nftnl_tunnel_opts_alloc(enum nftnl_tunnel_type type)
        switch (type) {
        case NFTNL_TUNNEL_TYPE_VXLAN:
        case NFTNL_TUNNEL_TYPE_ERSPAN:
+       case NFTNL_TUNNEL_TYPE_GENEVE:
                break;
        default:
                errno = EOPNOTSUPP;
@@ -679,6 +760,8 @@ int nftnl_tunnel_opts_add(struct nftnl_tunnel_opts *opts,
                        return -1;
                }
                break;
+       case NFTNL_TUNNEL_TYPE_GENEVE:
+               break;
        default:
                errno = EOPNOTSUPP;
                return -1;
@@ -698,6 +781,7 @@ void nftnl_tunnel_opts_free(struct nftnl_tunnel_opts *opts)
                switch(opts->type) {
                case NFTNL_TUNNEL_TYPE_VXLAN:
                case NFTNL_TUNNEL_TYPE_ERSPAN:
+               case NFTNL_TUNNEL_TYPE_GENEVE:
                        list_del(&opt->list);
                        xfree(opt);
                        break;
@@ -713,6 +797,7 @@ struct nftnl_tunnel_opt *nftnl_tunnel_opt_alloc(enum nftnl_tunnel_type type)
        switch (type) {
        case NFTNL_TUNNEL_TYPE_VXLAN:
        case NFTNL_TUNNEL_TYPE_ERSPAN:
+       case NFTNL_TUNNEL_TYPE_GENEVE:
                break;
        default:
                errno = EOPNOTSUPP;
@@ -771,6 +856,34 @@ static int nftnl_tunnel_opt_erspan_set(struct nftnl_tunnel_opt *opt, uint16_t ty
        return 0;
 }
 
+static int nftnl_tunnel_opt_geneve_set(struct nftnl_tunnel_opt *opt, uint16_t type,
+                                      const void *data, uint32_t data_len)
+{
+       switch(type) {
+       case NFTNL_TUNNEL_GENEVE_CLASS:
+               memcpy(&opt->geneve.geneve_class, data, data_len);
+               break;
+       case NFTNL_TUNNEL_GENEVE_TYPE:
+               memcpy(&opt->geneve.type, data, data_len);
+               break;
+       case NFTNL_TUNNEL_GENEVE_DATA:
+               if (data_len > NFTNL_TUNNEL_GENEVE_DATA_MAXLEN) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               memcpy(&opt->geneve.data, data, data_len);
+               opt->geneve.data_len = data_len;
+               break;
+       default:
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+
+       opt->flags |= (1 << type);
+
+       return 0;
+}
+
 EXPORT_SYMBOL(nftnl_tunnel_opt_set);
 int nftnl_tunnel_opt_set(struct nftnl_tunnel_opt *opt, uint16_t type,
                         const void *data, uint32_t data_len)
@@ -780,6 +893,8 @@ int nftnl_tunnel_opt_set(struct nftnl_tunnel_opt *opt, uint16_t type,
                return nftnl_tunnel_opt_vxlan_set(opt, type, data, data_len);
        case NFTNL_TUNNEL_TYPE_ERSPAN:
                return nftnl_tunnel_opt_erspan_set(opt, type, data, data_len);
+       case NFTNL_TUNNEL_TYPE_GENEVE:
+               return nftnl_tunnel_opt_geneve_set(opt, type, data, data_len);
        default:
                errno = EOPNOTSUPP;
                return -1;
@@ -826,6 +941,26 @@ static void nftnl_tunnel_opt_build_erspan(struct nlmsghdr *nlh,
        }
 }
 
+static void nftnl_tunnel_opt_build_geneve(struct nlmsghdr *nlh,
+                                         const struct nftnl_tunnel_opt *opt)
+{
+       struct nlattr *nest_inner;
+
+       if (opt->flags & (1 << NFTNL_TUNNEL_GENEVE_CLASS) &&
+           opt->flags & (1 << NFTNL_TUNNEL_GENEVE_TYPE) &&
+           opt->flags & (1 << NFTNL_TUNNEL_GENEVE_DATA)) {
+               nest_inner = mnl_attr_nest_start(nlh, NFTA_TUNNEL_KEY_OPTS_GENEVE);
+               mnl_attr_put_u16(nlh, NFTA_TUNNEL_KEY_GENEVE_CLASS,
+                                htons(opt->geneve.geneve_class));
+               mnl_attr_put_u8(nlh, NFTA_TUNNEL_KEY_GENEVE_TYPE,
+                               opt->geneve.type);
+               mnl_attr_put(nlh, NFTA_TUNNEL_KEY_GENEVE_DATA,
+                            opt->geneve.data_len,
+                            opt->geneve.data);
+               mnl_attr_nest_end(nlh, nest_inner);
+       }
+}
+
 void nftnl_tunnel_opts_build(struct nlmsghdr *nlh,
                             struct nftnl_tunnel_opts *opts)
 {
@@ -842,6 +977,9 @@ void nftnl_tunnel_opts_build(struct nlmsghdr *nlh,
                case NFTNL_TUNNEL_TYPE_ERSPAN:
                        nftnl_tunnel_opt_build_erspan(nlh, opt);
                        break;
+               case NFTNL_TUNNEL_TYPE_GENEVE:
+                       nftnl_tunnel_opt_build_geneve(nlh, opt);
+                       break;
                }
        }
        mnl_attr_nest_end(nlh, nest);