__aligned_be64  usec;
 };
 
+enum nfulnl_vlan_attr {
+       NFULA_VLAN_UNSPEC,
+       NFULA_VLAN_PROTO,               /* __be16 skb vlan_proto */
+       NFULA_VLAN_TCI,                 /* __be16 skb htons(vlan_tci) */
+       __NFULA_VLAN_MAX,
+};
+
+#define NFULA_VLAN_MAX (__NFULA_VLAN_MAX + 1)
+
 enum nfulnl_attr_type {
        NFULA_UNSPEC,
        NFULA_PACKET_HDR,
        NFULA_HWLEN,                    /* hardware header length */
        NFULA_CT,                       /* nf_conntrack_netlink.h */
        NFULA_CT_INFO,                  /* enum ip_conntrack_info */
+       NFULA_VLAN,                     /* nested attribute: packet vlan info */
+       NFULA_L2HDR,                    /* full L2 header */
 
        __NFULA_MAX
 };
 
        instance_put(inst);
 }
 
+static u32 nfulnl_get_bridge_size(const struct sk_buff *skb)
+{
+       u32 size = 0;
+
+       if (!skb_mac_header_was_set(skb))
+               return 0;
+
+       if (skb_vlan_tag_present(skb)) {
+               size += nla_total_size(0); /* nested */
+               size += nla_total_size(sizeof(u16)); /* id */
+               size += nla_total_size(sizeof(u16)); /* tag */
+       }
+
+       if (skb->network_header > skb->mac_header)
+               size += nla_total_size(skb->network_header - skb->mac_header);
+
+       return size;
+}
+
+static int nfulnl_put_bridge(struct nfulnl_instance *inst, const struct sk_buff *skb)
+{
+       if (!skb_mac_header_was_set(skb))
+               return 0;
+
+       if (skb_vlan_tag_present(skb)) {
+               struct nlattr *nest;
+
+               nest = nla_nest_start(inst->skb, NFULA_VLAN);
+               if (!nest)
+                       goto nla_put_failure;
+
+               if (nla_put_be16(inst->skb, NFULA_VLAN_TCI, htons(skb->vlan_tci)) ||
+                   nla_put_be16(inst->skb, NFULA_VLAN_PROTO, skb->vlan_proto))
+                       goto nla_put_failure;
+
+               nla_nest_end(inst->skb, nest);
+       }
+
+       if (skb->mac_header < skb->network_header) {
+               int len = (int)(skb->network_header - skb->mac_header);
+
+               if (nla_put(inst->skb, NFULA_L2HDR, len, skb_mac_header(skb)))
+                       goto nla_put_failure;
+       }
+
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
 /* This is an inline function, we don't really care about a long
  * list of arguments */
 static inline int
                                 NFULA_CT, NFULA_CT_INFO) < 0)
                goto nla_put_failure;
 
+       if ((pf == NFPROTO_NETDEV || pf == NFPROTO_BRIDGE) &&
+           nfulnl_put_bridge(inst, skb) < 0)
+               goto nla_put_failure;
+
        if (data_len) {
                struct nlattr *nla;
                int size = nla_attr_size(data_len);
                                size += nfnl_ct->build_size(ct);
                }
        }
+       if (pf == NFPROTO_NETDEV || pf == NFPROTO_BRIDGE)
+               size += nfulnl_get_bridge_size(skb);
 
        qthreshold = inst->qthreshold;
        /* per-rule qthreshold overrides per-instance */