inet_diag.c 22.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
 *
 * 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>
L
Linus Torvalds 已提交
30 31 32 33

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

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

36 37
static const struct inet_diag_handler **inet_diag_table;

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

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

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

52 53 54 55
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 已提交
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
nlmsg_failure:
	skb_trim(skb, b - skb->data);
156
	return -EMSGSIZE;
L
Linus Torvalds 已提交
157 158
}

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
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);
212
	return -EMSGSIZE;
213 214
}

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

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

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

	if (sk == NULL)
		return -ENOENT;

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

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

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

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

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

	bits &= 0x1f;

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

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

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

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

	return 1;
}


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return skb->len;

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

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

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

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

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

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

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

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

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

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

		s_reqnum = 0;
	}

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

	return err;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

808
static inline int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
809 810 811 812
{
	if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
		return 0;

813
	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX)
L
Linus Torvalds 已提交
814 815
		goto err_inval;

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

819
	if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len)
L
Linus Torvalds 已提交
820 821 822
		goto err_inval;

	if (nlh->nlmsg_flags&NLM_F_DUMP) {
823 824 825 826 827
		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 已提交
828
			    rta->rta_len < 8 ||
829 830 831
			    rta->rta_len >
			    (nlh->nlmsg_len -
			     NLMSG_SPACE(sizeof(struct inet_diag_req))))
L
Linus Torvalds 已提交
832
				goto err_inval;
833
			if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta)))
L
Linus Torvalds 已提交
834 835
				goto err_inval;
		}
836
		return netlink_dump_start(idiagnl, skb, nlh,
837
					  inet_diag_dump, NULL);
838
	} else
839
		return inet_diag_get_exact(skb, nlh);
L
Linus Torvalds 已提交
840 841 842 843 844 845

err_inval:
	return -EINVAL;
}


846
static inline void inet_diag_rcv_skb(struct sk_buff *skb)
L
Linus Torvalds 已提交
847 848
{
	if (skb->len >= NLMSG_SPACE(0)) {
849 850 851 852 853
		int err;
		struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;

		if (nlh->nlmsg_len < sizeof(*nlh) ||
		    skb->len < nlh->nlmsg_len)
L
Linus Torvalds 已提交
854
			return;
855
		err = inet_diag_rcv_msg(skb, nlh);
856
		if (err || nlh->nlmsg_flags & NLM_F_ACK)
L
Linus Torvalds 已提交
857 858 859 860
			netlink_ack(skb, nlh, err);
	}
}

861
static void inet_diag_rcv(struct sock *sk, int len)
L
Linus Torvalds 已提交
862 863
{
	struct sk_buff *skb;
864
	unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
L
Linus Torvalds 已提交
865

866
	while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) {
867
		inet_diag_rcv_skb(skb);
L
Linus Torvalds 已提交
868 869 870 871
		kfree_skb(skb);
	}
}

872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
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);

909
static int __init inet_diag_init(void)
L
Linus Torvalds 已提交
910
{
911 912 913 914
	const int inet_diag_table_size = (INET_DIAG_GETSOCK_MAX *
					  sizeof(struct inet_diag_handler *));
	int err = -ENOMEM;

915
	inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL);
916 917 918
	if (!inet_diag_table)
		goto out;

919
	idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv,
920 921
					THIS_MODULE);
	if (idiagnl == NULL)
922
		goto out_free_table;
923
	err = 0;
924 925 926 927 928
out:
	return err;
out_free_table:
	kfree(inet_diag_table);
	goto out;
L
Linus Torvalds 已提交
929 930
}

931
static void __exit inet_diag_exit(void)
L
Linus Torvalds 已提交
932
{
933
	sock_release(idiagnl->sk_socket);
934
	kfree(inet_diag_table);
L
Linus Torvalds 已提交
935 936
}

937 938
module_init(inet_diag_init);
module_exit(inet_diag_exit);
L
Linus Torvalds 已提交
939
MODULE_LICENSE("GPL");