inet_diag.c 20.2 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * inet_diag.c	Module for monitoring INET transport protocols sockets.
L
Linus Torvalds 已提交
3
 *
4
 * Version:	$Id: inet_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 *
 *	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.
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/random.h>
#include <linux/cache.h>
#include <linux/init.h>
#include <linux/time.h>

#include <net/icmp.h>
#include <net/tcp.h>
#include <net/ipv6.h>
#include <net/inet_common.h>
27 28 29 30
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
#include <net/inet_timewait_sock.h>
#include <net/inet6_hashtables.h>
L
Linus Torvalds 已提交
31 32 33 34

#include <linux/inet.h>
#include <linux/stddef.h>

35
#include <linux/inet_diag.h>
L
Linus Torvalds 已提交
36

37 38
static const struct inet_diag_handler **inet_diag_table;

39
struct inet_diag_entry {
L
Linus Torvalds 已提交
40 41 42 43 44 45 46 47
	u32 *saddr;
	u32 *daddr;
	u16 sport;
	u16 dport;
	u16 family;
	u16 userlocks;
};

48
static struct sock *idiagnl;
L
Linus Torvalds 已提交
49

50
#define INET_DIAG_PUT(skb, attrtype, attrlen) \
51
	RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
L
Linus Torvalds 已提交
52

53
static int inet_diag_fill(struct sk_buff *skb, struct sock *sk,
54 55
			  int ext, u32 pid, u32 seq, u16 nlmsg_flags,
			  const struct nlmsghdr *unlh)
L
Linus Torvalds 已提交
56
{
57 58
	const struct inet_sock *inet = inet_sk(sk);
	const struct inet_connection_sock *icsk = inet_csk(sk);
59
	struct inet_diag_msg *r;
L
Linus Torvalds 已提交
60
	struct nlmsghdr  *nlh;
61
	void *info = NULL;
62
	struct inet_diag_meminfo  *minfo = NULL;
L
Linus Torvalds 已提交
63
	unsigned char	 *b = skb->tail;
64 65 66 67
	const struct inet_diag_handler *handler;

	handler = inet_diag_table[unlh->nlmsg_type];
	BUG_ON(handler == NULL);
L
Linus Torvalds 已提交
68

69
	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
L
Linus Torvalds 已提交
70
	nlh->nlmsg_flags = nlmsg_flags;
71

L
Linus Torvalds 已提交
72 73
	r = NLMSG_DATA(nlh);
	if (sk->sk_state != TCP_TIME_WAIT) {
74 75 76 77 78
		if (ext & (1 << (INET_DIAG_MEMINFO - 1)))
			minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO,
					      sizeof(*minfo));
		if (ext & (1 << (INET_DIAG_INFO - 1)))
			info = INET_DIAG_PUT(skb, INET_DIAG_INFO,
79
					   handler->idiag_info_size);
80

81
		if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) {
82
			size_t len = strlen(icsk->icsk_ca_ops->name);
83
			strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1),
84
			       icsk->icsk_ca_ops->name);
85
		}
L
Linus Torvalds 已提交
86
	}
87 88 89 90
	r->idiag_family = sk->sk_family;
	r->idiag_state = sk->sk_state;
	r->idiag_timer = 0;
	r->idiag_retrans = 0;
L
Linus Torvalds 已提交
91

92 93 94
	r->id.idiag_if = sk->sk_bound_dev_if;
	r->id.idiag_cookie[0] = (u32)(unsigned long)sk;
	r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1);
L
Linus Torvalds 已提交
95

96
	if (r->idiag_state == TCP_TIME_WAIT) {
97
		const struct inet_timewait_sock *tw = inet_twsk(sk);
L
Linus Torvalds 已提交
98 99 100 101
		long tmo = tw->tw_ttd - jiffies;
		if (tmo < 0)
			tmo = 0;

102 103 104 105 106 107 108 109 110 111 112
		r->id.idiag_sport = tw->tw_sport;
		r->id.idiag_dport = tw->tw_dport;
		r->id.idiag_src[0] = tw->tw_rcv_saddr;
		r->id.idiag_dst[0] = tw->tw_daddr;
		r->idiag_state = tw->tw_substate;
		r->idiag_timer = 3;
		r->idiag_expires = (tmo * 1000 + HZ - 1) / HZ;
		r->idiag_rqueue = 0;
		r->idiag_wqueue = 0;
		r->idiag_uid = 0;
		r->idiag_inode = 0;
113
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
114
		if (r->idiag_family == AF_INET6) {
115
			const struct inet6_timewait_sock *tw6 = inet6_twsk(sk);
116

117
			ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
118
				       &tw6->tw_v6_rcv_saddr);
119
			ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
120
				       &tw6->tw_v6_daddr);
L
Linus Torvalds 已提交
121 122 123 124 125 126
		}
#endif
		nlh->nlmsg_len = skb->tail - b;
		return skb->len;
	}

127 128 129 130
	r->id.idiag_sport = inet->sport;
	r->id.idiag_dport = inet->dport;
	r->id.idiag_src[0] = inet->rcv_saddr;
	r->id.idiag_dst[0] = inet->daddr;
L
Linus Torvalds 已提交
131

132
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
133
	if (r->idiag_family == AF_INET6) {
L
Linus Torvalds 已提交
134 135
		struct ipv6_pinfo *np = inet6_sk(sk);

136
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
L
Linus Torvalds 已提交
137
			       &np->rcv_saddr);
138
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
L
Linus Torvalds 已提交
139 140 141 142
			       &np->daddr);
	}
#endif

143
#define EXPIRES_IN_MS(tmo)  ((tmo - jiffies) * 1000 + HZ - 1) / HZ
L
Linus Torvalds 已提交
144

145
	if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
146 147 148
		r->idiag_timer = 1;
		r->idiag_retrans = icsk->icsk_retransmits;
		r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
149
	} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
150 151 152
		r->idiag_timer = 4;
		r->idiag_retrans = icsk->icsk_probes_out;
		r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
L
Linus Torvalds 已提交
153
	} else if (timer_pending(&sk->sk_timer)) {
154 155 156
		r->idiag_timer = 2;
		r->idiag_retrans = icsk->icsk_probes_out;
		r->idiag_expires = EXPIRES_IN_MS(sk->sk_timer.expires);
L
Linus Torvalds 已提交
157
	} else {
158 159
		r->idiag_timer = 0;
		r->idiag_expires = 0;
L
Linus Torvalds 已提交
160 161
	}
#undef EXPIRES_IN_MS
162

163 164
	r->idiag_uid = sock_i_uid(sk);
	r->idiag_inode = sock_i_ino(sk);
L
Linus Torvalds 已提交
165 166

	if (minfo) {
167 168 169 170
		minfo->idiag_rmem = atomic_read(&sk->sk_rmem_alloc);
		minfo->idiag_wmem = sk->sk_wmem_queued;
		minfo->idiag_fmem = sk->sk_forward_alloc;
		minfo->idiag_tmem = atomic_read(&sk->sk_wmem_alloc);
L
Linus Torvalds 已提交
171 172
	}

173
	handler->idiag_get_info(sk, r, info);
L
Linus Torvalds 已提交
174

175 176 177
	if (sk->sk_state < TCP_TIME_WAIT &&
	    icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info)
		icsk->icsk_ca_ops->get_info(sk, ext, skb);
L
Linus Torvalds 已提交
178 179 180 181

	nlh->nlmsg_len = skb->tail - b;
	return skb->len;

182
rtattr_failure:
L
Linus Torvalds 已提交
183 184 185 186 187
nlmsg_failure:
	skb_trim(skb, b - skb->data);
	return -1;
}

188 189
static int inet_diag_get_exact(struct sk_buff *in_skb,
			       const struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
190 191 192
{
	int err;
	struct sock *sk;
193
	struct inet_diag_req *req = NLMSG_DATA(nlh);
L
Linus Torvalds 已提交
194
	struct sk_buff *rep;
195 196 197 198 199 200 201
	struct inet_hashinfo *hashinfo;
	const struct inet_diag_handler *handler;

	handler = inet_diag_table[nlh->nlmsg_type];
	BUG_ON(handler == NULL);
	hashinfo = handler->idiag_hashinfo;

202 203 204 205
	if (req->idiag_family == AF_INET) {
		sk = inet_lookup(hashinfo, req->id.idiag_dst[0],
				 req->id.idiag_dport, req->id.idiag_src[0],
				 req->id.idiag_sport, req->id.idiag_if);
L
Linus Torvalds 已提交
206
	}
207
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
208
	else if (req->idiag_family == AF_INET6) {
209
		sk = inet6_lookup(hashinfo,
210 211 212 213 214
				  (struct in6_addr *)req->id.idiag_dst,
				  req->id.idiag_dport,
				  (struct in6_addr *)req->id.idiag_src,
				  req->id.idiag_sport,
				  req->id.idiag_if);
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222 223 224
	}
#endif
	else {
		return -EINVAL;
	}

	if (sk == NULL)
		return -ENOENT;

	err = -ESTALE;
225 226 227 228
	if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE ||
	     req->id.idiag_cookie[1] != INET_DIAG_NOCOOKIE) &&
	    ((u32)(unsigned long)sk != req->id.idiag_cookie[0] ||
	     (u32)((((unsigned long)sk) >> 31) >> 1) != req->id.idiag_cookie[1]))
L
Linus Torvalds 已提交
229 230 231
		goto out;

	err = -ENOMEM;
232 233
	rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) +
				     sizeof(struct inet_diag_meminfo) +
234 235
				     handler->idiag_info_size + 64)),
			GFP_KERNEL);
L
Linus Torvalds 已提交
236 237 238
	if (!rep)
		goto out;

239
	if (inet_diag_fill(rep, sk, req->idiag_ext,
L
Linus Torvalds 已提交
240
			 NETLINK_CB(in_skb).pid,
241
			 nlh->nlmsg_seq, 0, nlh) <= 0)
L
Linus Torvalds 已提交
242 243
		BUG();

244 245
	err = netlink_unicast(idiagnl, rep, NETLINK_CB(in_skb).pid,
			      MSG_DONTWAIT);
L
Linus Torvalds 已提交
246 247 248 249 250 251
	if (err > 0)
		err = 0;

out:
	if (sk) {
		if (sk->sk_state == TCP_TIME_WAIT)
252
			inet_twsk_put((struct inet_timewait_sock *)sk);
L
Linus Torvalds 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
		else
			sock_put(sk);
	}
	return err;
}

static int bitstring_match(const u32 *a1, const u32 *a2, int bits)
{
	int words = bits >> 5;

	bits &= 0x1f;

	if (words) {
		if (memcmp(a1, a2, words << 2))
			return 0;
	}
	if (bits) {
		__u32 w1, w2;
		__u32 mask;

		w1 = a1[words];
		w2 = a2[words];

		mask = htonl((0xffffffff) << (32 - bits));

		if ((w1 ^ w2) & mask)
			return 0;
	}

	return 1;
}


286
static int inet_diag_bc_run(const void *bc, int len,
287
			    const struct inet_diag_entry *entry)
L
Linus Torvalds 已提交
288 289 290
{
	while (len > 0) {
		int yes = 1;
291
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
292 293

		switch (op->code) {
294
		case INET_DIAG_BC_NOP:
L
Linus Torvalds 已提交
295
			break;
296
		case INET_DIAG_BC_JMP:
L
Linus Torvalds 已提交
297 298
			yes = 0;
			break;
299
		case INET_DIAG_BC_S_GE:
L
Linus Torvalds 已提交
300 301
			yes = entry->sport >= op[1].no;
			break;
302
		case INET_DIAG_BC_S_LE:
L
Linus Torvalds 已提交
303 304
			yes = entry->dport <= op[1].no;
			break;
305
		case INET_DIAG_BC_D_GE:
L
Linus Torvalds 已提交
306 307
			yes = entry->dport >= op[1].no;
			break;
308
		case INET_DIAG_BC_D_LE:
L
Linus Torvalds 已提交
309 310
			yes = entry->dport <= op[1].no;
			break;
311
		case INET_DIAG_BC_AUTO:
L
Linus Torvalds 已提交
312 313
			yes = !(entry->userlocks & SOCK_BINDPORT_LOCK);
			break;
314
		case INET_DIAG_BC_S_COND:
315 316
		case INET_DIAG_BC_D_COND: {
			struct inet_diag_hostcond *cond;
L
Linus Torvalds 已提交
317 318
			u32 *addr;

319
			cond = (struct inet_diag_hostcond *)(op + 1);
L
Linus Torvalds 已提交
320
			if (cond->port != -1 &&
321
			    cond->port != (op->code == INET_DIAG_BC_S_COND ?
L
Linus Torvalds 已提交
322 323 324 325
					     entry->sport : entry->dport)) {
				yes = 0;
				break;
			}
326

L
Linus Torvalds 已提交
327 328 329
			if (cond->prefix_len == 0)
				break;

330
			if (op->code == INET_DIAG_BC_S_COND)
L
Linus Torvalds 已提交
331 332 333 334
				addr = entry->saddr;
			else
				addr = entry->daddr;

335 336
			if (bitstring_match(addr, cond->addr,
					    cond->prefix_len))
L
Linus Torvalds 已提交
337 338 339 340 341
				break;
			if (entry->family == AF_INET6 &&
			    cond->family == AF_INET) {
				if (addr[0] == 0 && addr[1] == 0 &&
				    addr[2] == htonl(0xffff) &&
342 343
				    bitstring_match(addr + 3, cond->addr,
					    	    cond->prefix_len))
L
Linus Torvalds 已提交
344 345 346 347 348 349 350
					break;
			}
			yes = 0;
			break;
		}
		}

351
		if (yes) {
L
Linus Torvalds 已提交
352 353 354 355 356 357 358 359 360 361 362 363 364
			len -= op->yes;
			bc += op->yes;
		} else {
			len -= op->no;
			bc += op->no;
		}
	}
	return (len == 0);
}

static int valid_cc(const void *bc, int len, int cc)
{
	while (len >= 0) {
365
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
366 367 368 369 370 371 372 373 374 375 376 377 378

		if (cc > len)
			return 0;
		if (cc == len)
			return 1;
		if (op->yes < 4)
			return 0;
		len -= op->yes;
		bc  += op->yes;
	}
	return 0;
}

379
static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
L
Linus Torvalds 已提交
380 381 382 383 384
{
	const unsigned char *bc = bytecode;
	int  len = bytecode_len;

	while (len > 0) {
385
		struct inet_diag_bc_op *op = (struct inet_diag_bc_op *)bc;
L
Linus Torvalds 已提交
386 387 388

//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len);
		switch (op->code) {
389 390 391 392 393 394 395
		case INET_DIAG_BC_AUTO:
		case INET_DIAG_BC_S_COND:
		case INET_DIAG_BC_D_COND:
		case INET_DIAG_BC_S_GE:
		case INET_DIAG_BC_S_LE:
		case INET_DIAG_BC_D_GE:
		case INET_DIAG_BC_D_LE:
396
			if (op->yes < 4 || op->yes > len + 4)
L
Linus Torvalds 已提交
397
				return -EINVAL;
398
		case INET_DIAG_BC_JMP:
399
			if (op->no < 4 || op->no > len + 4)
L
Linus Torvalds 已提交
400 401
				return -EINVAL;
			if (op->no < len &&
402
			    !valid_cc(bytecode, bytecode_len, len - op->no))
L
Linus Torvalds 已提交
403 404
				return -EINVAL;
			break;
405
		case INET_DIAG_BC_NOP:
406
			if (op->yes < 4 || op->yes > len + 4)
L
Linus Torvalds 已提交
407 408 409 410 411
				return -EINVAL;
			break;
		default:
			return -EINVAL;
		}
412
		bc  += op->yes;
L
Linus Torvalds 已提交
413 414 415 416 417
		len -= op->yes;
	}
	return len == 0 ? 0 : -EINVAL;
}

418
static int inet_diag_dump_sock(struct sk_buff *skb, struct sock *sk,
419
			       struct netlink_callback *cb)
L
Linus Torvalds 已提交
420
{
421
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
L
Linus Torvalds 已提交
422 423

	if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) {
424
		struct inet_diag_entry entry;
L
Linus Torvalds 已提交
425 426 427 428
		struct rtattr *bc = (struct rtattr *)(r + 1);
		struct inet_sock *inet = inet_sk(sk);

		entry.family = sk->sk_family;
429
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
		if (entry.family == AF_INET6) {
			struct ipv6_pinfo *np = inet6_sk(sk);

			entry.saddr = np->rcv_saddr.s6_addr32;
			entry.daddr = np->daddr.s6_addr32;
		} else
#endif
		{
			entry.saddr = &inet->rcv_saddr;
			entry.daddr = &inet->daddr;
		}
		entry.sport = inet->num;
		entry.dport = ntohs(inet->dport);
		entry.userlocks = sk->sk_userlocks;

445
		if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry))
L
Linus Torvalds 已提交
446 447 448
			return 0;
	}

449
	return inet_diag_fill(skb, sk, r->idiag_ext, NETLINK_CB(cb->skb).pid,
450
			      cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
L
Linus Torvalds 已提交
451 452
}

453
static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
454 455
			      struct request_sock *req, u32 pid, u32 seq,
			      const struct nlmsghdr *unlh)
L
Linus Torvalds 已提交
456
{
457
	const struct inet_request_sock *ireq = inet_rsk(req);
L
Linus Torvalds 已提交
458 459
	struct inet_sock *inet = inet_sk(sk);
	unsigned char *b = skb->tail;
460
	struct inet_diag_msg *r;
L
Linus Torvalds 已提交
461 462 463
	struct nlmsghdr *nlh;
	long tmo;

464
	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
L
Linus Torvalds 已提交
465 466 467
	nlh->nlmsg_flags = NLM_F_MULTI;
	r = NLMSG_DATA(nlh);

468 469 470 471
	r->idiag_family = sk->sk_family;
	r->idiag_state = TCP_SYN_RECV;
	r->idiag_timer = 1;
	r->idiag_retrans = req->retrans;
L
Linus Torvalds 已提交
472

473 474 475
	r->id.idiag_if = sk->sk_bound_dev_if;
	r->id.idiag_cookie[0] = (u32)(unsigned long)req;
	r->id.idiag_cookie[1] = (u32)(((unsigned long)req >> 31) >> 1);
L
Linus Torvalds 已提交
476 477 478 479 480

	tmo = req->expires - jiffies;
	if (tmo < 0)
		tmo = 0;

481 482 483 484 485 486 487 488 489
	r->id.idiag_sport = inet->sport;
	r->id.idiag_dport = ireq->rmt_port;
	r->id.idiag_src[0] = ireq->loc_addr;
	r->id.idiag_dst[0] = ireq->rmt_addr;
	r->idiag_expires = jiffies_to_msecs(tmo);
	r->idiag_rqueue = 0;
	r->idiag_wqueue = 0;
	r->idiag_uid = sock_i_uid(sk);
	r->idiag_inode = 0;
490
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
491 492
	if (r->idiag_family == AF_INET6) {
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
493
			       &inet6_rsk(req)->loc_addr);
494
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
495
			       &inet6_rsk(req)->rmt_addr);
L
Linus Torvalds 已提交
496 497 498 499 500 501 502 503 504 505 506
	}
#endif
	nlh->nlmsg_len = skb->tail - b;

	return skb->len;

nlmsg_failure:
	skb_trim(skb, b - skb->data);
	return -1;
}

507
static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
508
			       struct netlink_callback *cb)
L
Linus Torvalds 已提交
509
{
510 511
	struct inet_diag_entry entry;
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
512
	struct inet_connection_sock *icsk = inet_csk(sk);
513
	struct listen_sock *lopt;
L
Linus Torvalds 已提交
514 515 516 517 518 519 520 521 522 523 524 525 526 527
	struct rtattr *bc = NULL;
	struct inet_sock *inet = inet_sk(sk);
	int j, s_j;
	int reqnum, s_reqnum;
	int err = 0;

	s_j = cb->args[3];
	s_reqnum = cb->args[4];

	if (s_j > 0)
		s_j--;

	entry.family = sk->sk_family;

528
	read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
L
Linus Torvalds 已提交
529

530
	lopt = icsk->icsk_accept_queue.listen_opt;
L
Linus Torvalds 已提交
531 532 533 534 535 536 537 538 539
	if (!lopt || !lopt->qlen)
		goto out;

	if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) {
		bc = (struct rtattr *)(r + 1);
		entry.sport = inet->num;
		entry.userlocks = sk->sk_userlocks;
	}

540
	for (j = s_j; j < lopt->nr_table_entries; j++) {
541
		struct request_sock *req, *head = lopt->syn_table[j];
L
Linus Torvalds 已提交
542 543 544

		reqnum = 0;
		for (req = head; req; reqnum++, req = req->dl_next) {
545 546
			struct inet_request_sock *ireq = inet_rsk(req);

L
Linus Torvalds 已提交
547 548
			if (reqnum < s_reqnum)
				continue;
549 550
			if (r->id.idiag_dport != ireq->rmt_port &&
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
551 552 553 554
				continue;

			if (bc) {
				entry.saddr =
555
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
556
					(entry.family == AF_INET6) ?
557
					inet6_rsk(req)->loc_addr.s6_addr32 :
L
Linus Torvalds 已提交
558
#endif
559
					&ireq->loc_addr;
560
				entry.daddr =
561
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
562
					(entry.family == AF_INET6) ?
563
					inet6_rsk(req)->rmt_addr.s6_addr32 :
L
Linus Torvalds 已提交
564
#endif
565 566
					&ireq->rmt_addr;
				entry.dport = ntohs(ireq->rmt_port);
L
Linus Torvalds 已提交
567

568
				if (!inet_diag_bc_run(RTA_DATA(bc),
L
Linus Torvalds 已提交
569 570 571 572
						    RTA_PAYLOAD(bc), &entry))
					continue;
			}

573
			err = inet_diag_fill_req(skb, sk, req,
L
Linus Torvalds 已提交
574
					       NETLINK_CB(cb->skb).pid,
575
					       cb->nlh->nlmsg_seq, cb->nlh);
L
Linus Torvalds 已提交
576 577 578 579 580 581 582 583 584 585 586
			if (err < 0) {
				cb->args[3] = j + 1;
				cb->args[4] = reqnum;
				goto out;
			}
		}

		s_reqnum = 0;
	}

out:
587
	read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
L
Linus Torvalds 已提交
588 589 590 591

	return err;
}

592
static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
L
Linus Torvalds 已提交
593 594 595
{
	int i, num;
	int s_i, s_num;
596
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
597
	const struct inet_diag_handler *handler;
598
	struct inet_hashinfo *hashinfo;
L
Linus Torvalds 已提交
599

600 601 602
	handler = inet_diag_table[cb->nlh->nlmsg_type];
	BUG_ON(handler == NULL);
	hashinfo = handler->idiag_hashinfo;
603

L
Linus Torvalds 已提交
604 605
	s_i = cb->args[1];
	s_num = num = cb->args[2];
606

L
Linus Torvalds 已提交
607
	if (cb->args[0] == 0) {
608
		if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
L
Linus Torvalds 已提交
609
			goto skip_listen_ht;
610 611

		inet_listen_lock(hashinfo);
612
		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
L
Linus Torvalds 已提交
613 614 615 616
			struct sock *sk;
			struct hlist_node *node;

			num = 0;
617
			sk_for_each(sk, node, &hashinfo->listening_hash[i]) {
L
Linus Torvalds 已提交
618 619 620 621 622 623 624
				struct inet_sock *inet = inet_sk(sk);

				if (num < s_num) {
					num++;
					continue;
				}

625 626
				if (r->id.idiag_sport != inet->sport &&
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
627 628
					goto next_listen;

629 630
				if (!(r->idiag_states & TCPF_LISTEN) ||
				    r->id.idiag_dport ||
L
Linus Torvalds 已提交
631 632 633
				    cb->args[3] > 0)
					goto syn_recv;

634
				if (inet_diag_dump_sock(skb, sk, cb) < 0) {
635
					inet_listen_unlock(hashinfo);
L
Linus Torvalds 已提交
636 637 638 639
					goto done;
				}

syn_recv:
640
				if (!(r->idiag_states & TCPF_SYN_RECV))
L
Linus Torvalds 已提交
641 642
					goto next_listen;

643
				if (inet_diag_dump_reqs(skb, sk, cb) < 0) {
644
					inet_listen_unlock(hashinfo);
L
Linus Torvalds 已提交
645 646 647 648 649 650 651 652 653 654 655 656 657
					goto done;
				}

next_listen:
				cb->args[3] = 0;
				cb->args[4] = 0;
				++num;
			}

			s_num = 0;
			cb->args[3] = 0;
			cb->args[4] = 0;
		}
658
		inet_listen_unlock(hashinfo);
L
Linus Torvalds 已提交
659 660 661 662 663
skip_listen_ht:
		cb->args[0] = 1;
		s_i = num = s_num = 0;
	}

664
	if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
L
Linus Torvalds 已提交
665 666
		return skb->len;

667 668
	for (i = s_i; i < hashinfo->ehash_size; i++) {
		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
L
Linus Torvalds 已提交
669 670 671 672 673 674 675 676 677 678 679 680 681
		struct sock *sk;
		struct hlist_node *node;

		if (i > s_i)
			s_num = 0;

		read_lock_bh(&head->lock);
		num = 0;
		sk_for_each(sk, node, &head->chain) {
			struct inet_sock *inet = inet_sk(sk);

			if (num < s_num)
				goto next_normal;
682
			if (!(r->idiag_states & (1 << sk->sk_state)))
L
Linus Torvalds 已提交
683
				goto next_normal;
684 685
			if (r->id.idiag_sport != inet->sport &&
			    r->id.idiag_sport)
L
Linus Torvalds 已提交
686
				goto next_normal;
687 688
			if (r->id.idiag_dport != inet->dport &&
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
689
				goto next_normal;
690
			if (inet_diag_dump_sock(skb, sk, cb) < 0) {
L
Linus Torvalds 已提交
691 692 693 694 695 696 697
				read_unlock_bh(&head->lock);
				goto done;
			}
next_normal:
			++num;
		}

698
		if (r->idiag_states & TCPF_TIME_WAIT) {
L
Linus Torvalds 已提交
699
			sk_for_each(sk, node,
700
				    &hashinfo->ehash[i + hashinfo->ehash_size].chain) {
701
				const struct inet_timewait_sock *tw = inet_twsk(sk);
L
Linus Torvalds 已提交
702 703 704

				if (num < s_num)
					goto next_dying;
705
				if (r->id.idiag_sport != tw->tw_sport &&
706
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
707
					goto next_dying;
708
				if (r->id.idiag_dport != tw->tw_dport &&
709
				    r->id.idiag_dport)
L
Linus Torvalds 已提交
710
					goto next_dying;
711
				if (inet_diag_dump_sock(skb, sk, cb) < 0) {
L
Linus Torvalds 已提交
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
					read_unlock_bh(&head->lock);
					goto done;
				}
next_dying:
				++num;
			}
		}
		read_unlock_bh(&head->lock);
	}

done:
	cb->args[1] = i;
	cb->args[2] = num;
	return skb->len;
}

728
static inline int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
729 730 731 732
{
	if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
		return 0;

733
	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX)
L
Linus Torvalds 已提交
734 735
		goto err_inval;

736 737 738
	if (inet_diag_table[nlh->nlmsg_type] == NULL)
		return -ENOENT;

739
	if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len)
L
Linus Torvalds 已提交
740 741 742
		goto err_inval;

	if (nlh->nlmsg_flags&NLM_F_DUMP) {
743 744 745 746 747
		if (nlh->nlmsg_len >
		    (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) {
			struct rtattr *rta = (void *)(NLMSG_DATA(nlh) +
						 sizeof(struct inet_diag_req));
			if (rta->rta_type != INET_DIAG_REQ_BYTECODE ||
L
Linus Torvalds 已提交
748
			    rta->rta_len < 8 ||
749 750 751
			    rta->rta_len >
			    (nlh->nlmsg_len -
			     NLMSG_SPACE(sizeof(struct inet_diag_req))))
L
Linus Torvalds 已提交
752
				goto err_inval;
753
			if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta)))
L
Linus Torvalds 已提交
754 755
				goto err_inval;
		}
756
		return netlink_dump_start(idiagnl, skb, nlh,
757
					  inet_diag_dump, NULL);
758
	} else
759
		return inet_diag_get_exact(skb, nlh);
L
Linus Torvalds 已提交
760 761 762 763 764 765

err_inval:
	return -EINVAL;
}


766
static inline void inet_diag_rcv_skb(struct sk_buff *skb)
L
Linus Torvalds 已提交
767 768
{
	if (skb->len >= NLMSG_SPACE(0)) {
769 770 771 772 773
		int err;
		struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;

		if (nlh->nlmsg_len < sizeof(*nlh) ||
		    skb->len < nlh->nlmsg_len)
L
Linus Torvalds 已提交
774
			return;
775
		err = inet_diag_rcv_msg(skb, nlh);
776
		if (err || nlh->nlmsg_flags & NLM_F_ACK)
L
Linus Torvalds 已提交
777 778 779 780
			netlink_ack(skb, nlh, err);
	}
}

781
static void inet_diag_rcv(struct sock *sk, int len)
L
Linus Torvalds 已提交
782 783
{
	struct sk_buff *skb;
784
	unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
L
Linus Torvalds 已提交
785

786
	while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) {
787
		inet_diag_rcv_skb(skb);
L
Linus Torvalds 已提交
788 789 790 791
		kfree_skb(skb);
	}
}

792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
static DEFINE_SPINLOCK(inet_diag_register_lock);

int inet_diag_register(const struct inet_diag_handler *h)
{
	const __u16 type = h->idiag_type;
	int err = -EINVAL;

	if (type >= INET_DIAG_GETSOCK_MAX)
		goto out;

	spin_lock(&inet_diag_register_lock);
	err = -EEXIST;
	if (inet_diag_table[type] == NULL) {
		inet_diag_table[type] = h;
		err = 0;
	}
	spin_unlock(&inet_diag_register_lock);
out:
	return err;
}
EXPORT_SYMBOL_GPL(inet_diag_register);

void inet_diag_unregister(const struct inet_diag_handler *h)
{
	const __u16 type = h->idiag_type;

	if (type >= INET_DIAG_GETSOCK_MAX)
		return;

	spin_lock(&inet_diag_register_lock);
	inet_diag_table[type] = NULL;
	spin_unlock(&inet_diag_register_lock);

	synchronize_rcu();
}
EXPORT_SYMBOL_GPL(inet_diag_unregister);

829
static int __init inet_diag_init(void)
L
Linus Torvalds 已提交
830
{
831 832 833 834 835 836 837 838 839
	const int inet_diag_table_size = (INET_DIAG_GETSOCK_MAX *
					  sizeof(struct inet_diag_handler *));
	int err = -ENOMEM;

	inet_diag_table = kmalloc(inet_diag_table_size, GFP_KERNEL);
	if (!inet_diag_table)
		goto out;

	memset(inet_diag_table, 0, inet_diag_table_size);
840
	idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv,
841 842
					THIS_MODULE);
	if (idiagnl == NULL)
843
		goto out_free_table;
844
	err = 0;
845 846 847 848 849
out:
	return err;
out_free_table:
	kfree(inet_diag_table);
	goto out;
L
Linus Torvalds 已提交
850 851
}

852
static void __exit inet_diag_exit(void)
L
Linus Torvalds 已提交
853
{
854
	sock_release(idiagnl->sk_socket);
855
	kfree(inet_diag_table);
L
Linus Torvalds 已提交
856 857
}

858 859
module_init(inet_diag_init);
module_exit(inet_diag_exit);
L
Linus Torvalds 已提交
860
MODULE_LICENSE("GPL");