inet_diag.c 21.7 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
 *
 * 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/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>
26 27 28 29
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
#include <net/inet_timewait_sock.h>
#include <net/inet6_hashtables.h>
30
#include <net/netlink.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 {
A
Al Viro 已提交
40 41
	__be32 *saddr;
	__be32 *daddr;
L
Linus Torvalds 已提交
42 43 44 45 46 47
	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 54 55 56
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 已提交
57
{
58 59
	const struct inet_sock *inet = inet_sk(sk);
	const struct inet_connection_sock *icsk = inet_csk(sk);
60
	struct inet_diag_msg *r;
L
Linus Torvalds 已提交
61
	struct nlmsghdr  *nlh;
62
	void *info = NULL;
63
	struct inet_diag_meminfo  *minfo = NULL;
64
	unsigned char	 *b = skb_tail_pointer(skb);
65 66 67 68
	const struct inet_diag_handler *handler;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

160 161 162 163 164 165 166
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;
167
	const unsigned char *previous_tail = skb_tail_pointer(skb);
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
	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
209
	nlh->nlmsg_len = skb_tail_pointer(skb) - previous_tail;
210 211
	return skb->len;
nlmsg_failure:
212
	nlmsg_trim(skb, previous_tail);
213
	return -EMSGSIZE;
214 215
}

216 217 218 219 220 221 222 223 224 225 226
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);
}

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

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

	if (sk == NULL)
		return -ENOENT;

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

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

278 279 280 281 282 283 284 285
	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;
	}
286 287
	err = netlink_unicast(idiagnl, rep, NETLINK_CB(in_skb).pid,
			      MSG_DONTWAIT);
L
Linus Torvalds 已提交
288 289 290 291 292 293
	if (err > 0)
		err = 0;

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

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

	bits &= 0x1f;

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

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

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

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

	return 1;
}


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

492 493 494
	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 已提交
495 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
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);
522
		entry.userlocks = 0;
523 524 525 526 527 528 529 530 531 532

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

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

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

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

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

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

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

	return skb->len;

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

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

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

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

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

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

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

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

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

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

		s_reqnum = 0;
	}

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

	return err;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

824 825 826 827 828 829 830
			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 已提交
831

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

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

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

843 844 845
	do {
		netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg);
	} while (qlen);
L
Linus Torvalds 已提交
846 847
}

848 849 850 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
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);

885
static int __init inet_diag_init(void)
L
Linus Torvalds 已提交
886
{
887 888 889 890
	const int inet_diag_table_size = (INET_DIAG_GETSOCK_MAX *
					  sizeof(struct inet_diag_handler *));
	int err = -ENOMEM;

891
	inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL);
892 893 894
	if (!inet_diag_table)
		goto out;

895
	idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv,
896
					NULL, THIS_MODULE);
897
	if (idiagnl == NULL)
898
		goto out_free_table;
899
	err = 0;
900 901 902 903 904
out:
	return err;
out_free_table:
	kfree(inet_diag_table);
	goto out;
L
Linus Torvalds 已提交
905 906
}

907
static void __exit inet_diag_exit(void)
L
Linus Torvalds 已提交
908
{
909
	sock_release(idiagnl->sk_socket);
910
	kfree(inet_diag_table);
L
Linus Torvalds 已提交
911 912
}

913 914
module_init(inet_diag_init);
module_exit(inet_diag_exit);
L
Linus Torvalds 已提交
915
MODULE_LICENSE("GPL");