inet_diag.c 21.7 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 * inet_diag.c	Module for monitoring INET transport protocols sockets.
Linus Torvalds's avatar
Linus Torvalds committed
3
 *
4
 * Version:	$Id: inet_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $
Linus Torvalds's avatar
Linus Torvalds committed
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>
Linus Torvalds's avatar
Linus Torvalds committed
31 32 33 34

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

35
#include <linux/inet_diag.h>
Linus Torvalds's avatar
Linus Torvalds committed
36

37 38
static const struct inet_diag_handler **inet_diag_table;

39
struct inet_diag_entry {
Al Viro's avatar
Al Viro committed
40 41
	__be32 *saddr;
	__be32 *daddr;
Linus Torvalds's avatar
Linus Torvalds committed
42 43 44 45 46 47
	u16 sport;
	u16 dport;
	u16 family;
	u16 userlocks;
};

48
static struct sock *idiagnl;
Linus Torvalds's avatar
Linus Torvalds committed
49

50
#define INET_DIAG_PUT(skb, attrtype, attrlen) \
51
	RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
69

70
	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
Linus Torvalds's avatar
Linus Torvalds committed
71
	nlh->nlmsg_flags = nlmsg_flags;
72

Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
103

104
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
105
	if (r->idiag_family == AF_INET6) {
Linus Torvalds's avatar
Linus Torvalds committed
106 107
		struct ipv6_pinfo *np = inet6_sk(sk);

108
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
Linus Torvalds's avatar
Linus Torvalds committed
109
			       &np->rcv_saddr);
110
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
Linus Torvalds's avatar
Linus Torvalds committed
111 112 113 114
			       &np->daddr);
	}
#endif

115
#define EXPIRES_IN_MS(tmo)  ((tmo - jiffies) * 1000 + HZ - 1) / HZ
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
129
	} else {
130 131
		r->idiag_timer = 0;
		r->idiag_expires = 0;
Linus Torvalds's avatar
Linus Torvalds committed
132 133
	}
#undef EXPIRES_IN_MS
134

135 136
	r->idiag_uid = sock_i_uid(sk);
	r->idiag_inode = sock_i_ino(sk);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
143 144
	}

145
	handler->idiag_get_info(sk, r, info);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
150

151
	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
Linus Torvalds's avatar
Linus Torvalds committed
152 153
	return skb->len;

154
rtattr_failure:
Linus Torvalds's avatar
Linus Torvalds committed
155
nlmsg_failure:
156
	nlmsg_trim(skb, b);
157
	return -EMSGSIZE;
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
229 230 231
{
	int err;
	struct sock *sk;
232
	struct inet_diag_req *req = NLMSG_DATA(nlh);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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]))
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
295 296 297 298 299 300
		else
			sock_put(sk);
	}
	return err;
}

Al Viro's avatar
Al Viro committed
301
static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits)
Linus Torvalds's avatar
Linus Torvalds committed
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) {
Al Viro's avatar
Al Viro committed
312 313
		__be32 w1, w2;
		__be32 mask;
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
330 331 332
{
	while (len > 0) {
		int yes = 1;
333
		const struct inet_diag_bc_op *op = bc;
Linus Torvalds's avatar
Linus Torvalds committed
334 335

		switch (op->code) {
336
		case INET_DIAG_BC_NOP:
Linus Torvalds's avatar
Linus Torvalds committed
337
			break;
338
		case INET_DIAG_BC_JMP:
Linus Torvalds's avatar
Linus Torvalds committed
339 340
			yes = 0;
			break;
341
		case INET_DIAG_BC_S_GE:
Linus Torvalds's avatar
Linus Torvalds committed
342 343
			yes = entry->sport >= op[1].no;
			break;
344
		case INET_DIAG_BC_S_LE:
Linus Torvalds's avatar
Linus Torvalds committed
345 346
			yes = entry->dport <= op[1].no;
			break;
347
		case INET_DIAG_BC_D_GE:
Linus Torvalds's avatar
Linus Torvalds committed
348 349
			yes = entry->dport >= op[1].no;
			break;
350
		case INET_DIAG_BC_D_LE:
Linus Torvalds's avatar
Linus Torvalds committed
351 352
			yes = entry->dport <= op[1].no;
			break;
353
		case INET_DIAG_BC_AUTO:
Linus Torvalds's avatar
Linus Torvalds committed
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;
Al Viro's avatar
Al Viro committed
359
			__be32 *addr;
Linus Torvalds's avatar
Linus Torvalds committed
360

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

Linus Torvalds's avatar
Linus Torvalds committed
369 370 371
			if (cond->prefix_len == 0)
				break;

372
			if (op->code == INET_DIAG_BC_S_COND)
Linus Torvalds's avatar
Linus Torvalds committed
373 374 375 376
				addr = entry->saddr;
			else
				addr = entry->daddr;

377 378
			if (bitstring_match(addr, cond->addr,
					    cond->prefix_len))
Linus Torvalds's avatar
Linus Torvalds committed
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))
Linus Torvalds's avatar
Linus Torvalds committed
386 387 388 389 390 391 392
					break;
			}
			yes = 0;
			break;
		}
		}

393
		if (yes) {
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
439
				return -EINVAL;
440
		case INET_DIAG_BC_JMP:
441
			if (op->no < 4 || op->no > len + 4)
Linus Torvalds's avatar
Linus Torvalds committed
442 443
				return -EINVAL;
			if (op->no < len &&
444
			    !valid_cc(bytecode, bytecode_len, len - op->no))
Linus Torvalds's avatar
Linus Torvalds committed
445 446
				return -EINVAL;
			break;
447
		case INET_DIAG_BC_NOP:
448
			if (op->yes < 4 || op->yes > len + 4)
Linus Torvalds's avatar
Linus Torvalds committed
449 450 451 452 453
				return -EINVAL;
			break;
		default:
			return -EINVAL;
		}
454
		bc  += op->yes;
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
463
{
464
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
Linus Torvalds's avatar
Linus Torvalds committed
465 466

	if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) {
467
		struct inet_diag_entry entry;
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
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))
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
536
{
537
	const struct inet_request_sock *ireq = inet_rsk(req);
Linus Torvalds's avatar
Linus Torvalds committed
538
	struct inet_sock *inet = inet_sk(sk);
539
	unsigned char *b = skb_tail_pointer(skb);
540
	struct inet_diag_msg *r;
Linus Torvalds's avatar
Linus Torvalds committed
541 542 543
	struct nlmsghdr *nlh;
	long tmo;

544
	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
576 577
	}
#endif
578
	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
Linus Torvalds's avatar
Linus Torvalds committed
579 580 581 582

	return skb->len;

nlmsg_failure:
583
	nlmsg_trim(skb, b);
Linus Torvalds's avatar
Linus Torvalds committed
584 585 586
	return -1;
}

587
static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
588
			       struct netlink_callback *cb)
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
609

610
	lopt = icsk->icsk_accept_queue.listen_opt;
Linus Torvalds's avatar
Linus Torvalds committed
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];
Linus Torvalds's avatar
Linus Torvalds committed
622 623 624

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

Linus Torvalds's avatar
Linus Torvalds committed
627 628
			if (reqnum < s_reqnum)
				continue;
629 630
			if (r->id.idiag_dport != ireq->rmt_port &&
			    r->id.idiag_dport)
Linus Torvalds's avatar
Linus Torvalds committed
631 632 633 634
				continue;

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

648
				if (!inet_diag_bc_run(RTA_DATA(bc),
Linus Torvalds's avatar
Linus Torvalds committed
649 650 651 652
						    RTA_PAYLOAD(bc), &entry))
					continue;
			}

653
			err = inet_diag_fill_req(skb, sk, req,
Linus Torvalds's avatar
Linus Torvalds committed
654
					       NETLINK_CB(cb->skb).pid,
655
					       cb->nlh->nlmsg_seq, cb->nlh);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
668 669 670 671

	return err;
}

672
static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
679

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

Linus Torvalds's avatar
Linus Torvalds committed
684 685
	s_i = cb->args[1];
	s_num = num = cb->args[2];
686

Linus Torvalds's avatar
Linus Torvalds committed
687
	if (cb->args[0] == 0) {
688
		if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
Linus Torvalds's avatar
Linus Torvalds committed
689
			goto skip_listen_ht;
690 691

		inet_listen_lock(hashinfo);
692
		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
693 694 695 696
			struct sock *sk;
			struct hlist_node *node;

			num = 0;
697
			sk_for_each(sk, node, &hashinfo->listening_hash[i]) {
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
707 708
					goto next_listen;

709 710
				if (!(r->idiag_states & TCPF_LISTEN) ||
				    r->id.idiag_dport ||
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
716 717 718 719
					goto done;
				}

syn_recv:
720
				if (!(r->idiag_states & TCPF_SYN_RECV))
Linus Torvalds's avatar
Linus Torvalds committed
721 722
					goto next_listen;

723
				if (inet_diag_dump_reqs(skb, sk, cb) < 0) {
724
					inet_listen_unlock(hashinfo);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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)))
Linus Torvalds's avatar
Linus Torvalds committed
745 746
		return skb->len;

747 748
	for (i = s_i; i < hashinfo->ehash_size; i++) {
		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
Linus Torvalds's avatar
Linus Torvalds committed
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)))
Linus Torvalds's avatar
Linus Torvalds committed
763
				goto next_normal;
764 765
			if (r->id.idiag_sport != inet->sport &&
			    r->id.idiag_sport)
Linus Torvalds's avatar
Linus Torvalds committed
766
				goto next_normal;
767 768
			if (r->id.idiag_dport != inet->dport &&
			    r->id.idiag_dport)
Linus Torvalds's avatar
Linus Torvalds committed
769
				goto next_normal;
770
			if (inet_csk_diag_dump(sk, skb, cb) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
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) {
Linus Torvalds's avatar
Linus Torvalds committed
783 784 785

				if (num < s_num)
					goto next_dying;
786
				if (r->id.idiag_sport != tw->tw_sport &&
787
				    r->id.idiag_sport)
Linus Torvalds's avatar
Linus Torvalds committed
788
					goto next_dying;
789
				if (r->id.idiag_dport != tw->tw_dport &&
790
				    r->id.idiag_dport)
Linus Torvalds's avatar
Linus Torvalds committed
791
					goto next_dying;
792
				if (inet_twsk_diag_dump(tw, skb, cb) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
810
{
811
	int hdrlen = sizeof(struct inet_diag_req);
Linus Torvalds's avatar
Linus Torvalds committed
812

813 814 815
	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX ||
	    nlmsg_len(nlh) < hdrlen)
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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;
		}
Linus Torvalds's avatar
Linus Torvalds committed
831

832 833
		return netlink_dump_start(idiagnl, skb, nlh,
					  inet_diag_dump, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
834
	}
835 836

	return inet_diag_get_exact(skb, nlh);
Linus Torvalds's avatar
Linus Torvalds committed
837 838
}

839
static void inet_diag_rcv(struct sock *sk, int len)
Linus Torvalds's avatar
Linus Torvalds committed
840
{
841
	unsigned int qlen = 0;
Linus Torvalds's avatar
Linus Torvalds committed
842

843 844 845
	do {
		netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg);
	} while (qlen);
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
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 897
					THIS_MODULE);
	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;
Linus Torvalds's avatar
Linus Torvalds committed
905 906
}

907
static void __exit inet_diag_exit(void)
Linus Torvalds's avatar
Linus Torvalds committed
908
{
909
	sock_release(idiagnl->sk_socket);
910
	kfree(inet_diag_table);
Linus Torvalds's avatar
Linus Torvalds committed
911 912
}

913 914
module_init(inet_diag_init);
module_exit(inet_diag_exit);
Linus Torvalds's avatar
Linus Torvalds committed
915
MODULE_LICENSE("GPL");