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 c9662081fc60..ba4179cd8693 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -1230,6 +1230,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);
@@ -2189,6 +2198,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;
@@ -2203,6 +2215,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);
@@ -2214,6 +2229,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 87615e979f1a..e72fa4235198 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;
         }
     }
@@ -3439,11 +3479,12 @@ nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
     int len, cnt = 0;
 
     len = metadata->present.len;
-    if (!len) {
+    if (!len && !tunnel->gbp.id_present) {
         return;
     }
 
     outer = nl_msg_start_nested(request, type);
+    /* Geneve */
     while (len) {
         opt = &metadata->opts.gnv[cnt];
         inner = nl_msg_start_nested(request, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
@@ -3459,6 +3500,17 @@ nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
 
         nl_msg_end_nested(request, inner);
     }
+
+    /* VxLAN GBP */
+    if (tunnel->gbp.id_present) {
+        uint32_t gbp_raw;
+
+        gbp_raw = odp_encode_gbp_raw(tunnel->gbp.flags, tunnel->gbp.id);
+        inner = nl_msg_start_nested(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, inner);
+    }
     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;
 };
