diff options
Diffstat (limited to 'net/ipv4/ip_output.c')
-rw-r--r-- | net/ipv4/ip_output.c | 67 |
1 files changed, 56 insertions, 11 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 1a0755fea491..a52f50187b54 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -101,17 +101,17 @@ int __ip_local_out(struct sk_buff *skb) | |||
101 | skb_dst(skb)->dev, dst_output); | 101 | skb_dst(skb)->dev, dst_output); |
102 | } | 102 | } |
103 | 103 | ||
104 | int ip_local_out(struct sk_buff *skb) | 104 | int ip_local_out_sk(struct sock *sk, struct sk_buff *skb) |
105 | { | 105 | { |
106 | int err; | 106 | int err; |
107 | 107 | ||
108 | err = __ip_local_out(skb); | 108 | err = __ip_local_out(skb); |
109 | if (likely(err == 1)) | 109 | if (likely(err == 1)) |
110 | err = dst_output(skb); | 110 | err = dst_output_sk(sk, skb); |
111 | 111 | ||
112 | return err; | 112 | return err; |
113 | } | 113 | } |
114 | EXPORT_SYMBOL_GPL(ip_local_out); | 114 | EXPORT_SYMBOL_GPL(ip_local_out_sk); |
115 | 115 | ||
116 | static inline int ip_select_ttl(struct inet_sock *inet, struct dst_entry *dst) | 116 | static inline int ip_select_ttl(struct inet_sock *inet, struct dst_entry *dst) |
117 | { | 117 | { |
@@ -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,15 +262,17 @@ 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 sk_buff *skb) | 274 | int ip_mc_output(struct sock *sk, struct sk_buff *skb) |
230 | { | 275 | { |
231 | struct sock *sk = skb->sk; | ||
232 | struct rtable *rt = skb_rtable(skb); | 276 | struct rtable *rt = skb_rtable(skb); |
233 | struct net_device *dev = rt->dst.dev; | 277 | struct net_device *dev = rt->dst.dev; |
234 | 278 | ||
@@ -287,7 +331,7 @@ int ip_mc_output(struct sk_buff *skb) | |||
287 | !(IPCB(skb)->flags & IPSKB_REROUTED)); | 331 | !(IPCB(skb)->flags & IPSKB_REROUTED)); |
288 | } | 332 | } |
289 | 333 | ||
290 | int ip_output(struct sk_buff *skb) | 334 | int ip_output(struct sock *sk, struct sk_buff *skb) |
291 | { | 335 | { |
292 | struct net_device *dev = skb_dst(skb)->dev; | 336 | struct net_device *dev = skb_dst(skb)->dev; |
293 | 337 | ||
@@ -315,9 +359,9 @@ static void ip_copy_addrs(struct iphdr *iph, const struct flowi4 *fl4) | |||
315 | sizeof(fl4->saddr) + sizeof(fl4->daddr)); | 359 | sizeof(fl4->saddr) + sizeof(fl4->daddr)); |
316 | } | 360 | } |
317 | 361 | ||
318 | int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl) | 362 | /* Note: skb->sk can be different from sk, in case of tunnels */ |
363 | int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl) | ||
319 | { | 364 | { |
320 | struct sock *sk = skb->sk; | ||
321 | struct inet_sock *inet = inet_sk(sk); | 365 | struct inet_sock *inet = inet_sk(sk); |
322 | struct ip_options_rcu *inet_opt; | 366 | struct ip_options_rcu *inet_opt; |
323 | struct flowi4 *fl4; | 367 | struct flowi4 *fl4; |
@@ -389,6 +433,7 @@ packet_routed: | |||
389 | ip_select_ident_more(skb, &rt->dst, sk, | 433 | ip_select_ident_more(skb, &rt->dst, sk, |
390 | (skb_shinfo(skb)->gso_segs ?: 1) - 1); | 434 | (skb_shinfo(skb)->gso_segs ?: 1) - 1); |
391 | 435 | ||
436 | /* TODO : should we use skb->sk here instead of sk ? */ | ||
392 | skb->priority = sk->sk_priority; | 437 | skb->priority = sk->sk_priority; |
393 | skb->mark = sk->sk_mark; | 438 | skb->mark = sk->sk_mark; |
394 | 439 | ||