diff mbox series

[ovs-dev,v3,5/7] tc: Add vxlan gbp option flower match offload

Message ID 20230515082356.3471136-6-roid@nvidia.com
State Changes Requested
Delegated to: Simon Horman
Headers show
Series Add vxlan gbp offload with TC | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Roi Dayan May 15, 2023, 8:23 a.m. UTC
From: Gavin Li <gavinl@nvidia.com>

Add TC offload support for filtering vxlan tunnels with gbp option

Signed-off-by: Gavin Li <gavinl@nvidia.com>
Reviewed-by: Gavi Teitz <gavi@nvidia.com>
Reviewed-by: Roi Dayan <roid@nvidia.com>
---
 include/linux/pkt_cls.h | 13 ++++++
 lib/netdev-offload-tc.c | 17 ++++++++
 lib/tc.c                | 92 +++++++++++++++++++++++++++++++++++------
 lib/tc.h                |  7 ++++
 4 files changed, 117 insertions(+), 12 deletions(-)

Comments

Simon Horman May 23, 2023, 4:06 p.m. UTC | #1
On Mon, May 15, 2023 at 11:23:54AM +0300, Roi Dayan via dev wrote:
> From: Gavin Li <gavinl@nvidia.com>
> 
> Add TC offload support for filtering vxlan tunnels with gbp option
> 
> Signed-off-by: Gavin Li <gavinl@nvidia.com>
> Reviewed-by: Gavi Teitz <gavi@nvidia.com>
> Reviewed-by: Roi Dayan <roid@nvidia.com>

Reviewed-by: Simon Horman <simon.horman@corigine.com>
Eelco Chaudron May 26, 2023, 10:43 a.m. UTC | #2
On 15 May 2023, at 10:23, Roi Dayan wrote:

> From: Gavin Li <gavinl@nvidia.com>
>
> Add TC offload support for filtering vxlan tunnels with gbp option
>
> Signed-off-by: Gavin Li <gavinl@nvidia.com>
> Reviewed-by: Gavi Teitz <gavi@nvidia.com>
> Reviewed-by: Roi Dayan <roid@nvidia.com>

One comment below, the rest looks good to me.

//Eelco


> ---
>  include/linux/pkt_cls.h | 13 ++++++
>  lib/netdev-offload-tc.c | 17 ++++++++
>  lib/tc.c                | 92 +++++++++++++++++++++++++++++++++++------
>  lib/tc.h                |  7 ++++
>  4 files changed, 117 insertions(+), 12 deletions(-)
>
> diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
> index a8cd8db5bf88..fb4a7ecea4cc 100644
> --- a/include/linux/pkt_cls.h
> +++ b/include/linux/pkt_cls.h
> @@ -273,6 +273,10 @@ enum {
>  					 * TCA_TUNNEL_KEY_ENC_OPTS_GENEVE
>  					 * attributes
>  					 */
> +	TCA_FLOWER_KEY_ENC_OPTS_VXLAN,  /* Nested
> +					 * TCA_TUNNEL_KEY_ENC_OPTS_VXLAN
> +					 * attributes
> +					 */
>  	__TCA_FLOWER_KEY_ENC_OPTS_MAX,
>  };
>
> @@ -290,6 +294,15 @@ enum {
>  #define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \
>  		(__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1)
>
> +enum {
> +	TCA_FLOWER_KEY_ENC_OPT_VXLAN_UNSPEC,
> +	TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP,               /* u32 */
> +	__TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX,
> +};
> +
> +#define TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX \
> +		(__TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX - 1)
> +
>  enum {
>  	TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0),
>  	TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
> index 4f26dd8cca5f..1c97681bc92b 100644
> --- a/lib/netdev-offload-tc.c
> +++ b/lib/netdev-offload-tc.c
> @@ -1234,6 +1234,15 @@ parse_tc_flower_to_match(const struct netdev *netdev,
>              match_set_tun_tp_dst_masked(match, flower->key.tunnel.tp_dst,
>                                          flower->mask.tunnel.tp_dst);
>          }
> +        if (flower->mask.tunnel.gbp.id) {
> +            match_set_tun_gbp_id_masked(match, flower->key.tunnel.gbp.id,
> +                                        flower->mask.tunnel.gbp.id);
> +        }
> +        if (flower->mask.tunnel.gbp.flags) {
> +            match_set_tun_gbp_flags_masked(match,
> +                                           flower->key.tunnel.gbp.flags,
> +                                           flower->mask.tunnel.gbp.flags);
> +        }
>
>          if (!strcmp(netdev_get_type(netdev), "geneve")) {
>              flower_tun_opt_to_match(match, flower);
> @@ -2193,6 +2202,9 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
>          flower.key.tunnel.ttl = tnl->ip_ttl;
>          flower.key.tunnel.tp_src = tnl->tp_src;
>          flower.key.tunnel.tp_dst = tnl->tp_dst;
> +        flower.key.tunnel.gbp.id = tnl->gbp_id;
> +        flower.key.tunnel.gbp.flags = tnl->gbp_flags;
> +        flower.key.tunnel.gbp.id_present = !!tnl_mask->gbp_id;
>
>          flower.mask.tunnel.ipv4.ipv4_src = tnl_mask->ip_src;
>          flower.mask.tunnel.ipv4.ipv4_dst = tnl_mask->ip_dst;
> @@ -2207,6 +2219,9 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
>           * Degrading the flow down to exact match for now as a workaround. */
>          flower.mask.tunnel.tp_dst = OVS_BE16_MAX;
>          flower.mask.tunnel.id = (tnl->flags & FLOW_TNL_F_KEY) ? tnl_mask->tun_id : 0;
> +        flower.mask.tunnel.gbp.id = tnl_mask->gbp_id;
> +        flower.mask.tunnel.gbp.flags = tnl_mask->gbp_flags;
> +        flower.mask.tunnel.gbp.id_present = !!tnl_mask->gbp_id;
>
>          memset(&tnl_mask->ip_src, 0, sizeof tnl_mask->ip_src);
>          memset(&tnl_mask->ip_dst, 0, sizeof tnl_mask->ip_dst);
> @@ -2218,6 +2233,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
>          memset(&tnl_mask->tp_dst, 0, sizeof tnl_mask->tp_dst);
>
>          memset(&tnl_mask->tun_id, 0, sizeof tnl_mask->tun_id);
> +        memset(&tnl_mask->gbp_id, 0, sizeof tnl_mask->gbp_id);
> +        memset(&tnl_mask->gbp_flags, 0, sizeof tnl_mask->gbp_flags);
>          tnl_mask->flags &= ~FLOW_TNL_F_KEY;
>
>          /* XXX: This is wrong!  We're ignoring DF and CSUM flags configuration
> diff --git a/lib/tc.c b/lib/tc.c
> index f2a02eee5e7c..3c335d5272db 100644
> --- a/lib/tc.c
> +++ b/lib/tc.c
> @@ -38,6 +38,7 @@
>  #include "byte-order.h"
>  #include "netlink-socket.h"
>  #include "netlink.h"
> +#include "odp-util.h"
>  #include "openvswitch/ofpbuf.h"
>  #include "openvswitch/util.h"
>  #include "openvswitch/vlog.h"
> @@ -696,6 +697,38 @@ nl_parse_geneve_key(const struct nlattr *in_nlattr,
>      return 0;
>  }
>
> +static int
> +nl_parse_vxlan_key(const struct nlattr *in_nlattr,
> +                   struct tc_flower_tunnel *tunnel)
> +{
> +    const struct ofpbuf *msg;
> +    struct nlattr *nla;
> +    struct ofpbuf buf;
> +    uint32_t gbp_raw;
> +    size_t left;
> +
> +    nl_attr_get_nested(in_nlattr, &buf);
> +    msg = &buf;
> +
> +    NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
> +        uint16_t type = nl_attr_type(nla);
> +
> +        switch (type) {
> +        case TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP:
> +            gbp_raw = nl_attr_get_u32(nla);
> +            odp_decode_gbp_raw(gbp_raw, &tunnel->gbp.id,
> +                               &tunnel->gbp.flags);
> +            tunnel->gbp.id_present = true;
> +            break;
> +        default:
> +            VLOG_ERR_RL(&error_rl, "failed to parse vxlan tun options");
> +            return EINVAL;

Should this not be a warning, and silently ignore it? What if a newer kernel reports an additional value, we do not need, it will break existing implementations.

> +        }
> +    }
> +
> +    return 0;
> +}
> +
>  static int
>  nl_parse_flower_tunnel_opts(struct nlattr *options,
>                              struct tc_flower_tunnel *tunnel)
> @@ -718,6 +751,13 @@ nl_parse_flower_tunnel_opts(struct nlattr *options,
>                  return err;
>              }
>
> +            break;
> +        case TCA_FLOWER_KEY_ENC_OPTS_VXLAN:
> +            err = nl_parse_vxlan_key(nla, tunnel);
> +            if (err) {
> +                return err;
> +            }
> +
>              break;
>          }
>      }
> @@ -3432,23 +3472,18 @@ nl_msg_put_masked_value(struct ofpbuf *request, uint16_t type,
>  }
>
>  static void
> -nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
> -                              struct tc_flower_tunnel *tunnel)
> +nl_msg_put_flower_geneve(struct ofpbuf *request,
> +                         const struct tc_flower_tunnel *tunnel)
>  {
> -    struct tun_metadata *metadata = &tunnel->metadata;
> -    struct geneve_opt *opt;
> -    size_t outer, inner;
> +    const struct tun_metadata *metadata = &tunnel->metadata;
> +    const struct geneve_opt *opt;
>      int len, cnt = 0;
> +    size_t offset;
>
>      len = metadata->present.len;
> -    if (!len) {
> -        return;
> -    }
> -
> -    outer = nl_msg_start_nested(request, type);
>      while (len) {
>          opt = &metadata->opts.gnv[cnt];
> -        inner = nl_msg_start_nested(request, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
> +        offset = nl_msg_start_nested(request, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
>
>          nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS,
>                          opt->opt_class);
> @@ -3459,8 +3494,41 @@ nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
>          cnt += sizeof(struct geneve_opt) / 4 + opt->length;
>          len -= sizeof(struct geneve_opt) + opt->length * 4;
>
> -        nl_msg_end_nested(request, inner);
> +        nl_msg_end_nested(request, offset);
>      }
> +}
> +
> +static void
> +nl_msg_put_flower_vxlan_tun_opts(struct ofpbuf *request,
> +                                 const struct tc_flower_tunnel *tunnel)
> +{
> +    uint32_t gbp_raw;
> +    size_t offset;
> +
> +    if (!tunnel->gbp.id_present) {
> +        return;
> +    }
> +
> +    gbp_raw = odp_encode_gbp_raw(tunnel->gbp.flags, tunnel->gbp.id);
> +    offset = nl_msg_start_nested_with_flag(request,
> +                                           TCA_FLOWER_KEY_ENC_OPTS_VXLAN);
> +    nl_msg_put_u32(request, TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, gbp_raw);
> +    nl_msg_end_nested(request, offset);
> +}
> +
> +static void
> +nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
> +                              struct tc_flower_tunnel *tunnel)
> +{
> +    size_t outer;
> +
> +    if (!tunnel->metadata.present.len && !tunnel->gbp.id_present) {
> +        return;
> +    }
> +
> +    outer = nl_msg_start_nested(request, type);
> +    nl_msg_put_flower_geneve(request, tunnel);
> +    nl_msg_put_flower_vxlan_tun_opts(request, tunnel);
>      nl_msg_end_nested(request, outer);
>  }
>
> diff --git a/lib/tc.h b/lib/tc.h
> index b9d449677ed9..95fff37b9b61 100644
> --- a/lib/tc.h
> +++ b/lib/tc.h
> @@ -105,6 +105,12 @@ struct tc_cookie {
>      size_t len;
>  };
>
> +struct tc_tunnel_gbp {
> +    ovs_be16 id;
> +    uint8_t flags;
> +    bool id_present;
> +};
> +
>  struct tc_flower_tunnel {
>      struct {
>          ovs_be32 ipv4_src;
> @@ -118,6 +124,7 @@ struct tc_flower_tunnel {
>      uint8_t ttl;
>      ovs_be16 tp_src;
>      ovs_be16 tp_dst;
> +    struct tc_tunnel_gbp gbp;
>      ovs_be64 id;
>      struct tun_metadata metadata;
>  };
> -- 
> 2.38.0
Simon Horman May 26, 2023, 11:05 a.m. UTC | #3
On Fri, May 26, 2023 at 12:43:52PM +0200, Eelco Chaudron wrote:
> 
> 
> On 15 May 2023, at 10:23, Roi Dayan wrote:
> 
> > From: Gavin Li <gavinl@nvidia.com>
> >
> > Add TC offload support for filtering vxlan tunnels with gbp option
> >
> > Signed-off-by: Gavin Li <gavinl@nvidia.com>
> > Reviewed-by: Gavi Teitz <gavi@nvidia.com>
> > Reviewed-by: Roi Dayan <roid@nvidia.com>
> 
> One comment below, the rest looks good to me.
> 
> //Eelco

Thanks Eelco.

...

> > @@ -696,6 +697,38 @@ nl_parse_geneve_key(const struct nlattr *in_nlattr,
> >      return 0;
> >  }
> >
> > +static int
> > +nl_parse_vxlan_key(const struct nlattr *in_nlattr,
> > +                   struct tc_flower_tunnel *tunnel)
> > +{
> > +    const struct ofpbuf *msg;
> > +    struct nlattr *nla;
> > +    struct ofpbuf buf;
> > +    uint32_t gbp_raw;
> > +    size_t left;
> > +
> > +    nl_attr_get_nested(in_nlattr, &buf);
> > +    msg = &buf;
> > +
> > +    NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
> > +        uint16_t type = nl_attr_type(nla);
> > +
> > +        switch (type) {
> > +        case TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP:
> > +            gbp_raw = nl_attr_get_u32(nla);
> > +            odp_decode_gbp_raw(gbp_raw, &tunnel->gbp.id,
> > +                               &tunnel->gbp.flags);
> > +            tunnel->gbp.id_present = true;
> > +            break;
> > +        default:
> > +            VLOG_ERR_RL(&error_rl, "failed to parse vxlan tun options");
> > +            return EINVAL;
> 
> Should this not be a warning, and silently ignore it? What if a newer kernel reports an additional value, we do not need, it will break existing implementations.

Roi,

could you address this and post v4?
Eelco Chaudron May 26, 2023, 11:35 a.m. UTC | #4
On 26 May 2023, at 13:05, Simon Horman wrote:

> On Fri, May 26, 2023 at 12:43:52PM +0200, Eelco Chaudron wrote:
>>
>>
>> On 15 May 2023, at 10:23, Roi Dayan wrote:
>>
>>> From: Gavin Li <gavinl@nvidia.com>
>>>
>>> Add TC offload support for filtering vxlan tunnels with gbp option
>>>
>>> Signed-off-by: Gavin Li <gavinl@nvidia.com>
>>> Reviewed-by: Gavi Teitz <gavi@nvidia.com>
>>> Reviewed-by: Roi Dayan <roid@nvidia.com>
>>
>> One comment below, the rest looks good to me.
>>
>> //Eelco
>
> Thanks Eelco.

Just to clarify, this is for patch 5 only. Have some more comments on 6 and 7.

> ...
>
>>> @@ -696,6 +697,38 @@ nl_parse_geneve_key(const struct nlattr *in_nlattr,
>>>      return 0;
>>>  }
>>>
>>> +static int
>>> +nl_parse_vxlan_key(const struct nlattr *in_nlattr,
>>> +                   struct tc_flower_tunnel *tunnel)
>>> +{
>>> +    const struct ofpbuf *msg;
>>> +    struct nlattr *nla;
>>> +    struct ofpbuf buf;
>>> +    uint32_t gbp_raw;
>>> +    size_t left;
>>> +
>>> +    nl_attr_get_nested(in_nlattr, &buf);
>>> +    msg = &buf;
>>> +
>>> +    NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
>>> +        uint16_t type = nl_attr_type(nla);
>>> +
>>> +        switch (type) {
>>> +        case TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP:
>>> +            gbp_raw = nl_attr_get_u32(nla);
>>> +            odp_decode_gbp_raw(gbp_raw, &tunnel->gbp.id,
>>> +                               &tunnel->gbp.flags);
>>> +            tunnel->gbp.id_present = true;
>>> +            break;
>>> +        default:
>>> +            VLOG_ERR_RL(&error_rl, "failed to parse vxlan tun options");
>>> +            return EINVAL;
>>
>> Should this not be a warning, and silently ignore it? What if a newer kernel reports an additional value, we do not need, it will break existing implementations.
>
> Roi,
>
> could you address this and post v4?
diff mbox series

Patch

diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index a8cd8db5bf88..fb4a7ecea4cc 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -273,6 +273,10 @@  enum {
 					 * TCA_TUNNEL_KEY_ENC_OPTS_GENEVE
 					 * attributes
 					 */
+	TCA_FLOWER_KEY_ENC_OPTS_VXLAN,  /* Nested
+					 * TCA_TUNNEL_KEY_ENC_OPTS_VXLAN
+					 * attributes
+					 */
 	__TCA_FLOWER_KEY_ENC_OPTS_MAX,
 };
 
@@ -290,6 +294,15 @@  enum {
 #define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \
 		(__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1)
 
+enum {
+	TCA_FLOWER_KEY_ENC_OPT_VXLAN_UNSPEC,
+	TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP,               /* u32 */
+	__TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX,
+};
+
+#define TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX \
+		(__TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX - 1)
+
 enum {
 	TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0),
 	TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index 4f26dd8cca5f..1c97681bc92b 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -1234,6 +1234,15 @@  parse_tc_flower_to_match(const struct netdev *netdev,
             match_set_tun_tp_dst_masked(match, flower->key.tunnel.tp_dst,
                                         flower->mask.tunnel.tp_dst);
         }
+        if (flower->mask.tunnel.gbp.id) {
+            match_set_tun_gbp_id_masked(match, flower->key.tunnel.gbp.id,
+                                        flower->mask.tunnel.gbp.id);
+        }
+        if (flower->mask.tunnel.gbp.flags) {
+            match_set_tun_gbp_flags_masked(match,
+                                           flower->key.tunnel.gbp.flags,
+                                           flower->mask.tunnel.gbp.flags);
+        }
 
         if (!strcmp(netdev_get_type(netdev), "geneve")) {
             flower_tun_opt_to_match(match, flower);
@@ -2193,6 +2202,9 @@  netdev_tc_flow_put(struct netdev *netdev, struct match *match,
         flower.key.tunnel.ttl = tnl->ip_ttl;
         flower.key.tunnel.tp_src = tnl->tp_src;
         flower.key.tunnel.tp_dst = tnl->tp_dst;
+        flower.key.tunnel.gbp.id = tnl->gbp_id;
+        flower.key.tunnel.gbp.flags = tnl->gbp_flags;
+        flower.key.tunnel.gbp.id_present = !!tnl_mask->gbp_id;
 
         flower.mask.tunnel.ipv4.ipv4_src = tnl_mask->ip_src;
         flower.mask.tunnel.ipv4.ipv4_dst = tnl_mask->ip_dst;
@@ -2207,6 +2219,9 @@  netdev_tc_flow_put(struct netdev *netdev, struct match *match,
          * Degrading the flow down to exact match for now as a workaround. */
         flower.mask.tunnel.tp_dst = OVS_BE16_MAX;
         flower.mask.tunnel.id = (tnl->flags & FLOW_TNL_F_KEY) ? tnl_mask->tun_id : 0;
+        flower.mask.tunnel.gbp.id = tnl_mask->gbp_id;
+        flower.mask.tunnel.gbp.flags = tnl_mask->gbp_flags;
+        flower.mask.tunnel.gbp.id_present = !!tnl_mask->gbp_id;
 
         memset(&tnl_mask->ip_src, 0, sizeof tnl_mask->ip_src);
         memset(&tnl_mask->ip_dst, 0, sizeof tnl_mask->ip_dst);
@@ -2218,6 +2233,8 @@  netdev_tc_flow_put(struct netdev *netdev, struct match *match,
         memset(&tnl_mask->tp_dst, 0, sizeof tnl_mask->tp_dst);
 
         memset(&tnl_mask->tun_id, 0, sizeof tnl_mask->tun_id);
+        memset(&tnl_mask->gbp_id, 0, sizeof tnl_mask->gbp_id);
+        memset(&tnl_mask->gbp_flags, 0, sizeof tnl_mask->gbp_flags);
         tnl_mask->flags &= ~FLOW_TNL_F_KEY;
 
         /* XXX: This is wrong!  We're ignoring DF and CSUM flags configuration
diff --git a/lib/tc.c b/lib/tc.c
index f2a02eee5e7c..3c335d5272db 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -38,6 +38,7 @@ 
 #include "byte-order.h"
 #include "netlink-socket.h"
 #include "netlink.h"
+#include "odp-util.h"
 #include "openvswitch/ofpbuf.h"
 #include "openvswitch/util.h"
 #include "openvswitch/vlog.h"
@@ -696,6 +697,38 @@  nl_parse_geneve_key(const struct nlattr *in_nlattr,
     return 0;
 }
 
+static int
+nl_parse_vxlan_key(const struct nlattr *in_nlattr,
+                   struct tc_flower_tunnel *tunnel)
+{
+    const struct ofpbuf *msg;
+    struct nlattr *nla;
+    struct ofpbuf buf;
+    uint32_t gbp_raw;
+    size_t left;
+
+    nl_attr_get_nested(in_nlattr, &buf);
+    msg = &buf;
+
+    NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
+        uint16_t type = nl_attr_type(nla);
+
+        switch (type) {
+        case TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP:
+            gbp_raw = nl_attr_get_u32(nla);
+            odp_decode_gbp_raw(gbp_raw, &tunnel->gbp.id,
+                               &tunnel->gbp.flags);
+            tunnel->gbp.id_present = true;
+            break;
+        default:
+            VLOG_ERR_RL(&error_rl, "failed to parse vxlan tun options");
+            return EINVAL;
+        }
+    }
+
+    return 0;
+}
+
 static int
 nl_parse_flower_tunnel_opts(struct nlattr *options,
                             struct tc_flower_tunnel *tunnel)
@@ -718,6 +751,13 @@  nl_parse_flower_tunnel_opts(struct nlattr *options,
                 return err;
             }
 
+            break;
+        case TCA_FLOWER_KEY_ENC_OPTS_VXLAN:
+            err = nl_parse_vxlan_key(nla, tunnel);
+            if (err) {
+                return err;
+            }
+
             break;
         }
     }
@@ -3432,23 +3472,18 @@  nl_msg_put_masked_value(struct ofpbuf *request, uint16_t type,
 }
 
 static void
-nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
-                              struct tc_flower_tunnel *tunnel)
+nl_msg_put_flower_geneve(struct ofpbuf *request,
+                         const struct tc_flower_tunnel *tunnel)
 {
-    struct tun_metadata *metadata = &tunnel->metadata;
-    struct geneve_opt *opt;
-    size_t outer, inner;
+    const struct tun_metadata *metadata = &tunnel->metadata;
+    const struct geneve_opt *opt;
     int len, cnt = 0;
+    size_t offset;
 
     len = metadata->present.len;
-    if (!len) {
-        return;
-    }
-
-    outer = nl_msg_start_nested(request, type);
     while (len) {
         opt = &metadata->opts.gnv[cnt];
-        inner = nl_msg_start_nested(request, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
+        offset = nl_msg_start_nested(request, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
 
         nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS,
                         opt->opt_class);
@@ -3459,8 +3494,41 @@  nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
         cnt += sizeof(struct geneve_opt) / 4 + opt->length;
         len -= sizeof(struct geneve_opt) + opt->length * 4;
 
-        nl_msg_end_nested(request, inner);
+        nl_msg_end_nested(request, offset);
     }
+}
+
+static void
+nl_msg_put_flower_vxlan_tun_opts(struct ofpbuf *request,
+                                 const struct tc_flower_tunnel *tunnel)
+{
+    uint32_t gbp_raw;
+    size_t offset;
+
+    if (!tunnel->gbp.id_present) {
+        return;
+    }
+
+    gbp_raw = odp_encode_gbp_raw(tunnel->gbp.flags, tunnel->gbp.id);
+    offset = nl_msg_start_nested_with_flag(request,
+                                           TCA_FLOWER_KEY_ENC_OPTS_VXLAN);
+    nl_msg_put_u32(request, TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, gbp_raw);
+    nl_msg_end_nested(request, offset);
+}
+
+static void
+nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
+                              struct tc_flower_tunnel *tunnel)
+{
+    size_t outer;
+
+    if (!tunnel->metadata.present.len && !tunnel->gbp.id_present) {
+        return;
+    }
+
+    outer = nl_msg_start_nested(request, type);
+    nl_msg_put_flower_geneve(request, tunnel);
+    nl_msg_put_flower_vxlan_tun_opts(request, tunnel);
     nl_msg_end_nested(request, outer);
 }
 
diff --git a/lib/tc.h b/lib/tc.h
index b9d449677ed9..95fff37b9b61 100644
--- a/lib/tc.h
+++ b/lib/tc.h
@@ -105,6 +105,12 @@  struct tc_cookie {
     size_t len;
 };
 
+struct tc_tunnel_gbp {
+    ovs_be16 id;
+    uint8_t flags;
+    bool id_present;
+};
+
 struct tc_flower_tunnel {
     struct {
         ovs_be32 ipv4_src;
@@ -118,6 +124,7 @@  struct tc_flower_tunnel {
     uint8_t ttl;
     ovs_be16 tp_src;
     ovs_be16 tp_dst;
+    struct tc_tunnel_gbp gbp;
     ovs_be64 id;
     struct tun_metadata metadata;
 };