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 --- include/net/netfilter/nf_tables.h | 48 +++++++++++++++++++++++++++++++ net/netfilter/nft_chain_filter.c | 17 +++++++++-- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index e2128663b160..4a55972881b1 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,53 @@ static inline void nft_set_pktinfo_unspec(struct nft_pktinfo *pkt) pkt->fragoff = 0; } +/** + * nft_set_bridge_pktinfo - calls nft_set_pktinfo and advances + * network_header to the header that follows the pppoe- or vlan-header. + */ +static inline int nft_set_bridge_pktinfo(struct nft_pktinfo *pkt, + struct sk_buff *skb, + const struct nf_hook_state *state, + __be16 *proto) +{ + nft_set_pktinfo(pkt, skb, state); + + switch (*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)) + return -1; + ph = (struct ppp_hdr *)(skb->data); + switch (ph->proto) { + case htons(PPP_IP): + *proto = htons(ETH_P_IP); + skb_set_network_header(skb, PPPOE_SES_HLEN); + return PPPOE_SES_HLEN; + case htons(PPP_IPV6): + *proto = htons(ETH_P_IPV6); + skb_set_network_header(skb, PPPOE_SES_HLEN); + return PPPOE_SES_HLEN; + } + break; + } + case htons(ETH_P_8021Q): { + struct vlan_hdr *vhdr; + + if (!pskb_may_pull(skb, VLAN_HLEN)) + return -1; + vhdr = (struct vlan_hdr *)(skb->data); + *proto = vhdr->h_vlan_encapsulated_proto; + skb_set_network_header(skb, VLAN_HLEN); + return VLAN_HLEN; + } + } + return 0; +} + /** * struct nft_verdict - nf_tables verdict * diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c index b16185e9a6dd..a5174adb1abc 100644 --- a/net/netfilter/nft_chain_filter.c +++ b/net/netfilter/nft_chain_filter.c @@ -233,10 +233,16 @@ nft_do_chain_bridge(void *priv, const struct nf_hook_state *state) { struct nft_pktinfo pkt; + int ret, offset; + __be16 proto; - nft_set_pktinfo(&pkt, skb, state); + proto = eth_hdr(skb)->h_proto; + + offset = nft_set_bridge_pktinfo(&pkt, skb, state, &proto); + if (offset < 0) + return NF_ACCEPT; - switch (eth_hdr(skb)->h_proto) { + switch (proto) { case htons(ETH_P_IP): nft_set_pktinfo_ipv4_validate(&pkt); break; @@ -248,7 +254,12 @@ nft_do_chain_bridge(void *priv, break; } - return nft_do_chain(&pkt, priv); + ret = nft_do_chain(&pkt, priv); + + if (offset && ret == NF_ACCEPT) + skb_reset_network_header(skb); + + return ret; } static const struct nft_chain_type nft_chain_filter_bridge = { -- 2.50.0