Skip to content

Commit c7ba65d

Browse files
Florian Westphaldavem330
authored andcommitted
net: ip: push gso skb forwarding handling down the stack
Doing the segmentation in the forward path has one major drawback: When using virtio, we may process gso udp packets coming from host network stack. In that case, netfilter POSTROUTING will see one packet with udp header followed by multiple ip fragments. Delay the segmentation and do it after POSTROUTING invocation to avoid this. Fixes: fe6cc55 ("net: ip, ipv6: handle gso skbs in forwarding path") Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 418a315 commit c7ba65d

File tree

2 files changed

+48
-53
lines changed

2 files changed

+48
-53
lines changed

net/ipv4/ip_forward.c

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -56,53 +56,6 @@ static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
5656
return true;
5757
}
5858

59-
static bool ip_gso_exceeds_dst_mtu(const struct sk_buff *skb)
60-
{
61-
unsigned int mtu;
62-
63-
if (skb->local_df || !skb_is_gso(skb))
64-
return false;
65-
66-
mtu = ip_dst_mtu_maybe_forward(skb_dst(skb), true);
67-
68-
/* if seglen > mtu, do software segmentation for IP fragmentation on
69-
* output. DF bit cannot be set since ip_forward would have sent
70-
* icmp error.
71-
*/
72-
return skb_gso_network_seglen(skb) > mtu;
73-
}
74-
75-
/* called if GSO skb needs to be fragmented on forward */
76-
static int ip_forward_finish_gso(struct sk_buff *skb)
77-
{
78-
struct dst_entry *dst = skb_dst(skb);
79-
netdev_features_t features;
80-
struct sk_buff *segs;
81-
int ret = 0;
82-
83-
features = netif_skb_dev_features(skb, dst->dev);
84-
segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
85-
if (IS_ERR(segs)) {
86-
kfree_skb(skb);
87-
return -ENOMEM;
88-
}
89-
90-
consume_skb(skb);
91-
92-
do {
93-
struct sk_buff *nskb = segs->next;
94-
int err;
95-
96-
segs->next = NULL;
97-
err = dst_output(segs);
98-
99-
if (err && ret == 0)
100-
ret = err;
101-
segs = nskb;
102-
} while (segs);
103-
104-
return ret;
105-
}
10659

10760
static int ip_forward_finish(struct sk_buff *skb)
10861
{
@@ -114,9 +67,6 @@ static int ip_forward_finish(struct sk_buff *skb)
11467
if (unlikely(opt->optlen))
11568
ip_forward_options(skb);
11669

117-
if (ip_gso_exceeds_dst_mtu(skb))
118-
return ip_forward_finish_gso(skb);
119-
12070
return dst_output(skb);
12171
}
12272

net/ipv4/ip_output.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,48 @@ static inline int ip_finish_output2(struct sk_buff *skb)
211211
return -EINVAL;
212212
}
213213

214+
static int ip_finish_output_gso(struct sk_buff *skb)
215+
{
216+
netdev_features_t features;
217+
struct sk_buff *segs;
218+
int ret = 0;
219+
220+
/* common case: locally created skb or seglen is <= mtu */
221+
if (((IPCB(skb)->flags & IPSKB_FORWARDED) == 0) ||
222+
skb_gso_network_seglen(skb) <= ip_skb_dst_mtu(skb))
223+
return ip_finish_output2(skb);
224+
225+
/* Slowpath - GSO segment length is exceeding the dst MTU.
226+
*
227+
* This can happen in two cases:
228+
* 1) TCP GRO packet, DF bit not set
229+
* 2) skb arrived via virtio-net, we thus get TSO/GSO skbs directly
230+
* from host network stack.
231+
*/
232+
features = netif_skb_features(skb);
233+
segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
234+
if (IS_ERR(segs)) {
235+
kfree_skb(skb);
236+
return -ENOMEM;
237+
}
238+
239+
consume_skb(skb);
240+
241+
do {
242+
struct sk_buff *nskb = segs->next;
243+
int err;
244+
245+
segs->next = NULL;
246+
err = ip_fragment(segs, ip_finish_output2);
247+
248+
if (err && ret == 0)
249+
ret = err;
250+
segs = nskb;
251+
} while (segs);
252+
253+
return ret;
254+
}
255+
214256
static int ip_finish_output(struct sk_buff *skb)
215257
{
216258
#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
@@ -220,10 +262,13 @@ static int ip_finish_output(struct sk_buff *skb)
220262
return dst_output(skb);
221263
}
222264
#endif
223-
if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
265+
if (skb_is_gso(skb))
266+
return ip_finish_output_gso(skb);
267+
268+
if (skb->len > ip_skb_dst_mtu(skb))
224269
return ip_fragment(skb, ip_finish_output2);
225-
else
226-
return ip_finish_output2(skb);
270+
271+
return ip_finish_output2(skb);
227272
}
228273

229274
int ip_mc_output(struct sock *sk, struct sk_buff *skb)

0 commit comments

Comments
 (0)