Blame view

net/ipv4/ip_fragment.c 20.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
  /*
   * INET		An implementation of the TCP/IP protocol suite for the LINUX
   *		operating system.  INET is implemented using the  BSD Socket
   *		interface as the means of communication with the user level.
   *
   *		The IP fragmentation functionality.
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
7
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
   * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
113aa838e   Alan Cox   net: Rationalise ...
9
   *		Alan Cox <alan@lxorguk.ukuu.org.uk>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
17
18
19
20
21
   *
   * Fixes:
   *		Alan Cox	:	Split from ip.c , see ip_input.c for history.
   *		David S. Miller :	Begin massive cleanup...
   *		Andi Kleen	:	Add sysctls.
   *		xxxx		:	Overlapfrag bug.
   *		Ultima          :       ip_expire() kernel panic.
   *		Bill Hawes	:	Frag accounting and evictor fixes.
   *		John McDonald	:	0 length frag bug.
   *		Alexey Kuznetsov:	SMP races, threading, cleanup.
   *		Patrick McHardy :	LRU queue of frag heads for evictor.
   */
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
22
  #include <linux/compiler.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
29
30
31
32
33
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/mm.h>
  #include <linux/jiffies.h>
  #include <linux/skbuff.h>
  #include <linux/list.h>
  #include <linux/ip.h>
  #include <linux/icmp.h>
  #include <linux/netdevice.h>
  #include <linux/jhash.h>
  #include <linux/random.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
34
  #include <linux/slab.h>
e9017b551   Shan Wei   IP: Send an ICMP ...
35
36
  #include <net/route.h>
  #include <net/dst.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
  #include <net/sock.h>
  #include <net/ip.h>
  #include <net/icmp.h>
  #include <net/checksum.h>
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
41
  #include <net/inetpeer.h>
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
42
  #include <net/inet_frag.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
  #include <linux/tcp.h>
  #include <linux/udp.h>
  #include <linux/inet.h>
  #include <linux/netfilter_ipv4.h>
6623e3b24   Eric Dumazet   ipv4: IP defragme...
47
  #include <net/inet_ecn.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
51
52
  
  /* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6
   * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c
   * as well. Or notify me, at least. --ANK
   */
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
53
  static int sysctl_ipfrag_max_dist __read_mostly = 64;
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
54

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
58
59
  struct ipfrag_skb_cb
  {
  	struct inet_skb_parm	h;
  	int			offset;
  };
fd3f8c4cb   Jianjun Kong   net: clean up net...
60
  #define FRAG_CB(skb)	((struct ipfrag_skb_cb *)((skb)->cb))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
  
  /* Describe an entry in the "incomplete datagrams" queue. */
  struct ipq {
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
64
  	struct inet_frag_queue q;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
  	u32		user;
182777700   Al Viro   [IPV4]: ip_fragme...
66
67
68
  	__be32		saddr;
  	__be32		daddr;
  	__be16		id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  	u8		protocol;
6623e3b24   Eric Dumazet   ipv4: IP defragme...
70
  	u8		ecn; /* RFC3168 support */
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
71
72
73
  	int             iif;
  	unsigned int    rid;
  	struct inet_peer *peer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
  };
5173cc057   Eric Dumazet   ipv4: more compli...
75
76
77
78
  /* RFC 3168 support :
   * We want to check ECN values of all fragments, do detect invalid combinations.
   * In ipq->ecn, we store the OR value of each ip4_frag_ecn() fragment value.
   */
1d1652cbd   David S. Miller   ipv4: Don't use e...
79
80
81
82
  #define	IPFRAG_ECN_NOT_ECT	0x01 /* one frag had ECN_NOT_ECT */
  #define	IPFRAG_ECN_ECT_1	0x02 /* one frag had ECN_ECT_1 */
  #define	IPFRAG_ECN_ECT_0	0x04 /* one frag had ECN_ECT_0 */
  #define	IPFRAG_ECN_CE		0x08 /* one frag had ECN_CE */
6623e3b24   Eric Dumazet   ipv4: IP defragme...
83
84
85
  
  static inline u8 ip4_frag_ecn(u8 tos)
  {
5173cc057   Eric Dumazet   ipv4: more compli...
86
  	return 1 << (tos & INET_ECN_MASK);
6623e3b24   Eric Dumazet   ipv4: IP defragme...
87
  }
5173cc057   Eric Dumazet   ipv4: more compli...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  /* Given the OR values of all fragments, apply RFC 3168 5.3 requirements
   * Value : 0xff if frame should be dropped.
   *         0 or INET_ECN_CE value, to be ORed in to final iph->tos field
   */
  static const u8 ip4_frag_ecn_table[16] = {
  	/* at least one fragment had CE, and others ECT_0 or ECT_1 */
  	[IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0]			= INET_ECN_CE,
  	[IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1]			= INET_ECN_CE,
  	[IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1]	= INET_ECN_CE,
  
  	/* invalid combinations : drop frame */
  	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE] = 0xff,
  	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0] = 0xff,
  	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_1] = 0xff,
  	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
  	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = 0xff,
  	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = 0xff,
  	[IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
  };
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
107
  static struct inet_frags ip4_frags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108

e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
109
  int ip_frag_nqueues(struct net *net)
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
110
  {
e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
111
  	return net->ipv4.frags.nqueues;
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
112
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113

6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
114
  int ip_frag_mem(struct net *net)
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
115
  {
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
116
  	return atomic_read(&net->ipv4.frags.mem);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
117
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118

1706d5876   Herbert Xu   [IPV4]: Make ip_d...
119
120
  static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
  			 struct net_device *dev);
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
121
122
123
124
  struct ip4_create_arg {
  	struct iphdr *iph;
  	u32 user;
  };
182777700   Al Viro   [IPV4]: ip_fragme...
125
  static unsigned int ipqhashfn(__be16 id, __be32 saddr, __be32 daddr, u8 prot)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
  {
182777700   Al Viro   [IPV4]: ip_fragme...
127
128
  	return jhash_3words((__force u32)id << 16 | prot,
  			    (__force u32)saddr, (__force u32)daddr,
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
129
  			    ip4_frags.rnd) & (INETFRAGS_HASHSZ - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
  }
321a3a99e   Pavel Emelyanov   [INET]: Consolida...
131
  static unsigned int ip4_hashfn(struct inet_frag_queue *q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  {
321a3a99e   Pavel Emelyanov   [INET]: Consolida...
133
  	struct ipq *ipq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134

321a3a99e   Pavel Emelyanov   [INET]: Consolida...
135
136
  	ipq = container_of(q, struct ipq, q);
  	return ipqhashfn(ipq->id, ipq->saddr, ipq->daddr, ipq->protocol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
  }
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
138
139
140
141
142
143
  static int ip4_frag_match(struct inet_frag_queue *q, void *a)
  {
  	struct ipq *qp;
  	struct ip4_create_arg *arg = a;
  
  	qp = container_of(q, struct ipq, q);
a02cec215   Eric Dumazet   net: return opera...
144
  	return	qp->id == arg->iph->id &&
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
145
146
147
  			qp->saddr == arg->iph->saddr &&
  			qp->daddr == arg->iph->daddr &&
  			qp->protocol == arg->iph->protocol &&
a02cec215   Eric Dumazet   net: return opera...
148
  			qp->user == arg->user;
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
149
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
  /* Memory Tracking Functions. */
a95d8c88b   Eric Dumazet   ipfrag : frag_kfr...
151
  static void frag_kfree_skb(struct netns_frags *nf, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  {
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
153
  	atomic_sub(skb->truesize, &nf->mem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
  	kfree_skb(skb);
  }
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
156
157
158
159
160
161
162
  static void ip4_frag_init(struct inet_frag_queue *q, void *a)
  {
  	struct ipq *qp = container_of(q, struct ipq, q);
  	struct ip4_create_arg *arg = a;
  
  	qp->protocol = arg->iph->protocol;
  	qp->id = arg->iph->id;
6623e3b24   Eric Dumazet   ipv4: IP defragme...
163
  	qp->ecn = ip4_frag_ecn(arg->iph->tos);
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
164
165
166
167
  	qp->saddr = arg->iph->saddr;
  	qp->daddr = arg->iph->daddr;
  	qp->user = arg->user;
  	qp->peer = sysctl_ipfrag_max_dist ?
b534ecf1c   David S. Miller   inetpeer: Make in...
168
  		inet_getpeer_v4(arg->iph->saddr, 1) : NULL;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
169
  }
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
170
  static __inline__ void ip4_frag_free(struct inet_frag_queue *q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  {
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
172
173
174
175
176
  	struct ipq *qp;
  
  	qp = container_of(q, struct ipq, q);
  	if (qp->peer)
  		inet_putpeer(qp->peer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
  
  /* Destruction primitives. */
4b6cb5d8e   Pavel Emelyanov   [INET]: Small cle...
180
  static __inline__ void ipq_put(struct ipq *ipq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  {
762cc4080   Pavel Emelyanov   [INET]: Consolida...
182
  	inet_frag_put(&ipq->q, &ip4_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
186
187
188
189
  }
  
  /* Kill ipq entry. It is not destroyed immediately,
   * because caller (and someone more) holds reference count.
   */
  static void ipq_kill(struct ipq *ipq)
  {
277e650dd   Pavel Emelyanov   [INET]: Consolida...
190
  	inet_frag_kill(&ipq->q, &ip4_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  }
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
192
  /* Memory limiting on fragments.  Evictor trashes the oldest
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
   * fragment queue until we are back under the threshold.
   */
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
195
  static void ip_evictor(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  {
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
197
  	int evicted;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
198
  	evicted = inet_frag_evictor(&net->ipv4.frags, &ip4_frags);
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
199
  	if (evicted)
c5346fe39   Pavel Emelyanov   mib: add net to I...
200
  		IP_ADD_STATS_BH(net, IPSTATS_MIB_REASMFAILS, evicted);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
203
204
205
206
207
  }
  
  /*
   * Oops, a fragment queue timed out.  Kill it and send an ICMP reply.
   */
  static void ip_expire(unsigned long arg)
  {
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
208
  	struct ipq *qp;
84a3aa000   Pavel Emelyanov   ipv4: prepare net...
209
  	struct net *net;
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
210
211
  
  	qp = container_of((struct inet_frag_queue *) arg, struct ipq, q);
84a3aa000   Pavel Emelyanov   ipv4: prepare net...
212
  	net = container_of(qp->q.net, struct net, ipv4.frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213

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

bc578a54f   Joe Perches   [NET]: Rename ine...
216
  	if (qp->q.last_in & INET_FRAG_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
  		goto out;
  
  	ipq_kill(qp);
7c73a6faf   Pavel Emelyanov   mib: add net to I...
220
221
  	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMTIMEOUT);
  	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222

bc578a54f   Joe Perches   [NET]: Rename ine...
223
  	if ((qp->q.last_in & INET_FRAG_FIRST_IN) && qp->q.fragments != NULL) {
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
224
  		struct sk_buff *head = qp->q.fragments;
64f3b9e20   Eric Dumazet   net: ip_expire() ...
225
226
  		const struct iphdr *iph;
  		int err;
cb84663e4   Denis V. Lunev   [NETNS]: Process ...
227

69df9d599   Eric Dumazet   ip_frag: dont tou...
228
229
  		rcu_read_lock();
  		head->dev = dev_get_by_index_rcu(net, qp->iif);
e9017b551   Shan Wei   IP: Send an ICMP ...
230
231
  		if (!head->dev)
  			goto out_rcu_unlock;
64f3b9e20   Eric Dumazet   net: ip_expire() ...
232
233
234
235
236
237
238
  		/* skb dst is stale, drop it, and perform route lookup again */
  		skb_dst_drop(head);
  		iph = ip_hdr(head);
  		err = ip_route_input_noref(head, iph->daddr, iph->saddr,
  					   iph->tos, head->dev);
  		if (err)
  			goto out_rcu_unlock;
e9017b551   Shan Wei   IP: Send an ICMP ...
239
  		/*
64f3b9e20   Eric Dumazet   net: ip_expire() ...
240
241
  		 * Only an end host needs to send an ICMP
  		 * "Fragment Reassembly Timeout" message, per RFC792.
e9017b551   Shan Wei   IP: Send an ICMP ...
242
  		 */
595fc71ba   David S. Miller   ipv4: Add ip_defr...
243
244
245
  		if (qp->user == IP_DEFRAG_AF_PACKET ||
  		    (qp->user == IP_DEFRAG_CONNTRACK_IN &&
  		     skb_rtable(head)->rt_type != RTN_LOCAL))
64f3b9e20   Eric Dumazet   net: ip_expire() ...
246
  			goto out_rcu_unlock;
e9017b551   Shan Wei   IP: Send an ICMP ...
247
248
249
  
  		/* Send an ICMP "Fragment Reassembly Timeout" message. */
  		icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
e9017b551   Shan Wei   IP: Send an ICMP ...
250
  out_rcu_unlock:
d1c9ae6d1   Patrick McHardy   ipv4: ip_fragment...
251
252
  		rcu_read_unlock();
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  out:
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
254
  	spin_unlock(&qp->q.lock);
4b6cb5d8e   Pavel Emelyanov   [INET]: Small cle...
255
  	ipq_put(qp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  }
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
257
258
259
  /* Find the correct entry in the "incomplete datagrams" queue for
   * this IP datagram, and create new one, if nothing is found.
   */
ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
260
  static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  {
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
262
263
  	struct inet_frag_queue *q;
  	struct ip4_create_arg arg;
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
264
  	unsigned int hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265

c6fda2822   Pavel Emelyanov   [INET]: Consolida...
266
267
  	arg.iph = iph;
  	arg.user = user;
9a375803f   Pavel Emelyanov   inet fragments: f...
268
269
  
  	read_lock(&ip4_frags.lock);
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
270
  	hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271

ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
272
  	q = inet_frag_find(&net->ipv4.frags, &ip4_frags, &arg, hash);
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
273
274
  	if (q == NULL)
  		goto out_nomem;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275

c6fda2822   Pavel Emelyanov   [INET]: Consolida...
276
  	return container_of(q, struct ipq, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
  
  out_nomem:
64ce20730   Patrick McHardy   [NET]: Make NETDE...
279
280
  	LIMIT_NETDEBUG(KERN_ERR "ip_frag_create: no memory left !
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
  	return NULL;
  }
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  /* Is the fragment too far ahead to be part of ipq? */
  static inline int ip_frag_too_far(struct ipq *qp)
  {
  	struct inet_peer *peer = qp->peer;
  	unsigned int max = sysctl_ipfrag_max_dist;
  	unsigned int start, end;
  
  	int rc;
  
  	if (!peer || !max)
  		return 0;
  
  	start = qp->rid;
  	end = atomic_inc_return(&peer->rid);
  	qp->rid = end;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
298
  	rc = qp->q.fragments && (end - start) > max;
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
299
300
  
  	if (rc) {
7c73a6faf   Pavel Emelyanov   mib: add net to I...
301
302
303
304
  		struct net *net;
  
  		net = container_of(qp->q.net, struct net, ipv4.frags);
  		IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
305
306
307
308
309
310
311
312
  	}
  
  	return rc;
  }
  
  static int ip_frag_reinit(struct ipq *qp)
  {
  	struct sk_buff *fp;
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
313
  	if (!mod_timer(&qp->q.timer, jiffies + qp->q.net->timeout)) {
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
314
  		atomic_inc(&qp->q.refcnt);
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
315
316
  		return -ETIMEDOUT;
  	}
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
317
  	fp = qp->q.fragments;
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
318
319
  	do {
  		struct sk_buff *xp = fp->next;
a95d8c88b   Eric Dumazet   ipfrag : frag_kfr...
320
  		frag_kfree_skb(qp->q.net, fp);
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
321
322
  		fp = xp;
  	} while (fp);
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
323
324
325
326
  	qp->q.last_in = 0;
  	qp->q.len = 0;
  	qp->q.meat = 0;
  	qp->q.fragments = NULL;
d6bebca92   Changli Gao   fragment: add fas...
327
  	qp->q.fragments_tail = NULL;
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
328
  	qp->iif = 0;
6623e3b24   Eric Dumazet   ipv4: IP defragme...
329
  	qp->ecn = 0;
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
330
331
332
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
  /* Add new segment to existing queue. */
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
334
  static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
  {
  	struct sk_buff *prev, *next;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
337
  	struct net_device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
  	int flags, offset;
  	int ihl, end;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
340
  	int err = -ENOENT;
6623e3b24   Eric Dumazet   ipv4: IP defragme...
341
  	u8 ecn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342

bc578a54f   Joe Perches   [NET]: Rename ine...
343
  	if (qp->q.last_in & INET_FRAG_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
  		goto err;
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
345
  	if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) &&
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
346
347
  	    unlikely(ip_frag_too_far(qp)) &&
  	    unlikely(err = ip_frag_reinit(qp))) {
89cee8b1c   Herbert Xu   [IPV4]: Safer rea...
348
349
350
  		ipq_kill(qp);
  		goto err;
  	}
6623e3b24   Eric Dumazet   ipv4: IP defragme...
351
  	ecn = ip4_frag_ecn(ip_hdr(skb)->tos);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
352
  	offset = ntohs(ip_hdr(skb)->frag_off);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
  	flags = offset & ~IP_OFFSET;
  	offset &= IP_OFFSET;
  	offset <<= 3;		/* offset is in 8-byte chunks */
c9bdd4b52   Arnaldo Carvalho de Melo   [IP]: Introduce i...
356
  	ihl = ip_hdrlen(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
  
  	/* Determine the position of this fragment. */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
359
  	end = offset + skb->len - ihl;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
360
  	err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
  
  	/* Is this the final fragment? */
  	if ((flags & IP_MF) == 0) {
  		/* If we already have some bits beyond end
42b2aa86c   Justin P. Mattock   treewide: Fix typ...
365
  		 * or have different end, the segment is corrupted.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  		 */
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
367
  		if (end < qp->q.len ||
bc578a54f   Joe Perches   [NET]: Rename ine...
368
  		    ((qp->q.last_in & INET_FRAG_LAST_IN) && end != qp->q.len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
  			goto err;
bc578a54f   Joe Perches   [NET]: Rename ine...
370
  		qp->q.last_in |= INET_FRAG_LAST_IN;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
371
  		qp->q.len = end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
375
376
377
  	} else {
  		if (end&7) {
  			end &= ~7;
  			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
  				skb->ip_summed = CHECKSUM_NONE;
  		}
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
378
  		if (end > qp->q.len) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  			/* Some bits beyond end -> corruption. */
bc578a54f   Joe Perches   [NET]: Rename ine...
380
  			if (qp->q.last_in & INET_FRAG_LAST_IN)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
  				goto err;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
382
  			qp->q.len = end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
385
386
  		}
  	}
  	if (end == offset)
  		goto err;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
387
  	err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
  	if (pskb_pull(skb, ihl) == NULL)
  		goto err;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
390
391
392
  
  	err = pskb_trim_rcsum(skb, end - offset);
  	if (err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
395
396
397
398
  		goto err;
  
  	/* 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...
399
400
401
402
403
  	prev = qp->q.fragments_tail;
  	if (!prev || FRAG_CB(prev)->offset < offset) {
  		next = NULL;
  		goto found;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  	prev = NULL;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
405
  	for (next = qp->q.fragments; next != NULL; next = next->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
  		if (FRAG_CB(next)->offset >= offset)
  			break;	/* bingo! */
  		prev = next;
  	}
d6bebca92   Changli Gao   fragment: add fas...
410
  found:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
412
413
414
415
416
417
418
419
  	/* We found where to put this one.  Check for overlap with
  	 * preceding fragment, and, if needed, align things so that
  	 * any overlaps are eliminated.
  	 */
  	if (prev) {
  		int i = (FRAG_CB(prev)->offset + prev->len) - offset;
  
  		if (i > 0) {
  			offset += i;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
420
  			err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
  			if (end <= offset)
  				goto err;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
423
  			err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
426
427
428
429
  			if (!pskb_pull(skb, i))
  				goto err;
  			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
  				skb->ip_summed = CHECKSUM_NONE;
  		}
  	}
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
430
  	err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
434
435
436
437
438
439
440
  	while (next && FRAG_CB(next)->offset < end) {
  		int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */
  
  		if (i < next->len) {
  			/* Eat head of the next overlapped fragment
  			 * and leave the loop. The next ones cannot overlap.
  			 */
  			if (!pskb_pull(next, i))
  				goto err;
  			FRAG_CB(next)->offset += i;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
441
  			qp->q.meat -= i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
443
444
445
446
  			if (next->ip_summed != CHECKSUM_UNNECESSARY)
  				next->ip_summed = CHECKSUM_NONE;
  			break;
  		} else {
  			struct sk_buff *free_it = next;
47c6bf776   Peter Zijlstra   fix typo in net/i...
447
  			/* Old fragment is completely overridden with
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
  			 * new one drop it.
  			 */
  			next = next->next;
  
  			if (prev)
  				prev->next = next;
  			else
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
455
  				qp->q.fragments = next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456

5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
457
  			qp->q.meat -= free_it->len;
a95d8c88b   Eric Dumazet   ipfrag : frag_kfr...
458
  			frag_kfree_skb(qp->q.net, free_it);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
462
463
464
465
  		}
  	}
  
  	FRAG_CB(skb)->offset = offset;
  
  	/* Insert this fragment in the chain of fragments. */
  	skb->next = next;
d6bebca92   Changli Gao   fragment: add fas...
466
467
  	if (!next)
  		qp->q.fragments_tail = skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
470
  	if (prev)
  		prev->next = skb;
  	else
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
471
  		qp->q.fragments = skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472

1706d5876   Herbert Xu   [IPV4]: Make ip_d...
473
474
475
476
477
  	dev = skb->dev;
  	if (dev) {
  		qp->iif = dev->ifindex;
  		skb->dev = NULL;
  	}
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
478
479
  	qp->q.stamp = skb->tstamp;
  	qp->q.meat += skb->len;
6623e3b24   Eric Dumazet   ipv4: IP defragme...
480
  	qp->ecn |= ecn;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
481
  	atomic_add(skb->truesize, &qp->q.net->mem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
  	if (offset == 0)
bc578a54f   Joe Perches   [NET]: Rename ine...
483
  		qp->q.last_in |= INET_FRAG_FIRST_IN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484

bc578a54f   Joe Perches   [NET]: Rename ine...
485
486
  	if (qp->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
  	    qp->q.meat == qp->q.len)
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
487
  		return ip_frag_reasm(qp, prev, dev);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
488
  	write_lock(&ip4_frags.lock);
3140c25c8   Pavel Emelyanov   [NETNS][FRAGS]: M...
489
  	list_move_tail(&qp->q.lru_list, &qp->q.net->lru_list);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
490
  	write_unlock(&ip4_frags.lock);
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
491
  	return -EINPROGRESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
493
494
  
  err:
  	kfree_skb(skb);
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
495
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
498
499
  }
  
  
  /* Build a new IP datagram from all its fragments. */
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
500
501
  static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
  			 struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
  {
2bad35b7c   Jorge Boncompte [DTI2]   netns: oops in ip...
503
  	struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
  	struct iphdr *iph;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
505
  	struct sk_buff *fp, *head = qp->q.fragments;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
  	int len;
  	int ihlen;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
508
  	int err;
5173cc057   Eric Dumazet   ipv4: more compli...
509
  	u8 ecn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
511
  
  	ipq_kill(qp);
5173cc057   Eric Dumazet   ipv4: more compli...
512
513
514
515
516
  	ecn = ip4_frag_ecn_table[qp->ecn];
  	if (unlikely(ecn == 0xff)) {
  		err = -EINVAL;
  		goto out_fail;
  	}
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
517
518
519
520
  	/* Make the one we just received the head. */
  	if (prev) {
  		head = prev->next;
  		fp = skb_clone(head, GFP_ATOMIC);
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
521
522
523
524
  		if (!fp)
  			goto out_nomem;
  
  		fp->next = head->next;
d6bebca92   Changli Gao   fragment: add fas...
525
526
  		if (!fp->next)
  			qp->q.fragments_tail = fp;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
527
  		prev->next = fp;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
528
529
  		skb_morph(head, qp->q.fragments);
  		head->next = qp->q.fragments->next;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
530

5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
531
532
  		kfree_skb(qp->q.fragments);
  		qp->q.fragments = head;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
533
  	}
547b792ca   Ilpo Järvinen   net: convert BUG_...
534
535
  	WARN_ON(head == NULL);
  	WARN_ON(FRAG_CB(head)->offset != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
536
537
  
  	/* Allocate a new buffer for the datagram. */
c9bdd4b52   Arnaldo Carvalho de Melo   [IP]: Introduce i...
538
  	ihlen = ip_hdrlen(head);
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
539
  	len = ihlen + qp->q.len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540

1706d5876   Herbert Xu   [IPV4]: Make ip_d...
541
  	err = -E2BIG;
132adf546   Stephen Hemminger   [IPV4]: cleanup
542
  	if (len > 65535)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
544
545
546
547
548
549
550
551
  		goto out_oversize;
  
  	/* Head of list must not be cloned. */
  	if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
  		goto out_nomem;
  
  	/* 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...
552
  	if (skb_has_frag_list(head)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
556
557
558
559
560
  		struct sk_buff *clone;
  		int i, plen = 0;
  
  		if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
  			goto out_nomem;
  		clone->next = head->next;
  		head->next = clone;
  		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
d7fcf1a5c   David S. Miller   ipv4: Use frag li...
561
  		skb_frag_list_init(head);
9e903e085   Eric Dumazet   net: add skb frag...
562
563
  		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
564
565
566
567
568
  		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...
569
  		atomic_add(clone->truesize, &qp->q.net->mem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
572
  	}
  
  	skb_shinfo(head)->frag_list = head->next;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
573
  	skb_push(head, head->data - skb_network_header(head));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
579
  
  	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...
580
  		else if (head->ip_summed == CHECKSUM_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
582
  			head->csum = csum_add(head->csum, fp->csum);
  		head->truesize += fp->truesize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
  	}
d27f9b358   Eric Dumazet   ip_frag: Remove s...
584
  	atomic_sub(head->truesize, &qp->q.net->mem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
586
587
  
  	head->next = NULL;
  	head->dev = dev;
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
588
  	head->tstamp = qp->q.stamp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589

eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
590
  	iph = ip_hdr(head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
592
  	iph->frag_off = 0;
  	iph->tot_len = htons(len);
5173cc057   Eric Dumazet   ipv4: more compli...
593
  	iph->tos |= ecn;
2bad35b7c   Jorge Boncompte [DTI2]   netns: oops in ip...
594
  	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
595
  	qp->q.fragments = NULL;
d6bebca92   Changli Gao   fragment: add fas...
596
  	qp->q.fragments_tail = NULL;
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
597
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
  
  out_nomem:
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
600
  	LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing "
64ce20730   Patrick McHardy   [NET]: Make NETDE...
601
602
  			      "queue %p
  ", qp);
45542479f   David Howells   [NET]: Fix uninit...
603
  	err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
606
  	goto out_fail;
  out_oversize:
  	if (net_ratelimit())
673d57e72   Harvey Harrison   net: replace NIPQ...
607
608
609
  		printk(KERN_INFO "Oversized IP packet from %pI4.
  ",
  			&qp->saddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
  out_fail:
bbf31bf18   David Ford   ipv4: additional ...
611
  	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
612
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
614
615
  }
  
  /* Process an incoming IP datagram fragment. */
776c729e8   Herbert Xu   [IPV4]: Change ip...
616
  int ip_defrag(struct sk_buff *skb, u32 user)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
  	struct ipq *qp;
ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
619
  	struct net *net;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
620

adf30907d   Eric Dumazet   net: skb->dst acc...
621
  	net = skb->dev ? dev_net(skb->dev) : dev_net(skb_dst(skb)->dev);
7c73a6faf   Pavel Emelyanov   mib: add net to I...
622
  	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMREQDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
624
  
  	/* Start by cleaning up the memory. */
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
625
  	if (atomic_read(&net->ipv4.frags.mem) > net->ipv4.frags.high_thresh)
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
626
  		ip_evictor(net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
  	/* Lookup (or create) queue header */
ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
629
  	if ((qp = ip_find(net, ip_hdr(skb), user)) != NULL) {
1706d5876   Herbert Xu   [IPV4]: Make ip_d...
630
  		int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631

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

1706d5876   Herbert Xu   [IPV4]: Make ip_d...
634
  		ret = ip_frag_queue(qp, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635

5ab11c98d   Pavel Emelyanov   [INET]: Move comm...
636
  		spin_unlock(&qp->q.lock);
4b6cb5d8e   Pavel Emelyanov   [INET]: Small cle...
637
  		ipq_put(qp);
776c729e8   Herbert Xu   [IPV4]: Change ip...
638
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
  	}
7c73a6faf   Pavel Emelyanov   mib: add net to I...
640
  	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
  	kfree_skb(skb);
776c729e8   Herbert Xu   [IPV4]: Change ip...
642
  	return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
644
  EXPORT_SYMBOL(ip_defrag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645

bc416d976   Eric Dumazet   macvlan: handle f...
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
  struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user)
  {
  	const struct iphdr *iph;
  	u32 len;
  
  	if (skb->protocol != htons(ETH_P_IP))
  		return skb;
  
  	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
  		return skb;
  
  	iph = ip_hdr(skb);
  	if (iph->ihl < 5 || iph->version != 4)
  		return skb;
  	if (!pskb_may_pull(skb, iph->ihl*4))
  		return skb;
  	iph = ip_hdr(skb);
  	len = ntohs(iph->tot_len);
  	if (skb->len < len || len < (iph->ihl * 4))
  		return skb;
  
  	if (ip_is_fragment(ip_hdr(skb))) {
  		skb = skb_share_check(skb, GFP_ATOMIC);
  		if (skb) {
  			if (pskb_trim_rcsum(skb, len))
  				return skb;
  			memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
  			if (ip_defrag(skb, user))
  				return NULL;
  			skb->rxhash = 0;
  		}
  	}
  	return skb;
  }
  EXPORT_SYMBOL(ip_check_defrag);
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
681
682
  #ifdef CONFIG_SYSCTL
  static int zero;
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
683
  static struct ctl_table ip4_frags_ns_ctl_table[] = {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
684
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
685
  		.procname	= "ipfrag_high_thresh",
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
686
  		.data		= &init_net.ipv4.frags.high_thresh,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
687
688
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
689
  		.proc_handler	= proc_dointvec
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
690
691
  	},
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
692
  		.procname	= "ipfrag_low_thresh",
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
693
  		.data		= &init_net.ipv4.frags.low_thresh,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
694
695
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
696
  		.proc_handler	= proc_dointvec
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
697
698
  	},
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
699
  		.procname	= "ipfrag_time",
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
700
  		.data		= &init_net.ipv4.frags.timeout,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
701
702
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
703
  		.proc_handler	= proc_dointvec_jiffies,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
704
  	},
7d291ebb8   Pavel Emelyanov   inet: Register fr...
705
706
707
708
  	{ }
  };
  
  static struct ctl_table ip4_frags_ctl_table[] = {
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
709
  	{
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
710
  		.procname	= "ipfrag_secret_interval",
3b4bc4a2b   Pavel Emelyanov   [NETNS][FRAGS]: I...
711
  		.data		= &ip4_frags.secret_interval,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
712
713
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
714
  		.proc_handler	= proc_dointvec_jiffies,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
715
716
717
718
719
720
  	},
  	{
  		.procname	= "ipfrag_max_dist",
  		.data		= &sysctl_ipfrag_max_dist,
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
721
  		.proc_handler	= proc_dointvec_minmax,
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
722
723
724
725
  		.extra1		= &zero
  	},
  	{ }
  };
2c8c1e729   Alexey Dobriyan   net: spread __net...
726
  static int __net_init ip4_frags_ns_ctl_register(struct net *net)
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
727
  {
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
728
  	struct ctl_table *table;
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
729
  	struct ctl_table_header *hdr;
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
730
  	table = ip4_frags_ns_ctl_table;
09ad9bc75   Octavian Purdila   net: use net_eq t...
731
  	if (!net_eq(net, &init_net)) {
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
732
  		table = kmemdup(table, sizeof(ip4_frags_ns_ctl_table), GFP_KERNEL);
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
733
734
  		if (table == NULL)
  			goto err_alloc;
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
735
736
  		table[0].data = &net->ipv4.frags.high_thresh;
  		table[1].data = &net->ipv4.frags.low_thresh;
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
737
  		table[2].data = &net->ipv4.frags.timeout;
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
738
739
740
741
742
743
744
745
746
747
  	}
  
  	hdr = register_net_sysctl_table(net, net_ipv4_ctl_path, table);
  	if (hdr == NULL)
  		goto err_reg;
  
  	net->ipv4.frags_hdr = hdr;
  	return 0;
  
  err_reg:
09ad9bc75   Octavian Purdila   net: use net_eq t...
748
  	if (!net_eq(net, &init_net))
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
749
750
751
752
  		kfree(table);
  err_alloc:
  	return -ENOMEM;
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
753
  static void __net_exit ip4_frags_ns_ctl_unregister(struct net *net)
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
754
755
756
757
758
759
  {
  	struct ctl_table *table;
  
  	table = net->ipv4.frags_hdr->ctl_table_arg;
  	unregister_net_sysctl_table(net->ipv4.frags_hdr);
  	kfree(table);
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
760
  }
7d291ebb8   Pavel Emelyanov   inet: Register fr...
761
762
763
764
765
  
  static void ip4_frags_ctl_register(void)
  {
  	register_net_sysctl_rotable(net_ipv4_ctl_path, ip4_frags_ctl_table);
  }
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
766
  #else
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
767
  static inline int ip4_frags_ns_ctl_register(struct net *net)
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
768
769
770
  {
  	return 0;
  }
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
771

0a64b4b81   Pavel Emelyanov   inet: Rename frag...
772
  static inline void ip4_frags_ns_ctl_unregister(struct net *net)
e4a2d5c2b   Pavel Emelyanov   [NETNS][FRAGS]: D...
773
774
  {
  }
7d291ebb8   Pavel Emelyanov   inet: Register fr...
775
776
777
778
  
  static inline void ip4_frags_ctl_register(void)
  {
  }
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
779
  #endif
2c8c1e729   Alexey Dobriyan   net: spread __net...
780
  static int __net_init ipv4_frags_init_net(struct net *net)
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
781
  {
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
782
  	/*
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
783
784
785
786
787
788
789
790
  	 * Fragment cache limits. We will commit 256K at one time. Should we
  	 * cross that limit we will prune down to 192K. This should cope with
  	 * even the most extreme cases without allowing an attacker to
  	 * measurably harm machine performance.
  	 */
  	net->ipv4.frags.high_thresh = 256 * 1024;
  	net->ipv4.frags.low_thresh = 192 * 1024;
  	/*
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
791
792
793
794
795
  	 * Important NOTE! Fragment queue must be destroyed before MSL expires.
  	 * RFC791 is wrong proposing to prolongate timer each fragment arrival
  	 * by TTL.
  	 */
  	net->ipv4.frags.timeout = IP_FRAG_TIME;
e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
796
  	inet_frags_init_net(&net->ipv4.frags);
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
797
  	return ip4_frags_ns_ctl_register(net);
8d8354d2f   Pavel Emelyanov   [NETNS][FRAGS]: M...
798
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
799
  static void __net_exit ipv4_frags_exit_net(struct net *net)
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
800
  {
0a64b4b81   Pavel Emelyanov   inet: Rename frag...
801
  	ip4_frags_ns_ctl_unregister(net);
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
802
803
804
805
806
807
808
  	inet_frags_exit_net(&net->ipv4.frags, &ip4_frags);
  }
  
  static struct pernet_operations ip4_frags_ops = {
  	.init = ipv4_frags_init_net,
  	.exit = ipv4_frags_exit_net,
  };
b7aa0bf70   Eric Dumazet   [NET]: convert ne...
809
  void __init ipfrag_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  {
7d291ebb8   Pavel Emelyanov   inet: Register fr...
811
  	ip4_frags_ctl_register();
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
812
  	register_pernet_subsys(&ip4_frags_ops);
321a3a99e   Pavel Emelyanov   [INET]: Consolida...
813
  	ip4_frags.hashfn = ip4_hashfn;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
814
  	ip4_frags.constructor = ip4_frag_init;
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
815
816
817
  	ip4_frags.destructor = ip4_frag_free;
  	ip4_frags.skb_free = NULL;
  	ip4_frags.qsize = sizeof(struct ipq);
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
818
  	ip4_frags.match = ip4_frag_match;
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
819
  	ip4_frags.frag_expire = ip_expire;
3b4bc4a2b   Pavel Emelyanov   [NETNS][FRAGS]: I...
820
  	ip4_frags.secret_interval = 10 * 60 * HZ;
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
821
  	inet_frags_init(&ip4_frags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
  }