This adds the capability to evaluate 802.1ad, QinQ, PPPoE and PPPoE-in-Q
packets in the bridge filter chain.

Signed-off-by: Eric Woudstra <ericwo...@gmail.com>
---
 net/netfilter/nft_chain_filter.c | 52 +++++++++++++++++++++++++++++++-
 1 file changed, 51 insertions(+), 1 deletion(-)

diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index 19a553550c76..8445ddfb9cea 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -232,11 +232,55 @@ nft_do_chain_bridge(void *priv,
                    struct sk_buff *skb,
                    const struct nf_hook_state *state)
 {
+       __be16 outer_proto, proto = 0;
        struct nft_pktinfo pkt;
+       int ret, offset = 0;
 
        nft_set_pktinfo(&pkt, skb, state);
 
        switch (eth_hdr(skb)->h_proto) {
+       case htons(ETH_P_PPP_SES): {
+               struct ppp_hdr {
+                       struct pppoe_hdr hdr;
+                       __be16 proto;
+               } *ph;
+
+               if (!pskb_may_pull(skb, PPPOE_SES_HLEN))
+                       break;
+               offset = PPPOE_SES_HLEN;
+               outer_proto = skb->protocol;
+               ph = (struct ppp_hdr *)(skb->data);
+               switch (ph->proto) {
+               case htons(PPP_IP):
+                       proto = htons(ETH_P_IP);
+                       break;
+               case htons(PPP_IPV6):
+                       proto = htons(ETH_P_IPV6);
+                       break;
+               }
+               skb_set_network_header(skb, offset);
+               skb->protocol = proto;
+               break;
+       }
+       case htons(ETH_P_8021Q): {
+               struct vlan_hdr *vhdr;
+
+               if (!pskb_may_pull(skb, VLAN_HLEN))
+                       break;
+               offset = VLAN_HLEN;
+               outer_proto = skb->protocol;
+               vhdr = (struct vlan_hdr *)(skb->data);
+               proto = vhdr->h_vlan_encapsulated_proto;
+               skb_set_network_header(skb, offset);
+               skb->protocol = proto;
+               break;
+       }
+       default:
+               proto = eth_hdr(skb)->h_proto;
+               break;
+       }
+
+       switch (proto) {
        case htons(ETH_P_IP):
                nft_set_pktinfo_ipv4_validate(&pkt);
                break;
@@ -248,7 +292,13 @@ nft_do_chain_bridge(void *priv,
                break;
        }
 
-       return nft_do_chain(&pkt, priv);
+       ret = nft_do_chain(&pkt, priv);
+
+       if (offset) {
+               skb_reset_network_header(skb);
+               skb->protocol = outer_proto;
+       }
+       return ret;
 }
 
 static const struct nft_chain_type nft_chain_filter_bridge = {
-- 
2.47.1


Reply via email to