diff options
Diffstat (limited to 'net/ipv4/ip_output.c')
-rw-r--r-- | net/ipv4/ip_output.c | 51 |
1 files changed, 48 insertions, 3 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 1cbeba5edff9..a52f50187b54 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -211,6 +211,48 @@ static inline int ip_finish_output2(struct sk_buff *skb) | |||
211 | return -EINVAL; | 211 | return -EINVAL; |
212 | } | 212 | } |
213 | 213 | ||
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 | |||
214 | static int ip_finish_output(struct sk_buff *skb) | 256 | static int ip_finish_output(struct sk_buff *skb) |
215 | { | 257 | { |
216 | #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM) | 258 | #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM) |
@@ -220,10 +262,13 @@ static int ip_finish_output(struct sk_buff *skb) | |||
220 | return dst_output(skb); | 262 | return dst_output(skb); |
221 | } | 263 | } |
222 | #endif | 264 | #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)) | ||
224 | return ip_fragment(skb, ip_finish_output2); | 269 | return ip_fragment(skb, ip_finish_output2); |
225 | else | 270 | |
226 | return ip_finish_output2(skb); | 271 | return ip_finish_output2(skb); |
227 | } | 272 | } |
228 | 273 | ||
229 | int ip_mc_output(struct sock *sk, struct sk_buff *skb) | 274 | int ip_mc_output(struct sock *sk, struct sk_buff *skb) |