#include <net/pkt_cls.h>
 #include <net/ip.h>
 #include <net/flow_dissector.h>
+#include <net/geneve.h>
 
 #include <net/dst.h>
 #include <net/dst_metadata.h>
        struct flow_dissector_key_tcp tcp;
        struct flow_dissector_key_ip ip;
        struct flow_dissector_key_ip enc_ip;
+       struct flow_dissector_key_enc_opts enc_opts;
 } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
 
 struct fl_flow_mask_range {
        [TCA_FLOWER_KEY_ENC_IP_TOS_MASK] = { .type = NLA_U8 },
        [TCA_FLOWER_KEY_ENC_IP_TTL]      = { .type = NLA_U8 },
        [TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NLA_U8 },
+       [TCA_FLOWER_KEY_ENC_OPTS]       = { .type = NLA_NESTED },
+       [TCA_FLOWER_KEY_ENC_OPTS_MASK]  = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+enc_opts_policy[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1] = {
+       [TCA_FLOWER_KEY_ENC_OPTS_GENEVE]        = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+geneve_opt_policy[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1] = {
+       [TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]      = { .type = NLA_U16 },
+       [TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]       = { .type = NLA_U8 },
+       [TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]       = { .type = NLA_BINARY,
+                                                      .len = 128 },
 };
 
 static void fl_set_key_val(struct nlattr **tb,
        fl_set_key_val(tb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl));
 }
 
+static int fl_set_geneve_opt(const struct nlattr *nla, struct fl_flow_key *key,
+                            int depth, int option_len,
+                            struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1];
+       struct nlattr *class = NULL, *type = NULL, *data = NULL;
+       struct geneve_opt *opt;
+       int err, data_len = 0;
+
+       if (option_len > sizeof(struct geneve_opt))
+               data_len = option_len - sizeof(struct geneve_opt);
+
+       opt = (struct geneve_opt *)&key->enc_opts.data[key->enc_opts.len];
+       memset(opt, 0xff, option_len);
+       opt->length = data_len / 4;
+       opt->r1 = 0;
+       opt->r2 = 0;
+       opt->r3 = 0;
+
+       /* If no mask has been prodived we assume an exact match. */
+       if (!depth)
+               return sizeof(struct geneve_opt) + data_len;
+
+       if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_GENEVE) {
+               NL_SET_ERR_MSG(extack, "Non-geneve option type for mask");
+               return -EINVAL;
+       }
+
+       err = nla_parse_nested(tb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX,
+                              nla, geneve_opt_policy, extack);
+       if (err < 0)
+               return err;
+
+       /* We are not allowed to omit any of CLASS, TYPE or DATA
+        * fields from the key.
+        */
+       if (!option_len &&
+           (!tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS] ||
+            !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE] ||
+            !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA])) {
+               NL_SET_ERR_MSG(extack, "Missing tunnel key geneve option class, type or data");
+               return -EINVAL;
+       }
+
+       /* Omitting any of CLASS, TYPE or DATA fields is allowed
+        * for the mask.
+        */
+       if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]) {
+               int new_len = key->enc_opts.len;
+
+               data = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA];
+               data_len = nla_len(data);
+               if (data_len < 4) {
+                       NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4 bytes long");
+                       return -ERANGE;
+               }
+               if (data_len % 4) {
+                       NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a multiple of 4 bytes long");
+                       return -ERANGE;
+               }
+
+               new_len += sizeof(struct geneve_opt) + data_len;
+               BUILD_BUG_ON(FLOW_DIS_TUN_OPTS_MAX != IP_TUNNEL_OPTS_MAX);
+               if (new_len > FLOW_DIS_TUN_OPTS_MAX) {
+                       NL_SET_ERR_MSG(extack, "Tunnel options exceeds max size");
+                       return -ERANGE;
+               }
+               opt->length = data_len / 4;
+               memcpy(opt->opt_data, nla_data(data), data_len);
+       }
+
+       if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]) {
+               class = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS];
+               opt->opt_class = nla_get_be16(class);
+       }
+
+       if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]) {
+               type = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE];
+               opt->type = nla_get_u8(type);
+       }
+
+       return sizeof(struct geneve_opt) + data_len;
+}
+
+static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
+                         struct fl_flow_key *mask,
+                         struct netlink_ext_ack *extack)
+{
+       const struct nlattr *nla_enc_key, *nla_opt_key, *nla_opt_msk = NULL;
+       int option_len, key_depth, msk_depth = 0;
+
+       nla_enc_key = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS]);
+
+       if (tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]) {
+               nla_opt_msk = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]);
+               msk_depth = nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]);
+       }
+
+       nla_for_each_attr(nla_opt_key, nla_enc_key,
+                         nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS]), key_depth) {
+               switch (nla_type(nla_opt_key)) {
+               case TCA_FLOWER_KEY_ENC_OPTS_GENEVE:
+                       option_len = 0;
+                       key->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT;
+                       option_len = fl_set_geneve_opt(nla_opt_key, key,
+                                                      key_depth, option_len,
+                                                      extack);
+                       if (option_len < 0)
+                               return option_len;
+
+                       key->enc_opts.len += option_len;
+                       /* At the same time we need to parse through the mask
+                        * in order to verify exact and mask attribute lengths.
+                        */
+                       mask->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT;
+                       option_len = fl_set_geneve_opt(nla_opt_msk, mask,
+                                                      msk_depth, option_len,
+                                                      extack);
+                       if (option_len < 0)
+                               return option_len;
+
+                       mask->enc_opts.len += option_len;
+                       if (key->enc_opts.len != mask->enc_opts.len) {
+                               NL_SET_ERR_MSG(extack, "Key and mask miss aligned");
+                               return -EINVAL;
+                       }
+
+                       if (msk_depth)
+                               nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
+                       break;
+               default:
+                       NL_SET_ERR_MSG(extack, "Unknown tunnel option type");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static int fl_set_key(struct net *net, struct nlattr **tb,
                      struct fl_flow_key *key, struct fl_flow_key *mask,
                      struct netlink_ext_ack *extack)
 
        fl_set_key_ip(tb, true, &key->enc_ip, &mask->enc_ip);
 
+       if (tb[TCA_FLOWER_KEY_ENC_OPTS]) {
+               ret = fl_set_enc_opt(tb, key, mask, extack);
+               if (ret)
+                       return ret;
+       }
+
        if (tb[TCA_FLOWER_KEY_FLAGS])
                ret = fl_set_key_flags(tb, &key->control.flags, &mask->control.flags);
 
                             FLOW_DISSECTOR_KEY_ENC_PORTS, enc_tp);
        FL_KEY_SET_IF_MASKED(mask, keys, cnt,
                             FLOW_DISSECTOR_KEY_ENC_IP, enc_ip);
+       FL_KEY_SET_IF_MASKED(mask, keys, cnt,
+                            FLOW_DISSECTOR_KEY_ENC_OPTS, enc_opts);
 
        skb_flow_dissector_init(dissector, keys, cnt);
 }
        return nla_put(skb, TCA_FLOWER_KEY_FLAGS_MASK, 4, &_mask);
 }
 
+static int fl_dump_key_geneve_opt(struct sk_buff *skb,
+                                 struct flow_dissector_key_enc_opts *enc_opts)
+{
+       struct geneve_opt *opt;
+       struct nlattr *nest;
+       int opt_off = 0;
+
+       nest = nla_nest_start(skb, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
+       if (!nest)
+               goto nla_put_failure;
+
+       while (enc_opts->len > opt_off) {
+               opt = (struct geneve_opt *)&enc_opts->data[opt_off];
+
+               if (nla_put_be16(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS,
+                                opt->opt_class))
+                       goto nla_put_failure;
+               if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE,
+                              opt->type))
+                       goto nla_put_failure;
+               if (nla_put(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA,
+                           opt->length * 4, opt->opt_data))
+                       goto nla_put_failure;
+
+               opt_off += sizeof(struct geneve_opt) + opt->length * 4;
+       }
+       nla_nest_end(skb, nest);
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(skb, nest);
+       return -EMSGSIZE;
+}
+
+static int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type,
+                              struct flow_dissector_key_enc_opts *enc_opts)
+{
+       struct nlattr *nest;
+       int err;
+
+       if (!enc_opts->len)
+               return 0;
+
+       nest = nla_nest_start(skb, enc_opt_type);
+       if (!nest)
+               goto nla_put_failure;
+
+       switch (enc_opts->dst_opt_type) {
+       case TUNNEL_GENEVE_OPT:
+               err = fl_dump_key_geneve_opt(skb, enc_opts);
+               if (err)
+                       goto nla_put_failure;
+               break;
+       default:
+               goto nla_put_failure;
+       }
+       nla_nest_end(skb, nest);
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(skb, nest);
+       return -EMSGSIZE;
+}
+
+static int fl_dump_key_enc_opt(struct sk_buff *skb,
+                              struct flow_dissector_key_enc_opts *key_opts,
+                              struct flow_dissector_key_enc_opts *msk_opts)
+{
+       int err;
+
+       err = fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS, key_opts);
+       if (err)
+               return err;
+
+       return fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS_MASK, msk_opts);
+}
+
 static int fl_dump_key(struct sk_buff *skb, struct net *net,
                       struct fl_flow_key *key, struct fl_flow_key *mask)
 {
                            &mask->enc_tp.dst,
                            TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK,
                            sizeof(key->enc_tp.dst)) ||
-           fl_dump_key_ip(skb, true, &key->enc_ip, &mask->enc_ip))
+           fl_dump_key_ip(skb, true, &key->enc_ip, &mask->enc_ip) ||
+           fl_dump_key_enc_opt(skb, &key->enc_opts, &mask->enc_opts))
                goto nla_put_failure;
 
        if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags))