netfilter: nf_tables_bridge: set the pktinfo for IPv4/IPv6 traffic
authorAlvaro Neira <alvaroneay@gmail.com>
Wed, 26 Nov 2014 09:21:37 +0000 (10:21 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 27 Nov 2014 12:08:29 +0000 (13:08 +0100)
This patch adds the missing bits to allow to match per meta l4proto from
the bridge. Example:

  nft add rule bridge filter input ether type {ip, ip6} meta l4proto udp counter

Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/bridge/netfilter/nf_tables_bridge.c

index d468c19..19473a9 100644 (file)
@@ -16,6 +16,8 @@
 #include <net/netfilter/nf_tables_bridge.h>
 #include <linux/ip.h>
 #include <linux/ipv6.h>
+#include <net/netfilter/nf_tables_ipv4.h>
+#include <net/netfilter/nf_tables_ipv6.h>
 
 int nft_bridge_iphdr_validate(struct sk_buff *skb)
 {
@@ -62,6 +64,32 @@ int nft_bridge_ip6hdr_validate(struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(nft_bridge_ip6hdr_validate);
 
+static inline void nft_bridge_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
+                                              const struct nf_hook_ops *ops,
+                                              struct sk_buff *skb,
+                                              const struct net_device *in,
+                                              const struct net_device *out)
+{
+       if (nft_bridge_iphdr_validate(skb))
+               nft_set_pktinfo_ipv4(pkt, ops, skb, in, out);
+       else
+               nft_set_pktinfo(pkt, ops, skb, in, out);
+}
+
+static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
+                                             const struct nf_hook_ops *ops,
+                                             struct sk_buff *skb,
+                                             const struct net_device *in,
+                                             const struct net_device *out)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+       if (nft_bridge_ip6hdr_validate(skb) &&
+           nft_set_pktinfo_ipv6(pkt, ops, skb, in, out) == 0)
+               return;
+#endif
+       nft_set_pktinfo(pkt, ops, skb, in, out);
+}
+
 static unsigned int
 nft_do_chain_bridge(const struct nf_hook_ops *ops,
                    struct sk_buff *skb,
@@ -71,7 +99,17 @@ nft_do_chain_bridge(const struct nf_hook_ops *ops,
 {
        struct nft_pktinfo pkt;
 
-       nft_set_pktinfo(&pkt, ops, skb, in, out);
+       switch (eth_hdr(skb)->h_proto) {
+       case htons(ETH_P_IP):
+               nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+               break;
+       case htons(ETH_P_IPV6):
+               nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
+               break;
+       default:
+               nft_set_pktinfo(&pkt, ops, skb, in, out);
+               break;
+       }
 
        return nft_do_chain(&pkt, ops);
 }