inet_diag.c 22.0 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
	r = NLMSG_DATA(nlh);
73 74 75 76 77 78 79 80 81 82 83 84 85 86
	BUG_ON(sk->sk_state == TCP_TIME_WAIT);

	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,
				     handler->idiag_info_size);

	if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) {
		const size_t len = strlen(icsk->icsk_ca_ops->name);

		strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1),
		       icsk->icsk_ca_ops->name);
L
Linus Torvalds 已提交
87
	}
88

89 90 91 92
	r->idiag_family = sk->sk_family;
	r->idiag_state = sk->sk_state;
	r->idiag_timer = 0;
	r->idiag_retrans = 0;
L
Linus Torvalds 已提交
93

94 95 96
	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 已提交
97

98 99 100 101
	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 已提交
102

103
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
104
	if (r->idiag_family == AF_INET6) {
L
Linus Torvalds 已提交
105 106
		struct ipv6_pinfo *np = inet6_sk(sk);

107
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
L
Linus Torvalds 已提交
108
			       &np->rcv_saddr);
109
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
L
Linus Torvalds 已提交
110 111 112 113
			       &np->daddr);
	}
#endif

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

116
	if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
117 118 119
		r->idiag_timer = 1;
		r->idiag_retrans = icsk->icsk_retransmits;
		r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
120
	} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
121 122 123
		r->idiag_timer = 4;
		r->idiag_retrans = icsk->icsk_probes_out;
		r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
L
Linus Torvalds 已提交
124
	} else if (timer_pending(&sk->sk_timer)) {
125 126 127
		r->idiag_timer = 2;
		r->idiag_retrans = icsk->icsk_probes_out;
		r->idiag_expires = EXPIRES_IN_MS(sk->sk_timer.expires);
L
Linus Torvalds 已提交
128
	} else {
129 130
		r->idiag_timer = 0;
		r->idiag_expires = 0;
L
Linus Torvalds 已提交
131 132
	}
#undef EXPIRES_IN_MS
133

134 135
	r->idiag_uid = sock_i_uid(sk);
	r->idiag_inode = sock_i_ino(sk);
L
Linus Torvalds 已提交
136 137

	if (minfo) {
138 139 140 141
		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 已提交
142 143
	}

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

146 147 148
	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 已提交
149 150 151 152

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

153
rtattr_failure:
L
Linus Torvalds 已提交
154 155 156 157 158
nlmsg_failure:
	skb_trim(skb, b - skb->data);
	return -1;
}

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
			       struct sk_buff *skb, int ext, u32 pid,
			       u32 seq, u16 nlmsg_flags,
			       const struct nlmsghdr *unlh)
{
	long tmo;
	struct inet_diag_msg *r;
	const unsigned char *previous_tail = skb->tail;
	struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq,
					 unlh->nlmsg_type, sizeof(*r));

	r = NLMSG_DATA(nlh);
	BUG_ON(tw->tw_state != TCP_TIME_WAIT);

	nlh->nlmsg_flags = nlmsg_flags;

	tmo = tw->tw_ttd - jiffies;
	if (tmo < 0)
		tmo = 0;

	r->idiag_family	      = tw->tw_family;
	r->idiag_state	      = tw->tw_state;
	r->idiag_timer	      = 0;
	r->idiag_retrans      = 0;
	r->id.idiag_if	      = tw->tw_bound_dev_if;
	r->id.idiag_cookie[0] = (u32)(unsigned long)tw;
	r->id.idiag_cookie[1] = (u32)(((unsigned long)tw >> 31) >> 1);
	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;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
	if (tw->tw_family == AF_INET6) {
		const struct inet6_timewait_sock *tw6 =
						inet6_twsk((struct sock *)tw);

		ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
			       &tw6->tw_v6_rcv_saddr);
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
			       &tw6->tw_v6_daddr);
	}
#endif
	nlh->nlmsg_len = skb->tail - previous_tail;
	return skb->len;
nlmsg_failure:
	skb_trim(skb, previous_tail - skb->data);
	return -1;
}

215 216
static int inet_diag_get_exact(struct sk_buff *in_skb,
			       const struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
217 218 219
{
	int err;
	struct sock *sk;
220
	struct inet_diag_req *req = NLMSG_DATA(nlh);
L
Linus Torvalds 已提交
221
	struct sk_buff *rep;
222 223 224 225 226 227 228
	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;

229 230 231 232
	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 已提交
233
	}
234
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
235
	else if (req->idiag_family == AF_INET6) {
236
		sk = inet6_lookup(hashinfo,
237 238 239 240 241
				  (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 已提交
242 243 244 245 246 247 248 249 250 251
	}
#endif
	else {
		return -EINVAL;
	}

	if (sk == NULL)
		return -ENOENT;

	err = -ESTALE;
252 253 254 255
	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 已提交
256 257 258
		goto out;

	err = -ENOMEM;
259 260
	rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) +
				     sizeof(struct inet_diag_meminfo) +
261 262
				     handler->idiag_info_size + 64)),
			GFP_KERNEL);
L
Linus Torvalds 已提交
263 264 265
	if (!rep)
		goto out;

266
	if (inet_diag_fill(rep, sk, req->idiag_ext,
L
Linus Torvalds 已提交
267
			 NETLINK_CB(in_skb).pid,
268
			 nlh->nlmsg_seq, 0, nlh) <= 0)
L
Linus Torvalds 已提交
269 270
		BUG();

271 272
	err = netlink_unicast(idiagnl, rep, NETLINK_CB(in_skb).pid,
			      MSG_DONTWAIT);
L
Linus Torvalds 已提交
273 274 275 276 277 278
	if (err > 0)
		err = 0;

out:
	if (sk) {
		if (sk->sk_state == TCP_TIME_WAIT)
279
			inet_twsk_put((struct inet_timewait_sock *)sk);
L
Linus Torvalds 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
		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;
}


313
static int inet_diag_bc_run(const void *bc, int len,
314
			    const struct inet_diag_entry *entry)
L
Linus Torvalds 已提交
315 316 317
{
	while (len > 0) {
		int yes = 1;
318
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
319 320

		switch (op->code) {
321
		case INET_DIAG_BC_NOP:
L
Linus Torvalds 已提交
322
			break;
323
		case INET_DIAG_BC_JMP:
L
Linus Torvalds 已提交
324 325
			yes = 0;
			break;
326
		case INET_DIAG_BC_S_GE:
L
Linus Torvalds 已提交
327 328
			yes = entry->sport >= op[1].no;
			break;
329
		case INET_DIAG_BC_S_LE:
L
Linus Torvalds 已提交
330 331
			yes = entry->dport <= op[1].no;
			break;
332
		case INET_DIAG_BC_D_GE:
L
Linus Torvalds 已提交
333 334
			yes = entry->dport >= op[1].no;
			break;
335
		case INET_DIAG_BC_D_LE:
L
Linus Torvalds 已提交
336 337
			yes = entry->dport <= op[1].no;
			break;
338
		case INET_DIAG_BC_AUTO:
L
Linus Torvalds 已提交
339 340
			yes = !(entry->userlocks & SOCK_BINDPORT_LOCK);
			break;
341
		case INET_DIAG_BC_S_COND:
342 343
		case INET_DIAG_BC_D_COND: {
			struct inet_diag_hostcond *cond;
L
Linus Torvalds 已提交
344 345
			u32 *addr;

346
			cond = (struct inet_diag_hostcond *)(op + 1);
L
Linus Torvalds 已提交
347
			if (cond->port != -1 &&
348
			    cond->port != (op->code == INET_DIAG_BC_S_COND ?
L
Linus Torvalds 已提交
349 350 351 352
					     entry->sport : entry->dport)) {
				yes = 0;
				break;
			}
353

L
Linus Torvalds 已提交
354 355 356
			if (cond->prefix_len == 0)
				break;

357
			if (op->code == INET_DIAG_BC_S_COND)
L
Linus Torvalds 已提交
358 359 360 361
				addr = entry->saddr;
			else
				addr = entry->daddr;

362 363
			if (bitstring_match(addr, cond->addr,
					    cond->prefix_len))
L
Linus Torvalds 已提交
364 365 366 367 368
				break;
			if (entry->family == AF_INET6 &&
			    cond->family == AF_INET) {
				if (addr[0] == 0 && addr[1] == 0 &&
				    addr[2] == htonl(0xffff) &&
369 370
				    bitstring_match(addr + 3, cond->addr,
					    	    cond->prefix_len))
L
Linus Torvalds 已提交
371 372 373 374 375 376 377
					break;
			}
			yes = 0;
			break;
		}
		}

378
		if (yes) {
L
Linus Torvalds 已提交
379 380 381 382 383 384 385 386 387 388 389 390 391
			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) {
392
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
393 394 395 396 397 398 399 400 401 402 403 404 405

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

406
static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
L
Linus Torvalds 已提交
407 408 409 410 411
{
	const unsigned char *bc = bytecode;
	int  len = bytecode_len;

	while (len > 0) {
412
		struct inet_diag_bc_op *op = (struct inet_diag_bc_op *)bc;
L
Linus Torvalds 已提交
413 414 415

//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len);
		switch (op->code) {
416 417 418 419 420 421 422
		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:
423
			if (op->yes < 4 || op->yes > len + 4)
L
Linus Torvalds 已提交
424
				return -EINVAL;
425
		case INET_DIAG_BC_JMP:
426
			if (op->no < 4 || op->no > len + 4)
L
Linus Torvalds 已提交
427 428
				return -EINVAL;
			if (op->no < len &&
429
			    !valid_cc(bytecode, bytecode_len, len - op->no))
L
Linus Torvalds 已提交
430 431
				return -EINVAL;
			break;
432
		case INET_DIAG_BC_NOP:
433
			if (op->yes < 4 || op->yes > len + 4)
L
Linus Torvalds 已提交
434 435 436 437 438
				return -EINVAL;
			break;
		default:
			return -EINVAL;
		}
439
		bc  += op->yes;
L
Linus Torvalds 已提交
440 441 442 443 444
		len -= op->yes;
	}
	return len == 0 ? 0 : -EINVAL;
}

445
static int inet_diag_dump_sock(struct sk_buff *skb, struct sock *sk,
446
			       struct netlink_callback *cb)
L
Linus Torvalds 已提交
447
{
448
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
L
Linus Torvalds 已提交
449 450

	if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) {
451
		struct inet_diag_entry entry;
L
Linus Torvalds 已提交
452 453 454 455
		struct rtattr *bc = (struct rtattr *)(r + 1);
		struct inet_sock *inet = inet_sk(sk);

		entry.family = sk->sk_family;
456
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
		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;

472
		if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry))
L
Linus Torvalds 已提交
473 474 475
			return 0;
	}

476
	return inet_diag_fill(skb, sk, r->idiag_ext, NETLINK_CB(cb->skb).pid,
477
			      cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
L
Linus Torvalds 已提交
478 479
}

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
			       struct sk_buff *skb,
			       struct netlink_callback *cb)
{
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);

	if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) {
		struct inet_diag_entry entry;
		struct rtattr *bc = (struct rtattr *)(r + 1);

		entry.family = tw->tw_family;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
		if (tw->tw_family == AF_INET6) {
			struct inet6_timewait_sock *tw6 =
						inet6_twsk((struct sock *)tw);
			entry.saddr = tw6->tw_v6_rcv_saddr.s6_addr32;
			entry.daddr = tw6->tw_v6_daddr.s6_addr32;
		} else
#endif
		{
			entry.saddr = &tw->tw_rcv_saddr;
			entry.daddr = &tw->tw_daddr;
		}
		entry.sport = tw->tw_num;
		entry.dport = ntohs(tw->tw_dport);
		entry.userlocks = 0; 

		if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry))
			return 0;
	}

	return inet_twsk_diag_fill(tw, skb, r->idiag_ext,
				   NETLINK_CB(cb->skb).pid,
				   cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
}

516
static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
517 518
			      struct request_sock *req, u32 pid, u32 seq,
			      const struct nlmsghdr *unlh)
L
Linus Torvalds 已提交
519
{
520
	const struct inet_request_sock *ireq = inet_rsk(req);
L
Linus Torvalds 已提交
521 522
	struct inet_sock *inet = inet_sk(sk);
	unsigned char *b = skb->tail;
523
	struct inet_diag_msg *r;
L
Linus Torvalds 已提交
524 525 526
	struct nlmsghdr *nlh;
	long tmo;

527
	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
L
Linus Torvalds 已提交
528 529 530
	nlh->nlmsg_flags = NLM_F_MULTI;
	r = NLMSG_DATA(nlh);

531 532 533 534
	r->idiag_family = sk->sk_family;
	r->idiag_state = TCP_SYN_RECV;
	r->idiag_timer = 1;
	r->idiag_retrans = req->retrans;
L
Linus Torvalds 已提交
535

536 537 538
	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 已提交
539 540 541 542 543

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

544 545 546 547 548 549 550 551 552
	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;
553
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
554 555
	if (r->idiag_family == AF_INET6) {
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
556
			       &inet6_rsk(req)->loc_addr);
557
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
558
			       &inet6_rsk(req)->rmt_addr);
L
Linus Torvalds 已提交
559 560 561 562 563 564 565 566 567 568 569
	}
#endif
	nlh->nlmsg_len = skb->tail - b;

	return skb->len;

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

570
static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
571
			       struct netlink_callback *cb)
L
Linus Torvalds 已提交
572
{
573 574
	struct inet_diag_entry entry;
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
575
	struct inet_connection_sock *icsk = inet_csk(sk);
576
	struct listen_sock *lopt;
L
Linus Torvalds 已提交
577 578 579 580 581 582 583 584 585 586 587 588 589 590
	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;

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

593
	lopt = icsk->icsk_accept_queue.listen_opt;
L
Linus Torvalds 已提交
594 595 596 597 598 599 600 601 602
	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;
	}

603
	for (j = s_j; j < lopt->nr_table_entries; j++) {
604
		struct request_sock *req, *head = lopt->syn_table[j];
L
Linus Torvalds 已提交
605 606 607

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

L
Linus Torvalds 已提交
610 611
			if (reqnum < s_reqnum)
				continue;
612 613
			if (r->id.idiag_dport != ireq->rmt_port &&
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
614 615 616 617
				continue;

			if (bc) {
				entry.saddr =
618
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
619
					(entry.family == AF_INET6) ?
620
					inet6_rsk(req)->loc_addr.s6_addr32 :
L
Linus Torvalds 已提交
621
#endif
622
					&ireq->loc_addr;
623
				entry.daddr =
624
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
625
					(entry.family == AF_INET6) ?
626
					inet6_rsk(req)->rmt_addr.s6_addr32 :
L
Linus Torvalds 已提交
627
#endif
628 629
					&ireq->rmt_addr;
				entry.dport = ntohs(ireq->rmt_port);
L
Linus Torvalds 已提交
630

631
				if (!inet_diag_bc_run(RTA_DATA(bc),
L
Linus Torvalds 已提交
632 633 634 635
						    RTA_PAYLOAD(bc), &entry))
					continue;
			}

636
			err = inet_diag_fill_req(skb, sk, req,
L
Linus Torvalds 已提交
637
					       NETLINK_CB(cb->skb).pid,
638
					       cb->nlh->nlmsg_seq, cb->nlh);
L
Linus Torvalds 已提交
639 640 641 642 643 644 645 646 647 648 649
			if (err < 0) {
				cb->args[3] = j + 1;
				cb->args[4] = reqnum;
				goto out;
			}
		}

		s_reqnum = 0;
	}

out:
650
	read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
L
Linus Torvalds 已提交
651 652 653 654

	return err;
}

655
static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
L
Linus Torvalds 已提交
656 657 658
{
	int i, num;
	int s_i, s_num;
659
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
660
	const struct inet_diag_handler *handler;
661
	struct inet_hashinfo *hashinfo;
L
Linus Torvalds 已提交
662

663 664 665
	handler = inet_diag_table[cb->nlh->nlmsg_type];
	BUG_ON(handler == NULL);
	hashinfo = handler->idiag_hashinfo;
666

L
Linus Torvalds 已提交
667 668
	s_i = cb->args[1];
	s_num = num = cb->args[2];
669

L
Linus Torvalds 已提交
670
	if (cb->args[0] == 0) {
671
		if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
L
Linus Torvalds 已提交
672
			goto skip_listen_ht;
673 674

		inet_listen_lock(hashinfo);
675
		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
L
Linus Torvalds 已提交
676 677 678 679
			struct sock *sk;
			struct hlist_node *node;

			num = 0;
680
			sk_for_each(sk, node, &hashinfo->listening_hash[i]) {
L
Linus Torvalds 已提交
681 682 683 684 685 686 687
				struct inet_sock *inet = inet_sk(sk);

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

688 689
				if (r->id.idiag_sport != inet->sport &&
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
690 691
					goto next_listen;

692 693
				if (!(r->idiag_states & TCPF_LISTEN) ||
				    r->id.idiag_dport ||
L
Linus Torvalds 已提交
694 695 696
				    cb->args[3] > 0)
					goto syn_recv;

697
				if (inet_diag_dump_sock(skb, sk, cb) < 0) {
698
					inet_listen_unlock(hashinfo);
L
Linus Torvalds 已提交
699 700 701 702
					goto done;
				}

syn_recv:
703
				if (!(r->idiag_states & TCPF_SYN_RECV))
L
Linus Torvalds 已提交
704 705
					goto next_listen;

706
				if (inet_diag_dump_reqs(skb, sk, cb) < 0) {
707
					inet_listen_unlock(hashinfo);
L
Linus Torvalds 已提交
708 709 710 711 712 713 714 715 716 717 718 719 720
					goto done;
				}

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

			s_num = 0;
			cb->args[3] = 0;
			cb->args[4] = 0;
		}
721
		inet_listen_unlock(hashinfo);
L
Linus Torvalds 已提交
722 723 724 725 726
skip_listen_ht:
		cb->args[0] = 1;
		s_i = num = s_num = 0;
	}

727
	if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
L
Linus Torvalds 已提交
728 729
		return skb->len;

730 731
	for (i = s_i; i < hashinfo->ehash_size; i++) {
		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
L
Linus Torvalds 已提交
732 733 734 735 736 737 738 739 740 741 742 743 744
		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;
745
			if (!(r->idiag_states & (1 << sk->sk_state)))
L
Linus Torvalds 已提交
746
				goto next_normal;
747 748
			if (r->id.idiag_sport != inet->sport &&
			    r->id.idiag_sport)
L
Linus Torvalds 已提交
749
				goto next_normal;
750 751
			if (r->id.idiag_dport != inet->dport &&
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
752
				goto next_normal;
753
			if (inet_diag_dump_sock(skb, sk, cb) < 0) {
L
Linus Torvalds 已提交
754 755 756 757 758 759 760
				read_unlock_bh(&head->lock);
				goto done;
			}
next_normal:
			++num;
		}

761
		if (r->idiag_states & TCPF_TIME_WAIT) {
762 763 764
			struct inet_timewait_sock *tw;

			inet_twsk_for_each(tw, node,
765
				    &hashinfo->ehash[i + hashinfo->ehash_size].chain) {
L
Linus Torvalds 已提交
766 767 768

				if (num < s_num)
					goto next_dying;
769
				if (r->id.idiag_sport != tw->tw_sport &&
770
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
771
					goto next_dying;
772
				if (r->id.idiag_dport != tw->tw_dport &&
773
				    r->id.idiag_dport)
L
Linus Torvalds 已提交
774
					goto next_dying;
775
				if (inet_twsk_diag_dump(tw, skb, cb) < 0) {
L
Linus Torvalds 已提交
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
					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;
}

792
static inline int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
793 794 795 796
{
	if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
		return 0;

797
	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX)
L
Linus Torvalds 已提交
798 799
		goto err_inval;

800 801 802
	if (inet_diag_table[nlh->nlmsg_type] == NULL)
		return -ENOENT;

803
	if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len)
L
Linus Torvalds 已提交
804 805 806
		goto err_inval;

	if (nlh->nlmsg_flags&NLM_F_DUMP) {
807 808 809 810 811
		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 已提交
812
			    rta->rta_len < 8 ||
813 814 815
			    rta->rta_len >
			    (nlh->nlmsg_len -
			     NLMSG_SPACE(sizeof(struct inet_diag_req))))
L
Linus Torvalds 已提交
816
				goto err_inval;
817
			if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta)))
L
Linus Torvalds 已提交
818 819
				goto err_inval;
		}
820
		return netlink_dump_start(idiagnl, skb, nlh,
821
					  inet_diag_dump, NULL);
822
	} else
823
		return inet_diag_get_exact(skb, nlh);
L
Linus Torvalds 已提交
824 825 826 827 828 829

err_inval:
	return -EINVAL;
}


830
static inline void inet_diag_rcv_skb(struct sk_buff *skb)
L
Linus Torvalds 已提交
831 832
{
	if (skb->len >= NLMSG_SPACE(0)) {
833 834 835 836 837
		int err;
		struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;

		if (nlh->nlmsg_len < sizeof(*nlh) ||
		    skb->len < nlh->nlmsg_len)
L
Linus Torvalds 已提交
838
			return;
839
		err = inet_diag_rcv_msg(skb, nlh);
840
		if (err || nlh->nlmsg_flags & NLM_F_ACK)
L
Linus Torvalds 已提交
841 842 843 844
			netlink_ack(skb, nlh, err);
	}
}

845
static void inet_diag_rcv(struct sock *sk, int len)
L
Linus Torvalds 已提交
846 847
{
	struct sk_buff *skb;
848
	unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
L
Linus Torvalds 已提交
849

850
	while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) {
851
		inet_diag_rcv_skb(skb);
L
Linus Torvalds 已提交
852 853 854 855
		kfree_skb(skb);
	}
}

856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
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);

893
static int __init inet_diag_init(void)
L
Linus Torvalds 已提交
894
{
895 896 897 898 899 900 901 902 903
	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);
904
	idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv,
905 906
					THIS_MODULE);
	if (idiagnl == NULL)
907
		goto out_free_table;
908
	err = 0;
909 910 911 912 913
out:
	return err;
out_free_table:
	kfree(inet_diag_table);
	goto out;
L
Linus Torvalds 已提交
914 915
}

916
static void __exit inet_diag_exit(void)
L
Linus Torvalds 已提交
917
{
918
	sock_release(idiagnl->sk_socket);
919
	kfree(inet_diag_table);
L
Linus Torvalds 已提交
920 921
}

922 923
module_init(inet_diag_init);
module_exit(inet_diag_exit);
L
Linus Torvalds 已提交
924
MODULE_LICENSE("GPL");