Message ID | 20250107090530.5035-3-ericwouds@gmail.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | bridge-fastpath and related improvements | expand |
On 1/7/25 11:05, Eric Woudstra wrote: > This adds the capability to conntrack 802.1ad, QinQ, PPPoE and PPPoE-in-Q > packets that are passing a bridge. > > Signed-off-by: Eric Woudstra <ericwouds@gmail.com> > --- > net/bridge/netfilter/nf_conntrack_bridge.c | 88 ++++++++++++++++++---- > 1 file changed, 75 insertions(+), 13 deletions(-) > > diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c > index 816bb0fde718..31e2bcd71735 100644 > --- a/net/bridge/netfilter/nf_conntrack_bridge.c > +++ b/net/bridge/netfilter/nf_conntrack_bridge.c > @@ -241,56 +241,118 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb, > const struct nf_hook_state *state) > { > struct nf_hook_state bridge_state = *state; > + __be16 outer_proto, inner_proto; > enum ip_conntrack_info ctinfo; > + int ret, offset = 0; > struct nf_conn *ct; > - u32 len; > - int ret; > + u32 len, data_len; > > ct = nf_ct_get(skb, &ctinfo); > if ((ct && !nf_ct_is_template(ct)) || > ctinfo == IP_CT_UNTRACKED) > return NF_ACCEPT; > In all cases below I think you should make sure the headers are present in the linear part of the skb, either do pskb_may_pull() or use skb_header_pointer(). This is executed in the PRE bridge hook, so nothing has been pulled yet by it. > + switch (skb->protocol) { > + case htons(ETH_P_PPP_SES): { > + struct ppp_hdr { > + struct pppoe_hdr hdr; > + __be16 proto; > + } *ph = (struct ppp_hdr *)(skb->data); > + > + data_len = ntohs(ph->hdr.length) - 2; > + offset = PPPOE_SES_HLEN; > + outer_proto = skb->protocol; > + switch (ph->proto) { > + case htons(PPP_IP): > + inner_proto = htons(ETH_P_IP); > + break; > + case htons(PPP_IPV6): > + inner_proto = htons(ETH_P_IPV6); > + break; > + default: > + return NF_ACCEPT; > + } > + break; > + } > + case htons(ETH_P_8021Q): { > + struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data); > + > + data_len = 0xffffffff; > + offset = VLAN_HLEN; > + outer_proto = skb->protocol; > + inner_proto = vhdr->h_vlan_encapsulated_proto; > + break; > + } > + default: > + data_len = 0xffffffff; > + break; > + } > + > + if (offset) { > + switch (inner_proto) { > + case htons(ETH_P_IP): > + case htons(ETH_P_IPV6): > + if (!pskb_may_pull(skb, offset)) > + return NF_ACCEPT; > + skb_pull_rcsum(skb, offset);> + skb_reset_network_header(skb); > + skb->protocol = inner_proto; > + break; > + default: > + return NF_ACCEPT; > + } > + } > + > + ret = NF_ACCEPT; > switch (skb->protocol) { > case htons(ETH_P_IP): > if (!pskb_may_pull(skb, sizeof(struct iphdr))) > - return NF_ACCEPT; > + goto do_not_track; > > len = skb_ip_totlen(skb); > + if (data_len < len) > + len = data_len; > if (pskb_trim_rcsum(skb, len)) > - return NF_ACCEPT; > + goto do_not_track; > > if (nf_ct_br_ip_check(skb)) > - return NF_ACCEPT; > + goto do_not_track; > > bridge_state.pf = NFPROTO_IPV4; > ret = nf_ct_br_defrag4(skb, &bridge_state); > break; > case htons(ETH_P_IPV6): > if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) > - return NF_ACCEPT; > + goto do_not_track; > > len = sizeof(struct ipv6hdr) + ntohs(ipv6_hdr(skb)->payload_len); > + if (data_len < len) > + len = data_len; > if (pskb_trim_rcsum(skb, len)) > - return NF_ACCEPT; > + goto do_not_track; > > if (nf_ct_br_ipv6_check(skb)) > - return NF_ACCEPT; > + goto do_not_track; > > bridge_state.pf = NFPROTO_IPV6; > ret = nf_ct_br_defrag6(skb, &bridge_state); > break; > default: > nf_ct_set(skb, NULL, IP_CT_UNTRACKED); > - return NF_ACCEPT; > + goto do_not_track; > } > > - if (ret != NF_ACCEPT) > - return ret; > + if (ret == NF_ACCEPT) > + ret = nf_conntrack_in(skb, &bridge_state); > > - return nf_conntrack_in(skb, &bridge_state); > +do_not_track: > + if (offset) { > + skb_push_rcsum(skb, offset); > + skb_reset_network_header(skb); > + skb->protocol = outer_proto; > + } > + return ret; > } > - > static unsigned int nf_ct_bridge_in(void *priv, struct sk_buff *skb, > const struct nf_hook_state *state) > {
diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c index 816bb0fde718..31e2bcd71735 100644 --- a/net/bridge/netfilter/nf_conntrack_bridge.c +++ b/net/bridge/netfilter/nf_conntrack_bridge.c @@ -241,56 +241,118 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct nf_hook_state bridge_state = *state; + __be16 outer_proto, inner_proto; enum ip_conntrack_info ctinfo; + int ret, offset = 0; struct nf_conn *ct; - u32 len; - int ret; + u32 len, data_len; ct = nf_ct_get(skb, &ctinfo); if ((ct && !nf_ct_is_template(ct)) || ctinfo == IP_CT_UNTRACKED) return NF_ACCEPT; + switch (skb->protocol) { + case htons(ETH_P_PPP_SES): { + struct ppp_hdr { + struct pppoe_hdr hdr; + __be16 proto; + } *ph = (struct ppp_hdr *)(skb->data); + + data_len = ntohs(ph->hdr.length) - 2; + offset = PPPOE_SES_HLEN; + outer_proto = skb->protocol; + switch (ph->proto) { + case htons(PPP_IP): + inner_proto = htons(ETH_P_IP); + break; + case htons(PPP_IPV6): + inner_proto = htons(ETH_P_IPV6); + break; + default: + return NF_ACCEPT; + } + break; + } + case htons(ETH_P_8021Q): { + struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data); + + data_len = 0xffffffff; + offset = VLAN_HLEN; + outer_proto = skb->protocol; + inner_proto = vhdr->h_vlan_encapsulated_proto; + break; + } + default: + data_len = 0xffffffff; + break; + } + + if (offset) { + switch (inner_proto) { + case htons(ETH_P_IP): + case htons(ETH_P_IPV6): + if (!pskb_may_pull(skb, offset)) + return NF_ACCEPT; + skb_pull_rcsum(skb, offset); + skb_reset_network_header(skb); + skb->protocol = inner_proto; + break; + default: + return NF_ACCEPT; + } + } + + ret = NF_ACCEPT; switch (skb->protocol) { case htons(ETH_P_IP): if (!pskb_may_pull(skb, sizeof(struct iphdr))) - return NF_ACCEPT; + goto do_not_track; len = skb_ip_totlen(skb); + if (data_len < len) + len = data_len; if (pskb_trim_rcsum(skb, len)) - return NF_ACCEPT; + goto do_not_track; if (nf_ct_br_ip_check(skb)) - return NF_ACCEPT; + goto do_not_track; bridge_state.pf = NFPROTO_IPV4; ret = nf_ct_br_defrag4(skb, &bridge_state); break; case htons(ETH_P_IPV6): if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) - return NF_ACCEPT; + goto do_not_track; len = sizeof(struct ipv6hdr) + ntohs(ipv6_hdr(skb)->payload_len); + if (data_len < len) + len = data_len; if (pskb_trim_rcsum(skb, len)) - return NF_ACCEPT; + goto do_not_track; if (nf_ct_br_ipv6_check(skb)) - return NF_ACCEPT; + goto do_not_track; bridge_state.pf = NFPROTO_IPV6; ret = nf_ct_br_defrag6(skb, &bridge_state); break; default: nf_ct_set(skb, NULL, IP_CT_UNTRACKED); - return NF_ACCEPT; + goto do_not_track; } - if (ret != NF_ACCEPT) - return ret; + if (ret == NF_ACCEPT) + ret = nf_conntrack_in(skb, &bridge_state); - return nf_conntrack_in(skb, &bridge_state); +do_not_track: + if (offset) { + skb_push_rcsum(skb, offset); + skb_reset_network_header(skb); + skb->protocol = outer_proto; + } + return ret; } - static unsigned int nf_ct_bridge_in(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
This adds the capability to conntrack 802.1ad, QinQ, PPPoE and PPPoE-in-Q packets that are passing a bridge. Signed-off-by: Eric Woudstra <ericwouds@gmail.com> --- net/bridge/netfilter/nf_conntrack_bridge.c | 88 ++++++++++++++++++---- 1 file changed, 75 insertions(+), 13 deletions(-)