netfilter: nfnetlink_log: add support for VLAN information
authorMichael Braun <michael-dev@fami-braun.de>
Tue, 20 Aug 2019 13:11:46 +0000 (15:11 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 26 Aug 2019 09:06:07 +0000 (11:06 +0200)
Currently, there is no vlan information (e.g. when used with a vlan aware
bridge) passed to userspache, HWHEADER will contain an 08 00 (ip) suffix
even for tagged ip packets.

Therefore, add an extra netlink attribute that passes the vlan information
to userspace similarly to 15824ab29f for nfqueue.

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
Reviewed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/uapi/linux/netfilter/nfnetlink_log.h
net/netfilter/nfnetlink_log.c

index 20983cb195a0f77926c2b7b23c63b5d1ec8417bb..45c8d3b027e027aae6b98fd32bd36e9a1daf6748 100644 (file)
@@ -33,6 +33,15 @@ struct nfulnl_msg_packet_timestamp {
        __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,
@@ -54,6 +63,8 @@ enum nfulnl_attr_type {
        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
 };
index d69e1863e5369ce9e2ca73be6aa9f2e6037dab3e..0ba020ca38e68cde127fca47439d5bb2a5a456d6 100644 (file)
@@ -385,6 +385,57 @@ nfulnl_timer(struct timer_list *t)
        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
@@ -580,6 +631,10 @@ __build_packet_message(struct nfnl_log_net *log,
                                 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);
@@ -687,6 +742,8 @@ nfulnl_log_packet(struct net *net,
                                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 */