__u64 *timestamp) __ksym;
 extern int bpf_xdp_metadata_rx_hash(const struct xdp_md *ctx, __u32 *hash,
                                    enum xdp_rss_hash_type *rss_type) __ksym;
+extern int bpf_xdp_metadata_rx_vlan_tag(const struct xdp_md *ctx,
+                                       __be16 *vlan_proto,
+                                       __u16 *vlan_tci) __ksym;
 
 SEC("xdp.frags")
 int rx(struct xdp_md *ctx)
                return XDP_PASS;
        }
 
+       meta->hint_valid = 0;
+
+       meta->xdp_timestamp = bpf_ktime_get_tai_ns();
        err = bpf_xdp_metadata_rx_timestamp(ctx, &meta->rx_timestamp);
-       if (!err)
-               meta->xdp_timestamp = bpf_ktime_get_tai_ns();
+       if (err)
+               meta->rx_timestamp_err = err;
+       else
+               meta->hint_valid |= XDP_META_FIELD_TS;
+
+       err = bpf_xdp_metadata_rx_hash(ctx, &meta->rx_hash,
+                                      &meta->rx_hash_type);
+       if (err)
+               meta->rx_hash_err = err;
        else
-               meta->rx_timestamp = 0; /* Used by AF_XDP as not avail signal */
+               meta->hint_valid |= XDP_META_FIELD_RSS;
 
-       err = bpf_xdp_metadata_rx_hash(ctx, &meta->rx_hash, &meta->rx_hash_type);
-       if (err < 0)
-               meta->rx_hash_err = err; /* Used by AF_XDP as no hash signal */
+       err = bpf_xdp_metadata_rx_vlan_tag(ctx, &meta->rx_vlan_proto,
+                                          &meta->rx_vlan_tci);
+       if (err)
+               meta->rx_vlan_tag_err = err;
+       else
+               meta->hint_valid |= XDP_META_FIELD_VLAN_TAG;
 
        __sync_add_and_fetch(&pkts_redir, 1);
        return bpf_redirect_map(&xsk, ctx->rx_queue_index, XDP_PASS);
 
 #include "xsk.h"
 
 #include <error.h>
+#include <linux/kernel.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
 #include <linux/errqueue.h>
 #include <linux/if_link.h>
 #include <linux/net_tstamp.h>
               (double)delta / 1000);
 }
 
+#define VLAN_PRIO_MASK         GENMASK(15, 13) /* Priority Code Point */
+#define VLAN_DEI_MASK          GENMASK(12, 12) /* Drop Eligible Indicator */
+#define VLAN_VID_MASK          GENMASK(11, 0)  /* VLAN Identifier */
+static void print_vlan_tci(__u16 tag)
+{
+       __u16 vlan_id = FIELD_GET(VLAN_VID_MASK, tag);
+       __u8 pcp = FIELD_GET(VLAN_PRIO_MASK, tag);
+       bool dei = FIELD_GET(VLAN_DEI_MASK, tag);
+
+       printf("PCP=%u, DEI=%d, VID=0x%X\n", pcp, dei, vlan_id);
+}
+
 static void verify_xdp_metadata(void *data, clockid_t clock_id)
 {
        struct xdp_meta *meta;
 
        meta = data - sizeof(*meta);
 
-       if (meta->rx_hash_err < 0)
-               printf("No rx_hash err=%d\n", meta->rx_hash_err);
-       else
+       if (meta->hint_valid & XDP_META_FIELD_RSS)
                printf("rx_hash: 0x%X with RSS type:0x%X\n",
                       meta->rx_hash, meta->rx_hash_type);
+       else
+               printf("No rx_hash, err=%d\n", meta->rx_hash_err);
 
-       if (meta->rx_timestamp) {
+       if (meta->hint_valid & XDP_META_FIELD_TS) {
                __u64 ref_tstamp = gettime(clock_id);
 
                /* store received timestamps to calculate a delta at tx */
                print_tstamp_delta("XDP RX-time", "User RX-time",
                                   meta->xdp_timestamp, ref_tstamp);
        } else {
-               printf("No rx_timestamp\n");
+               printf("No rx_timestamp, err=%d\n", meta->rx_timestamp_err);
+       }
+
+       if (meta->hint_valid & XDP_META_FIELD_VLAN_TAG) {
+               printf("rx_vlan_proto: 0x%X\n", ntohs(meta->rx_vlan_proto));
+               printf("rx_vlan_tci: ");
+               print_vlan_tci(meta->rx_vlan_tci);
+       } else {
+               printf("No rx_vlan_tci or rx_vlan_proto, err=%d\n",
+                      meta->rx_vlan_tag_err);
        }
 }
 
 
 #define ETH_P_8021AD 0x88A8
 #endif
 
+#ifndef BIT
+#define BIT(nr)                        (1 << (nr))
+#endif
+
+/* Non-existent checksum status */
+#define XDP_CHECKSUM_MAGIC     BIT(2)
+
+enum xdp_meta_field {
+       XDP_META_FIELD_TS       = BIT(0),
+       XDP_META_FIELD_RSS      = BIT(1),
+       XDP_META_FIELD_VLAN_TAG = BIT(2),
+};
+
 struct xdp_meta {
-       __u64 rx_timestamp;
+       union {
+               __u64 rx_timestamp;
+               __s32 rx_timestamp_err;
+       };
        __u64 xdp_timestamp;
        __u32 rx_hash;
        union {
                __u32 rx_hash_type;
                __s32 rx_hash_err;
        };
+       union {
+               struct {
+                       __be16 rx_vlan_proto;
+                       __u16 rx_vlan_tci;
+               };
+               __s32 rx_vlan_tag_err;
+       };
+       enum xdp_meta_field hint_valid;
 };