inet_diag.c 20.3 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);
L
Linus Torvalds 已提交
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 116
			const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk);

117
			ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
118
				       &tcp6tw->tw_v6_rcv_saddr);
119
			ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
120
				       &tcp6tw->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
static int inet_diag_get_exact(struct sk_buff *in_skb, const struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
189 190 191
{
	int err;
	struct sock *sk;
192
	struct inet_diag_req *req = NLMSG_DATA(nlh);
L
Linus Torvalds 已提交
193
	struct sk_buff *rep;
194 195 196 197 198 199 200
	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;

201 202 203 204
	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 已提交
205
	}
206
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
207
	else if (req->idiag_family == AF_INET6) {
208
		sk = inet6_lookup(hashinfo,
209 210 211 212 213
				  (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 已提交
214 215 216 217 218 219 220 221 222 223
	}
#endif
	else {
		return -EINVAL;
	}

	if (sk == NULL)
		return -ENOENT;

	err = -ESTALE;
224 225 226 227
	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 已提交
228 229 230
		goto out;

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

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

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

out:
	if (sk) {
		if (sk->sk_state == TCP_TIME_WAIT)
251
			inet_twsk_put((struct inet_timewait_sock *)sk);
L
Linus Torvalds 已提交
252 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
		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;
}


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

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

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

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

			if (bitstring_match(addr, cond->addr, cond->prefix_len))
				break;
			if (entry->family == AF_INET6 &&
			    cond->family == AF_INET) {
				if (addr[0] == 0 && addr[1] == 0 &&
				    addr[2] == htonl(0xffff) &&
340 341
				    bitstring_match(addr + 3, cond->addr,
					    	    cond->prefix_len))
L
Linus Torvalds 已提交
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
					break;
			}
			yes = 0;
			break;
		}
		}

		if (yes) { 
			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) {
363
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
364 365 366 367 368 369 370 371 372 373 374 375 376

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

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

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

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

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

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

		entry.family = sk->sk_family;
427
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
		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;

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

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

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

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

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

472 473 474
	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 已提交
475 476 477 478 479

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

480 481 482 483 484 485 486 487 488
	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;
489
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
490 491
	if (r->idiag_family == AF_INET6) {
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
492
			       &tcp6_rsk(req)->loc_addr);
493
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
494
			       &tcp6_rsk(req)->rmt_addr);
L
Linus Torvalds 已提交
495 496 497 498 499 500 501 502 503 504 505
	}
#endif
	nlh->nlmsg_len = skb->tail - b;

	return skb->len;

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

506
static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
L
Linus Torvalds 已提交
507 508
			     struct netlink_callback *cb)
{
509 510
	struct inet_diag_entry entry;
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
511
	struct inet_connection_sock *icsk = inet_csk(sk);
512
	struct listen_sock *lopt;
L
Linus Torvalds 已提交
513 514 515 516 517 518 519 520 521 522 523 524 525 526
	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;

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

529
	lopt = icsk->icsk_accept_queue.listen_opt;
L
Linus Torvalds 已提交
530 531 532 533 534 535 536 537 538
	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;
	}

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

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

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

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

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

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

		s_reqnum = 0;
	}

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

	return err;
}

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

599 600 601 602
	handler = inet_diag_table[cb->nlh->nlmsg_type];
	BUG_ON(handler == NULL);
	hashinfo = handler->idiag_hashinfo;
		
L
Linus Torvalds 已提交
603 604
	s_i = cb->args[1];
	s_num = num = cb->args[2];
605

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

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

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

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

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

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

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

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

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

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

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

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

666 667
	for (i = s_i; i < hashinfo->ehash_size; i++) {
		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
L
Linus Torvalds 已提交
668 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
			if (r->id.idiag_dport != inet->dport && r->id.idiag_dport)
L
Linus Torvalds 已提交
688
				goto next_normal;
689
			if (inet_diag_dump_sock(skb, sk, cb) < 0) {
L
Linus Torvalds 已提交
690 691 692 693 694 695 696
				read_unlock_bh(&head->lock);
				goto done;
			}
next_normal:
			++num;
		}

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

				if (num < s_num)
					goto next_dying;
704 705
				if (r->id.idiag_sport != inet->sport &&
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
706
					goto next_dying;
707 708
				if (r->id.idiag_dport != inet->dport &&
				    r->id.idiag_dport)
L
Linus Torvalds 已提交
709
					goto next_dying;
710
				if (inet_diag_dump_sock(skb, sk, cb) < 0) {
L
Linus Torvalds 已提交
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
					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;
}

727
static int inet_diag_dump_done(struct netlink_callback *cb)
L
Linus Torvalds 已提交
728 729 730 731 732 733
{
	return 0;
}


static __inline__ int
734
inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
735 736 737 738
{
	if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
		return 0;

739
	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX)
L
Linus Torvalds 已提交
740 741
		goto err_inval;

742 743 744
	if (inet_diag_table[nlh->nlmsg_type] == NULL)
		return -ENOENT;

745
	if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len)
L
Linus Torvalds 已提交
746 747 748
		goto err_inval;

	if (nlh->nlmsg_flags&NLM_F_DUMP) {
749 750 751 752 753
		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 已提交
754
			    rta->rta_len < 8 ||
755 756 757
			    rta->rta_len >
			    (nlh->nlmsg_len -
			     NLMSG_SPACE(sizeof(struct inet_diag_req))))
L
Linus Torvalds 已提交
758
				goto err_inval;
759
			if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta)))
L
Linus Torvalds 已提交
760 761
				goto err_inval;
		}
762 763 764
		return netlink_dump_start(idiagnl, skb, nlh,
					  inet_diag_dump,
					  inet_diag_dump_done);
L
Linus Torvalds 已提交
765
	} else {
766
		return inet_diag_get_exact(skb, nlh);
L
Linus Torvalds 已提交
767 768 769 770 771 772 773
	}

err_inval:
	return -EINVAL;
}


774
static inline void inet_diag_rcv_skb(struct sk_buff *skb)
L
Linus Torvalds 已提交
775 776 777 778 779 780 781 782
{
	int err;
	struct nlmsghdr * nlh;

	if (skb->len >= NLMSG_SPACE(0)) {
		nlh = (struct nlmsghdr *)skb->data;
		if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
			return;
783
		err = inet_diag_rcv_msg(skb, nlh);
L
Linus Torvalds 已提交
784 785 786 787 788
		if (err || nlh->nlmsg_flags & NLM_F_ACK) 
			netlink_ack(skb, nlh, err);
	}
}

789
static void inet_diag_rcv(struct sock *sk, int len)
L
Linus Torvalds 已提交
790 791
{
	struct sk_buff *skb;
792
	unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
L
Linus Torvalds 已提交
793

794
	while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) {
795
		inet_diag_rcv_skb(skb);
L
Linus Torvalds 已提交
796 797 798 799
		kfree_skb(skb);
	}
}

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 829 830 831 832 833 834 835 836
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);

837
static int __init inet_diag_init(void)
L
Linus Torvalds 已提交
838
{
839 840 841 842 843 844 845 846 847
	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);
848 849 850
	idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, inet_diag_rcv,
					THIS_MODULE);
	if (idiagnl == NULL)
851
		goto out_free_table;
852
	err = 0;
853 854 855 856 857
out:
	return err;
out_free_table:
	kfree(inet_diag_table);
	goto out;
L
Linus Torvalds 已提交
858 859
}

860
static void __exit inet_diag_exit(void)
L
Linus Torvalds 已提交
861
{
862
	sock_release(idiagnl->sk_socket);
863
	kfree(inet_diag_table);
L
Linus Torvalds 已提交
864 865
}

866 867
module_init(inet_diag_init);
module_exit(inet_diag_exit);
L
Linus Torvalds 已提交
868
MODULE_LICENSE("GPL");