ip_fragment.c 17.2 KB
Newer Older
L
Linus Torvalds 已提交
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.
7
 *
L
Linus Torvalds 已提交
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * Version:	$Id: ip_fragment.c,v 1.59 2002/01/12 07:54:56 davem Exp $
 *
 * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
 *		Alan Cox <Alan.Cox@linux.org>
 *
 * 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.
 */

H
Herbert Xu 已提交
25
#include <linux/compiler.h>
L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
#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>
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/checksum.h>
H
Herbert Xu 已提交
41
#include <net/inetpeer.h>
42
#include <net/inet_frag.h>
L
Linus Torvalds 已提交
43 44 45 46 47 48 49 50 51 52
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/inet.h>
#include <linux/netfilter_ipv4.h>

/* 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
 */

53
static int sysctl_ipfrag_max_dist __read_mostly = 64;
H
Herbert Xu 已提交
54

L
Linus Torvalds 已提交
55 56 57 58 59 60 61 62 63 64
struct ipfrag_skb_cb
{
	struct inet_skb_parm	h;
	int			offset;
};

#define FRAG_CB(skb)	((struct ipfrag_skb_cb*)((skb)->cb))

/* Describe an entry in the "incomplete datagrams" queue. */
struct ipq {
65 66
	struct inet_frag_queue q;

L
Linus Torvalds 已提交
67
	u32		user;
68 69 70
	__be32		saddr;
	__be32		daddr;
	__be16		id;
L
Linus Torvalds 已提交
71
	u8		protocol;
H
Herbert Xu 已提交
72 73 74
	int             iif;
	unsigned int    rid;
	struct inet_peer *peer;
L
Linus Torvalds 已提交
75 76
};

77
static struct inet_frags ip4_frags;
L
Linus Torvalds 已提交
78

79
int ip_frag_nqueues(struct net *net)
80
{
81
	return net->ipv4.frags.nqueues;
82
}
L
Linus Torvalds 已提交
83

84
int ip_frag_mem(struct net *net)
85
{
86
	return atomic_read(&net->ipv4.frags.mem);
87
}
L
Linus Torvalds 已提交
88

89 90 91
static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
			 struct net_device *dev);

92 93 94 95 96
struct ip4_create_arg {
	struct iphdr *iph;
	u32 user;
};

97
static unsigned int ipqhashfn(__be16 id, __be32 saddr, __be32 daddr, u8 prot)
L
Linus Torvalds 已提交
98
{
99 100
	return jhash_3words((__force u32)id << 16 | prot,
			    (__force u32)saddr, (__force u32)daddr,
101
			    ip4_frags.rnd) & (INETFRAGS_HASHSZ - 1);
L
Linus Torvalds 已提交
102 103
}

104
static unsigned int ip4_hashfn(struct inet_frag_queue *q)
L
Linus Torvalds 已提交
105
{
106
	struct ipq *ipq;
L
Linus Torvalds 已提交
107

108 109
	ipq = container_of(q, struct ipq, q);
	return ipqhashfn(ipq->id, ipq->saddr, ipq->daddr, ipq->protocol);
L
Linus Torvalds 已提交
110 111
}

112 113 114 115 116 117 118 119 120 121 122 123 124
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);
	return (qp->id == arg->iph->id &&
			qp->saddr == arg->iph->saddr &&
			qp->daddr == arg->iph->daddr &&
			qp->protocol == arg->iph->protocol &&
			qp->user == arg->user);
}

L
Linus Torvalds 已提交
125
/* Memory Tracking Functions. */
126 127
static __inline__ void frag_kfree_skb(struct netns_frags *nf,
		struct sk_buff *skb, int *work)
L
Linus Torvalds 已提交
128 129 130
{
	if (work)
		*work -= skb->truesize;
131
	atomic_sub(skb->truesize, &nf->mem);
L
Linus Torvalds 已提交
132 133 134
	kfree_skb(skb);
}

135 136 137 138 139 140 141 142 143 144 145 146 147 148
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;
	qp->saddr = arg->iph->saddr;
	qp->daddr = arg->iph->daddr;
	qp->user = arg->user;
	qp->peer = sysctl_ipfrag_max_dist ?
		inet_getpeer(arg->iph->saddr, 1) : NULL;
}

149
static __inline__ void ip4_frag_free(struct inet_frag_queue *q)
L
Linus Torvalds 已提交
150
{
151 152 153 154 155
	struct ipq *qp;

	qp = container_of(q, struct ipq, q);
	if (qp->peer)
		inet_putpeer(qp->peer);
L
Linus Torvalds 已提交
156 157 158 159 160
}


/* Destruction primitives. */

161
static __inline__ void ipq_put(struct ipq *ipq)
L
Linus Torvalds 已提交
162
{
P
Pavel Emelyanov 已提交
163
	inet_frag_put(&ipq->q, &ip4_frags);
L
Linus Torvalds 已提交
164 165 166 167 168 169 170
}

/* Kill ipq entry. It is not destroyed immediately,
 * because caller (and someone more) holds reference count.
 */
static void ipq_kill(struct ipq *ipq)
{
171
	inet_frag_kill(&ipq->q, &ip4_frags);
L
Linus Torvalds 已提交
172 173
}

174
/* Memory limiting on fragments.  Evictor trashes the oldest
L
Linus Torvalds 已提交
175 176
 * fragment queue until we are back under the threshold.
 */
177
static void ip_evictor(struct net *net)
L
Linus Torvalds 已提交
178
{
179 180
	int evicted;

181
	evicted = inet_frag_evictor(&net->ipv4.frags, &ip4_frags);
182 183
	if (evicted)
		IP_ADD_STATS_BH(IPSTATS_MIB_REASMFAILS, evicted);
L
Linus Torvalds 已提交
184 185 186 187 188 189 190
}

/*
 * Oops, a fragment queue timed out.  Kill it and send an ICMP reply.
 */
static void ip_expire(unsigned long arg)
{
191 192 193
	struct ipq *qp;

	qp = container_of((struct inet_frag_queue *) arg, struct ipq, q);
L
Linus Torvalds 已提交
194

195
	spin_lock(&qp->q.lock);
L
Linus Torvalds 已提交
196

197
	if (qp->q.last_in & INET_FRAG_COMPLETE)
L
Linus Torvalds 已提交
198 199 200 201 202 203 204
		goto out;

	ipq_kill(qp);

	IP_INC_STATS_BH(IPSTATS_MIB_REASMTIMEOUT);
	IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);

205
	if ((qp->q.last_in & INET_FRAG_FIRST_IN) && qp->q.fragments != NULL) {
206
		struct sk_buff *head = qp->q.fragments;
207 208 209
		struct net *net;

		net = container_of(qp->q.net, struct net, ipv4.frags);
L
Linus Torvalds 已提交
210
		/* Send an ICMP "Fragment Reassembly Timeout" message. */
211
		if ((head->dev = dev_get_by_index(net, qp->iif)) != NULL) {
L
Linus Torvalds 已提交
212 213 214 215 216
			icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
			dev_put(head->dev);
		}
	}
out:
217
	spin_unlock(&qp->q.lock);
218
	ipq_put(qp);
L
Linus Torvalds 已提交
219 220
}

221 222 223
/* Find the correct entry in the "incomplete datagrams" queue for
 * this IP datagram, and create new one, if nothing is found.
 */
224
static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user)
L
Linus Torvalds 已提交
225
{
226 227
	struct inet_frag_queue *q;
	struct ip4_create_arg arg;
228
	unsigned int hash;
L
Linus Torvalds 已提交
229

230 231
	arg.iph = iph;
	arg.user = user;
232 233

	read_lock(&ip4_frags.lock);
234
	hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol);
L
Linus Torvalds 已提交
235

236
	q = inet_frag_find(&net->ipv4.frags, &ip4_frags, &arg, hash);
237 238
	if (q == NULL)
		goto out_nomem;
L
Linus Torvalds 已提交
239

240
	return container_of(q, struct ipq, q);
L
Linus Torvalds 已提交
241 242

out_nomem:
243
	LIMIT_NETDEBUG(KERN_ERR "ip_frag_create: no memory left !\n");
L
Linus Torvalds 已提交
244 245 246
	return NULL;
}

H
Herbert Xu 已提交
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
/* 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;

263
	rc = qp->q.fragments && (end - start) > max;
H
Herbert Xu 已提交
264 265 266 267 268 269 270 271 272 273 274 275

	if (rc) {
		IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
	}

	return rc;
}

static int ip_frag_reinit(struct ipq *qp)
{
	struct sk_buff *fp;

276
	if (!mod_timer(&qp->q.timer, jiffies + qp->q.net->timeout)) {
277
		atomic_inc(&qp->q.refcnt);
H
Herbert Xu 已提交
278 279 280
		return -ETIMEDOUT;
	}

281
	fp = qp->q.fragments;
H
Herbert Xu 已提交
282 283
	do {
		struct sk_buff *xp = fp->next;
284
		frag_kfree_skb(qp->q.net, fp, NULL);
H
Herbert Xu 已提交
285 286 287
		fp = xp;
	} while (fp);

288 289 290 291
	qp->q.last_in = 0;
	qp->q.len = 0;
	qp->q.meat = 0;
	qp->q.fragments = NULL;
H
Herbert Xu 已提交
292 293 294 295 296
	qp->iif = 0;

	return 0;
}

L
Linus Torvalds 已提交
297
/* Add new segment to existing queue. */
298
static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
L
Linus Torvalds 已提交
299 300
{
	struct sk_buff *prev, *next;
301
	struct net_device *dev;
L
Linus Torvalds 已提交
302 303
	int flags, offset;
	int ihl, end;
304
	int err = -ENOENT;
L
Linus Torvalds 已提交
305

306
	if (qp->q.last_in & INET_FRAG_COMPLETE)
L
Linus Torvalds 已提交
307 308
		goto err;

H
Herbert Xu 已提交
309
	if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) &&
310 311
	    unlikely(ip_frag_too_far(qp)) &&
	    unlikely(err = ip_frag_reinit(qp))) {
H
Herbert Xu 已提交
312 313 314 315
		ipq_kill(qp);
		goto err;
	}

316
	offset = ntohs(ip_hdr(skb)->frag_off);
L
Linus Torvalds 已提交
317 318 319
	flags = offset & ~IP_OFFSET;
	offset &= IP_OFFSET;
	offset <<= 3;		/* offset is in 8-byte chunks */
320
	ihl = ip_hdrlen(skb);
L
Linus Torvalds 已提交
321 322

	/* Determine the position of this fragment. */
323
	end = offset + skb->len - ihl;
324
	err = -EINVAL;
L
Linus Torvalds 已提交
325 326 327 328 329 330

	/* Is this the final fragment? */
	if ((flags & IP_MF) == 0) {
		/* If we already have some bits beyond end
		 * or have different end, the segment is corrrupted.
		 */
331
		if (end < qp->q.len ||
332
		    ((qp->q.last_in & INET_FRAG_LAST_IN) && end != qp->q.len))
L
Linus Torvalds 已提交
333
			goto err;
334
		qp->q.last_in |= INET_FRAG_LAST_IN;
335
		qp->q.len = end;
L
Linus Torvalds 已提交
336 337 338 339 340 341
	} else {
		if (end&7) {
			end &= ~7;
			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
				skb->ip_summed = CHECKSUM_NONE;
		}
342
		if (end > qp->q.len) {
L
Linus Torvalds 已提交
343
			/* Some bits beyond end -> corruption. */
344
			if (qp->q.last_in & INET_FRAG_LAST_IN)
L
Linus Torvalds 已提交
345
				goto err;
346
			qp->q.len = end;
L
Linus Torvalds 已提交
347 348 349 350 351
		}
	}
	if (end == offset)
		goto err;

352
	err = -ENOMEM;
L
Linus Torvalds 已提交
353 354
	if (pskb_pull(skb, ihl) == NULL)
		goto err;
355 356 357

	err = pskb_trim_rcsum(skb, end - offset);
	if (err)
L
Linus Torvalds 已提交
358 359 360 361 362 363 364
		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?
	 */
	prev = NULL;
365
	for (next = qp->q.fragments; next != NULL; next = next->next) {
L
Linus Torvalds 已提交
366 367 368 369 370 371 372 373 374 375 376 377 378 379
		if (FRAG_CB(next)->offset >= offset)
			break;	/* bingo! */
		prev = next;
	}

	/* 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;
380
			err = -EINVAL;
L
Linus Torvalds 已提交
381 382
			if (end <= offset)
				goto err;
383
			err = -ENOMEM;
L
Linus Torvalds 已提交
384 385 386 387 388 389 390
			if (!pskb_pull(skb, i))
				goto err;
			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
				skb->ip_summed = CHECKSUM_NONE;
		}
	}

391 392
	err = -ENOMEM;

L
Linus Torvalds 已提交
393 394 395 396 397 398 399 400 401 402
	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;
403
			qp->q.meat -= i;
L
Linus Torvalds 已提交
404 405 406 407 408 409
			if (next->ip_summed != CHECKSUM_UNNECESSARY)
				next->ip_summed = CHECKSUM_NONE;
			break;
		} else {
			struct sk_buff *free_it = next;

410
			/* Old fragment is completely overridden with
L
Linus Torvalds 已提交
411 412 413 414 415 416 417
			 * new one drop it.
			 */
			next = next->next;

			if (prev)
				prev->next = next;
			else
418
				qp->q.fragments = next;
L
Linus Torvalds 已提交
419

420
			qp->q.meat -= free_it->len;
421
			frag_kfree_skb(qp->q.net, free_it, NULL);
L
Linus Torvalds 已提交
422 423 424 425 426 427 428 429 430 431
		}
	}

	FRAG_CB(skb)->offset = offset;

	/* Insert this fragment in the chain of fragments. */
	skb->next = next;
	if (prev)
		prev->next = skb;
	else
432
		qp->q.fragments = skb;
L
Linus Torvalds 已提交
433

434 435 436 437 438
	dev = skb->dev;
	if (dev) {
		qp->iif = dev->ifindex;
		skb->dev = NULL;
	}
439 440
	qp->q.stamp = skb->tstamp;
	qp->q.meat += skb->len;
441
	atomic_add(skb->truesize, &qp->q.net->mem);
L
Linus Torvalds 已提交
442
	if (offset == 0)
443
		qp->q.last_in |= INET_FRAG_FIRST_IN;
L
Linus Torvalds 已提交
444

445 446
	if (qp->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
	    qp->q.meat == qp->q.len)
447 448
		return ip_frag_reasm(qp, prev, dev);

449
	write_lock(&ip4_frags.lock);
450
	list_move_tail(&qp->q.lru_list, &qp->q.net->lru_list);
451
	write_unlock(&ip4_frags.lock);
452
	return -EINPROGRESS;
L
Linus Torvalds 已提交
453 454 455

err:
	kfree_skb(skb);
456
	return err;
L
Linus Torvalds 已提交
457 458 459 460 461
}


/* Build a new IP datagram from all its fragments. */

462 463
static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
			 struct net_device *dev)
L
Linus Torvalds 已提交
464 465
{
	struct iphdr *iph;
466
	struct sk_buff *fp, *head = qp->q.fragments;
L
Linus Torvalds 已提交
467 468
	int len;
	int ihlen;
469
	int err;
L
Linus Torvalds 已提交
470 471 472

	ipq_kill(qp);

473 474 475 476 477 478 479 480 481 482
	/* Make the one we just received the head. */
	if (prev) {
		head = prev->next;
		fp = skb_clone(head, GFP_ATOMIC);
		if (!fp)
			goto out_nomem;

		fp->next = head->next;
		prev->next = fp;

483 484
		skb_morph(head, qp->q.fragments);
		head->next = qp->q.fragments->next;
485

486 487
		kfree_skb(qp->q.fragments);
		qp->q.fragments = head;
488 489
	}

L
Linus Torvalds 已提交
490 491 492 493
	BUG_TRAP(head != NULL);
	BUG_TRAP(FRAG_CB(head)->offset == 0);

	/* Allocate a new buffer for the datagram. */
494
	ihlen = ip_hdrlen(head);
495
	len = ihlen + qp->q.len;
L
Linus Torvalds 已提交
496

497
	err = -E2BIG;
S
Stephen Hemminger 已提交
498
	if (len > 65535)
L
Linus Torvalds 已提交
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
		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. */
	if (skb_shinfo(head)->frag_list) {
		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;
		skb_shinfo(head)->frag_list = NULL;
		for (i=0; i<skb_shinfo(head)->nr_frags; i++)
			plen += skb_shinfo(head)->frags[i].size;
		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;
525
		atomic_add(clone->truesize, &qp->q.net->mem);
L
Linus Torvalds 已提交
526 527 528
	}

	skb_shinfo(head)->frag_list = head->next;
529
	skb_push(head, head->data - skb_network_header(head));
530
	atomic_sub(head->truesize, &qp->q.net->mem);
L
Linus Torvalds 已提交
531 532 533 534 535 536

	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;
537
		else if (head->ip_summed == CHECKSUM_COMPLETE)
L
Linus Torvalds 已提交
538 539
			head->csum = csum_add(head->csum, fp->csum);
		head->truesize += fp->truesize;
540
		atomic_sub(fp->truesize, &qp->q.net->mem);
L
Linus Torvalds 已提交
541 542 543 544
	}

	head->next = NULL;
	head->dev = dev;
545
	head->tstamp = qp->q.stamp;
L
Linus Torvalds 已提交
546

547
	iph = ip_hdr(head);
L
Linus Torvalds 已提交
548 549 550
	iph->frag_off = 0;
	iph->tot_len = htons(len);
	IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
551
	qp->q.fragments = NULL;
552
	return 0;
L
Linus Torvalds 已提交
553 554

out_nomem:
555
	LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing "
556
			      "queue %p\n", qp);
557
	err = -ENOMEM;
L
Linus Torvalds 已提交
558 559 560 561
	goto out_fail;
out_oversize:
	if (net_ratelimit())
		printk(KERN_INFO
562
			"Oversized IP packet from " NIPQUAD_FMT ".\n",
L
Linus Torvalds 已提交
563 564 565
			NIPQUAD(qp->saddr));
out_fail:
	IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
566
	return err;
L
Linus Torvalds 已提交
567 568 569
}

/* Process an incoming IP datagram fragment. */
570
int ip_defrag(struct sk_buff *skb, u32 user)
L
Linus Torvalds 已提交
571 572
{
	struct ipq *qp;
573
	struct net *net;
574

L
Linus Torvalds 已提交
575 576
	IP_INC_STATS_BH(IPSTATS_MIB_REASMREQDS);

577
	net = skb->dev ? dev_net(skb->dev) : dev_net(skb->dst->dev);
L
Linus Torvalds 已提交
578
	/* Start by cleaning up the memory. */
579
	if (atomic_read(&net->ipv4.frags.mem) > net->ipv4.frags.high_thresh)
580
		ip_evictor(net);
L
Linus Torvalds 已提交
581 582

	/* Lookup (or create) queue header */
583
	if ((qp = ip_find(net, ip_hdr(skb), user)) != NULL) {
584
		int ret;
L
Linus Torvalds 已提交
585

586
		spin_lock(&qp->q.lock);
L
Linus Torvalds 已提交
587

588
		ret = ip_frag_queue(qp, skb);
L
Linus Torvalds 已提交
589

590
		spin_unlock(&qp->q.lock);
591
		ipq_put(qp);
592
		return ret;
L
Linus Torvalds 已提交
593 594 595 596
	}

	IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
	kfree_skb(skb);
597
	return -ENOMEM;
L
Linus Torvalds 已提交
598 599
}

600 601 602 603 604 605 606
#ifdef CONFIG_SYSCTL
static int zero;

static struct ctl_table ip4_frags_ctl_table[] = {
	{
		.ctl_name	= NET_IPV4_IPFRAG_HIGH_THRESH,
		.procname	= "ipfrag_high_thresh",
607
		.data		= &init_net.ipv4.frags.high_thresh,
608 609 610 611 612 613 614
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= &proc_dointvec
	},
	{
		.ctl_name	= NET_IPV4_IPFRAG_LOW_THRESH,
		.procname	= "ipfrag_low_thresh",
615
		.data		= &init_net.ipv4.frags.low_thresh,
616 617 618 619 620 621 622
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= &proc_dointvec
	},
	{
		.ctl_name	= NET_IPV4_IPFRAG_TIME,
		.procname	= "ipfrag_time",
623
		.data		= &init_net.ipv4.frags.timeout,
624 625 626 627 628 629 630 631
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= &proc_dointvec_jiffies,
		.strategy	= &sysctl_jiffies
	},
	{
		.ctl_name	= NET_IPV4_IPFRAG_SECRET_INTERVAL,
		.procname	= "ipfrag_secret_interval",
632
		.data		= &ip4_frags.secret_interval,
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= &proc_dointvec_jiffies,
		.strategy	= &sysctl_jiffies
	},
	{
		.procname	= "ipfrag_max_dist",
		.data		= &sysctl_ipfrag_max_dist,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= &proc_dointvec_minmax,
		.extra1		= &zero
	},
	{ }
};

static int ip4_frags_ctl_register(struct net *net)
{
651
	struct ctl_table *table;
652 653
	struct ctl_table_header *hdr;

654 655 656 657 658 659
	table = ip4_frags_ctl_table;
	if (net != &init_net) {
		table = kmemdup(table, sizeof(ip4_frags_ctl_table), GFP_KERNEL);
		if (table == NULL)
			goto err_alloc;

660 661
		table[0].data = &net->ipv4.frags.high_thresh;
		table[1].data = &net->ipv4.frags.low_thresh;
662
		table[2].data = &net->ipv4.frags.timeout;
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
		table[3].mode &= ~0222;
		table[4].mode &= ~0222;
	}

	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:
	if (net != &init_net)
		kfree(table);
err_alloc:
	return -ENOMEM;
}

static void ip4_frags_ctl_unregister(struct net *net)
{
	struct ctl_table *table;

	table = net->ipv4.frags_hdr->ctl_table_arg;
	unregister_net_sysctl_table(net->ipv4.frags_hdr);
	kfree(table);
688 689 690 691 692 693
}
#else
static inline int ip4_frags_ctl_register(struct net *net)
{
	return 0;
}
694 695 696 697

static inline void ip4_frags_ctl_unregister(struct net *net)
{
}
698 699 700 701
#endif

static int ipv4_frags_init_net(struct net *net)
{
702 703 704 705 706 707 708 709
	/*
	 * 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;
710 711 712 713 714 715 716
	/*
	 * 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;

717 718
	inet_frags_init_net(&net->ipv4.frags);

719 720 721
	return ip4_frags_ctl_register(net);
}

722 723 724 725 726 727 728 729 730 731 732
static void ipv4_frags_exit_net(struct net *net)
{
	ip4_frags_ctl_unregister(net);
	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,
};

733
void __init ipfrag_init(void)
L
Linus Torvalds 已提交
734
{
735
	register_pernet_subsys(&ip4_frags_ops);
736
	ip4_frags.hashfn = ip4_hashfn;
737
	ip4_frags.constructor = ip4_frag_init;
738 739 740
	ip4_frags.destructor = ip4_frag_free;
	ip4_frags.skb_free = NULL;
	ip4_frags.qsize = sizeof(struct ipq);
741
	ip4_frags.match = ip4_frag_match;
742
	ip4_frags.frag_expire = ip_expire;
743
	ip4_frags.secret_interval = 10 * 60 * HZ;
744
	inet_frags_init(&ip4_frags);
L
Linus Torvalds 已提交
745 746 747
}

EXPORT_SYMBOL(ip_defrag);