Blame view

net/ipv6/reassembly.c 18.9 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.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  #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...
42
  #include <linux/skbuff.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
43
  #include <linux/slab.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
44
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
49
  
  #include <net/sock.h>
  #include <net/snmp.h>
  
  #include <net/ipv6.h>
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
50
  #include <net/ip6_route.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
  #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...
56
  #include <net/inet_frag.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  struct ip6frag_skb_cb
  {
  	struct inet6_skb_parm	h;
  	int			offset;
  };
  
  #define FRAG6_CB(skb)	((struct ip6frag_skb_cb*)((skb)->cb))
  
  
  /*
   *	Equivalent of ipv4 struct ipq
   */
  
  struct frag_queue
  {
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
73
  	struct inet_frag_queue	q;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74

e69a4adc6   Al Viro   [IPV6]: Misc endi...
75
  	__be32			id;		/* fragment id		*/
0b5ccb2ee   Patrick McHardy   ipv6: reassembly:...
76
  	u32			user;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
  	struct in6_addr		saddr;
  	struct in6_addr		daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  	int			iif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
  	unsigned int		csum;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
  	__u16			nhoffset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
  };
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
83
  static struct inet_frags ip6_frags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84

e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
85
  int ip6_frag_nqueues(struct net *net)
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
86
  {
e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
87
  	return net->ipv6.frags.nqueues;
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
88
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89

6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
90
  int ip6_frag_mem(struct net *net)
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
91
  {
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
92
  	return atomic_read(&net->ipv6.frags.mem);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
93
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94

f61944efd   Herbert Xu   [IPV6]: Make ipv6...
95
96
  static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
  			  struct net_device *dev);
f6596f9d2   Zach Brown   [IPv6] reassembly...
97
98
99
100
  /*
   * 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...
101
102
  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
103
  {
82a39eb6b   Jozsef Kadlecsik   ipv6: Prepare the...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  	u32 c;
  
  	c = jhash_3words((__force u32)saddr->s6_addr32[0],
  			 (__force u32)saddr->s6_addr32[1],
  			 (__force u32)saddr->s6_addr32[2],
  			 rnd);
  
  	c = jhash_3words((__force u32)saddr->s6_addr32[3],
  			 (__force u32)daddr->s6_addr32[0],
  			 (__force u32)daddr->s6_addr32[1],
  			 c);
  
  	c =  jhash_3words((__force u32)daddr->s6_addr32[2],
  			  (__force u32)daddr->s6_addr32[3],
  			  (__force u32)id,
  			  c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120

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

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

321a3a99e   Pavel Emelyanov   [INET]: Consolida...
129
  	fq = container_of(q, struct frag_queue, q);
93c8b90f0   Ilpo Järvinen   ipv6: almost iden...
130
  	return inet6_hash_frag(fq->id, &fq->saddr, &fq->daddr, ip6_frags.rnd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  }
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
132
133
134
135
136
137
  int ip6_frag_match(struct inet_frag_queue *q, void *a)
  {
  	struct frag_queue *fq;
  	struct ip6_create_arg *arg = a;
  
  	fq = container_of(q, struct frag_queue, q);
0b5ccb2ee   Patrick McHardy   ipv6: reassembly:...
138
  	return (fq->id == arg->id && fq->user == arg->user &&
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
139
140
141
142
  			ipv6_addr_equal(&fq->saddr, arg->src) &&
  			ipv6_addr_equal(&fq->daddr, arg->dst));
  }
  EXPORT_SYMBOL(ip6_frag_match);
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
143
  void ip6_frag_init(struct inet_frag_queue *q, void *a)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  {
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
145
146
147
148
  	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:...
149
  	fq->user = arg->user;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
150
151
  	ipv6_addr_copy(&fq->saddr, arg->src);
  	ipv6_addr_copy(&fq->daddr, arg->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  }
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
153
  EXPORT_SYMBOL(ip6_frag_init);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  /* Destruction primitives. */
4b6cb5d8e   Pavel Emelyanov   [INET]: Small cle...
156
  static __inline__ void fq_put(struct frag_queue *fq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  {
762cc4080   Pavel Emelyanov   [INET]: Consolida...
158
  	inet_frag_put(&fq->q, &ip6_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
161
162
163
164
165
  }
  
  /* Kill fq entry. It is not destroyed immediately,
   * because caller (and someone more) holds reference count.
   */
  static __inline__ void fq_kill(struct frag_queue *fq)
  {
277e650dd   Pavel Emelyanov   [INET]: Consolida...
166
  	inet_frag_kill(&fq->q, &ip6_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
  }
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
168
  static void ip6_evictor(struct net *net, struct inet6_dev *idev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
  {
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
170
  	int evicted;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
171
  	evicted = inet_frag_evictor(&net->ipv6.frags, &ip6_frags);
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
172
  	if (evicted)
821d57776   Denis V. Lunev   ipv6: added net a...
173
  		IP6_ADD_STATS_BH(net, idev, IPSTATS_MIB_REASMFAILS, evicted);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
  }
  
  static void ip6_frag_expire(unsigned long data)
  {
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
178
  	struct frag_queue *fq;
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
179
  	struct net_device *dev = NULL;
4ac2ccd01   Daniel Lezcano   netns: Fix reasse...
180
  	struct net *net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181

e521db9d7   Pavel Emelyanov   [INET]: Consolida...
182
  	fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q);
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
183
  	spin_lock(&fq->q.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184

bc578a54f   Joe Perches   [NET]: Rename ine...
185
  	if (fq->q.last_in & INET_FRAG_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
  		goto out;
  
  	fq_kill(fq);
4ac2ccd01   Daniel Lezcano   netns: Fix reasse...
189
  	net = container_of(fq->q.net, struct net, ipv6.frags);
69df9d599   Eric Dumazet   ip_frag: dont tou...
190
191
  	rcu_read_lock();
  	dev = dev_get_by_index_rcu(net, fq->iif);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
192
  	if (!dev)
69df9d599   Eric Dumazet   ip_frag: dont tou...
193
  		goto out_rcu_unlock;
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
194

483a47d2f   Denis V. Lunev   ipv6: added net a...
195
196
  	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
197

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

78c784c47   Ingo Oeser   [IPV6]: Cleanup o...
202
203
204
205
206
  	/*
  	   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...
207
  	fq->q.fragments->dev = dev;
3ffe533c8   Alexey Dobriyan   ipv6: drop unused...
208
  	icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0);
69df9d599   Eric Dumazet   ip_frag: dont tou...
209
210
  out_rcu_unlock:
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  out:
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
212
  	spin_unlock(&fq->q.lock);
4b6cb5d8e   Pavel Emelyanov   [INET]: Small cle...
213
  	fq_put(fq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  }
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
215
  static __inline__ struct frag_queue *
b71d1d426   Eric Dumazet   inet: constify ip...
216
  fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6_addr *dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  {
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
218
219
  	struct inet_frag_queue *q;
  	struct ip6_create_arg arg;
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
220
  	unsigned int hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221

c6fda2822   Pavel Emelyanov   [INET]: Consolida...
222
  	arg.id = id;
0b5ccb2ee   Patrick McHardy   ipv6: reassembly:...
223
  	arg.user = IP6_DEFRAG_LOCAL_DELIVER;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
224
225
  	arg.src = src;
  	arg.dst = dst;
9a375803f   Pavel Emelyanov   inet fragments: f...
226
227
  
  	read_lock(&ip6_frags.lock);
93c8b90f0   Ilpo Järvinen   ipv6: almost iden...
228
  	hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229

ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
230
  	q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash);
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
231
  	if (q == NULL)
9546377c4   Shan Wei   IPv6: Delete redu...
232
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233

c6fda2822   Pavel Emelyanov   [INET]: Consolida...
234
  	return container_of(q, struct frag_queue, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
  }
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
236
  static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
  			   struct frag_hdr *fhdr, int nhoff)
  {
  	struct sk_buff *prev, *next;
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
240
  	struct net_device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
  	int offset, end;
adf30907d   Eric Dumazet   net: skb->dst acc...
242
  	struct net *net = dev_net(skb_dst(skb)->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243

bc578a54f   Joe Perches   [NET]: Rename ine...
244
  	if (fq->q.last_in & INET_FRAG_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
  		goto err;
  
  	offset = ntohs(fhdr->frag_off) & ~0x7;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
248
249
  	end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
  			((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
  
  	if ((unsigned int)end > IPV6_MAXPLEN) {
adf30907d   Eric Dumazet   net: skb->dst acc...
252
  		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
253
  				 IPSTATS_MIB_INHDRERRORS);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
254
255
256
  		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
  				  ((u8 *)&fhdr->frag_off -
  				   skb_network_header(skb)));
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
257
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  	}
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
259
260
  	if (skb->ip_summed == CHECKSUM_COMPLETE) {
  		const unsigned char *nh = skb_network_header(skb);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
261
  		skb->csum = csum_sub(skb->csum,
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
262
263
264
  				     csum_partial(nh, (u8 *)(fhdr + 1) - nh,
  						  0));
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
  
  	/* 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...
271
  		if (end < fq->q.len ||
bc578a54f   Joe Perches   [NET]: Rename ine...
272
  		    ((fq->q.last_in & INET_FRAG_LAST_IN) && end != fq->q.len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  			goto err;
bc578a54f   Joe Perches   [NET]: Rename ine...
274
  		fq->q.last_in |= INET_FRAG_LAST_IN;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
275
  		fq->q.len = end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
277
278
279
280
281
282
283
  	} 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...
284
  			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
285
  					 IPSTATS_MIB_INHDRERRORS);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
286
  			icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
  					  offsetof(struct ipv6hdr, payload_len));
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
288
  			return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
  		}
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
290
  		if (end > fq->q.len) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
  			/* Some bits beyond end -> corruption. */
bc578a54f   Joe Perches   [NET]: Rename ine...
292
  			if (fq->q.last_in & INET_FRAG_LAST_IN)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  				goto err;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
294
  			fq->q.len = end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
298
299
300
301
302
303
  		}
  	}
  
  	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...
304

42ca89c18   Stephen Hemminger   [IPV6]: Need to u...
305
306
  	if (pskb_trim_rcsum(skb, end - offset))
  		goto err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
310
311
  
  	/* 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...
312
313
314
315
316
  	prev = fq->q.fragments_tail;
  	if (!prev || FRAG6_CB(prev)->offset < offset) {
  		next = NULL;
  		goto found;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
  	prev = NULL;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
318
  	for(next = fq->q.fragments; next != NULL; next = next->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
  		if (FRAG6_CB(next)->offset >= offset)
  			break;	/* bingo! */
  		prev = next;
  	}
d6bebca92   Changli Gao   fragment: add fas...
323
  found:
70789d705   Nicolas Dichtel   ipv6: discard ove...
324
325
326
327
328
329
  	/* RFC5722, Section 4:
  	 *                                  When reassembling an IPv6 datagram, if
  	 *   one or more its constituent fragments is determined to be an
  	 *   overlapping fragment, the entire datagram (and any constituent
  	 *   fragments, including those not yet received) MUST be silently
  	 *   discarded.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331

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

70789d705   Nicolas Dichtel   ipv6: discard ove...
337
338
339
  	/* Look for overlap with succeeding segment. */
  	if (next && FRAG6_CB(next)->offset < end)
  		goto discard_fq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
343
344
  
  	FRAG6_CB(skb)->offset = offset;
  
  	/* Insert this fragment in the chain of fragments. */
  	skb->next = next;
d6bebca92   Changli Gao   fragment: add fas...
345
346
  	if (!next)
  		fq->q.fragments_tail = skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
  	if (prev)
  		prev->next = skb;
  	else
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
350
  		fq->q.fragments = skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351

f61944efd   Herbert Xu   [IPV6]: Make ipv6...
352
353
354
355
356
  	dev = skb->dev;
  	if (dev) {
  		fq->iif = dev->ifindex;
  		skb->dev = NULL;
  	}
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
357
358
  	fq->q.stamp = skb->tstamp;
  	fq->q.meat += skb->len;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
359
  	atomic_add(skb->truesize, &fq->q.net->mem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
362
363
364
365
  
  	/* The first fragment.
  	 * nhoffset is obtained from the first fragment, of course.
  	 */
  	if (offset == 0) {
  		fq->nhoffset = nhoff;
bc578a54f   Joe Perches   [NET]: Rename ine...
366
  		fq->q.last_in |= INET_FRAG_FIRST_IN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
  	}
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
368

bc578a54f   Joe Perches   [NET]: Rename ine...
369
370
  	if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
  	    fq->q.meat == fq->q.len)
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
371
  		return ip6_frag_reasm(fq, prev, dev);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
372
  	write_lock(&ip6_frags.lock);
3140c25c8   Pavel Emelyanov   [NETNS][FRAGS]: M...
373
  	list_move_tail(&fq->q.lru_list, &fq->q.net->lru_list);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
374
  	write_unlock(&ip6_frags.lock);
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
375
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376

70789d705   Nicolas Dichtel   ipv6: discard ove...
377
378
  discard_fq:
  	fq_kill(fq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  err:
adf30907d   Eric Dumazet   net: skb->dst acc...
380
  	IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
3bd653c84   Denis V. Lunev   netns: add net pa...
381
  		      IPSTATS_MIB_REASMFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  	kfree_skb(skb);
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
383
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
388
389
390
391
392
393
394
  }
  
  /*
   *	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...
395
  static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
  			  struct net_device *dev)
  {
2bad35b7c   Jorge Boncompte [DTI2]   netns: oops in ip...
398
  	struct net *net = container_of(fq->q.net, struct net, ipv6.frags);
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
399
  	struct sk_buff *fp, *head = fq->q.fragments;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
402
403
  	int    payload_len;
  	unsigned int nhoff;
  
  	fq_kill(fq);
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
404
405
406
407
408
409
410
411
412
  	/* 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...
413
414
  		if (!fp->next)
  			fq->q.fragments_tail = fp;
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
415
  		prev->next = fp;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
416
417
  		skb_morph(head, fq->q.fragments);
  		head->next = fq->q.fragments->next;
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
418

5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
419
420
  		kfree_skb(fq->q.fragments);
  		fq->q.fragments = head;
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
421
  	}
547b792ca   Ilpo Järvinen   net: convert BUG_...
422
423
  	WARN_ON(head == NULL);
  	WARN_ON(FRAG6_CB(head)->offset != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
  
  	/* Unfragmented part is taken from the first segment. */
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
426
  	payload_len = ((head->data - skb_network_header(head)) -
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
427
  		       sizeof(struct ipv6hdr) + fq->q.len -
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
428
  		       sizeof(struct frag_hdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
432
433
434
435
436
437
438
  	if (payload_len > IPV6_MAXPLEN)
  		goto out_oversize;
  
  	/* Head of list must not be cloned. */
  	if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
  		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...
439
  	if (skb_has_frag_list(head)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
442
443
444
445
446
447
  		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...
448
  		skb_frag_list_init(head);
9e903e085   Eric Dumazet   net: add skb frag...
449
450
  		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
451
452
453
454
455
  		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;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
456
  		atomic_add(clone->truesize, &fq->q.net->mem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
460
461
  	}
  
  	/* 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...
462
  	skb_network_header(head)[nhoff] = skb_transport_header(head)[0];
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
463
  	memmove(head->head + sizeof(struct frag_hdr), head->head,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
  		(head->data - head->head) - sizeof(struct frag_hdr));
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
465
466
  	head->mac_header += sizeof(struct frag_hdr);
  	head->network_header += sizeof(struct frag_hdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
  
  	skb_shinfo(head)->frag_list = head->next;
badff6d01   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
469
  	skb_reset_transport_header(head);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
470
  	skb_push(head, head->data - skb_network_header(head));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
472
473
474
475
476
  
  	for (fp=head->next; fp; fp = fp->next) {
  		head->data_len += fp->len;
  		head->len += fp->len;
  		if (head->ip_summed != fp->ip_summed)
  			head->ip_summed = CHECKSUM_NONE;
84fa7933a   Patrick McHardy   [NET]: Replace CH...
477
  		else if (head->ip_summed == CHECKSUM_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
  			head->csum = csum_add(head->csum, fp->csum);
  		head->truesize += fp->truesize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
  	}
d27f9b358   Eric Dumazet   ip_frag: Remove s...
481
  	atomic_sub(head->truesize, &fq->q.net->mem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
484
  
  	head->next = NULL;
  	head->dev = dev;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
485
  	head->tstamp = fq->q.stamp;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
486
  	ipv6_hdr(head)->payload_len = htons(payload_len);
951dbc8ac   Patrick McHardy   [IPV6]: Move next...
487
  	IP6CB(head)->nhoff = nhoff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
  	/* Yes, and fold redundant checksum back. 8) */
84fa7933a   Patrick McHardy   [NET]: Replace CH...
490
  	if (head->ip_summed == CHECKSUM_COMPLETE)
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
491
  		head->csum = csum_partial(skb_network_header(head),
cfe1fc775   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
492
  					  skb_network_header_len(head),
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
493
  					  head->csum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494

a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
495
  	rcu_read_lock();
2bad35b7c   Jorge Boncompte [DTI2]   netns: oops in ip...
496
  	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMOKS);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
497
  	rcu_read_unlock();
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
498
  	fq->q.fragments = NULL;
d6bebca92   Changli Gao   fragment: add fas...
499
  	fq->q.fragments_tail = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
503
504
505
506
507
508
509
510
511
  	return 1;
  
  out_oversize:
  	if (net_ratelimit())
  		printk(KERN_DEBUG "ip6_frag_reasm: payload len = %d
  ", payload_len);
  	goto out_fail;
  out_oom:
  	if (net_ratelimit())
  		printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly
  ");
  out_fail:
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
512
  	rcu_read_lock();
2bad35b7c   Jorge Boncompte [DTI2]   netns: oops in ip...
513
  	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
514
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
  	return -1;
  }
e5bbef20e   Herbert Xu   [IPV6]: Replace s...
517
  static int ipv6_frag_rcv(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
  	struct frag_hdr *fhdr;
  	struct frag_queue *fq;
b71d1d426   Eric Dumazet   inet: constify ip...
521
  	const struct ipv6hdr *hdr = ipv6_hdr(skb);
adf30907d   Eric Dumazet   net: skb->dst acc...
522
  	struct net *net = dev_net(skb_dst(skb)->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523

adf30907d   Eric Dumazet   net: skb->dst acc...
524
  	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMREQDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
526
  
  	/* Jumbo payload inhibits frag. header */
98b3377ca   Denis V. Lunev   ipv6: consolidate...
527
528
  	if (hdr->payload_len==0)
  		goto fail_hdr;
ea2ae17d6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
529
  	if (!pskb_may_pull(skb, (skb_transport_offset(skb) +
98b3377ca   Denis V. Lunev   ipv6: consolidate...
530
531
  				 sizeof(struct frag_hdr))))
  		goto fail_hdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532

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

d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
542
  		IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
544
  		return 1;
  	}
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
545
  	if (atomic_read(&net->ipv6.frags.mem) > net->ipv6.frags.high_thresh)
adf30907d   Eric Dumazet   net: skb->dst acc...
546
  		ip6_evictor(net, ip6_dst_idev(skb_dst(skb)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547

9546377c4   Shan Wei   IPv6: Delete redu...
548
549
  	fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr);
  	if (fq != NULL) {
f61944efd   Herbert Xu   [IPV6]: Make ipv6...
550
  		int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551

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

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

5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
556
  		spin_unlock(&fq->q.lock);
4b6cb5d8e   Pavel Emelyanov   [INET]: Small cle...
557
  		fq_put(fq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
  		return ret;
  	}
adf30907d   Eric Dumazet   net: skb->dst acc...
560
  	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
562
  	kfree_skb(skb);
  	return -1;
98b3377ca   Denis V. Lunev   ipv6: consolidate...
563
564
  
  fail_hdr:
adf30907d   Eric Dumazet   net: skb->dst acc...
565
  	IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INHDRERRORS);
98b3377ca   Denis V. Lunev   ipv6: consolidate...
566
567
  	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb_network_header_len(skb));
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
  }
41135cc83   Alexey Dobriyan   net: constify str...
569
  static const struct inet6_protocol frag_protocol =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
572
573
  {
  	.handler	=	ipv6_frag_rcv,
  	.flags		=	INET6_PROTO_NOPOLICY,
  };
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
574
  #ifdef CONFIG_SYSCTL
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
575
  static struct ctl_table ip6_frags_ns_ctl_table[] = {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
576
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
577
  		.procname	= "ip6frag_high_thresh",
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
578
  		.data		= &init_net.ipv6.frags.high_thresh,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
579
580
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
581
  		.proc_handler	= proc_dointvec
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
582
583
  	},
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
584
  		.procname	= "ip6frag_low_thresh",
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
585
  		.data		= &init_net.ipv6.frags.low_thresh,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
586
587
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
588
  		.proc_handler	= proc_dointvec
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
589
590
  	},
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
591
  		.procname	= "ip6frag_time",
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
592
  		.data		= &init_net.ipv6.frags.timeout,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
593
594
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
595
  		.proc_handler	= proc_dointvec_jiffies,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
596
  	},
7d291ebb8   Pavel Emelyanov   inet: Register fr...
597
598
599
600
  	{ }
  };
  
  static struct ctl_table ip6_frags_ctl_table[] = {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
601
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
602
  		.procname	= "ip6frag_secret_interval",
3b4bc4a2b   Pavel Emelyanov   [NETNS][FRAGS]: I...
603
  		.data		= &ip6_frags.secret_interval,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
604
605
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
606
  		.proc_handler	= proc_dointvec_jiffies,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
607
608
609
  	},
  	{ }
  };
2c8c1e729   Alexey Dobriyan   net: spread __net...
610
  static int __net_init ip6_frags_ns_sysctl_register(struct net *net)
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
611
  {
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
612
  	struct ctl_table *table;
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
613
  	struct ctl_table_header *hdr;
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
614
  	table = ip6_frags_ns_ctl_table;
09ad9bc75   Octavian Purdila   net: use net_eq t...
615
  	if (!net_eq(net, &init_net)) {
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
616
  		table = kmemdup(table, sizeof(ip6_frags_ns_ctl_table), GFP_KERNEL);
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
617
618
  		if (table == NULL)
  			goto err_alloc;
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
619
620
  		table[0].data = &net->ipv6.frags.high_thresh;
  		table[1].data = &net->ipv6.frags.low_thresh;
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
621
  		table[2].data = &net->ipv6.frags.timeout;
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
622
623
624
625
626
627
628
629
630
631
  	}
  
  	hdr = register_net_sysctl_table(net, net_ipv6_ctl_path, table);
  	if (hdr == NULL)
  		goto err_reg;
  
  	net->ipv6.sysctl.frags_hdr = hdr;
  	return 0;
  
  err_reg:
09ad9bc75   Octavian Purdila   net: use net_eq t...
632
  	if (!net_eq(net, &init_net))
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
633
634
635
636
  		kfree(table);
  err_alloc:
  	return -ENOMEM;
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
637
  static void __net_exit ip6_frags_ns_sysctl_unregister(struct net *net)
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
638
639
640
641
642
  {
  	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...
643
644
  	if (!net_eq(net, &init_net))
  		kfree(table);
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
645
  }
7d291ebb8   Pavel Emelyanov   inet: Register fr...
646
647
648
649
650
651
652
653
654
655
656
657
658
659
  
  static struct ctl_table_header *ip6_ctl_header;
  
  static int ip6_frags_sysctl_register(void)
  {
  	ip6_ctl_header = register_net_sysctl_rotable(net_ipv6_ctl_path,
  			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...
660
  #else
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
661
  static inline int ip6_frags_ns_sysctl_register(struct net *net)
e71e0349e   Daniel Lezcano   [NETNS][IPV6]: Ma...
662
  {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
663
664
  	return 0;
  }
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
665

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

2c8c1e729   Alexey Dobriyan   net: spread __net...
680
  static int __net_init ipv6_frags_init_net(struct net *net)
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
681
  {
7c070aa94   Shan Wei   IPv6: reassembly:...
682
683
  	net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
  	net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
684
  	net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT;
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
685

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

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

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

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