Blame view

net/ipv6/ip6_output.c 36.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *	IPv6 output functions
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
3
   *	Linux INET6 implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
   *
   *	Authors:
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
6
   *	Pedro Roque		<roque@di.fc.ul.pt>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
   *
   *	$Id: ip6_output.c,v 1.34 2002/02/01 22:01:04 davem Exp $
   *
   *	Based on linux/net/ipv4/ip_output.c
   *
   *	This program is free software; you can redistribute it and/or
   *      modify it under the terms of the GNU General Public License
   *      as published by the Free Software Foundation; either version
   *      2 of the License, or (at your option) any later version.
   *
   *	Changes:
   *	A.N.Kuznetsov	:	airthmetics in fragmentation.
   *				extension headers are implemented.
   *				route changes now work.
   *				ip6_forward does not confuse sniffers.
   *				etc.
   *
   *      H. von Brand    :       Added missing #include <linux/string.h>
   *	Imran Patel	: 	frag id should be in NBO
   *      Kazunori MIYAZAWA @USAGI
   *			:       add ip6_append_data and related functions
   *				for datagram xmit
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
39
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/string.h>
  #include <linux/socket.h>
  #include <linux/net.h>
  #include <linux/netdevice.h>
  #include <linux/if_arp.h>
  #include <linux/in6.h>
  #include <linux/tcp.h>
  #include <linux/route.h>
b59f45d0b   Herbert Xu   [IPSEC] xfrm: Abs...
40
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  
  #include <linux/netfilter.h>
  #include <linux/netfilter_ipv6.h>
  
  #include <net/sock.h>
  #include <net/snmp.h>
  
  #include <net/ipv6.h>
  #include <net/ndisc.h>
  #include <net/protocol.h>
  #include <net/ip6_route.h>
  #include <net/addrconf.h>
  #include <net/rawv6.h>
  #include <net/icmp.h>
  #include <net/xfrm.h>
  #include <net/checksum.h>
  
  static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
  
  static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr)
  {
  	static u32 ipv6_fragmentation_id = 1;
  	static DEFINE_SPINLOCK(ip6_id_lock);
  
  	spin_lock_bh(&ip6_id_lock);
  	fhdr->identification = htonl(ipv6_fragmentation_id);
  	if (++ipv6_fragmentation_id == 0)
  		ipv6_fragmentation_id = 1;
  	spin_unlock_bh(&ip6_id_lock);
  }
  
  static inline int ip6_output_finish(struct sk_buff *skb)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
  	struct dst_entry *dst = skb->dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75

3644f0cee   Stephen Hemminger   [NET]: Convert hh...
76
77
78
  	if (dst->hh)
  		return neigh_hh_output(dst->hh, skb);
  	else if (dst->neighbour)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  		return dst->neighbour->output(skb);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
80
  	IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
86
87
88
  	kfree_skb(skb);
  	return -EINVAL;
  
  }
  
  /* dev_loopback_xmit for use with netfilter. */
  static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
  {
459a98ed8   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
89
  	skb_reset_mac_header(newskb);
bbe735e42   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
90
  	__skb_pull(newskb, skb_network_offset(newskb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  	newskb->pkt_type = PACKET_LOOPBACK;
  	newskb->ip_summed = CHECKSUM_UNNECESSARY;
  	BUG_TRAP(newskb->dst);
  
  	netif_rx(newskb);
  	return 0;
  }
  
  
  static int ip6_output2(struct sk_buff *skb)
  {
  	struct dst_entry *dst = skb->dst;
  	struct net_device *dev = dst->dev;
  
  	skb->protocol = htons(ETH_P_IPV6);
  	skb->dev = dev;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
107
  	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
  		struct ipv6_pinfo* np = skb->sk ? inet6_sk(skb->sk) : NULL;
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
109
  		struct inet6_dev *idev = ip6_dst_idev(skb->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
  
  		if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) &&
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
112
113
  		    ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
  					&ipv6_hdr(skb)->saddr)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
117
118
119
120
121
122
  			struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
  
  			/* Do not check for IFF_ALLMULTI; multicast routing
  			   is not supported in any case.
  			 */
  			if (newskb)
  				NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, newskb, NULL,
  					newskb->dev,
  					ip6_dev_loopback_xmit);
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
123
  			if (ipv6_hdr(skb)->hop_limit == 0) {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
124
  				IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
  				kfree_skb(skb);
  				return 0;
  			}
  		}
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
129
  		IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
  	}
  
  	return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
  }
628a5c561   John Heffner   [INET]: Add IP(V6...
134
135
136
137
138
139
140
  static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
  {
  	struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
  
  	return (np && np->pmtudisc == IPV6_PMTUDISC_PROBE) ?
  	       skb->dst->dev->mtu : dst_mtu(skb->dst);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
  int ip6_output(struct sk_buff *skb)
  {
628a5c561   John Heffner   [INET]: Add IP(V6...
143
  	if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
e89e9cf53   Ananda Raju   [IPv4/IPv6]: UFO ...
144
  				dst_allfrag(skb->dst))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
148
  		return ip6_fragment(skb, ip6_output2);
  	else
  		return ip6_output2(skb);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
153
154
155
  /*
   *	xmit an sk_buff (used by TCP)
   */
  
  int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
  	     struct ipv6_txoptions *opt, int ipfragok)
  {
b30bd282c   Patrick McHardy   [IPV6]: ip6_xmit:...
156
  	struct ipv6_pinfo *np = inet6_sk(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
  	struct in6_addr *first_hop = &fl->fl6_dst;
  	struct dst_entry *dst = skb->dst;
  	struct ipv6hdr *hdr;
  	u8  proto = fl->proto;
  	int seg_len = skb->len;
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
162
  	int hlimit, tclass;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  	u32 mtu;
  
  	if (opt) {
  		int head_room;
  
  		/* First: exthdrs may take lots of space (~8K for now)
  		   MAX_HEADER is not enough.
  		 */
  		head_room = opt->opt_nflen + opt->opt_flen;
  		seg_len += head_room;
  		head_room += sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev);
  
  		if (skb_headroom(skb) < head_room) {
  			struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
177
178
179
180
  			if (skb2 == NULL) {
  				IP6_INC_STATS(ip6_dst_idev(skb->dst),
  					      IPSTATS_MIB_OUTDISCARDS);
  				kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
  				return -ENOBUFS;
  			}
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
183
184
  			kfree_skb(skb);
  			skb = skb2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
190
191
192
  			if (sk)
  				skb_set_owner_w(skb, sk);
  		}
  		if (opt->opt_flen)
  			ipv6_push_frag_opts(skb, opt, &proto);
  		if (opt->opt_nflen)
  			ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop);
  	}
e2d1bca7e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
193
194
  	skb_push(skb, sizeof(struct ipv6hdr));
  	skb_reset_network_header(skb);
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
195
  	hdr = ipv6_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
  
  	/*
  	 *	Fill in the IPv6 header
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
204
205
206
  	hlimit = -1;
  	if (np)
  		hlimit = np->hop_limit;
  	if (hlimit < 0)
  		hlimit = dst_metric(dst, RTAX_HOPLIMIT);
  	if (hlimit < 0)
  		hlimit = ipv6_get_hoplimit(dst->dev);
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
207
208
209
210
211
  	tclass = -1;
  	if (np)
  		tclass = np->tclass;
  	if (tclass < 0)
  		tclass = 0;
90bcaf7b4   Al Viro   [IPV6]: flowlabel...
212
  	*(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl->fl6_flowlabel;
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
213

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
219
  	hdr->payload_len = htons(seg_len);
  	hdr->nexthdr = proto;
  	hdr->hop_limit = hlimit;
  
  	ipv6_addr_copy(&hdr->saddr, &fl->fl6_src);
  	ipv6_addr_copy(&hdr->daddr, first_hop);
a2c2064f7   Patrick McHardy   [IPV6]: Set skb->...
220
  	skb->priority = sk->sk_priority;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
  	mtu = dst_mtu(dst);
89114afd4   Herbert Xu   [NET] gso: Add sk...
222
  	if ((skb->len <= mtu) || ipfragok || skb_is_gso(skb)) {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
223
224
  		IP6_INC_STATS(ip6_dst_idev(skb->dst),
  			      IPSTATS_MIB_OUTREQUESTS);
6869c4d8e   Harald Welte   [NETFILTER]: redu...
225
226
  		return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev,
  				dst_output);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
231
232
233
  	}
  
  	if (net_ratelimit())
  		printk(KERN_DEBUG "IPv6: sending pkt_too_big to self
  ");
  	skb->dev = dst->dev;
  	icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
234
  	IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
237
  	kfree_skb(skb);
  	return -EMSGSIZE;
  }
7159039a1   YOSHIFUJI Hideaki   [IPV6]: Decentral...
238
  EXPORT_SYMBOL(ip6_xmit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  /*
   *	To avoid extra problems ND packets are send through this
   *	routine. It's code duplication but I really want to avoid
   *	extra checks since ipv6_build_header is used by TCP (which
   *	is for us performance critical)
   */
  
  int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct net_device *dev,
  	       struct in6_addr *saddr, struct in6_addr *daddr,
  	       int proto, int len)
  {
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct ipv6hdr *hdr;
  	int totlen;
  
  	skb->protocol = htons(ETH_P_IPV6);
  	skb->dev = dev;
  
  	totlen = len + sizeof(struct ipv6hdr);
55f79cc0c   Arnaldo Carvalho de Melo   [IPV6]: Reset the...
258
259
  	skb_reset_network_header(skb);
  	skb_put(skb, sizeof(struct ipv6hdr));
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
260
  	hdr = ipv6_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261

ae08e1f09   Al Viro   [IPV6]: ip6_outpu...
262
  	*(__be32*)hdr = htonl(0x60000000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
  
  	hdr->payload_len = htons(len);
  	hdr->nexthdr = proto;
  	hdr->hop_limit = np->hop_limit;
  
  	ipv6_addr_copy(&hdr->saddr, saddr);
  	ipv6_addr_copy(&hdr->daddr, daddr);
  
  	return 0;
  }
  
  static int ip6_call_ra_chain(struct sk_buff *skb, int sel)
  {
  	struct ip6_ra_chain *ra;
  	struct sock *last = NULL;
  
  	read_lock(&ip6_ra_lock);
  	for (ra = ip6_ra_chain; ra; ra = ra->next) {
  		struct sock *sk = ra->sk;
0bd1b59b1   Andrew McDonald   [IPV6]: Check int...
282
283
284
  		if (sk && ra->sel == sel &&
  		    (!sk->sk_bound_dev_if ||
  		     sk->sk_bound_dev_if == skb->dev->ifindex)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  			if (last) {
  				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
  				if (skb2)
  					rawv6_rcv(last, skb2);
  			}
  			last = sk;
  		}
  	}
  
  	if (last) {
  		rawv6_rcv(last, skb);
  		read_unlock(&ip6_ra_lock);
  		return 1;
  	}
  	read_unlock(&ip6_ra_lock);
  	return 0;
  }
e21e0b5f1   Ville Nuorvala   [IPV6] NDISC: Han...
302
303
  static int ip6_forward_proxy_check(struct sk_buff *skb)
  {
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
304
  	struct ipv6hdr *hdr = ipv6_hdr(skb);
e21e0b5f1   Ville Nuorvala   [IPV6] NDISC: Han...
305
306
307
308
309
310
311
312
313
314
315
316
  	u8 nexthdr = hdr->nexthdr;
  	int offset;
  
  	if (ipv6_ext_hdr(nexthdr)) {
  		offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr);
  		if (offset < 0)
  			return 0;
  	} else
  		offset = sizeof(struct ipv6hdr);
  
  	if (nexthdr == IPPROTO_ICMPV6) {
  		struct icmp6hdr *icmp6;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
317
318
  		if (!pskb_may_pull(skb, (skb_network_header(skb) +
  					 offset + 1 - skb->data)))
e21e0b5f1   Ville Nuorvala   [IPV6] NDISC: Han...
319
  			return 0;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
320
  		icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset);
e21e0b5f1   Ville Nuorvala   [IPV6] NDISC: Han...
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
  
  		switch (icmp6->icmp6_type) {
  		case NDISC_ROUTER_SOLICITATION:
  		case NDISC_ROUTER_ADVERTISEMENT:
  		case NDISC_NEIGHBOUR_SOLICITATION:
  		case NDISC_NEIGHBOUR_ADVERTISEMENT:
  		case NDISC_REDIRECT:
  			/* For reaction involving unicast neighbor discovery
  			 * message destined to the proxied address, pass it to
  			 * input function.
  			 */
  			return 1;
  		default:
  			break;
  		}
  	}
74553b09d   Ville Nuorvala   [IPV6]: Don't for...
337
338
339
340
341
342
343
344
345
  	/*
  	 * The proxying router can't forward traffic sent to a link-local
  	 * address, so signal the sender and discard the packet. This
  	 * behavior is clarified by the MIPv6 specification.
  	 */
  	if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL) {
  		dst_link_failure(skb);
  		return -1;
  	}
e21e0b5f1   Ville Nuorvala   [IPV6] NDISC: Han...
346
347
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
350
351
352
353
354
355
  static inline int ip6_forward_finish(struct sk_buff *skb)
  {
  	return dst_output(skb);
  }
  
  int ip6_forward(struct sk_buff *skb)
  {
  	struct dst_entry *dst = skb->dst;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
356
  	struct ipv6hdr *hdr = ipv6_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  	struct inet6_skb_parm *opt = IP6CB(skb);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
358

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
362
  	if (ipv6_devconf.forwarding == 0)
  		goto error;
  
  	if (!xfrm6_policy_check(NULL, XFRM_POLICY_FWD, skb)) {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
363
  		IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
  		goto drop;
  	}
35fc92a9d   Herbert Xu   [NET]: Allow forw...
366
  	skb_forward_csum(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
  
  	/*
  	 *	We DO NOT make any processing on
  	 *	RA packets, pushing them to user level AS IS
  	 *	without ane WARRANTY that application will be able
  	 *	to interpret them. The reason is that we
  	 *	cannot make anything clever here.
  	 *
  	 *	We are not end-node, so that if packet contains
  	 *	AH/ESP, we cannot make anything.
  	 *	Defragmentation also would be mistake, RA packets
  	 *	cannot be fragmented, because there is no warranty
  	 *	that different fragments will go along one path. --ANK
  	 */
  	if (opt->ra) {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
382
  		u8 *ptr = skb_network_header(skb) + opt->ra;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
385
386
387
388
389
390
391
392
393
394
  		if (ip6_call_ra_chain(skb, (ptr[2]<<8) + ptr[3]))
  			return 0;
  	}
  
  	/*
  	 *	check and decrement ttl
  	 */
  	if (hdr->hop_limit <= 1) {
  		/* Force OUTPUT device used as source address */
  		skb->dev = dst->dev;
  		icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
  			    0, skb->dev);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
395
  		IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
  
  		kfree_skb(skb);
  		return -ETIMEDOUT;
  	}
fbea49e1e   YOSHIFUJI Hideaki   [IPV6] NDISC: Add...
400
401
402
  	/* XXX: idev->cnf.proxy_ndp? */
  	if (ipv6_devconf.proxy_ndp &&
  	    pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) {
74553b09d   Ville Nuorvala   [IPV6]: Don't for...
403
404
  		int proxied = ip6_forward_proxy_check(skb);
  		if (proxied > 0)
e21e0b5f1   Ville Nuorvala   [IPV6] NDISC: Han...
405
  			return ip6_input(skb);
74553b09d   Ville Nuorvala   [IPV6]: Don't for...
406
  		else if (proxied < 0) {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
407
  			IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS);
74553b09d   Ville Nuorvala   [IPV6]: Don't for...
408
409
  			goto drop;
  		}
e21e0b5f1   Ville Nuorvala   [IPV6] NDISC: Han...
410
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
  	if (!xfrm6_route_forward(skb)) {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
412
  		IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
  		goto drop;
  	}
  	dst = skb->dst;
  
  	/* IPv6 specs say nothing about it, but it is clear that we cannot
  	   send redirects to source routed frames.
  	 */
  	if (skb->dev == dst->dev && dst->neighbour && opt->srcrt == 0) {
  		struct in6_addr *target = NULL;
  		struct rt6_info *rt;
  		struct neighbour *n = dst->neighbour;
  
  		/*
  		 *	incoming and outgoing devices are the same
  		 *	send a redirect.
  		 */
  
  		rt = (struct rt6_info *) dst;
  		if ((rt->rt6i_flags & RTF_GATEWAY))
  			target = (struct in6_addr*)&n->primary_key;
  		else
  			target = &hdr->daddr;
  
  		/* Limit redirects both by destination (here)
  		   and by source (inside ndisc_send_redirect)
  		 */
  		if (xrlim_allow(dst, 1*HZ))
  			ndisc_send_redirect(skb, n, target);
5bb1ab09e   David L Stevens   [IPV6]: Send ICMP...
441
442
  	} else {
  		int addrtype = ipv6_addr_type(&hdr->saddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
  		/* This check is security critical. */
5bb1ab09e   David L Stevens   [IPV6]: Send ICMP...
444
445
446
447
448
449
450
  		if (addrtype & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LOOPBACK))
  			goto error;
  		if (addrtype & IPV6_ADDR_LINKLOCAL) {
  			icmpv6_send(skb, ICMPV6_DEST_UNREACH,
  				ICMPV6_NOT_NEIGHBOUR, 0, skb->dev);
  			goto error;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
454
455
456
  	}
  
  	if (skb->len > dst_mtu(dst)) {
  		/* Again, force OUTPUT device used as source address */
  		skb->dev = dst->dev;
  		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst_mtu(dst), skb->dev);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
457
458
  		IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_INTOOBIGERRORS);
  		IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_FRAGFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
462
463
  		kfree_skb(skb);
  		return -EMSGSIZE;
  	}
  
  	if (skb_cow(skb, dst->dev->hard_header_len)) {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
464
  		IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_OUTDISCARDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
  		goto drop;
  	}
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
467
  	hdr = ipv6_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
  
  	/* Mangling hops number delayed to point after skb COW */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
470

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
  	hdr->hop_limit--;
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
472
  	IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
  	return NF_HOOK(PF_INET6,NF_IP6_FORWARD, skb, skb->dev, dst->dev, ip6_forward_finish);
  
  error:
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
476
  	IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_INADDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
480
481
482
483
484
485
486
  drop:
  	kfree_skb(skb);
  	return -EINVAL;
  }
  
  static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
  {
  	to->pkt_type = from->pkt_type;
  	to->priority = from->priority;
  	to->protocol = from->protocol;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
489
  	dst_release(to->dst);
  	to->dst = dst_clone(from->dst);
  	to->dev = from->dev;
82e91ffef   Thomas Graf   [NET]: Turn nfmar...
490
  	to->mark = from->mark;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
492
493
494
  
  #ifdef CONFIG_NET_SCHED
  	to->tc_index = from->tc_index;
  #endif
e7ac05f34   Yasuyuki Kozakai   [NETFILTER]: nf_c...
495
  	nf_copy(to, from);
ba9dda3ab   Jozsef Kadlecsik   [NETFILTER]: x_ta...
496
497
498
499
  #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
      defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
  	to->nf_trace = from->nf_trace;
  #endif
984bc16cc   James Morris   [SECMARK]: Add se...
500
  	skb_copy_secmark(to, from);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
  }
  
  int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
  {
  	u16 offset = sizeof(struct ipv6hdr);
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
506
507
  	struct ipv6_opt_hdr *exthdr =
  				(struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
508
  	unsigned int packet_len = skb->tail - skb->network_header;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
  	int found_rhdr = 0;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
510
  	*nexthdr = &ipv6_hdr(skb)->nexthdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
513
514
515
516
  
  	while (offset + 1 <= packet_len) {
  
  		switch (**nexthdr) {
  
  		case NEXTHDR_HOP:
27637df92   Masahide NAKAMURA   [IPV6] IPSEC: Sup...
517
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  		case NEXTHDR_ROUTING:
27637df92   Masahide NAKAMURA   [IPV6] IPSEC: Sup...
519
520
  			found_rhdr = 1;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
  		case NEXTHDR_DEST:
59fbb3a61   Masahide NAKAMURA   [IPV6] MIP6: Load...
522
  #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
27637df92   Masahide NAKAMURA   [IPV6] IPSEC: Sup...
523
524
525
526
527
  			if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
  				break;
  #endif
  			if (found_rhdr)
  				return offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
529
530
531
  			break;
  		default :
  			return offset;
  		}
27637df92   Masahide NAKAMURA   [IPV6] IPSEC: Sup...
532
533
534
  
  		offset += ipv6_optlen(exthdr);
  		*nexthdr = &exthdr->nexthdr;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
535
536
  		exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
  						 offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
539
540
  	}
  
  	return offset;
  }
b59f45d0b   Herbert Xu   [IPSEC] xfrm: Abs...
541
  EXPORT_SYMBOL_GPL(ip6_find_1stfragopt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
544
545
546
547
  
  static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
  {
  	struct net_device *dev;
  	struct sk_buff *frag;
  	struct rt6_info *rt = (struct rt6_info*)skb->dst;
d91675f9c   YOSHIFUJI Hideaki   [IPV6]: Do not ig...
548
  	struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
550
551
  	struct ipv6hdr *tmp_hdr;
  	struct frag_hdr *fh;
  	unsigned int mtu, hlen, left, len;
ae08e1f09   Al Viro   [IPV6]: ip6_outpu...
552
  	__be32 frag_id = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
556
557
558
  	int ptr, offset = 0, err=0;
  	u8 *prevhdr, nexthdr = 0;
  
  	dev = rt->u.dst.dev;
  	hlen = ip6_find_1stfragopt(skb, &prevhdr);
  	nexthdr = *prevhdr;
628a5c561   John Heffner   [INET]: Add IP(V6...
559
  	mtu = ip6_skb_dst_mtu(skb);
b881ef760   John Heffner   [IPV6]: MTU disco...
560
561
562
563
564
565
566
567
568
569
570
571
  
  	/* We must not fragment if the socket is set to force MTU discovery
  	 * or if the skb it not generated by a local socket.  (This last
  	 * check should be redundant, but it's free.)
  	 */
  	if (!np || np->pmtudisc >= IPV6_PMTUDISC_DO) {
  		skb->dev = skb->dst->dev;
  		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
  		IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
  		kfree_skb(skb);
  		return -EMSGSIZE;
  	}
d91675f9c   YOSHIFUJI Hideaki   [IPV6]: Do not ig...
572
573
574
575
576
  	if (np && np->frag_size < mtu) {
  		if (np->frag_size)
  			mtu = np->frag_size;
  	}
  	mtu -= hlen + sizeof(struct frag_hdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
  
  	if (skb_shinfo(skb)->frag_list) {
  		int first_len = skb_pagelen(skb);
  
  		if (first_len - hlen > mtu ||
  		    ((first_len - hlen) & 7) ||
  		    skb_cloned(skb))
  			goto slow_path;
  
  		for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
  			/* Correct geometry. */
  			if (frag->len > mtu ||
  			    ((frag->len & 7) && frag->next) ||
  			    skb_headroom(frag) < hlen)
  			    goto slow_path;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
593
594
  			/* Partially cloned skb? */
  			if (skb_shared(frag))
  				goto slow_path;
2fdba6b08   Herbert Xu   [IPV4/IPV6] Ensur...
595
596
597
598
599
600
601
602
  
  			BUG_ON(frag->sk);
  			if (skb->sk) {
  				sock_hold(skb->sk);
  				frag->sk = skb->sk;
  				frag->destructor = sock_wfree;
  				skb->truesize -= frag->truesize;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
604
605
606
607
608
609
  		}
  
  		err = 0;
  		offset = 0;
  		frag = skb_shinfo(skb)->frag_list;
  		skb_shinfo(skb)->frag_list = NULL;
  		/* BUILD HEADER */
9a217a1c7   YOSHIFUJI Hideaki   [IPV6]: Repair IP...
610
  		*prevhdr = NEXTHDR_FRAGMENT;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
611
  		tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
  		if (!tmp_hdr) {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
613
  			IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
615
  			return -ENOMEM;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
  		__skb_pull(skb, hlen);
  		fh = (struct frag_hdr*)__skb_push(skb, sizeof(struct frag_hdr));
e2d1bca7e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
618
619
  		__skb_push(skb, hlen);
  		skb_reset_network_header(skb);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
620
  		memcpy(skb_network_header(skb), tmp_hdr, hlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
622
623
624
625
626
627
628
629
630
  
  		ipv6_select_ident(skb, fh);
  		fh->nexthdr = nexthdr;
  		fh->reserved = 0;
  		fh->frag_off = htons(IP6_MF);
  		frag_id = fh->identification;
  
  		first_len = skb_pagelen(skb);
  		skb->data_len = first_len - skb_headlen(skb);
  		skb->len = first_len;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
631
632
  		ipv6_hdr(skb)->payload_len = htons(first_len -
  						   sizeof(struct ipv6hdr));
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
633
634
  
  		dst_hold(&rt->u.dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
636
637
638
639
640
  
  		for (;;) {
  			/* Prepare header of the next frame,
  			 * before previous one went down. */
  			if (frag) {
  				frag->ip_summed = CHECKSUM_NONE;
badff6d01   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
641
  				skb_reset_transport_header(frag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
  				fh = (struct frag_hdr*)__skb_push(frag, sizeof(struct frag_hdr));
e2d1bca7e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
643
644
  				__skb_push(frag, hlen);
  				skb_reset_network_header(frag);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
645
646
  				memcpy(skb_network_header(frag), tmp_hdr,
  				       hlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
648
649
650
651
652
653
  				offset += skb->len - hlen - sizeof(struct frag_hdr);
  				fh->nexthdr = nexthdr;
  				fh->reserved = 0;
  				fh->frag_off = htons(offset);
  				if (frag->next != NULL)
  					fh->frag_off |= htons(IP6_MF);
  				fh->identification = frag_id;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
654
655
656
  				ipv6_hdr(frag)->payload_len =
  						htons(frag->len -
  						      sizeof(struct ipv6hdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
658
  				ip6_copy_metadata(frag, skb);
  			}
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
659

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
  			err = output(skb);
dafee4908   Wei Dong   [IPV6]: SNMPv2 "i...
661
  			if(!err)
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
662
  				IP6_INC_STATS(ip6_dst_idev(&rt->u.dst), IPSTATS_MIB_FRAGCREATES);
dafee4908   Wei Dong   [IPV6]: SNMPv2 "i...
663

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
665
666
667
668
669
670
  			if (err || !frag)
  				break;
  
  			skb = frag;
  			frag = skb->next;
  			skb->next = NULL;
  		}
a51482bde   Jesper Juhl   [NET]: kfree cleanup
671
  		kfree(tmp_hdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
  
  		if (err == 0) {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
674
675
  			IP6_INC_STATS(ip6_dst_idev(&rt->u.dst), IPSTATS_MIB_FRAGOKS);
  			dst_release(&rt->u.dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
677
678
679
680
681
682
683
  			return 0;
  		}
  
  		while (frag) {
  			skb = frag->next;
  			kfree_skb(frag);
  			frag = skb;
  		}
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
684
685
  		IP6_INC_STATS(ip6_dst_idev(&rt->u.dst), IPSTATS_MIB_FRAGFAILS);
  		dst_release(&rt->u.dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
  		return err;
  	}
  
  slow_path:
  	left = skb->len - hlen;		/* Space per frame */
  	ptr = hlen;			/* Where to start from */
  
  	/*
  	 *	Fragment the datagram.
  	 */
  
  	*prevhdr = NEXTHDR_FRAGMENT;
  
  	/*
  	 *	Keep copying data until we run out.
  	 */
  	while(left > 0)	{
  		len = left;
  		/* IF: it doesn't fit, use 'mtu' - the data space left */
  		if (len > mtu)
  			len = mtu;
  		/* IF: we are not sending upto and including the packet end
  		   then align the next start on an eight byte boundary */
  		if (len < left)	{
  			len &= ~7;
  		}
  		/*
  		 *	Allocate buffer.
  		 */
  
  		if ((frag = alloc_skb(len+hlen+sizeof(struct frag_hdr)+LL_RESERVED_SPACE(rt->u.dst.dev), GFP_ATOMIC)) == NULL) {
64ce20730   Patrick McHardy   [NET]: Make NETDE...
717
718
  			NETDEBUG(KERN_INFO "IPv6: frag: no memory for new fragment!
  ");
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
719
720
  			IP6_INC_STATS(ip6_dst_idev(skb->dst),
  				      IPSTATS_MIB_FRAGFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
722
723
724
725
726
727
728
729
730
731
  			err = -ENOMEM;
  			goto fail;
  		}
  
  		/*
  		 *	Set up data on packet
  		 */
  
  		ip6_copy_metadata(frag, skb);
  		skb_reserve(frag, LL_RESERVED_SPACE(rt->u.dst.dev));
  		skb_put(frag, len + hlen + sizeof(struct frag_hdr));
c1d2bbe1c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
732
  		skb_reset_network_header(frag);
badff6d01   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
733
  		fh = (struct frag_hdr *)(skb_network_header(frag) + hlen);
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
734
735
  		frag->transport_header = (frag->network_header + hlen +
  					  sizeof(struct frag_hdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
738
739
740
741
742
743
744
745
746
  
  		/*
  		 *	Charge the memory for the fragment to any owner
  		 *	it might possess
  		 */
  		if (skb->sk)
  			skb_set_owner_w(frag, skb->sk);
  
  		/*
  		 *	Copy the packet header into the new buffer.
  		 */
d626f62b1   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
747
  		skb_copy_from_linear_data(skb, skb_network_header(frag), hlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748
749
750
751
752
753
  
  		/*
  		 *	Build fragment header.
  		 */
  		fh->nexthdr = nexthdr;
  		fh->reserved = 0;
f36d6ab18   Yan Zheng   [IPV6]: Fix ipv6 ...
754
  		if (!frag_id) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
756
757
758
759
760
761
762
  			ipv6_select_ident(skb, fh);
  			frag_id = fh->identification;
  		} else
  			fh->identification = frag_id;
  
  		/*
  		 *	Copy a block of the IP datagram.
  		 */
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
763
  		if (skb_copy_bits(skb, ptr, skb_transport_header(skb), len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
765
766
767
768
769
  			BUG();
  		left -= len;
  
  		fh->frag_off = htons(offset);
  		if (left > 0)
  			fh->frag_off |= htons(IP6_MF);
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
770
771
  		ipv6_hdr(frag)->payload_len = htons(frag->len -
  						    sizeof(struct ipv6hdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
773
774
775
776
777
778
  
  		ptr += len;
  		offset += len;
  
  		/*
  		 *	Put this fragment into the sending queue.
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
  		err = output(frag);
  		if (err)
  			goto fail;
dafee4908   Wei Dong   [IPV6]: SNMPv2 "i...
782

a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
783
  		IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGCREATES);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
  	}
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
785
786
  	IP6_INC_STATS(ip6_dst_idev(skb->dst),
  		      IPSTATS_MIB_FRAGOKS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
  	kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
790
  	return err;
  
  fail:
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
791
792
  	IP6_INC_STATS(ip6_dst_idev(skb->dst),
  		      IPSTATS_MIB_FRAGFAILS);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
793
  	kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794
795
  	return err;
  }
cf6b19825   YOSHIFUJI Hideaki   [IPV6] ROUTE: Int...
796
797
798
799
800
801
802
  static inline int ip6_rt_check(struct rt6key *rt_key,
  			       struct in6_addr *fl_addr,
  			       struct in6_addr *addr_cache)
  {
  	return ((rt_key->plen != 128 || !ipv6_addr_equal(fl_addr, &rt_key->addr)) &&
  		(addr_cache == NULL || !ipv6_addr_equal(fl_addr, addr_cache)));
  }
497c615ab   Herbert Xu   [IPV6]: Audit all...
803
804
805
  static struct dst_entry *ip6_sk_dst_check(struct sock *sk,
  					  struct dst_entry *dst,
  					  struct flowi *fl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
806
  {
497c615ab   Herbert Xu   [IPV6]: Audit all...
807
808
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct rt6_info *rt = (struct rt6_info *)dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
809

497c615ab   Herbert Xu   [IPV6]: Audit all...
810
811
812
813
814
815
816
817
  	if (!dst)
  		goto out;
  
  	/* Yes, checking route validity in not connected
  	 * case is not very simple. Take into account,
  	 * that we do not support routing by source, TOS,
  	 * and MSG_DONTROUTE 		--ANK (980726)
  	 *
cf6b19825   YOSHIFUJI Hideaki   [IPV6] ROUTE: Int...
818
819
  	 * 1. ip6_rt_check(): If route was host route,
  	 *    check that cached destination is current.
497c615ab   Herbert Xu   [IPV6]: Audit all...
820
821
822
823
824
825
826
827
828
829
  	 *    If it is network route, we still may
  	 *    check its validity using saved pointer
  	 *    to the last used address: daddr_cache.
  	 *    We do not want to save whole address now,
  	 *    (because main consumer of this service
  	 *    is tcp, which has not this problem),
  	 *    so that the last trick works only on connected
  	 *    sockets.
  	 * 2. oif also should be the same.
  	 */
cf6b19825   YOSHIFUJI Hideaki   [IPV6] ROUTE: Int...
830
  	if (ip6_rt_check(&rt->rt6i_dst, &fl->fl6_dst, np->daddr_cache) ||
8e1ef0a95   YOSHIFUJI Hideaki   [IPV6]: Cache sou...
831
832
833
  #ifdef CONFIG_IPV6_SUBTREES
  	    ip6_rt_check(&rt->rt6i_src, &fl->fl6_src, np->saddr_cache) ||
  #endif
cf6b19825   YOSHIFUJI Hideaki   [IPV6] ROUTE: Int...
834
  	    (fl->oif && fl->oif != dst->dev->ifindex)) {
497c615ab   Herbert Xu   [IPV6]: Audit all...
835
836
  		dst_release(dst);
  		dst = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
  	}
497c615ab   Herbert Xu   [IPV6]: Audit all...
838
839
840
841
842
843
844
845
  out:
  	return dst;
  }
  
  static int ip6_dst_lookup_tail(struct sock *sk,
  			       struct dst_entry **dst, struct flowi *fl)
  {
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
846
847
848
849
850
851
852
853
  	if (*dst == NULL)
  		*dst = ip6_route_output(sk, fl);
  
  	if ((err = (*dst)->error))
  		goto out_err_release;
  
  	if (ipv6_addr_any(&fl->fl6_src)) {
  		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
44456d37b   Olaf Hering   [PATCH] turn many...
854
  		if (err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
  			goto out_err_release;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
  	}
95c385b4d   Neil Horman   [IPV6] ADDRCONF: ...
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
  #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
  		/*
  		 * Here if the dst entry we've looked up
  		 * has a neighbour entry that is in the INCOMPLETE
  		 * state and the src address from the flow is
  		 * marked as OPTIMISTIC, we release the found
  		 * dst entry and replace it instead with the
  		 * dst entry of the nexthop router
  		 */
  		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
  			struct inet6_ifaddr *ifp;
  			struct flowi fl_gw;
  			int redirect;
  
  			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
  
  			redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
  			if (ifp)
  				in6_ifa_put(ifp);
  
  			if (redirect) {
  				/*
  				 * We need to get the dst entry for the
  				 * default router instead
  				 */
  				dst_release(*dst);
  				memcpy(&fl_gw, fl, sizeof(struct flowi));
  				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
  				*dst = ip6_route_output(sk, &fl_gw);
  				if ((err = (*dst)->error))
  					goto out_err_release;
  			}
  		}
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
891
892
893
894
895
896
897
  	return 0;
  
  out_err_release:
  	dst_release(*dst);
  	*dst = NULL;
  	return err;
  }
34a0b3cdc   Adrian Bunk   [IPV6]: make two ...
898

497c615ab   Herbert Xu   [IPV6]: Audit all...
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
  /**
   *	ip6_dst_lookup - perform route lookup on flow
   *	@sk: socket which provides route info
   *	@dst: pointer to dst_entry * for result
   *	@fl: flow to lookup
   *
   *	This function performs a route lookup on the given flow.
   *
   *	It returns zero on success, or a standard errno code on error.
   */
  int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
  {
  	*dst = NULL;
  	return ip6_dst_lookup_tail(sk, dst, fl);
  }
3cf3dc6c2   Arnaldo Carvalho de Melo   [IPV6]: Export so...
914
  EXPORT_SYMBOL_GPL(ip6_dst_lookup);
497c615ab   Herbert Xu   [IPV6]: Audit all...
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
  /**
   *	ip6_sk_dst_lookup - perform socket cached route lookup on flow
   *	@sk: socket which provides the dst cache and route info
   *	@dst: pointer to dst_entry * for result
   *	@fl: flow to lookup
   *
   *	This function performs a route lookup on the given flow with the
   *	possibility of using the cached route in the socket if it is valid.
   *	It will take the socket dst lock when operating on the dst cache.
   *	As a result, this function can only be used in process context.
   *
   *	It returns zero on success, or a standard errno code on error.
   */
  int ip6_sk_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
  {
  	*dst = NULL;
  	if (sk) {
  		*dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie);
  		*dst = ip6_sk_dst_check(sk, *dst, fl);
  	}
  
  	return ip6_dst_lookup_tail(sk, dst, fl);
  }
  EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup);
34a0b3cdc   Adrian Bunk   [IPV6]: make two ...
939
  static inline int ip6_ufo_append_data(struct sock *sk,
e89e9cf53   Ananda Raju   [IPv4/IPv6]: UFO ...
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
  			int getfrag(void *from, char *to, int offset, int len,
  			int odd, struct sk_buff *skb),
  			void *from, int length, int hh_len, int fragheaderlen,
  			int transhdrlen, int mtu,unsigned int flags)
  
  {
  	struct sk_buff *skb;
  	int err;
  
  	/* There is support for UDP large send offload by network
  	 * device, so create one single skb packet containing complete
  	 * udp datagram
  	 */
  	if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {
  		skb = sock_alloc_send_skb(sk,
  			hh_len + fragheaderlen + transhdrlen + 20,
  			(flags & MSG_DONTWAIT), &err);
  		if (skb == NULL)
  			return -ENOMEM;
  
  		/* reserve space for Hardware header */
  		skb_reserve(skb, hh_len);
  
  		/* create space for UDP/IP header */
  		skb_put(skb,fragheaderlen + transhdrlen);
  
  		/* initialize network header pointer */
c1d2bbe1c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
967
  		skb_reset_network_header(skb);
e89e9cf53   Ananda Raju   [IPv4/IPv6]: UFO ...
968
969
  
  		/* initialize protocol header pointer */
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
970
  		skb->transport_header = skb->network_header + fragheaderlen;
e89e9cf53   Ananda Raju   [IPv4/IPv6]: UFO ...
971

84fa7933a   Patrick McHardy   [NET]: Replace CH...
972
  		skb->ip_summed = CHECKSUM_PARTIAL;
e89e9cf53   Ananda Raju   [IPv4/IPv6]: UFO ...
973
974
975
976
977
978
979
980
981
982
  		skb->csum = 0;
  		sk->sk_sndmsg_off = 0;
  	}
  
  	err = skb_append_datato_frags(sk,skb, getfrag, from,
  				      (length - transhdrlen));
  	if (!err) {
  		struct frag_hdr fhdr;
  
  		/* specify the length of each IP datagram fragment*/
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
983
  		skb_shinfo(skb)->gso_size = mtu - fragheaderlen -
7967168ce   Herbert Xu   [NET]: Merge TSO/...
984
  					    sizeof(struct frag_hdr);
f83ef8c0b   Herbert Xu   [IPV6]: Added GSO...
985
  		skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
e89e9cf53   Ananda Raju   [IPv4/IPv6]: UFO ...
986
987
988
989
990
991
992
993
994
995
996
997
998
  		ipv6_select_ident(skb, &fhdr);
  		skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
  		__skb_queue_tail(&sk->sk_write_queue, skb);
  
  		return 0;
  	}
  	/* There is not enough support do UPD LSO,
  	 * so follow normal path
  	 */
  	kfree_skb(skb);
  
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
999

41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
1000
1001
1002
1003
1004
  int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
  	int offset, int len, int odd, struct sk_buff *skb),
  	void *from, int length, int transhdrlen,
  	int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi *fl,
  	struct rt6_info *rt, unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
  {
  	struct inet_sock *inet = inet_sk(sk);
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct sk_buff *skb;
  	unsigned int maxfraglen, fragheaderlen;
  	int exthdrlen;
  	int hh_len;
  	int mtu;
  	int copy;
  	int err;
  	int offset = 0;
  	int csummode = CHECKSUM_NONE;
  
  	if (flags&MSG_PROBE)
  		return 0;
  	if (skb_queue_empty(&sk->sk_write_queue)) {
  		/*
  		 * setup for corking
  		 */
  		if (opt) {
  			if (np->cork.opt == NULL) {
  				np->cork.opt = kmalloc(opt->tot_len,
  						       sk->sk_allocation);
  				if (unlikely(np->cork.opt == NULL))
  					return -ENOBUFS;
  			} else if (np->cork.opt->tot_len < opt->tot_len) {
  				printk(KERN_DEBUG "ip6_append_data: invalid option length
  ");
  				return -EINVAL;
  			}
  			memcpy(np->cork.opt, opt, opt->tot_len);
  			inet->cork.flags |= IPCORK_OPT;
  			/* need source address above miyazawa*/
  		}
  		dst_hold(&rt->u.dst);
  		np->cork.rt = rt;
  		inet->cork.fl = *fl;
  		np->cork.hop_limit = hlimit;
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
1043
  		np->cork.tclass = tclass;
628a5c561   John Heffner   [INET]: Add IP(V6...
1044
1045
  		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
  		      rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
c75036093   Dave Jones   [IPV6]: remove us...
1046
  		if (np->frag_size < mtu) {
d91675f9c   YOSHIFUJI Hideaki   [IPV6]: Do not ig...
1047
1048
1049
1050
  			if (np->frag_size)
  				mtu = np->frag_size;
  		}
  		inet->cork.fragsize = mtu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
  		if (dst_allfrag(rt->u.dst.path))
  			inet->cork.flags |= IPCORK_ALLFRAG;
  		inet->cork.length = 0;
  		sk->sk_sndmsg_page = NULL;
  		sk->sk_sndmsg_off = 0;
  		exthdrlen = rt->u.dst.header_len + (opt ? opt->opt_flen : 0);
  		length += exthdrlen;
  		transhdrlen += exthdrlen;
  	} else {
  		rt = np->cork.rt;
  		fl = &inet->cork.fl;
  		if (inet->cork.flags & IPCORK_OPT)
  			opt = np->cork.opt;
  		transhdrlen = 0;
  		exthdrlen = 0;
  		mtu = inet->cork.fragsize;
  	}
  
  	hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);
1b5c22998   Masahide NAKAMURA   [XFRM] STATE: Sup...
1070
  	fragheaderlen = sizeof(struct ipv6hdr) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen : 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
  	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr);
  
  	if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
  		if (inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) {
  			ipv6_local_error(sk, EMSGSIZE, fl, mtu-exthdrlen);
  			return -EMSGSIZE;
  		}
  	}
  
  	/*
  	 * Let's try using as much space as possible.
  	 * Use MTU if total length of the message fits into the MTU.
  	 * Otherwise, we need to reserve fragment header and
  	 * fragment alignment (= 8-15 octects, in total).
  	 *
  	 * Note that we may need to "move" the data from the tail of
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1087
  	 * of the buffer to the new fragment when we split
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1088
1089
  	 * the message.
  	 *
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1090
  	 * FIXME: It may be fragmented into multiple chunks
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1091
1092
  	 *        at once if non-fragmentable extension headers
  	 *        are too large.
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1093
  	 * --yoshfuji
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1094
1095
1096
  	 */
  
  	inet->cork.length += length;
e89e9cf53   Ananda Raju   [IPv4/IPv6]: UFO ...
1097
1098
  	if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&
  	    (rt->u.dst.dev->features & NETIF_F_UFO)) {
baa829d89   Patrick McHardy   [IPV4/6]: Fix UFO...
1099
1100
1101
1102
  		err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len,
  					  fragheaderlen, transhdrlen, mtu,
  					  flags);
  		if (err)
e89e9cf53   Ananda Raju   [IPv4/IPv6]: UFO ...
1103
  			goto error;
e89e9cf53   Ananda Raju   [IPv4/IPv6]: UFO ...
1104
1105
  		return 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
  
  	if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
  		goto alloc_new_skb;
  
  	while (length > 0) {
  		/* Check if the remaining data fits into current packet. */
  		copy = (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - skb->len;
  		if (copy < length)
  			copy = maxfraglen - skb->len;
  
  		if (copy <= 0) {
  			char *data;
  			unsigned int datalen;
  			unsigned int fraglen;
  			unsigned int fraggap;
  			unsigned int alloclen;
  			struct sk_buff *skb_prev;
  alloc_new_skb:
  			skb_prev = skb;
  
  			/* There's no room in the current skb */
  			if (skb_prev)
  				fraggap = skb_prev->len - maxfraglen;
  			else
  				fraggap = 0;
  
  			/*
  			 * If remaining data exceeds the mtu,
  			 * we know we need more fragment(s).
  			 */
  			datalen = length + fraggap;
  			if (datalen > (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
  				datalen = maxfraglen - fragheaderlen;
  
  			fraglen = datalen + fragheaderlen;
  			if ((flags & MSG_MORE) &&
  			    !(rt->u.dst.dev->features&NETIF_F_SG))
  				alloclen = mtu;
  			else
  				alloclen = datalen + fragheaderlen;
  
  			/*
  			 * The last fragment gets additional space at tail.
  			 * Note: we overallocate on fragments with MSG_MODE
  			 * because we have no idea if we're the last one.
  			 */
  			if (datalen == length + fraggap)
  				alloclen += rt->u.dst.trailer_len;
  
  			/*
  			 * We just reserve space for fragment header.
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1157
  			 * Note: this may be overallocation if the message
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
  			 * (without MSG_MORE) fits into the MTU.
  			 */
  			alloclen += sizeof(struct frag_hdr);
  
  			if (transhdrlen) {
  				skb = sock_alloc_send_skb(sk,
  						alloclen + hh_len,
  						(flags & MSG_DONTWAIT), &err);
  			} else {
  				skb = NULL;
  				if (atomic_read(&sk->sk_wmem_alloc) <=
  				    2 * sk->sk_sndbuf)
  					skb = sock_wmalloc(sk,
  							   alloclen + hh_len, 1,
  							   sk->sk_allocation);
  				if (unlikely(skb == NULL))
  					err = -ENOBUFS;
  			}
  			if (skb == NULL)
  				goto error;
  			/*
  			 *	Fill in the control structures
  			 */
  			skb->ip_summed = csummode;
  			skb->csum = 0;
  			/* reserve for fragmentation */
  			skb_reserve(skb, hh_len+sizeof(struct frag_hdr));
  
  			/*
  			 *	Find where to start putting bytes
  			 */
  			data = skb_put(skb, fraglen);
c14d2450c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1190
  			skb_set_network_header(skb, exthdrlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1191
  			data += fragheaderlen;
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
1192
1193
  			skb->transport_header = (skb->network_header +
  						 fragheaderlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1194
1195
1196
1197
1198
1199
1200
  			if (fraggap) {
  				skb->csum = skb_copy_and_csum_bits(
  					skb_prev, maxfraglen,
  					data + transhdrlen, fraggap, 0);
  				skb_prev->csum = csum_sub(skb_prev->csum,
  							  skb->csum);
  				data += fraggap;
e9fa4f7bd   Herbert Xu   [INET]: Use pskb_...
1201
  				pskb_trim_unique(skb_prev, maxfraglen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
  			}
  			copy = datalen - transhdrlen - fraggap;
  			if (copy < 0) {
  				err = -EINVAL;
  				kfree_skb(skb);
  				goto error;
  			} else if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {
  				err = -EFAULT;
  				kfree_skb(skb);
  				goto error;
  			}
  
  			offset += copy;
  			length -= datalen - fraggap;
  			transhdrlen = 0;
  			exthdrlen = 0;
  			csummode = CHECKSUM_NONE;
  
  			/*
  			 * Put the packet on the pending queue
  			 */
  			__skb_queue_tail(&sk->sk_write_queue, skb);
  			continue;
  		}
  
  		if (copy > length)
  			copy = length;
  
  		if (!(rt->u.dst.dev->features&NETIF_F_SG)) {
  			unsigned int off;
  
  			off = skb->len;
  			if (getfrag(from, skb_put(skb, copy),
  						offset, copy, off, skb) < 0) {
  				__skb_trim(skb, off);
  				err = -EFAULT;
  				goto error;
  			}
  		} else {
  			int i = skb_shinfo(skb)->nr_frags;
  			skb_frag_t *frag = &skb_shinfo(skb)->frags[i-1];
  			struct page *page = sk->sk_sndmsg_page;
  			int off = sk->sk_sndmsg_off;
  			unsigned int left;
  
  			if (page && (left = PAGE_SIZE - off) > 0) {
  				if (copy >= left)
  					copy = left;
  				if (page != frag->page) {
  					if (i == MAX_SKB_FRAGS) {
  						err = -EMSGSIZE;
  						goto error;
  					}
  					get_page(page);
  					skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
  					frag = &skb_shinfo(skb)->frags[i];
  				}
  			} else if(i < MAX_SKB_FRAGS) {
  				if (copy > PAGE_SIZE)
  					copy = PAGE_SIZE;
  				page = alloc_pages(sk->sk_allocation, 0);
  				if (page == NULL) {
  					err = -ENOMEM;
  					goto error;
  				}
  				sk->sk_sndmsg_page = page;
  				sk->sk_sndmsg_off = 0;
  
  				skb_fill_page_desc(skb, i, page, 0, 0);
  				frag = &skb_shinfo(skb)->frags[i];
  				skb->truesize += PAGE_SIZE;
  				atomic_add(PAGE_SIZE, &sk->sk_wmem_alloc);
  			} else {
  				err = -EMSGSIZE;
  				goto error;
  			}
  			if (getfrag(from, page_address(frag->page)+frag->page_offset+frag->size, offset, copy, skb->len, skb) < 0) {
  				err = -EFAULT;
  				goto error;
  			}
  			sk->sk_sndmsg_off += copy;
  			frag->size += copy;
  			skb->len += copy;
  			skb->data_len += copy;
  		}
  		offset += copy;
  		length -= copy;
  	}
  	return 0;
  error:
  	inet->cork.length -= length;
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
1293
  	IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
  	return err;
  }
  
  int ip6_push_pending_frames(struct sock *sk)
  {
  	struct sk_buff *skb, *tmp_skb;
  	struct sk_buff **tail_skb;
  	struct in6_addr final_dst_buf, *final_dst = &final_dst_buf;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct ipv6hdr *hdr;
  	struct ipv6_txoptions *opt = np->cork.opt;
  	struct rt6_info *rt = np->cork.rt;
  	struct flowi *fl = &inet->cork.fl;
  	unsigned char proto = fl->proto;
  	int err = 0;
  
  	if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)
  		goto out;
  	tail_skb = &(skb_shinfo(skb)->frag_list);
  
  	/* move skb->data to ip header from ext header */
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1316
  	if (skb->data < skb_network_header(skb))
bbe735e42   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1317
  		__skb_pull(skb, skb_network_offset(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1318
  	while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
cfe1fc775   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1319
  		__skb_pull(tmp_skb, skb_network_header_len(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1320
1321
1322
1323
  		*tail_skb = tmp_skb;
  		tail_skb = &(tmp_skb->next);
  		skb->len += tmp_skb->len;
  		skb->data_len += tmp_skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1324
1325
1326
1327
  		skb->truesize += tmp_skb->truesize;
  		__sock_put(tmp_skb->sk);
  		tmp_skb->destructor = NULL;
  		tmp_skb->sk = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1328
1329
1330
  	}
  
  	ipv6_addr_copy(final_dst, &fl->fl6_dst);
cfe1fc775   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1331
  	__skb_pull(skb, skb_network_header_len(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1332
1333
1334
1335
  	if (opt && opt->opt_flen)
  		ipv6_push_frag_opts(skb, opt, &proto);
  	if (opt && opt->opt_nflen)
  		ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst);
e2d1bca7e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
1336
1337
  	skb_push(skb, sizeof(struct ipv6hdr));
  	skb_reset_network_header(skb);
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1338
  	hdr = ipv6_hdr(skb);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1339

90bcaf7b4   Al Viro   [IPV6]: flowlabel...
1340
  	*(__be32*)hdr = fl->fl6_flowlabel |
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
1341
  		     htonl(0x60000000 | ((int)np->cork.tclass << 20));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1342
1343
1344
1345
1346
1347
1348
1349
1350
  
  	if (skb->len <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN)
  		hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
  	else
  		hdr->payload_len = 0;
  	hdr->hop_limit = np->cork.hop_limit;
  	hdr->nexthdr = proto;
  	ipv6_addr_copy(&hdr->saddr, &fl->fl6_src);
  	ipv6_addr_copy(&hdr->daddr, final_dst);
a2c2064f7   Patrick McHardy   [IPV6]: Set skb->...
1351
  	skb->priority = sk->sk_priority;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1352
  	skb->dst = dst_clone(&rt->u.dst);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
1353
  	IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1354
1355
1356
  	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dst->dev, dst_output);
  	if (err) {
  		if (err > 0)
3320da890   Herbert Xu   [IPV6]: Replace b...
1357
  			err = np->recverr ? net_xmit_errno(err) : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1358
1359
1360
1361
1362
1363
  		if (err)
  			goto error;
  	}
  
  out:
  	inet->cork.flags &= ~IPCORK_OPT;
a51482bde   Jesper Juhl   [NET]: kfree cleanup
1364
1365
  	kfree(np->cork.opt);
  	np->cork.opt = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
  	if (np->cork.rt) {
  		dst_release(&np->cork.rt->u.dst);
  		np->cork.rt = NULL;
  		inet->cork.flags &= ~IPCORK_ALLFRAG;
  	}
  	memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
  	return err;
  error:
  	goto out;
  }
  
  void ip6_flush_pending_frames(struct sock *sk)
  {
  	struct inet_sock *inet = inet_sk(sk);
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct sk_buff *skb;
  
  	while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
1384
1385
  		IP6_INC_STATS(ip6_dst_idev(skb->dst),
  			      IPSTATS_MIB_OUTDISCARDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1386
1387
1388
1389
  		kfree_skb(skb);
  	}
  
  	inet->cork.flags &= ~IPCORK_OPT;
a51482bde   Jesper Juhl   [NET]: kfree cleanup
1390
1391
  	kfree(np->cork.opt);
  	np->cork.opt = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1392
1393
1394
1395
1396
1397
1398
  	if (np->cork.rt) {
  		dst_release(&np->cork.rt->u.dst);
  		np->cork.rt = NULL;
  		inet->cork.flags &= ~IPCORK_ALLFRAG;
  	}
  	memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
  }