Blame view

net/ipv6/reassembly.c 19.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *	IPv6 fragment reassembly
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
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
14
   *	Based on: net/ipv4/ip_fragment.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.
   */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
15
16
  /*
   *	Fixes:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
24
25
26
27
   *	Andi Kleen	Make it work with multiple hosts.
   *			More RFC compliance.
   *
   *      Horst von Brand Add missing #include <linux/string.h>
   *	Alexey Kuznetsov	SMP races, threading, cleanup.
   *	Patrick McHardy		LRU queue of frag heads for evictor.
   *	Mitsuru KANDA @USAGI	Register inet6_protocol{}.
   *	David Stevens and
   *	YOSHIFUJI,H. @USAGI	Always remove fragment header to
   *				calculate ICV correctly.
   */
5a3da1fe9   Hannes Frederic Sowa   inet: limit lengt...
28
29
  
  #define pr_fmt(fmt) "IPv6: " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/string.h>
  #include <linux/socket.h>
  #include <linux/sockios.h>
  #include <linux/jiffies.h>
  #include <linux/net.h>
  #include <linux/list.h>
  #include <linux/netdevice.h>
  #include <linux/in6.h>
  #include <linux/ipv6.h>
  #include <linux/icmpv6.h>
  #include <linux/random.h>
  #include <linux/jhash.h>
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
44
  #include <linux/skbuff.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
45
  #include <linux/slab.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
46
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
  
  #include <net/sock.h>
  #include <net/snmp.h>
  
  #include <net/ipv6.h>
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
52
  #include <net/ip6_route.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
57
  #include <net/protocol.h>
  #include <net/transp_v6.h>
  #include <net/rawv6.h>
  #include <net/ndisc.h>
  #include <net/addrconf.h>
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
58
  #include <net/inet_frag.h>
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
59
  #include <net/inet_ecn.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60

d4ad4d22e   Nikolay Aleksandrov   inet: frags: use ...
61
  static const char ip6_frag_cache_name[] = "ip6-frags";
cc24becae   Ian Morris   ipv6: White-space...
62
  struct ip6frag_skb_cb {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
  	struct inet6_skb_parm	h;
  	int			offset;
  };
67ba4152e   Ian Morris   ipv6: White-space...
66
  #define FRAG6_CB(skb)	((struct ip6frag_skb_cb *)((skb)->cb))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67

fc08c2581   Fabian Frederick   ipv6: remove inli...
68
  static u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
69
70
71
  {
  	return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72

7eb95156d   Pavel Emelyanov   [INET]: Collect f...
73
  static struct inet_frags ip6_frags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74

f61944efd   Herbert Xu   [IPV6]: Make ipv6...
75
76
  static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
  			  struct net_device *dev);
f6596f9d2   Zach Brown   [IPv6] reassembly...
77
78
79
80
  /*
   * callers should be careful not to use the hash value outside the ipfrag_lock
   * as doing so could race with ipfrag_hash_rnd being recalculated.
   */
b1190570b   Hannes Frederic Sowa   ipv6: split inet6...
81
82
  static unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr,
  				    const struct in6_addr *daddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  {
b1190570b   Hannes Frederic Sowa   ipv6: split inet6...
84
  	net_get_random_once(&ip6_frags.rnd, sizeof(ip6_frags.rnd));
fb3cfe6e7   Florian Westphal   inet: frag: remov...
85
86
  	return jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr),
  			    (__force u32)id, ip6_frags.rnd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  }
36c777821   Florian Westphal   inet: frag: const...
88
  static unsigned int ip6_hashfn(const struct inet_frag_queue *q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  {
36c777821   Florian Westphal   inet: frag: const...
90
  	const struct frag_queue *fq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91

321a3a99e   Pavel Emelyanov   [INET]: Consolida...
92
  	fq = container_of(q, struct frag_queue, q);
b1190570b   Hannes Frederic Sowa   ipv6: split inet6...
93
  	return inet6_hash_frag(fq->id, &fq->saddr, &fq->daddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  }
36c777821   Florian Westphal   inet: frag: const...
95
  bool ip6_frag_match(const struct inet_frag_queue *q, const void *a)
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
96
  {
36c777821   Florian Westphal   inet: frag: const...
97
98
  	const struct frag_queue *fq;
  	const struct ip6_create_arg *arg = a;
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
99
100
  
  	fq = container_of(q, struct frag_queue, q);
cbc264cac   Eric Dumazet   ip_frag: struct i...
101
102
103
  	return	fq->id == arg->id &&
  		fq->user == arg->user &&
  		ipv6_addr_equal(&fq->saddr, arg->src) &&
264640fc2   Michal Kubeček   ipv6: distinguish...
104
105
106
107
  		ipv6_addr_equal(&fq->daddr, arg->dst) &&
  		(arg->iif == fq->iif ||
  		 !(ipv6_addr_type(arg->dst) & (IPV6_ADDR_MULTICAST |
  					       IPV6_ADDR_LINKLOCAL)));
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
108
109
  }
  EXPORT_SYMBOL(ip6_frag_match);
36c777821   Florian Westphal   inet: frag: const...
110
  void ip6_frag_init(struct inet_frag_queue *q, const void *a)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  {
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
112
  	struct frag_queue *fq = container_of(q, struct frag_queue, q);
36c777821   Florian Westphal   inet: frag: const...
113
  	const struct ip6_create_arg *arg = a;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
114
115
  
  	fq->id = arg->id;
0b5ccb2ee   Patrick McHardy   ipv6: reassembly:...
116
  	fq->user = arg->user;
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
117
118
  	fq->saddr = *arg->src;
  	fq->daddr = *arg->dst;
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
119
  	fq->ecn = arg->ecn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
  }
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
121
  EXPORT_SYMBOL(ip6_frag_init);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122

b836c99fd   Amerigo Wang   ipv6: unify connt...
123
124
  void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq,
  			   struct inet_frags *frags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
  {
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
126
  	struct net_device *dev = NULL;
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
127

5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
128
  	spin_lock(&fq->q.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129

06aa8b8a0   Nikolay Aleksandrov   inet: frags: rena...
130
  	if (fq->q.flags & INET_FRAG_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  		goto out;
b836c99fd   Amerigo Wang   ipv6: unify connt...
132
  	inet_frag_kill(&fq->q, frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133

69df9d599   Eric Dumazet   ip_frag: dont tou...
134
135
  	rcu_read_lock();
  	dev = dev_get_by_index_rcu(net, fq->iif);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
136
  	if (!dev)
69df9d599   Eric Dumazet   ip_frag: dont tou...
137
  		goto out_rcu_unlock;
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
138

483a47d2f   Denis V. Lunev   ipv6: added net a...
139
  	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140

caaecdd3d   Nikolay Aleksandrov   inet: frags: remo...
141
  	if (inet_frag_evicting(&fq->q))
2e404f632   Nikolay Aleksandrov   inet: frags: use ...
142
143
144
  		goto out_rcu_unlock;
  
  	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT);
78c784c47   Ingo Oeser   [IPV6]: Cleanup o...
145
  	/* Don't send error if the first segment did not arrive. */
06aa8b8a0   Nikolay Aleksandrov   inet: frags: rena...
146
  	if (!(fq->q.flags & INET_FRAG_FIRST_IN) || !fq->q.fragments)
69df9d599   Eric Dumazet   ip_frag: dont tou...
147
  		goto out_rcu_unlock;
78c784c47   Ingo Oeser   [IPV6]: Cleanup o...
148

2e404f632   Nikolay Aleksandrov   inet: frags: use ...
149
150
151
  	/* But use as source device on which LAST ARRIVED
  	 * segment was received. And do not use fq->dev
  	 * pointer directly, device might already disappeared.
78c784c47   Ingo Oeser   [IPV6]: Cleanup o...
152
  	 */
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
153
  	fq->q.fragments->dev = dev;
3ffe533c8   Alexey Dobriyan   ipv6: drop unused...
154
  	icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0);
69df9d599   Eric Dumazet   ip_frag: dont tou...
155
156
  out_rcu_unlock:
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  out:
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
158
  	spin_unlock(&fq->q.lock);
b836c99fd   Amerigo Wang   ipv6: unify connt...
159
160
161
162
163
164
165
166
167
168
169
170
171
  	inet_frag_put(&fq->q, frags);
  }
  EXPORT_SYMBOL(ip6_expire_frag_queue);
  
  static void ip6_frag_expire(unsigned long data)
  {
  	struct frag_queue *fq;
  	struct net *net;
  
  	fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q);
  	net = container_of(fq->q.net, struct net, ipv6.frags);
  
  	ip6_expire_frag_queue(net, fq, &ip6_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  }
fc08c2581   Fabian Frederick   ipv6: remove inli...
173
  static struct frag_queue *
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
174
  fq_find(struct net *net, __be32 id, const struct in6_addr *src,
264640fc2   Michal Kubeček   ipv6: distinguish...
175
  	const struct in6_addr *dst, int iif, u8 ecn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  {
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
177
178
  	struct inet_frag_queue *q;
  	struct ip6_create_arg arg;
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
179
  	unsigned int hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180

c6fda2822   Pavel Emelyanov   [INET]: Consolida...
181
  	arg.id = id;
0b5ccb2ee   Patrick McHardy   ipv6: reassembly:...
182
  	arg.user = IP6_DEFRAG_LOCAL_DELIVER;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
183
184
  	arg.src = src;
  	arg.dst = dst;
264640fc2   Michal Kubeček   ipv6: distinguish...
185
  	arg.iif = iif;
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
186
  	arg.ecn = ecn;
9a375803f   Pavel Emelyanov   inet fragments: f...
187

b1190570b   Hannes Frederic Sowa   ipv6: split inet6...
188
  	hash = inet6_hash_frag(id, src, dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189

ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
190
  	q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash);
5a3da1fe9   Hannes Frederic Sowa   inet: limit lengt...
191
192
  	if (IS_ERR_OR_NULL(q)) {
  		inet_frag_maybe_warn_overflow(q, pr_fmt());
9546377c4   Shan Wei   IPv6: Delete redu...
193
  		return NULL;
5a3da1fe9   Hannes Frederic Sowa   inet: limit lengt...
194
  	}
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
195
  	return container_of(q, struct frag_queue, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  }
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
197
  static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
  			   struct frag_hdr *fhdr, int nhoff)
  {
  	struct sk_buff *prev, *next;
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
201
  	struct net_device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  	int offset, end;
adf30907d   Eric Dumazet   net: skb->dst acc...
203
  	struct net *net = dev_net(skb_dst(skb)->dev);
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
204
  	u8 ecn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205

06aa8b8a0   Nikolay Aleksandrov   inet: frags: rena...
206
  	if (fq->q.flags & INET_FRAG_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
  		goto err;
  
  	offset = ntohs(fhdr->frag_off) & ~0x7;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
210
211
  	end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
  			((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
  
  	if ((unsigned int)end > IPV6_MAXPLEN) {
adf30907d   Eric Dumazet   net: skb->dst acc...
214
  		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
215
  				 IPSTATS_MIB_INHDRERRORS);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
216
217
218
  		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
  				  ((u8 *)&fhdr->frag_off -
  				   skb_network_header(skb)));
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
219
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
  	}
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
221
  	ecn = ip6_frag_ecn(ipv6_hdr(skb));
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
222
223
  	if (skb->ip_summed == CHECKSUM_COMPLETE) {
  		const unsigned char *nh = skb_network_header(skb);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
224
  		skb->csum = csum_sub(skb->csum,
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
225
226
227
  				     csum_partial(nh, (u8 *)(fhdr + 1) - nh,
  						  0));
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
230
231
232
233
  
  	/* Is this the final fragment? */
  	if (!(fhdr->frag_off & htons(IP6_MF))) {
  		/* If we already have some bits beyond end
  		 * or have different end, the segment is corrupted.
  		 */
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
234
  		if (end < fq->q.len ||
06aa8b8a0   Nikolay Aleksandrov   inet: frags: rena...
235
  		    ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  			goto err;
06aa8b8a0   Nikolay Aleksandrov   inet: frags: rena...
237
  		fq->q.flags |= INET_FRAG_LAST_IN;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
238
  		fq->q.len = end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
244
245
246
  	} else {
  		/* Check if the fragment is rounded to 8 bytes.
  		 * Required by the RFC.
  		 */
  		if (end & 0x7) {
  			/* RFC2460 says always send parameter problem in
  			 * this case. -DaveM
  			 */
adf30907d   Eric Dumazet   net: skb->dst acc...
247
  			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
248
  					 IPSTATS_MIB_INHDRERRORS);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
249
  			icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  					  offsetof(struct ipv6hdr, payload_len));
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
251
  			return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
  		}
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
253
  		if (end > fq->q.len) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
  			/* Some bits beyond end -> corruption. */
06aa8b8a0   Nikolay Aleksandrov   inet: frags: rena...
255
  			if (fq->q.flags & INET_FRAG_LAST_IN)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  				goto err;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
257
  			fq->q.len = end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
261
262
263
264
265
266
  		}
  	}
  
  	if (end == offset)
  		goto err;
  
  	/* Point into the IP datagram 'data' part. */
  	if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data))
  		goto err;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
267

42ca89c18   Stephen Hemminger   [IPV6]: Need to u...
268
269
  	if (pskb_trim_rcsum(skb, end - offset))
  		goto err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
272
273
274
  
  	/* Find out which fragments are in front and at the back of us
  	 * in the chain of fragments so far.  We must know where to put
  	 * this fragment, right?
  	 */
d6bebca92   Changli Gao   fragment: add fas...
275
276
277
278
279
  	prev = fq->q.fragments_tail;
  	if (!prev || FRAG6_CB(prev)->offset < offset) {
  		next = NULL;
  		goto found;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	prev = NULL;
67ba4152e   Ian Morris   ipv6: White-space...
281
  	for (next = fq->q.fragments; next != NULL; next = next->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
  		if (FRAG6_CB(next)->offset >= offset)
  			break;	/* bingo! */
  		prev = next;
  	}
d6bebca92   Changli Gao   fragment: add fas...
286
  found:
5de658f87   Eric Dumazet   ipv6: fix RFC5722...
287
288
  	/* RFC5722, Section 4, amended by Errata ID : 3089
  	 *                          When reassembling an IPv6 datagram, if
70789d705   Nicolas Dichtel   ipv6: discard ove...
289
290
  	 *   one or more its constituent fragments is determined to be an
  	 *   overlapping fragment, the entire datagram (and any constituent
5de658f87   Eric Dumazet   ipv6: fix RFC5722...
291
  	 *   fragments) MUST be silently discarded.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293

70789d705   Nicolas Dichtel   ipv6: discard ove...
294
295
  	/* Check for overlap with preceding fragment. */
  	if (prev &&
f46421416   Shan Wei   ipv6: fix overlap...
296
  	    (FRAG6_CB(prev)->offset + prev->len) > offset)
70789d705   Nicolas Dichtel   ipv6: discard ove...
297
  		goto discard_fq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298

70789d705   Nicolas Dichtel   ipv6: discard ove...
299
300
301
  	/* Look for overlap with succeeding segment. */
  	if (next && FRAG6_CB(next)->offset < end)
  		goto discard_fq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
  
  	FRAG6_CB(skb)->offset = offset;
  
  	/* Insert this fragment in the chain of fragments. */
  	skb->next = next;
d6bebca92   Changli Gao   fragment: add fas...
307
308
  	if (!next)
  		fq->q.fragments_tail = skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
  	if (prev)
  		prev->next = skb;
  	else
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
312
  		fq->q.fragments = skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313

f61944efd   Herbert Xu   [IPV6]: Make ipv6...
314
315
316
317
318
  	dev = skb->dev;
  	if (dev) {
  		fq->iif = dev->ifindex;
  		skb->dev = NULL;
  	}
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
319
320
  	fq->q.stamp = skb->tstamp;
  	fq->q.meat += skb->len;
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
321
  	fq->ecn |= ecn;
0e60d245a   Florian Westphal   inet: frag: chang...
322
  	add_frag_mem_limit(fq->q.net, skb->truesize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
326
327
328
  
  	/* The first fragment.
  	 * nhoffset is obtained from the first fragment, of course.
  	 */
  	if (offset == 0) {
  		fq->nhoffset = nhoff;
06aa8b8a0   Nikolay Aleksandrov   inet: frags: rena...
329
  		fq->q.flags |= INET_FRAG_FIRST_IN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  	}
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
331

06aa8b8a0   Nikolay Aleksandrov   inet: frags: rena...
332
  	if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
97599dc79   Eric Dumazet   net: drop dst bef...
333
334
335
336
337
338
339
340
341
  	    fq->q.meat == fq->q.len) {
  		int res;
  		unsigned long orefdst = skb->_skb_refdst;
  
  		skb->_skb_refdst = 0UL;
  		res = ip6_frag_reasm(fq, prev, dev);
  		skb->_skb_refdst = orefdst;
  		return res;
  	}
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
342

97599dc79   Eric Dumazet   net: drop dst bef...
343
  	skb_dst_drop(skb);
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
344
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345

70789d705   Nicolas Dichtel   ipv6: discard ove...
346
  discard_fq:
b836c99fd   Amerigo Wang   ipv6: unify connt...
347
  	inet_frag_kill(&fq->q, &ip6_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
  err:
d2373862b   Nikolay Aleksandrov   inet: frags: use ...
349
350
  	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
  			 IPSTATS_MIB_REASMFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  	kfree_skb(skb);
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
352
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
357
358
359
360
361
362
363
  }
  
  /*
   *	Check if this packet is complete.
   *	Returns NULL on failure by any reason, and pointer
   *	to current nexthdr field in reassembled frame.
   *
   *	It is called with locked fq, and caller must check that
   *	queue is eligible for reassembly i.e. it is not COMPLETE,
   *	the last and the first frames arrived and all the bits are here.
   */
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
364
  static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
  			  struct net_device *dev)
  {
2bad35b7c   Jorge Boncompte [DTI2]   netns: oops in ip...
367
  	struct net *net = container_of(fq->q.net, struct net, ipv6.frags);
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
368
  	struct sk_buff *fp, *head = fq->q.fragments;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
  	int    payload_len;
  	unsigned int nhoff;
ec16439e1   Eric Dumazet   ipv6: use skb coa...
371
  	int sum_truesize;
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
372
  	u8 ecn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373

b836c99fd   Amerigo Wang   ipv6: unify connt...
374
  	inet_frag_kill(&fq->q, &ip6_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375

eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
376
377
378
  	ecn = ip_frag_ecn_table[fq->ecn];
  	if (unlikely(ecn == 0xff))
  		goto out_fail;
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
379
380
381
382
383
384
385
386
387
  	/* Make the one we just received the head. */
  	if (prev) {
  		head = prev->next;
  		fp = skb_clone(head, GFP_ATOMIC);
  
  		if (!fp)
  			goto out_oom;
  
  		fp->next = head->next;
d6bebca92   Changli Gao   fragment: add fas...
388
389
  		if (!fp->next)
  			fq->q.fragments_tail = fp;
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
390
  		prev->next = fp;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
391
392
  		skb_morph(head, fq->q.fragments);
  		head->next = fq->q.fragments->next;
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
393

808db80a7   Eric Dumazet   ipv6: call consum...
394
  		consume_skb(fq->q.fragments);
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
395
  		fq->q.fragments = head;
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
396
  	}
547b792ca   Ilpo Järvinen   net: convert BUG_...
397
398
  	WARN_ON(head == NULL);
  	WARN_ON(FRAG6_CB(head)->offset != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
400
  
  	/* Unfragmented part is taken from the first segment. */
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
401
  	payload_len = ((head->data - skb_network_header(head)) -
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
402
  		       sizeof(struct ipv6hdr) + fq->q.len -
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
403
  		       sizeof(struct frag_hdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
405
406
407
  	if (payload_len > IPV6_MAXPLEN)
  		goto out_oversize;
  
  	/* Head of list must not be cloned. */
14bbd6a56   Pravin B Shelar   net: Add skb_uncl...
408
  	if (skb_unclone(head, GFP_ATOMIC))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
411
412
413
  		goto out_oom;
  
  	/* If the first fragment is fragmented itself, we split
  	 * it to two chunks: the first with data and paged part
  	 * and the second, holding only fragments. */
21dc33015   David S. Miller   net: Rename skb_h...
414
  	if (skb_has_frag_list(head)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
  		struct sk_buff *clone;
  		int i, plen = 0;
e5d08d718   Ian Morris   ipv6: coding styl...
417
  		clone = alloc_skb(0, GFP_ATOMIC);
63159f29b   Ian Morris   ipv6: coding styl...
418
  		if (!clone)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
421
422
  			goto out_oom;
  		clone->next = head->next;
  		head->next = clone;
  		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
4d9092bb4   David S. Miller   ipv6: Use frag li...
423
  		skb_frag_list_init(head);
9e903e085   Eric Dumazet   net: add skb frag...
424
425
  		for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
  			plen += skb_frag_size(&skb_shinfo(head)->frags[i]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
428
429
430
  		clone->len = clone->data_len = head->data_len - plen;
  		head->data_len -= clone->len;
  		head->len -= clone->len;
  		clone->csum = 0;
  		clone->ip_summed = head->ip_summed;
0e60d245a   Florian Westphal   inet: frag: chang...
431
  		add_frag_mem_limit(fq->q.net, clone->truesize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
434
435
436
  	}
  
  	/* We have to remove fragment header from datagram and to relocate
  	 * header in order to calculate ICV correctly. */
  	nhoff = fq->nhoffset;
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
437
  	skb_network_header(head)[nhoff] = skb_transport_header(head)[0];
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
438
  	memmove(head->head + sizeof(struct frag_hdr), head->head,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
  		(head->data - head->head) - sizeof(struct frag_hdr));
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
440
441
  	head->mac_header += sizeof(struct frag_hdr);
  	head->network_header += sizeof(struct frag_hdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442

badff6d01   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
443
  	skb_reset_transport_header(head);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
444
  	skb_push(head, head->data - skb_network_header(head));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445

ec16439e1   Eric Dumazet   ipv6: use skb coa...
446
447
448
449
450
451
452
  	sum_truesize = head->truesize;
  	for (fp = head->next; fp;) {
  		bool headstolen;
  		int delta;
  		struct sk_buff *next = fp->next;
  
  		sum_truesize += fp->truesize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
454
  		if (head->ip_summed != fp->ip_summed)
  			head->ip_summed = CHECKSUM_NONE;
84fa7933a   Patrick McHardy   [NET]: Replace CH...
455
  		else if (head->ip_summed == CHECKSUM_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
  			head->csum = csum_add(head->csum, fp->csum);
ec16439e1   Eric Dumazet   ipv6: use skb coa...
457
458
459
460
461
462
463
464
465
466
467
  
  		if (skb_try_coalesce(head, fp, &headstolen, &delta)) {
  			kfree_skb_partial(fp, headstolen);
  		} else {
  			if (!skb_shinfo(head)->frag_list)
  				skb_shinfo(head)->frag_list = fp;
  			head->data_len += fp->len;
  			head->len += fp->len;
  			head->truesize += fp->truesize;
  		}
  		fp = next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
  	}
0e60d245a   Florian Westphal   inet: frag: chang...
469
  	sub_frag_mem_limit(fq->q.net, sum_truesize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
471
472
  
  	head->next = NULL;
  	head->dev = dev;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
473
  	head->tstamp = fq->q.stamp;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
474
  	ipv6_hdr(head)->payload_len = htons(payload_len);
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
475
  	ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn);
951dbc8ac   Patrick McHardy   [IPV6]: Move next...
476
  	IP6CB(head)->nhoff = nhoff;
f46078cfc   Hannes Frederic Sowa   ipv6: drop packet...
477
  	IP6CB(head)->flags |= IP6SKB_FRAGMENTED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
  	/* Yes, and fold redundant checksum back. 8) */
6b83d28a5   Daniel Borkmann   net: use skb_post...
480
481
  	skb_postpush_rcsum(head, skb_network_header(head),
  			   skb_network_header_len(head));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482

a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
483
  	rcu_read_lock();
2bad35b7c   Jorge Boncompte [DTI2]   netns: oops in ip...
484
  	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMOKS);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
485
  	rcu_read_unlock();
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
486
  	fq->q.fragments = NULL;
d6bebca92   Changli Gao   fragment: add fas...
487
  	fq->q.fragments_tail = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
490
  	return 1;
  
  out_oversize:
e87cc4728   Joe Perches   net: Convert net_...
491
492
  	net_dbg_ratelimited("ip6_frag_reasm: payload len = %d
  ", payload_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
  	goto out_fail;
  out_oom:
e87cc4728   Joe Perches   net: Convert net_...
495
496
  	net_dbg_ratelimited("ip6_frag_reasm: no memory for reassembly
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
  out_fail:
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
498
  	rcu_read_lock();
2bad35b7c   Jorge Boncompte [DTI2]   netns: oops in ip...
499
  	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
500
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
  	return -1;
  }
e5bbef20e   Herbert Xu   [IPV6]: Replace s...
503
  static int ipv6_frag_rcv(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
  	struct frag_hdr *fhdr;
  	struct frag_queue *fq;
b71d1d426   Eric Dumazet   inet: constify ip...
507
  	const struct ipv6hdr *hdr = ipv6_hdr(skb);
adf30907d   Eric Dumazet   net: skb->dst acc...
508
  	struct net *net = dev_net(skb_dst(skb)->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509

f46078cfc   Hannes Frederic Sowa   ipv6: drop packet...
510
511
  	if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED)
  		goto fail_hdr;
adf30907d   Eric Dumazet   net: skb->dst acc...
512
  	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMREQDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
  
  	/* Jumbo payload inhibits frag. header */
67ba4152e   Ian Morris   ipv6: White-space...
515
  	if (hdr->payload_len == 0)
98b3377ca   Denis V. Lunev   ipv6: consolidate...
516
  		goto fail_hdr;
ea2ae17d6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
517
  	if (!pskb_may_pull(skb, (skb_transport_offset(skb) +
98b3377ca   Denis V. Lunev   ipv6: consolidate...
518
519
  				 sizeof(struct frag_hdr))))
  		goto fail_hdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520

0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
521
  	hdr = ipv6_hdr(skb);
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
522
  	fhdr = (struct frag_hdr *)skb_transport_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
  
  	if (!(fhdr->frag_off & htons(0xFFF9))) {
  		/* It is not a fragmented frame */
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
526
  		skb->transport_header += sizeof(struct frag_hdr);
483a47d2f   Denis V. Lunev   ipv6: added net a...
527
  		IP6_INC_STATS_BH(net,
adf30907d   Eric Dumazet   net: skb->dst acc...
528
  				 ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMOKS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529

d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
530
  		IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb);
f46078cfc   Hannes Frederic Sowa   ipv6: drop packet...
531
  		IP6CB(skb)->flags |= IP6SKB_FRAGMENTED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
  		return 1;
  	}
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
534
  	fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr,
264640fc2   Michal Kubeček   ipv6: distinguish...
535
  		     skb->dev ? skb->dev->ifindex : 0, ip6_frag_ecn(hdr));
53b24b8f9   Ian Morris   ipv6: coding styl...
536
  	if (fq) {
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
537
  		int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538

5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
539
  		spin_lock(&fq->q.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540

f61944efd   Herbert Xu   [IPV6]: Make ipv6...
541
  		ret = ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542

5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
543
  		spin_unlock(&fq->q.lock);
b836c99fd   Amerigo Wang   ipv6: unify connt...
544
  		inet_frag_put(&fq->q, &ip6_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
  		return ret;
  	}
adf30907d   Eric Dumazet   net: skb->dst acc...
547
  	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
  	kfree_skb(skb);
  	return -1;
98b3377ca   Denis V. Lunev   ipv6: consolidate...
550
551
  
  fail_hdr:
d2373862b   Nikolay Aleksandrov   inet: frags: use ...
552
553
  	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
  			 IPSTATS_MIB_INHDRERRORS);
98b3377ca   Denis V. Lunev   ipv6: consolidate...
554
555
  	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb_network_header_len(skb));
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
  }
cc24becae   Ian Morris   ipv6: White-space...
557
  static const struct inet6_protocol frag_protocol = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
  	.handler	=	ipv6_frag_rcv,
  	.flags		=	INET6_PROTO_NOPOLICY,
  };
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
561
  #ifdef CONFIG_SYSCTL
1bab4c750   Nikolay Aleksandrov   inet: frag: set l...
562
  static int zero;
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
563
  static struct ctl_table ip6_frags_ns_ctl_table[] = {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
564
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
565
  		.procname	= "ip6frag_high_thresh",
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
566
  		.data		= &init_net.ipv6.frags.high_thresh,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
567
568
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
1bab4c750   Nikolay Aleksandrov   inet: frag: set l...
569
570
  		.proc_handler	= proc_dointvec_minmax,
  		.extra1		= &init_net.ipv6.frags.low_thresh
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
571
572
  	},
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
573
  		.procname	= "ip6frag_low_thresh",
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
574
  		.data		= &init_net.ipv6.frags.low_thresh,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
575
576
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
1bab4c750   Nikolay Aleksandrov   inet: frag: set l...
577
578
579
  		.proc_handler	= proc_dointvec_minmax,
  		.extra1		= &zero,
  		.extra2		= &init_net.ipv6.frags.high_thresh
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
580
581
  	},
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
582
  		.procname	= "ip6frag_time",
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
583
  		.data		= &init_net.ipv6.frags.timeout,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
584
585
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
586
  		.proc_handler	= proc_dointvec_jiffies,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
587
  	},
7d291ebb8   Pavel Emelyanov   inet: Register fr...
588
589
  	{ }
  };
e3a57d18b   Florian Westphal   inet: frag: remov...
590
591
  /* secret interval has been deprecated */
  static int ip6_frags_secret_interval_unused;
7d291ebb8   Pavel Emelyanov   inet: Register fr...
592
  static struct ctl_table ip6_frags_ctl_table[] = {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
593
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
594
  		.procname	= "ip6frag_secret_interval",
e3a57d18b   Florian Westphal   inet: frag: remov...
595
  		.data		= &ip6_frags_secret_interval_unused,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
596
597
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
598
  		.proc_handler	= proc_dointvec_jiffies,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
599
600
601
  	},
  	{ }
  };
2c8c1e729   Alexey Dobriyan   net: spread __net...
602
  static int __net_init ip6_frags_ns_sysctl_register(struct net *net)
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
603
  {
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
604
  	struct ctl_table *table;
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
605
  	struct ctl_table_header *hdr;
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
606
  	table = ip6_frags_ns_ctl_table;
09ad9bc75   Octavian Purdila   net: use net_eq t...
607
  	if (!net_eq(net, &init_net)) {
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
608
  		table = kmemdup(table, sizeof(ip6_frags_ns_ctl_table), GFP_KERNEL);
63159f29b   Ian Morris   ipv6: coding styl...
609
  		if (!table)
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
610
  			goto err_alloc;
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
611
  		table[0].data = &net->ipv6.frags.high_thresh;
1bab4c750   Nikolay Aleksandrov   inet: frag: set l...
612
613
  		table[0].extra1 = &net->ipv6.frags.low_thresh;
  		table[0].extra2 = &init_net.ipv6.frags.high_thresh;
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
614
  		table[1].data = &net->ipv6.frags.low_thresh;
1bab4c750   Nikolay Aleksandrov   inet: frag: set l...
615
  		table[1].extra2 = &net->ipv6.frags.high_thresh;
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
616
  		table[2].data = &net->ipv6.frags.timeout;
464dc801c   Eric W. Biederman   net: Don't export...
617
618
619
620
  
  		/* Don't export sysctls to unprivileged users */
  		if (net->user_ns != &init_user_ns)
  			table[0].procname = NULL;
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
621
  	}
ec8f23ce0   Eric W. Biederman   net: Convert all ...
622
  	hdr = register_net_sysctl(net, "net/ipv6", table);
63159f29b   Ian Morris   ipv6: coding styl...
623
  	if (!hdr)
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
624
625
626
627
628
629
  		goto err_reg;
  
  	net->ipv6.sysctl.frags_hdr = hdr;
  	return 0;
  
  err_reg:
09ad9bc75   Octavian Purdila   net: use net_eq t...
630
  	if (!net_eq(net, &init_net))
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
631
632
633
634
  		kfree(table);
  err_alloc:
  	return -ENOMEM;
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
635
  static void __net_exit ip6_frags_ns_sysctl_unregister(struct net *net)
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
636
637
638
639
640
  {
  	struct ctl_table *table;
  
  	table = net->ipv6.sysctl.frags_hdr->ctl_table_arg;
  	unregister_net_sysctl_table(net->ipv6.sysctl.frags_hdr);
3705e11a2   Yang Hongyang   ipv6: fix an oops...
641
642
  	if (!net_eq(net, &init_net))
  		kfree(table);
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
643
  }
7d291ebb8   Pavel Emelyanov   inet: Register fr...
644
645
646
647
648
  
  static struct ctl_table_header *ip6_ctl_header;
  
  static int ip6_frags_sysctl_register(void)
  {
434447579   Eric W. Biederman   net: Kill registe...
649
  	ip6_ctl_header = register_net_sysctl(&init_net, "net/ipv6",
7d291ebb8   Pavel Emelyanov   inet: Register fr...
650
651
652
653
654
655
656
657
  			ip6_frags_ctl_table);
  	return ip6_ctl_header == NULL ? -ENOMEM : 0;
  }
  
  static void ip6_frags_sysctl_unregister(void)
  {
  	unregister_net_sysctl_table(ip6_ctl_header);
  }
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
658
  #else
fc08c2581   Fabian Frederick   ipv6: remove inli...
659
  static int ip6_frags_ns_sysctl_register(struct net *net)
e71e0349e   Daniel Lezcano   [NETNS][IPV6]: Ma...
660
  {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
661
662
  	return 0;
  }
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
663

fc08c2581   Fabian Frederick   ipv6: remove inli...
664
  static void ip6_frags_ns_sysctl_unregister(struct net *net)
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
665
666
  {
  }
7d291ebb8   Pavel Emelyanov   inet: Register fr...
667

fc08c2581   Fabian Frederick   ipv6: remove inli...
668
  static int ip6_frags_sysctl_register(void)
7d291ebb8   Pavel Emelyanov   inet: Register fr...
669
670
671
  {
  	return 0;
  }
fc08c2581   Fabian Frederick   ipv6: remove inli...
672
  static void ip6_frags_sysctl_unregister(void)
7d291ebb8   Pavel Emelyanov   inet: Register fr...
673
674
  {
  }
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
675
  #endif
7d460db95   Daniel Lezcano   [IPV6]: Fix ip6_f...
676

2c8c1e729   Alexey Dobriyan   net: spread __net...
677
  static int __net_init ipv6_frags_init_net(struct net *net)
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
678
  {
1d6119baf   Eric Dumazet   net: fix percpu m...
679
  	int res;
7c070aa94   Shan Wei   IPv6: reassembly:...
680
681
  	net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
  	net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
682
  	net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT;
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
683

1d6119baf   Eric Dumazet   net: fix percpu m...
684
685
686
687
688
689
690
  	res = inet_frags_init_net(&net->ipv6.frags);
  	if (res)
  		return res;
  	res = ip6_frags_ns_sysctl_register(net);
  	if (res)
  		inet_frags_uninit_net(&net->ipv6.frags);
  	return res;
e71e0349e   Daniel Lezcano   [NETNS][IPV6]: Ma...
691
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
692
  static void __net_exit ipv6_frags_exit_net(struct net *net)
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
693
  {
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
694
  	ip6_frags_ns_sysctl_unregister(net);
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
695
696
697
698
699
700
701
  	inet_frags_exit_net(&net->ipv6.frags, &ip6_frags);
  }
  
  static struct pernet_operations ip6_frags_ops = {
  	.init = ipv6_frags_init_net,
  	.exit = ipv6_frags_exit_net,
  };
853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
702
  int __init ipv6_frag_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
703
  {
853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
704
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705

853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
706
707
708
  	ret = inet6_add_protocol(&frag_protocol, IPPROTO_FRAGMENT);
  	if (ret)
  		goto out;
e71e0349e   Daniel Lezcano   [NETNS][IPV6]: Ma...
709

7d291ebb8   Pavel Emelyanov   inet: Register fr...
710
711
712
  	ret = ip6_frags_sysctl_register();
  	if (ret)
  		goto err_sysctl;
0002c630c   Pavel Emelyanov   ipv6: In fragment...
713
714
715
  	ret = register_pernet_subsys(&ip6_frags_ops);
  	if (ret)
  		goto err_pernet;
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
716

321a3a99e   Pavel Emelyanov   [INET]: Consolida...
717
  	ip6_frags.hashfn = ip6_hashfn;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
718
  	ip6_frags.constructor = ip6_frag_init;
c95477090   Pavel Emelyanov   [INET]: Consolida...
719
  	ip6_frags.destructor = NULL;
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
720
  	ip6_frags.qsize = sizeof(struct frag_queue);
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
721
  	ip6_frags.match = ip6_frag_match;
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
722
  	ip6_frags.frag_expire = ip6_frag_expire;
d4ad4d22e   Nikolay Aleksandrov   inet: frags: use ...
723
724
725
726
  	ip6_frags.frags_cache_name = ip6_frag_cache_name;
  	ret = inet_frags_init(&ip6_frags);
  	if (ret)
  		goto err_pernet;
853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
727
728
  out:
  	return ret;
0002c630c   Pavel Emelyanov   ipv6: In fragment...
729
730
  
  err_pernet:
7d291ebb8   Pavel Emelyanov   inet: Register fr...
731
732
  	ip6_frags_sysctl_unregister();
  err_sysctl:
0002c630c   Pavel Emelyanov   ipv6: In fragment...
733
734
  	inet6_del_protocol(&frag_protocol, IPPROTO_FRAGMENT);
  	goto out;
853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
735
736
737
738
739
  }
  
  void ipv6_frag_exit(void)
  {
  	inet_frags_fini(&ip6_frags);
7d291ebb8   Pavel Emelyanov   inet: Register fr...
740
  	ip6_frags_sysctl_unregister();
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
741
  	unregister_pernet_subsys(&ip6_frags_ops);
853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
742
  	inet6_del_protocol(&frag_protocol, IPPROTO_FRAGMENT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
743
  }