Skip to contentSkip to conversation

bridge-netfilter: fix refragmenting IP traffic encapsulated in PPPoE traffic

PATCH Applied Bart De Schuymer 2 participants 4 messages Apr 14, 2010 – Apr 20, 2010

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 3/3 bridge-netfilter: fix refragmenting IP traffic encapsulated in PPPoE traffic

bridge-netfilter: fix refragmenting IP traffic encapsulated in PPPoE traffic

The MTU for IP traffic encapsulated inside PPPoE traffic is smaller than the MTU of the Ethernet device (1500). Connection tracking gathers all IP packets and sometimes will refragment them in ip_fragment(). We then need to subtract the length of the encapsulating header from the mtu used in ip_fragment(). The check in br_nf_dev_queue_xmit() which determines if ip_fragment() has to be called is also updated for the PPPoE-encapsulated packets. nf_bridge_copy_header() is also updated to make sure the PPPoE data length field has the correct value.

net/bridge/br_netfilter.c renamed from net/bridge/br_netfilter.c.ori3 +3 -1
@@ -221,6 +221,8 @@ int nf_bridge_copy_header(struct sk_buff
 	skb_copy_to_linear_data_offset(skb, -header_size,
 				       skb->nf_bridge->data, header_size);
 	__skb_push(skb, nf_bridge_encap_header_len(skb));
+	if (unlikely(skb->protocol == htons(ETH_P_PPP_SES)))
+		((struct pppoe_hdr *)skb->data)->length = htons(skb->len-sizeof(struct pppoe_hdr));
 	return 0;
 }
 
@@ -744,7 +746,7 @@ static unsigned int br_nf_forward_arp(un
 static int br_nf_dev_queue_xmit(struct sk_buff *skb)
 {
 	if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) &&
-	    skb->len > skb->dev->mtu &&
+	    skb->len + ((skb->nf_bridge->mask & BRNF_PPPoE) ? PPPOE_SES_HLEN:0) > skb->dev->mtu &&
 	    !skb_is_gso(skb))
 		return ip_fragment(skb, br_dev_queue_push_xmit);
 	else
net/ipv4/ip_output.c renamed from net/ipv4/ip_output.c.ori +4 -0
@@ -468,6 +468,10 @@ int ip_fragment(struct sk_buff *skb, int
 
 	hlen = iph->ihl * 4;
 	mtu = dst_mtu(&rt->u.dst) - hlen;	/* Size of data space */
+#ifdef CONFIG_BRIDGE_NETFILTER
+	if (unlikely(skb->nf_bridge && (skb->nf_bridge->mask & BRNF_PPPoE)))
+		mtu -= PPPOE_SES_HLEN;
+#endif
 	IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE;
 
 	/* When frag_list is given, use it. First, check its validity:
Signature · 2 lines
Bart De Schuymer
www.artinalgorithms.be
#1 lore raw
Quoted 38 lines · Bart De Schuymer

bridge-netfilter: fix refragmenting IP traffic encapsulated in PPPoE traffic

The MTU for IP traffic encapsulated inside PPPoE traffic is smaller than the MTU of the Ethernet device (1500). Connection tracking gathers all IP packets and sometimes will refragment them in ip_fragment(). We then need to subtract the length of the encapsulating header from the mtu used in ip_fragment(). The check in br_nf_dev_queue_xmit() which determines if ip_fragment() has to be called is also updated for the PPPoE-encapsulated packets. nf_bridge_copy_header() is also updated to make sure the PPPoE data length field has the correct value.

net/bridge/br_netfilter.c renamed from net/bridge/br_netfilter.c.ori3 +3 -1
@@ -221,6 +221,8 @@ int nf_bridge_copy_header(struct sk_buff
 	skb_copy_to_linear_data_offset(skb, -header_size,
 				       skb->nf_bridge->data, header_size);
 	__skb_push(skb, nf_bridge_encap_header_len(skb));
+	if (unlikely(skb->protocol == htons(ETH_P_PPP_SES)))
+		((struct pppoe_hdr *)skb->data)->length = htons(skb->len-sizeof(struct pppoe_hdr));
 	return 0;
 }
 
@@ -744,7 +746,7 @@ static unsigned int br_nf_forward_arp(un
 static int br_nf_dev_queue_xmit(struct sk_buff *skb)
 {
 	if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) &&
-	    skb->len > skb->dev->mtu &&
+	    skb->len + ((skb->nf_bridge->mask & BRNF_PPPoE) ? PPPOE_SES_HLEN:0) > skb->dev->mtu &&
 	    !skb_is_gso(skb))
 		return ip_fragment(skb, br_dev_queue_push_xmit);
 	else

net/ipv4/ip_output.c int ip_fragment(struct sk_buff *skb, int · line 474

Quoted 7 lines
net/ipv4/ip_output.c renamed from net/ipv4/ip_output.c.ori +4 -0
@@ -468,6 +468,10 @@ int ip_fragment(struct sk_buff *skb, int
 
 	hlen = iph->ihl * 4;
 	mtu = dst_mtu(&rt->u.dst) - hlen;	/* Size of data space */
+#ifdef CONFIG_BRIDGE_NETFILTER
+	if (unlikely(skb->nf_bridge && (skb->nf_bridge->mask & BRNF_PPPoE)))
+		mtu -= PPPOE_SES_HLEN;
+#endif

I think it would be nice to encapsulate this in a small inline function, perhaps nf_bridge_adjust_mtu(skb, mtu) or something like that.

Please also fix the overly long lines.

#2 lore raw
Quoted 9 lines · Patrick McHardy
Quoted 2 lines · Bart De Schuymer

bridge-netfilter: fix refragmenting IP traffic encapsulated in PPPoE traffic

I think it would be nice to encapsulate this in a small inline function, perhaps nf_bridge_adjust_mtu(skb, mtu) or something like that.

Please also fix the overly long lines.

Done, see below.

bridge-netfilter: fix refragmenting IP traffic encapsulated in PPPoE traffic

The MTU for IP traffic encapsulated inside PPPoE traffic is smaller than the MTU of the Ethernet device (1500). Connection tracking gathers all IP packets and sometimes will refragment them in ip_fragment(). We then need to subtract the length of the encapsulating header from the mtu used in ip_fragment(). The check in br_nf_dev_queue_xmit() which determines if ip_fragment() has to be called is also updated for the PPPoE-encapsulated packets. nf_bridge_copy_header() is also updated to make sure the PPPoE data length field has the correct value.

include/linux/netfilter_bridge.h renamed from include/linux/netfilter_bridge.h.ori3 +7 -0
@@ -68,6 +68,13 @@ static inline unsigned int nf_bridge_enc
 	}
 }
 
+static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb)
+{
+	if (unlikely(skb->nf_bridge->mask & BRNF_PPPoE))
+		return PPPOE_SES_HLEN;
+	return 0;
+}
+
 extern int br_handle_frame_finish(struct sk_buff *skb);
 /* Only used in br_device.c */
 static inline int br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
net/ipv4/ip_output.c renamed from net/ipv4/ip_output.c.ori +4 -0
@@ -468,6 +468,10 @@ int ip_fragment(struct sk_buff *skb, int
 
 	hlen = iph->ihl * 4;
 	mtu = dst_mtu(&rt->u.dst) - hlen;	/* Size of data space */
+#ifdef CONFIG_BRIDGE_NETFILTER
+	if (skb->nf_bridge)
+		mtu -= nf_bridge_mtu_reduction(skb);
+#endif
 	IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE;
 
 	/* When frag_list is given, use it. First, check its validity:
net/bridge/br_netfilter.c renamed from net/bridge/br_netfilter.c.ori3 +1 -1
@@ -744,7 +744,7 @@ static unsigned int br_nf_forward_arp(un
 static int br_nf_dev_queue_xmit(struct sk_buff *skb)
 {
 	if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) &&
-	    skb->len > skb->dev->mtu &&
+	    skb->len + nf_bridge_mtu_reduction(skb) > skb->dev->mtu &&
 	    !skb_is_gso(skb))
 		return ip_fragment(skb, br_dev_queue_push_xmit);
 	else
Signature · 2 lines
Bart De Schuymer
www.artinalgorithms.be
#3 lore raw
Quoted 12 lines · Bart De Schuymer

bridge-netfilter: fix refragmenting IP traffic encapsulated in PPPoE traffic

The MTU for IP traffic encapsulated inside PPPoE traffic is smaller than the MTU of the Ethernet device (1500). Connection tracking gathers all IP packets and sometimes will refragment them in ip_fragment(). We then need to subtract the length of the encapsulating header from the mtu used in ip_fragment(). The check in br_nf_dev_queue_xmit() which determines if ip_fragment() has to be called is also updated for the PPPoE-encapsulated packets. nf_bridge_copy_header() is also updated to make sure the PPPoE data length field has the correct value.

Applied, thanks Bart.