In nft_do_chain_bridge() pktinfo is only fully populated for plain packets and packets encapsulated in single 802.1q or 802.1ad. When implementing the software bridge-fastpath and testing all possible encapulations, there can be more encapsulations: The packet could (also) be encapsulated in PPPoE, or the packet could be encapsulated in an inner 802.1q, combined with an outer 802.1ad or 802.1q encapsulation. nft_flow_offload_eval() also examines the L4 header, with the L4 protocol known from the conntrack-tuplehash. To access the header it uses nft_thoff(), but for these packets it returns zero. Introduce nft_set_bridge_pktinfo() to help populate pktinfo with the offsets, without setting pkt->tprot and the corresponding pkt->flags. This will not change rule processing, but does make these offsets available for code that is not checking pkt->flags to use the offsets, like nft_flow_offload_eval(). Existing behaviour for a rule like "tcp dport 22 accept" is not changed when, for instance, a PPPoE packet is being matched. Signed-off-by: Eric Woudstra --- net/netfilter/nft_chain_filter.c | 58 ++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c index b16185e9a6dd..1f3ae5687917 100644 --- a/net/netfilter/nft_chain_filter.c +++ b/net/netfilter/nft_chain_filter.c @@ -227,16 +227,64 @@ static inline void nft_chain_filter_inet_fini(void) {} #endif /* CONFIG_NF_TABLES_IPV6 */ #if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) +static 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; +} + static unsigned int nft_do_chain_bridge(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct nft_pktinfo pkt; + __be16 proto; + int offset; - nft_set_pktinfo(&pkt, skb, state); + proto = eth_hdr(skb)->h_proto; - switch (eth_hdr(skb)->h_proto) { + offset = nft_set_bridge_pktinfo(&pkt, skb, state, &proto); + if (offset < 0) + return NF_ACCEPT; + + switch (proto) { case htons(ETH_P_IP): nft_set_pktinfo_ipv4_validate(&pkt); break; @@ -248,6 +296,12 @@ nft_do_chain_bridge(void *priv, break; } + if (offset) { + skb_reset_network_header(skb); + pkt.flags = 0; + pkt.tprot = 0; + } + return nft_do_chain(&pkt, priv); } -- 2.50.0