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) |
