Blame view

net/ipv6/reassembly.c 19 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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
65
66
67
  struct ip6frag_skb_cb
  {
  	struct inet6_skb_parm	h;
  	int			offset;
  };
  
  #define FRAG6_CB(skb)	((struct ip6frag_skb_cb*)((skb)->cb))
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
68
69
70
71
  static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
  {
  	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.
   */
93c8b90f0   Ilpo Järvinen   ipv6: almost iden...
81
82
  unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr,
  			     const struct in6_addr *daddr, u32 rnd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  {
82a39eb6b   Jozsef Kadlecsik   ipv6: Prepare the...
84
  	u32 c;
8064b3cf7   Eric Dumazet   ipv6: fix a spars...
85
86
  	c = jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr),
  			 (__force u32)id, rnd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87

7eb95156d   Pavel Emelyanov   [INET]: Collect f...
88
  	return c & (INETFRAGS_HASHSZ - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  }
93c8b90f0   Ilpo Järvinen   ipv6: almost iden...
90
  EXPORT_SYMBOL_GPL(inet6_hash_frag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91

321a3a99e   Pavel Emelyanov   [INET]: Consolida...
92
  static unsigned int ip6_hashfn(struct inet_frag_queue *q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  {
321a3a99e   Pavel Emelyanov   [INET]: Consolida...
94
  	struct frag_queue *fq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95

321a3a99e   Pavel Emelyanov   [INET]: Consolida...
96
  	fq = container_of(q, struct frag_queue, q);
93c8b90f0   Ilpo Järvinen   ipv6: almost iden...
97
  	return inet6_hash_frag(fq->id, &fq->saddr, &fq->daddr, ip6_frags.rnd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  }
cbc264cac   Eric Dumazet   ip_frag: struct i...
99
  bool ip6_frag_match(struct inet_frag_queue *q, void *a)
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
100
101
102
103
104
  {
  	struct frag_queue *fq;
  	struct ip6_create_arg *arg = a;
  
  	fq = container_of(q, struct frag_queue, q);
cbc264cac   Eric Dumazet   ip_frag: struct i...
105
106
107
108
  	return	fq->id == arg->id &&
  		fq->user == arg->user &&
  		ipv6_addr_equal(&fq->saddr, arg->src) &&
  		ipv6_addr_equal(&fq->daddr, arg->dst);
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
109
110
  }
  EXPORT_SYMBOL(ip6_frag_match);
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
111
  void ip6_frag_init(struct inet_frag_queue *q, void *a)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
  {
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
113
114
115
116
  	struct frag_queue *fq = container_of(q, struct frag_queue, q);
  	struct ip6_create_arg *arg = a;
  
  	fq->id = arg->id;
0b5ccb2ee   Patrick McHardy   ipv6: reassembly:...
117
  	fq->user = arg->user;
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
118
119
  	fq->saddr = *arg->src;
  	fq->daddr = *arg->dst;
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
120
  	fq->ecn = arg->ecn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
  }
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
122
  EXPORT_SYMBOL(ip6_frag_init);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123

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

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

bc578a54f   Joe Perches   [NET]: Rename ine...
131
  	if (fq->q.last_in & INET_FRAG_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  		goto out;
b836c99fd   Amerigo Wang   ipv6: unify connt...
133
  	inet_frag_kill(&fq->q, frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134

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

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

78c784c47   Ingo Oeser   [IPV6]: Cleanup o...
143
  	/* Don't send error if the first segment did not arrive. */
bc578a54f   Joe Perches   [NET]: Rename ine...
144
  	if (!(fq->q.last_in & INET_FRAG_FIRST_IN) || !fq->q.fragments)
69df9d599   Eric Dumazet   ip_frag: dont tou...
145
  		goto out_rcu_unlock;
78c784c47   Ingo Oeser   [IPV6]: Cleanup o...
146

78c784c47   Ingo Oeser   [IPV6]: Cleanup o...
147
148
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.
  	 */
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
152
  	fq->q.fragments->dev = dev;
3ffe533c8   Alexey Dobriyan   ipv6: drop unused...
153
  	icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0);
69df9d599   Eric Dumazet   ip_frag: dont tou...
154
155
  out_rcu_unlock:
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  out:
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
157
  	spin_unlock(&fq->q.lock);
b836c99fd   Amerigo Wang   ipv6: unify connt...
158
159
160
161
162
163
164
165
166
167
168
169
170
  	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
171
  }
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
172
  static __inline__ struct frag_queue *
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
173
174
  fq_find(struct net *net, __be32 id, const struct in6_addr *src,
  	const struct in6_addr *dst, u8 ecn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  {
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
176
177
  	struct inet_frag_queue *q;
  	struct ip6_create_arg arg;
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
178
  	unsigned int hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179

c6fda2822   Pavel Emelyanov   [INET]: Consolida...
180
  	arg.id = id;
0b5ccb2ee   Patrick McHardy   ipv6: reassembly:...
181
  	arg.user = IP6_DEFRAG_LOCAL_DELIVER;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
182
183
  	arg.src = src;
  	arg.dst = dst;
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
184
  	arg.ecn = ecn;
9a375803f   Pavel Emelyanov   inet fragments: f...
185
186
  
  	read_lock(&ip6_frags.lock);
93c8b90f0   Ilpo Järvinen   ipv6: almost iden...
187
  	hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188

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

bc578a54f   Joe Perches   [NET]: Rename ine...
205
  	if (fq->q.last_in & INET_FRAG_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
  		goto err;
  
  	offset = ntohs(fhdr->frag_off) & ~0x7;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
209
210
  	end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
  			((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
  
  	if ((unsigned int)end > IPV6_MAXPLEN) {
adf30907d   Eric Dumazet   net: skb->dst acc...
213
  		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
214
  				 IPSTATS_MIB_INHDRERRORS);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
215
216
217
  		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
  				  ((u8 *)&fhdr->frag_off -
  				   skb_network_header(skb)));
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
218
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
  	}
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
220
  	ecn = ip6_frag_ecn(ipv6_hdr(skb));
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
221
222
  	if (skb->ip_summed == CHECKSUM_COMPLETE) {
  		const unsigned char *nh = skb_network_header(skb);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
223
  		skb->csum = csum_sub(skb->csum,
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
224
225
226
  				     csum_partial(nh, (u8 *)(fhdr + 1) - nh,
  						  0));
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
231
232
  
  	/* 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...
233
  		if (end < fq->q.len ||
bc578a54f   Joe Perches   [NET]: Rename ine...
234
  		    ((fq->q.last_in & INET_FRAG_LAST_IN) && end != fq->q.len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
  			goto err;
bc578a54f   Joe Perches   [NET]: Rename ine...
236
  		fq->q.last_in |= INET_FRAG_LAST_IN;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
237
  		fq->q.len = end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
242
243
244
245
  	} 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...
246
  			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
247
  					 IPSTATS_MIB_INHDRERRORS);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
248
  			icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  					  offsetof(struct ipv6hdr, payload_len));
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
250
  			return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  		}
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
252
  		if (end > fq->q.len) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  			/* Some bits beyond end -> corruption. */
bc578a54f   Joe Perches   [NET]: Rename ine...
254
  			if (fq->q.last_in & INET_FRAG_LAST_IN)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  				goto err;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
256
  			fq->q.len = end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
260
261
262
263
264
265
  		}
  	}
  
  	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...
266

42ca89c18   Stephen Hemminger   [IPV6]: Need to u...
267
268
  	if (pskb_trim_rcsum(skb, end - offset))
  		goto err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
  
  	/* 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...
274
275
276
277
278
  	prev = fq->q.fragments_tail;
  	if (!prev || FRAG6_CB(prev)->offset < offset) {
  		next = NULL;
  		goto found;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
  	prev = NULL;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
280
  	for(next = fq->q.fragments; next != NULL; next = next->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
  		if (FRAG6_CB(next)->offset >= offset)
  			break;	/* bingo! */
  		prev = next;
  	}
d6bebca92   Changli Gao   fragment: add fas...
285
  found:
5de658f87   Eric Dumazet   ipv6: fix RFC5722...
286
287
  	/* RFC5722, Section 4, amended by Errata ID : 3089
  	 *                          When reassembling an IPv6 datagram, if
70789d705   Nicolas Dichtel   ipv6: discard ove...
288
289
  	 *   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...
290
  	 *   fragments) MUST be silently discarded.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292

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

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

f61944efd   Herbert Xu   [IPV6]: Make ipv6...
313
314
315
316
317
  	dev = skb->dev;
  	if (dev) {
  		fq->iif = dev->ifindex;
  		skb->dev = NULL;
  	}
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
318
319
  	fq->q.stamp = skb->tstamp;
  	fq->q.meat += skb->len;
eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
320
  	fq->ecn |= ecn;
d433673e5   Jesper Dangaard Brouer   net: frag helper ...
321
  	add_frag_mem_limit(&fq->q, skb->truesize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
326
327
  
  	/* The first fragment.
  	 * nhoffset is obtained from the first fragment, of course.
  	 */
  	if (offset == 0) {
  		fq->nhoffset = nhoff;
bc578a54f   Joe Perches   [NET]: Rename ine...
328
  		fq->q.last_in |= INET_FRAG_FIRST_IN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
  	}
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
330

bc578a54f   Joe Perches   [NET]: Rename ine...
331
  	if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
97599dc79   Eric Dumazet   net: drop dst bef...
332
333
334
335
336
337
338
339
340
  	    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...
341

97599dc79   Eric Dumazet   net: drop dst bef...
342
  	skb_dst_drop(skb);
3ef0eb0db   Jesper Dangaard Brouer   net: frag, move L...
343
  	inet_frag_lru_move(&fq->q);
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:
adf30907d   Eric Dumazet   net: skb->dst acc...
349
  	IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
3bd653c84   Denis V. Lunev   netns: add net pa...
350
  		      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
417
418
419
420
421
422
  		struct sk_buff *clone;
  		int i, plen = 0;
  
  		if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
  			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;
d433673e5   Jesper Dangaard Brouer   net: frag helper ...
431
  		add_frag_mem_limit(&fq->q, 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
  	}
d433673e5   Jesper Dangaard Brouer   net: frag helper ...
469
  	sub_frag_mem_limit(&fq->q, 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;
a829a2887   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) */
84fa7933a   Patrick McHardy   [NET]: Replace CH...
480
  	if (head->ip_summed == CHECKSUM_COMPLETE)
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
481
  		head->csum = csum_partial(skb_network_header(head),
cfe1fc775   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
482
  					  skb_network_header_len(head),
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
483
  					  head->csum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484

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

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

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

d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
533
  		IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb);
a829a2887   Hannes Frederic Sowa   ipv6: drop packet...
534
  		IP6CB(skb)->flags |= IP6SKB_FRAGMENTED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
  		return 1;
  	}
6b102865e   Amerigo Wang   ipv6: unify fragm...
537
538
539
540
  	evicted = inet_frag_evictor(&net->ipv6.frags, &ip6_frags, false);
  	if (evicted)
  		IP6_ADD_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
  				 IPSTATS_MIB_REASMFAILS, evicted);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541

eec2e6185   Hannes Frederic Sowa   ipv6: implement R...
542
543
  	fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr,
  		     ip6_frag_ecn(hdr));
9546377c4   Shan Wei   IPv6: Delete redu...
544
  	if (fq != NULL) {
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
545
  		int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546

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

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

5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
551
  		spin_unlock(&fq->q.lock);
b836c99fd   Amerigo Wang   ipv6: unify connt...
552
  		inet_frag_put(&fq->q, &ip6_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
  		return ret;
  	}
adf30907d   Eric Dumazet   net: skb->dst acc...
555
  	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
  	kfree_skb(skb);
  	return -1;
98b3377ca   Denis V. Lunev   ipv6: consolidate...
558
559
  
  fail_hdr:
adf30907d   Eric Dumazet   net: skb->dst acc...
560
  	IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INHDRERRORS);
98b3377ca   Denis V. Lunev   ipv6: consolidate...
561
562
  	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb_network_header_len(skb));
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
  }
41135cc83   Alexey Dobriyan   net: constify str...
564
  static const struct inet6_protocol frag_protocol =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
566
567
568
  {
  	.handler	=	ipv6_frag_rcv,
  	.flags		=	INET6_PROTO_NOPOLICY,
  };
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
569
  #ifdef CONFIG_SYSCTL
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
570
  static struct ctl_table ip6_frags_ns_ctl_table[] = {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
571
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
572
  		.procname	= "ip6frag_high_thresh",
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
573
  		.data		= &init_net.ipv6.frags.high_thresh,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
574
575
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
576
  		.proc_handler	= proc_dointvec
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
577
578
  	},
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
579
  		.procname	= "ip6frag_low_thresh",
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
580
  		.data		= &init_net.ipv6.frags.low_thresh,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
581
582
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
583
  		.proc_handler	= proc_dointvec
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
584
585
  	},
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
586
  		.procname	= "ip6frag_time",
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
587
  		.data		= &init_net.ipv6.frags.timeout,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
588
589
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
590
  		.proc_handler	= proc_dointvec_jiffies,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
591
  	},
7d291ebb8   Pavel Emelyanov   inet: Register fr...
592
593
594
595
  	{ }
  };
  
  static struct ctl_table ip6_frags_ctl_table[] = {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
596
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
597
  		.procname	= "ip6frag_secret_interval",
3b4bc4a2b   Pavel Emelyanov   [NETNS][FRAGS]: I...
598
  		.data		= &ip6_frags.secret_interval,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
599
600
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
601
  		.proc_handler	= proc_dointvec_jiffies,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
602
603
604
  	},
  	{ }
  };
2c8c1e729   Alexey Dobriyan   net: spread __net...
605
  static int __net_init ip6_frags_ns_sysctl_register(struct net *net)
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
606
  {
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
607
  	struct ctl_table *table;
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
608
  	struct ctl_table_header *hdr;
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
609
  	table = ip6_frags_ns_ctl_table;
09ad9bc75   Octavian Purdila   net: use net_eq t...
610
  	if (!net_eq(net, &init_net)) {
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
611
  		table = kmemdup(table, sizeof(ip6_frags_ns_ctl_table), GFP_KERNEL);
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
612
613
  		if (table == NULL)
  			goto err_alloc;
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
614
615
  		table[0].data = &net->ipv6.frags.high_thresh;
  		table[1].data = &net->ipv6.frags.low_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);
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
623
624
625
626
627
628
629
  	if (hdr == NULL)
  		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
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
659
  static inline 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

0a64b4b81   Pavel Emelyanov   inet: Rename frag...
664
  static inline void ip6_frags_ns_sysctl_unregister(struct net *net)
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
665
666
  {
  }
7d291ebb8   Pavel Emelyanov   inet: Register fr...
667
668
669
670
671
672
673
674
675
  
  static inline int ip6_frags_sysctl_register(void)
  {
  	return 0;
  }
  
  static inline void ip6_frags_sysctl_unregister(void)
  {
  }
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
676
  #endif
7d460db95   Daniel Lezcano   [IPV6]: Fix ip6_f...
677

2c8c1e729   Alexey Dobriyan   net: spread __net...
678
  static int __net_init ipv6_frags_init_net(struct net *net)
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
679
  {
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

e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
684
  	inet_frags_init_net(&net->ipv6.frags);
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
685
  	return ip6_frags_ns_sysctl_register(net);
e71e0349e   Daniel Lezcano   [NETNS][IPV6]: Ma...
686
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
687
  static void __net_exit ipv6_frags_exit_net(struct net *net)
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
688
  {
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
689
  	ip6_frags_ns_sysctl_unregister(net);
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
690
691
692
693
694
695
696
  	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...
697
  int __init ipv6_frag_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
  {
853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
699
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700

853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
701
702
703
  	ret = inet6_add_protocol(&frag_protocol, IPPROTO_FRAGMENT);
  	if (ret)
  		goto out;
e71e0349e   Daniel Lezcano   [NETNS][IPV6]: Ma...
704

7d291ebb8   Pavel Emelyanov   inet: Register fr...
705
706
707
  	ret = ip6_frags_sysctl_register();
  	if (ret)
  		goto err_sysctl;
0002c630c   Pavel Emelyanov   ipv6: In fragment...
708
709
710
  	ret = register_pernet_subsys(&ip6_frags_ops);
  	if (ret)
  		goto err_pernet;
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
711

321a3a99e   Pavel Emelyanov   [INET]: Consolida...
712
  	ip6_frags.hashfn = ip6_hashfn;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
713
  	ip6_frags.constructor = ip6_frag_init;
c95477090   Pavel Emelyanov   [INET]: Consolida...
714
  	ip6_frags.destructor = NULL;
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
715
716
  	ip6_frags.skb_free = NULL;
  	ip6_frags.qsize = sizeof(struct frag_queue);
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
717
  	ip6_frags.match = ip6_frag_match;
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
718
  	ip6_frags.frag_expire = ip6_frag_expire;
3b4bc4a2b   Pavel Emelyanov   [NETNS][FRAGS]: I...
719
  	ip6_frags.secret_interval = 10 * 60 * HZ;
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
720
  	inet_frags_init(&ip6_frags);
853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
721
722
  out:
  	return ret;
0002c630c   Pavel Emelyanov   ipv6: In fragment...
723
724
  
  err_pernet:
7d291ebb8   Pavel Emelyanov   inet: Register fr...
725
726
  	ip6_frags_sysctl_unregister();
  err_sysctl:
0002c630c   Pavel Emelyanov   ipv6: In fragment...
727
728
  	inet6_del_protocol(&frag_protocol, IPPROTO_FRAGMENT);
  	goto out;
853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
729
730
731
732
733
  }
  
  void ipv6_frag_exit(void)
  {
  	inet_frags_fini(&ip6_frags);
7d291ebb8   Pavel Emelyanov   inet: Register fr...
734
  	ip6_frags_sysctl_unregister();
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
735
  	unregister_pernet_subsys(&ip6_frags_ops);
853cbbaaa   Daniel Lezcano   [IPV6]: make frag...
736
  	inet6_del_protocol(&frag_protocol, IPPROTO_FRAGMENT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
  }