inet_diag.c 21.9 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
 *
 * 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.
 */

14
#include <linux/kernel.h>
L
Linus Torvalds 已提交
15 16 17 18 19 20 21 22 23 24 25 26
#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>
31
#include <net/netlink.h>
L
Linus Torvalds 已提交
32 33 34 35

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

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

38 39
static const struct inet_diag_handler **inet_diag_table;

40
struct inet_diag_entry {
A
Al Viro 已提交
41 42
	__be32 *saddr;
	__be32 *daddr;
L
Linus Torvalds 已提交
43 44 45 46 47 48
	u16 sport;
	u16 dport;
	u16 family;
	u16 userlocks;
};

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

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

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

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

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

L
Linus Torvalds 已提交
74
	r = NLMSG_DATA(nlh);
75 76 77 78 79 80 81 82 83 84 85 86 87 88
	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 已提交
89
	}
90

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

96 97 98
	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 已提交
99

100 101 102 103
	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 已提交
104

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

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

116
#define EXPIRES_IN_MS(tmo)  DIV_ROUND_UP((tmo - jiffies) * 1000, HZ)
L
Linus Torvalds 已提交
117

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

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

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

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

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

152
	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
L
Linus Torvalds 已提交
153 154
	return skb->len;

155
rtattr_failure:
L
Linus Torvalds 已提交
156
nlmsg_failure:
157
	nlmsg_trim(skb, b);
158
	return -EMSGSIZE;
L
Linus Torvalds 已提交
159 160
}

161 162 163 164 165 166 167
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;
168
	const unsigned char *previous_tail = skb_tail_pointer(skb);
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
	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;
194
	r->idiag_expires      = DIV_ROUND_UP(tmo * 1000, HZ);
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
	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
210
	nlh->nlmsg_len = skb_tail_pointer(skb) - previous_tail;
211 212
	return skb->len;
nlmsg_failure:
213
	nlmsg_trim(skb, previous_tail);
214
	return -EMSGSIZE;
215 216
}

217 218 219 220 221 222 223 224 225 226 227
static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
			int ext, u32 pid, u32 seq, u16 nlmsg_flags,
			const struct nlmsghdr *unlh)
{
	if (sk->sk_state == TCP_TIME_WAIT)
		return inet_twsk_diag_fill((struct inet_timewait_sock *)sk,
					   skb, ext, pid, seq, nlmsg_flags,
					   unlh);
	return inet_csk_diag_fill(sk, skb, ext, pid, seq, nlmsg_flags, unlh);
}

228 229
static int inet_diag_get_exact(struct sk_buff *in_skb,
			       const struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
230 231 232
{
	int err;
	struct sock *sk;
233
	struct inet_diag_req *req = NLMSG_DATA(nlh);
L
Linus Torvalds 已提交
234
	struct sk_buff *rep;
235 236 237 238 239 240 241
	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;

242 243 244 245
	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 已提交
246
	}
247
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
248
	else if (req->idiag_family == AF_INET6) {
249
		sk = inet6_lookup(hashinfo,
250 251 252 253 254
				  (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 已提交
255 256 257 258 259 260 261 262 263 264
	}
#endif
	else {
		return -EINVAL;
	}

	if (sk == NULL)
		return -ENOENT;

	err = -ESTALE;
265 266 267 268
	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 已提交
269 270 271
		goto out;

	err = -ENOMEM;
272 273
	rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) +
				     sizeof(struct inet_diag_meminfo) +
274 275
				     handler->idiag_info_size + 64)),
			GFP_KERNEL);
L
Linus Torvalds 已提交
276 277 278
	if (!rep)
		goto out;

279 280 281 282 283 284 285 286
	err = sk_diag_fill(sk, rep, req->idiag_ext,
			   NETLINK_CB(in_skb).pid,
			   nlh->nlmsg_seq, 0, nlh);
	if (err < 0) {
		WARN_ON(err == -EMSGSIZE);
		kfree_skb(rep);
		goto out;
	}
287 288
	err = netlink_unicast(idiagnl, rep, NETLINK_CB(in_skb).pid,
			      MSG_DONTWAIT);
L
Linus Torvalds 已提交
289 290 291 292 293 294
	if (err > 0)
		err = 0;

out:
	if (sk) {
		if (sk->sk_state == TCP_TIME_WAIT)
295
			inet_twsk_put((struct inet_timewait_sock *)sk);
L
Linus Torvalds 已提交
296 297 298 299 300 301
		else
			sock_put(sk);
	}
	return err;
}

A
Al Viro 已提交
302
static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits)
L
Linus Torvalds 已提交
303 304 305 306 307 308 309 310 311 312
{
	int words = bits >> 5;

	bits &= 0x1f;

	if (words) {
		if (memcmp(a1, a2, words << 2))
			return 0;
	}
	if (bits) {
A
Al Viro 已提交
313 314
		__be32 w1, w2;
		__be32 mask;
L
Linus Torvalds 已提交
315 316 317 318 319 320 321 322 323 324 325 326 327 328

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

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

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

	return 1;
}


329
static int inet_diag_bc_run(const void *bc, int len,
330
			    const struct inet_diag_entry *entry)
L
Linus Torvalds 已提交
331 332 333
{
	while (len > 0) {
		int yes = 1;
334
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
335 336

		switch (op->code) {
337
		case INET_DIAG_BC_NOP:
L
Linus Torvalds 已提交
338
			break;
339
		case INET_DIAG_BC_JMP:
L
Linus Torvalds 已提交
340 341
			yes = 0;
			break;
342
		case INET_DIAG_BC_S_GE:
L
Linus Torvalds 已提交
343 344
			yes = entry->sport >= op[1].no;
			break;
345
		case INET_DIAG_BC_S_LE:
L
Linus Torvalds 已提交
346 347
			yes = entry->dport <= op[1].no;
			break;
348
		case INET_DIAG_BC_D_GE:
L
Linus Torvalds 已提交
349 350
			yes = entry->dport >= op[1].no;
			break;
351
		case INET_DIAG_BC_D_LE:
L
Linus Torvalds 已提交
352 353
			yes = entry->dport <= op[1].no;
			break;
354
		case INET_DIAG_BC_AUTO:
L
Linus Torvalds 已提交
355 356
			yes = !(entry->userlocks & SOCK_BINDPORT_LOCK);
			break;
357
		case INET_DIAG_BC_S_COND:
358 359
		case INET_DIAG_BC_D_COND: {
			struct inet_diag_hostcond *cond;
A
Al Viro 已提交
360
			__be32 *addr;
L
Linus Torvalds 已提交
361

362
			cond = (struct inet_diag_hostcond *)(op + 1);
L
Linus Torvalds 已提交
363
			if (cond->port != -1 &&
364
			    cond->port != (op->code == INET_DIAG_BC_S_COND ?
L
Linus Torvalds 已提交
365 366 367 368
					     entry->sport : entry->dport)) {
				yes = 0;
				break;
			}
369

L
Linus Torvalds 已提交
370 371 372
			if (cond->prefix_len == 0)
				break;

373
			if (op->code == INET_DIAG_BC_S_COND)
L
Linus Torvalds 已提交
374 375 376 377
				addr = entry->saddr;
			else
				addr = entry->daddr;

378 379
			if (bitstring_match(addr, cond->addr,
					    cond->prefix_len))
L
Linus Torvalds 已提交
380 381 382 383 384
				break;
			if (entry->family == AF_INET6 &&
			    cond->family == AF_INET) {
				if (addr[0] == 0 && addr[1] == 0 &&
				    addr[2] == htonl(0xffff) &&
385
				    bitstring_match(addr + 3, cond->addr,
386
						    cond->prefix_len))
L
Linus Torvalds 已提交
387 388 389 390 391 392 393
					break;
			}
			yes = 0;
			break;
		}
		}

394
		if (yes) {
L
Linus Torvalds 已提交
395 396 397 398 399 400 401 402 403 404 405 406 407
			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) {
408
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
409 410 411 412 413 414 415 416 417 418 419 420 421

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

422
static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
L
Linus Torvalds 已提交
423 424 425 426 427
{
	const unsigned char *bc = bytecode;
	int  len = bytecode_len;

	while (len > 0) {
428
		struct inet_diag_bc_op *op = (struct inet_diag_bc_op *)bc;
L
Linus Torvalds 已提交
429 430 431

//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len);
		switch (op->code) {
432 433 434 435 436 437 438
		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:
439
			if (op->yes < 4 || op->yes > len + 4)
L
Linus Torvalds 已提交
440
				return -EINVAL;
441
		case INET_DIAG_BC_JMP:
442
			if (op->no < 4 || op->no > len + 4)
L
Linus Torvalds 已提交
443 444
				return -EINVAL;
			if (op->no < len &&
445
			    !valid_cc(bytecode, bytecode_len, len - op->no))
L
Linus Torvalds 已提交
446 447
				return -EINVAL;
			break;
448
		case INET_DIAG_BC_NOP:
449
			if (op->yes < 4 || op->yes > len + 4)
L
Linus Torvalds 已提交
450 451 452 453 454
				return -EINVAL;
			break;
		default:
			return -EINVAL;
		}
455
		bc  += op->yes;
L
Linus Torvalds 已提交
456 457 458 459 460
		len -= op->yes;
	}
	return len == 0 ? 0 : -EINVAL;
}

461 462 463
static int inet_csk_diag_dump(struct sock *sk,
			      struct sk_buff *skb,
			      struct netlink_callback *cb)
L
Linus Torvalds 已提交
464
{
465
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
L
Linus Torvalds 已提交
466 467

	if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) {
468
		struct inet_diag_entry entry;
L
Linus Torvalds 已提交
469 470 471 472
		struct rtattr *bc = (struct rtattr *)(r + 1);
		struct inet_sock *inet = inet_sk(sk);

		entry.family = sk->sk_family;
473
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
		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;

489
		if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry))
L
Linus Torvalds 已提交
490 491 492
			return 0;
	}

493 494 495
	return inet_csk_diag_fill(sk, skb, r->idiag_ext,
				  NETLINK_CB(cb->skb).pid,
				  cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
L
Linus Torvalds 已提交
496 497
}

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
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);
523
		entry.userlocks = 0;
524 525 526 527 528 529 530 531 532 533

		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);
}

534
static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
535 536
			      struct request_sock *req, u32 pid, u32 seq,
			      const struct nlmsghdr *unlh)
L
Linus Torvalds 已提交
537
{
538
	const struct inet_request_sock *ireq = inet_rsk(req);
L
Linus Torvalds 已提交
539
	struct inet_sock *inet = inet_sk(sk);
540
	unsigned char *b = skb_tail_pointer(skb);
541
	struct inet_diag_msg *r;
L
Linus Torvalds 已提交
542 543 544
	struct nlmsghdr *nlh;
	long tmo;

545
	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
L
Linus Torvalds 已提交
546 547 548
	nlh->nlmsg_flags = NLM_F_MULTI;
	r = NLMSG_DATA(nlh);

549 550 551 552
	r->idiag_family = sk->sk_family;
	r->idiag_state = TCP_SYN_RECV;
	r->idiag_timer = 1;
	r->idiag_retrans = req->retrans;
L
Linus Torvalds 已提交
553

554 555 556
	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 已提交
557 558 559 560 561

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

562 563 564 565 566 567 568 569 570
	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;
571
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
572 573
	if (r->idiag_family == AF_INET6) {
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
574
			       &inet6_rsk(req)->loc_addr);
575
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
576
			       &inet6_rsk(req)->rmt_addr);
L
Linus Torvalds 已提交
577 578
	}
#endif
579
	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
L
Linus Torvalds 已提交
580 581 582 583

	return skb->len;

nlmsg_failure:
584
	nlmsg_trim(skb, b);
L
Linus Torvalds 已提交
585 586 587
	return -1;
}

588
static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
589
			       struct netlink_callback *cb)
L
Linus Torvalds 已提交
590
{
591 592
	struct inet_diag_entry entry;
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
593
	struct inet_connection_sock *icsk = inet_csk(sk);
594
	struct listen_sock *lopt;
L
Linus Torvalds 已提交
595 596 597 598 599 600 601 602 603 604 605 606 607 608
	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;

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

611
	lopt = icsk->icsk_accept_queue.listen_opt;
L
Linus Torvalds 已提交
612 613 614 615 616 617 618 619 620
	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;
	}

621
	for (j = s_j; j < lopt->nr_table_entries; j++) {
622
		struct request_sock *req, *head = lopt->syn_table[j];
L
Linus Torvalds 已提交
623 624 625

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

L
Linus Torvalds 已提交
628 629
			if (reqnum < s_reqnum)
				continue;
630 631
			if (r->id.idiag_dport != ireq->rmt_port &&
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
632 633 634 635
				continue;

			if (bc) {
				entry.saddr =
636
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
637
					(entry.family == AF_INET6) ?
638
					inet6_rsk(req)->loc_addr.s6_addr32 :
L
Linus Torvalds 已提交
639
#endif
640
					&ireq->loc_addr;
641
				entry.daddr =
642
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
643
					(entry.family == AF_INET6) ?
644
					inet6_rsk(req)->rmt_addr.s6_addr32 :
L
Linus Torvalds 已提交
645
#endif
646 647
					&ireq->rmt_addr;
				entry.dport = ntohs(ireq->rmt_port);
L
Linus Torvalds 已提交
648

649
				if (!inet_diag_bc_run(RTA_DATA(bc),
L
Linus Torvalds 已提交
650 651 652 653
						    RTA_PAYLOAD(bc), &entry))
					continue;
			}

654
			err = inet_diag_fill_req(skb, sk, req,
L
Linus Torvalds 已提交
655
					       NETLINK_CB(cb->skb).pid,
656
					       cb->nlh->nlmsg_seq, cb->nlh);
L
Linus Torvalds 已提交
657 658 659 660 661 662 663 664 665 666 667
			if (err < 0) {
				cb->args[3] = j + 1;
				cb->args[4] = reqnum;
				goto out;
			}
		}

		s_reqnum = 0;
	}

out:
668
	read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
L
Linus Torvalds 已提交
669 670 671 672

	return err;
}

673
static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
L
Linus Torvalds 已提交
674 675 676
{
	int i, num;
	int s_i, s_num;
677
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
678
	const struct inet_diag_handler *handler;
679
	struct inet_hashinfo *hashinfo;
L
Linus Torvalds 已提交
680

681 682 683
	handler = inet_diag_table[cb->nlh->nlmsg_type];
	BUG_ON(handler == NULL);
	hashinfo = handler->idiag_hashinfo;
684

L
Linus Torvalds 已提交
685 686
	s_i = cb->args[1];
	s_num = num = cb->args[2];
687

L
Linus Torvalds 已提交
688
	if (cb->args[0] == 0) {
689
		if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
L
Linus Torvalds 已提交
690
			goto skip_listen_ht;
691 692

		inet_listen_lock(hashinfo);
693
		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
L
Linus Torvalds 已提交
694 695 696 697
			struct sock *sk;
			struct hlist_node *node;

			num = 0;
698
			sk_for_each(sk, node, &hashinfo->listening_hash[i]) {
L
Linus Torvalds 已提交
699 700 701 702 703 704 705
				struct inet_sock *inet = inet_sk(sk);

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

706 707
				if (r->id.idiag_sport != inet->sport &&
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
708 709
					goto next_listen;

710 711
				if (!(r->idiag_states & TCPF_LISTEN) ||
				    r->id.idiag_dport ||
L
Linus Torvalds 已提交
712 713 714
				    cb->args[3] > 0)
					goto syn_recv;

715
				if (inet_csk_diag_dump(sk, skb, cb) < 0) {
716
					inet_listen_unlock(hashinfo);
L
Linus Torvalds 已提交
717 718 719 720
					goto done;
				}

syn_recv:
721
				if (!(r->idiag_states & TCPF_SYN_RECV))
L
Linus Torvalds 已提交
722 723
					goto next_listen;

724
				if (inet_diag_dump_reqs(skb, sk, cb) < 0) {
725
					inet_listen_unlock(hashinfo);
L
Linus Torvalds 已提交
726 727 728 729 730 731 732 733 734 735 736 737 738
					goto done;
				}

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

			s_num = 0;
			cb->args[3] = 0;
			cb->args[4] = 0;
		}
739
		inet_listen_unlock(hashinfo);
L
Linus Torvalds 已提交
740 741 742 743 744
skip_listen_ht:
		cb->args[0] = 1;
		s_i = num = s_num = 0;
	}

745
	if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
L
Linus Torvalds 已提交
746 747
		return skb->len;

748 749
	for (i = s_i; i < hashinfo->ehash_size; i++) {
		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
L
Linus Torvalds 已提交
750 751 752 753 754 755 756 757 758 759 760 761 762
		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;
763
			if (!(r->idiag_states & (1 << sk->sk_state)))
L
Linus Torvalds 已提交
764
				goto next_normal;
765 766
			if (r->id.idiag_sport != inet->sport &&
			    r->id.idiag_sport)
L
Linus Torvalds 已提交
767
				goto next_normal;
768 769
			if (r->id.idiag_dport != inet->dport &&
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
770
				goto next_normal;
771
			if (inet_csk_diag_dump(sk, skb, cb) < 0) {
L
Linus Torvalds 已提交
772 773 774 775 776 777 778
				read_unlock_bh(&head->lock);
				goto done;
			}
next_normal:
			++num;
		}

779
		if (r->idiag_states & TCPF_TIME_WAIT) {
780 781 782
			struct inet_timewait_sock *tw;

			inet_twsk_for_each(tw, node,
783
				    &head->twchain) {
L
Linus Torvalds 已提交
784 785 786

				if (num < s_num)
					goto next_dying;
787
				if (r->id.idiag_sport != tw->tw_sport &&
788
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
789
					goto next_dying;
790
				if (r->id.idiag_dport != tw->tw_dport &&
791
				    r->id.idiag_dport)
L
Linus Torvalds 已提交
792
					goto next_dying;
793
				if (inet_twsk_diag_dump(tw, skb, cb) < 0) {
L
Linus Torvalds 已提交
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
					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;
}

810
static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
811
{
812
	int hdrlen = sizeof(struct inet_diag_req);
L
Linus Torvalds 已提交
813

814 815 816
	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX ||
	    nlmsg_len(nlh) < hdrlen)
		return -EINVAL;
L
Linus Torvalds 已提交
817

818 819 820
	if (inet_diag_table[nlh->nlmsg_type] == NULL)
		return -ENOENT;

821 822 823
	if (nlh->nlmsg_flags & NLM_F_DUMP) {
		if (nlmsg_attrlen(nlh, hdrlen)) {
			struct nlattr *attr;
L
Linus Torvalds 已提交
824

825 826 827 828 829 830 831
			attr = nlmsg_find_attr(nlh, hdrlen,
					       INET_DIAG_REQ_BYTECODE);
			if (attr == NULL ||
			    nla_len(attr) < sizeof(struct inet_diag_bc_op) ||
			    inet_diag_bc_audit(nla_data(attr), nla_len(attr)))
				return -EINVAL;
		}
L
Linus Torvalds 已提交
832

833 834
		return netlink_dump_start(idiagnl, skb, nlh,
					  inet_diag_dump, NULL);
L
Linus Torvalds 已提交
835
	}
836 837

	return inet_diag_get_exact(skb, nlh);
L
Linus Torvalds 已提交
838 839
}

840 841
static DEFINE_MUTEX(inet_diag_mutex);

842
static void inet_diag_rcv(struct sock *sk, int len)
L
Linus Torvalds 已提交
843
{
844
	unsigned int qlen = 0;
L
Linus Torvalds 已提交
845

846
	do {
847
		mutex_lock(&inet_diag_mutex);
848
		netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg);
849
		mutex_unlock(&inet_diag_mutex);
850
	} while (qlen);
L
Linus Torvalds 已提交
851 852
}

853 854 855 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
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);

890
static int __init inet_diag_init(void)
L
Linus Torvalds 已提交
891
{
892 893 894 895
	const int inet_diag_table_size = (INET_DIAG_GETSOCK_MAX *
					  sizeof(struct inet_diag_handler *));
	int err = -ENOMEM;

896
	inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL);
897 898 899
	if (!inet_diag_table)
		goto out;

900 901
	idiagnl = netlink_kernel_create(&init_net, NETLINK_INET_DIAG, 0,
					inet_diag_rcv, NULL, THIS_MODULE);
902
	if (idiagnl == NULL)
903
		goto out_free_table;
904
	err = 0;
905 906 907 908 909
out:
	return err;
out_free_table:
	kfree(inet_diag_table);
	goto out;
L
Linus Torvalds 已提交
910 911
}

912
static void __exit inet_diag_exit(void)
L
Linus Torvalds 已提交
913
{
914
	sock_release(idiagnl->sk_socket);
915
	kfree(inet_diag_table);
L
Linus Torvalds 已提交
916 917
}

918 919
module_init(inet_diag_init);
module_exit(inet_diag_exit);
L
Linus Torvalds 已提交
920
MODULE_LICENSE("GPL");