inet_diag.c 27.2 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * inet_diag.c	Module for monitoring INET transport protocols sockets.
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10 11
 *
 * 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.
 */

12
#include <linux/kernel.h>
L
Linus Torvalds 已提交
13 14 15 16
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/random.h>
17
#include <linux/slab.h>
L
Linus Torvalds 已提交
18 19 20 21 22 23 24 25
#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>
P
Pavel Emelyanov 已提交
36
#include <linux/sock_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 *sdiagnl;
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 58 59 60 61 62 63 64 65
static inline int inet_diag_type2proto(int type)
{
	switch (type) {
	case TCPDIAG_GETSOCK:
		return IPPROTO_TCP;
	case DCCPDIAG_GETSOCK:
		return IPPROTO_DCCP;
	default:
		return 0;
	}
}

66 67
static DEFINE_MUTEX(inet_diag_table_mutex);

68
static const struct inet_diag_handler *inet_diag_lock_handler(int proto)
69
{
70
	if (!inet_diag_table[proto])
71
		request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
72
			       NETLINK_SOCK_DIAG, proto);
73 74

	mutex_lock(&inet_diag_table_mutex);
75
	if (!inet_diag_table[proto])
76 77
		return ERR_PTR(-ENOENT);

78
	return inet_diag_table[proto];
79 80 81 82 83 84 85 86
}

static inline void inet_diag_unlock_handler(
	const struct inet_diag_handler *handler)
{
	mutex_unlock(&inet_diag_table_mutex);
}

87 88 89 90
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 已提交
91
{
92 93
	const struct inet_sock *inet = inet_sk(sk);
	const struct inet_connection_sock *icsk = inet_csk(sk);
94
	struct inet_diag_msg *r;
L
Linus Torvalds 已提交
95
	struct nlmsghdr  *nlh;
96
	void *info = NULL;
97
	struct inet_diag_meminfo  *minfo = NULL;
98
	unsigned char	 *b = skb_tail_pointer(skb);
99 100
	const struct inet_diag_handler *handler;

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

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

L
Linus Torvalds 已提交
107
	r = NLMSG_DATA(nlh);
108 109 110 111 112 113 114 115 116 117 118 119 120 121
	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 已提交
122
	}
123

124 125 126 127
	r->idiag_family = sk->sk_family;
	r->idiag_state = sk->sk_state;
	r->idiag_timer = 0;
	r->idiag_retrans = 0;
L
Linus Torvalds 已提交
128

129 130 131
	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 已提交
132

E
Eric Dumazet 已提交
133 134 135 136
	r->id.idiag_sport = inet->inet_sport;
	r->id.idiag_dport = inet->inet_dport;
	r->id.idiag_src[0] = inet->inet_rcv_saddr;
	r->id.idiag_dst[0] = inet->inet_daddr;
L
Linus Torvalds 已提交
137

138 139 140 141 142 143
	/* IPv6 dual-stack sockets use inet->tos for IPv4 connections,
	 * hence this needs to be included regardless of socket family.
	 */
	if (ext & (1 << (INET_DIAG_TOS - 1)))
		RTA_PUT_U8(skb, INET_DIAG_TOS, inet->tos);

144
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
145
	if (r->idiag_family == AF_INET6) {
146
		const struct ipv6_pinfo *np = inet6_sk(sk);
L
Linus Torvalds 已提交
147

A
Alexey Dobriyan 已提交
148 149
		*(struct in6_addr *)r->id.idiag_src = np->rcv_saddr;
		*(struct in6_addr *)r->id.idiag_dst = np->daddr;
150 151
		if (ext & (1 << (INET_DIAG_TCLASS - 1)))
			RTA_PUT_U8(skb, INET_DIAG_TCLASS, np->tclass);
L
Linus Torvalds 已提交
152 153 154
	}
#endif

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

157
	if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
158 159 160
		r->idiag_timer = 1;
		r->idiag_retrans = icsk->icsk_retransmits;
		r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
161
	} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
162 163 164
		r->idiag_timer = 4;
		r->idiag_retrans = icsk->icsk_probes_out;
		r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
L
Linus Torvalds 已提交
165
	} else if (timer_pending(&sk->sk_timer)) {
166 167 168
		r->idiag_timer = 2;
		r->idiag_retrans = icsk->icsk_probes_out;
		r->idiag_expires = EXPIRES_IN_MS(sk->sk_timer.expires);
L
Linus Torvalds 已提交
169
	} else {
170 171
		r->idiag_timer = 0;
		r->idiag_expires = 0;
L
Linus Torvalds 已提交
172 173
	}
#undef EXPIRES_IN_MS
174

175 176
	r->idiag_uid = sock_i_uid(sk);
	r->idiag_inode = sock_i_ino(sk);
L
Linus Torvalds 已提交
177 178

	if (minfo) {
179
		minfo->idiag_rmem = sk_rmem_alloc_get(sk);
180 181
		minfo->idiag_wmem = sk->sk_wmem_queued;
		minfo->idiag_fmem = sk->sk_forward_alloc;
182
		minfo->idiag_tmem = sk_wmem_alloc_get(sk);
L
Linus Torvalds 已提交
183 184
	}

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

187 188 189
	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 已提交
190

191
	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
L
Linus Torvalds 已提交
192 193
	return skb->len;

194
rtattr_failure:
L
Linus Torvalds 已提交
195
nlmsg_failure:
196
	nlmsg_trim(skb, b);
197
	return -EMSGSIZE;
L
Linus Torvalds 已提交
198 199
}

200 201 202 203 204 205 206
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;
207
	const unsigned char *previous_tail = skb_tail_pointer(skb);
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
	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_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;
231
	r->idiag_expires      = DIV_ROUND_UP(tmo * 1000, HZ);
232 233 234 235 236 237 238 239 240
	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);

A
Alexey Dobriyan 已提交
241 242
		*(struct in6_addr *)r->id.idiag_src = tw6->tw_v6_rcv_saddr;
		*(struct in6_addr *)r->id.idiag_dst = tw6->tw_v6_daddr;
243 244
	}
#endif
245
	nlh->nlmsg_len = skb_tail_pointer(skb) - previous_tail;
246 247
	return skb->len;
nlmsg_failure:
248
	nlmsg_trim(skb, previous_tail);
249
	return -EMSGSIZE;
250 251
}

252 253 254 255 256 257 258 259 260 261 262
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);
}

263
static int inet_diag_get_exact(struct sk_buff *in_skb,
264 265
			       const struct nlmsghdr *nlh,
			       struct inet_diag_req *req)
L
Linus Torvalds 已提交
266 267 268 269
{
	int err;
	struct sock *sk;
	struct sk_buff *rep;
270 271 272
	struct inet_hashinfo *hashinfo;
	const struct inet_diag_handler *handler;

273
	handler = inet_diag_lock_handler(req->sdiag_protocol);
274 275 276 277
	if (IS_ERR(handler)) {
		err = PTR_ERR(handler);
		goto unlock;
	}
278

279
	hashinfo = handler->idiag_hashinfo;
280
	err = -EINVAL;
281

282
	if (req->sdiag_family == AF_INET) {
283
		sk = inet_lookup(&init_net, hashinfo, req->id.idiag_dst[0],
284 285
				 req->id.idiag_dport, req->id.idiag_src[0],
				 req->id.idiag_sport, req->id.idiag_if);
L
Linus Torvalds 已提交
286
	}
287
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
288
	else if (req->sdiag_family == AF_INET6) {
289
		sk = inet6_lookup(&init_net, hashinfo,
290 291 292 293 294
				  (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 已提交
295 296 297
	}
#endif
	else {
298
		goto unlock;
L
Linus Torvalds 已提交
299 300
	}

301
	err = -ENOENT;
L
Linus Torvalds 已提交
302
	if (sk == NULL)
303
		goto unlock;
L
Linus Torvalds 已提交
304 305

	err = -ESTALE;
306 307 308 309
	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 已提交
310 311 312
		goto out;

	err = -ENOMEM;
313 314
	rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) +
				     sizeof(struct inet_diag_meminfo) +
315 316
				     handler->idiag_info_size + 64)),
			GFP_KERNEL);
L
Linus Torvalds 已提交
317 318 319
	if (!rep)
		goto out;

320 321 322 323 324 325 326 327
	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;
	}
328
	err = netlink_unicast(sdiagnl, rep, NETLINK_CB(in_skb).pid,
329
			      MSG_DONTWAIT);
L
Linus Torvalds 已提交
330 331 332 333 334 335
	if (err > 0)
		err = 0;

out:
	if (sk) {
		if (sk->sk_state == TCP_TIME_WAIT)
336
			inet_twsk_put((struct inet_timewait_sock *)sk);
L
Linus Torvalds 已提交
337 338 339
		else
			sock_put(sk);
	}
340 341
unlock:
	inet_diag_unlock_handler(handler);
L
Linus Torvalds 已提交
342 343 344
	return err;
}

A
Al Viro 已提交
345
static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits)
L
Linus Torvalds 已提交
346 347 348 349 350 351 352 353 354 355
{
	int words = bits >> 5;

	bits &= 0x1f;

	if (words) {
		if (memcmp(a1, a2, words << 2))
			return 0;
	}
	if (bits) {
A
Al Viro 已提交
356 357
		__be32 w1, w2;
		__be32 mask;
L
Linus Torvalds 已提交
358 359 360 361 362 363 364 365 366 367 368 369 370 371

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

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

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

	return 1;
}


372
static int inet_diag_bc_run(const void *bc, int len,
373
			    const struct inet_diag_entry *entry)
L
Linus Torvalds 已提交
374 375 376
{
	while (len > 0) {
		int yes = 1;
377
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
378 379

		switch (op->code) {
380
		case INET_DIAG_BC_NOP:
L
Linus Torvalds 已提交
381
			break;
382
		case INET_DIAG_BC_JMP:
L
Linus Torvalds 已提交
383 384
			yes = 0;
			break;
385
		case INET_DIAG_BC_S_GE:
L
Linus Torvalds 已提交
386 387
			yes = entry->sport >= op[1].no;
			break;
388
		case INET_DIAG_BC_S_LE:
389
			yes = entry->sport <= op[1].no;
L
Linus Torvalds 已提交
390
			break;
391
		case INET_DIAG_BC_D_GE:
L
Linus Torvalds 已提交
392 393
			yes = entry->dport >= op[1].no;
			break;
394
		case INET_DIAG_BC_D_LE:
L
Linus Torvalds 已提交
395 396
			yes = entry->dport <= op[1].no;
			break;
397
		case INET_DIAG_BC_AUTO:
L
Linus Torvalds 已提交
398 399
			yes = !(entry->userlocks & SOCK_BINDPORT_LOCK);
			break;
400
		case INET_DIAG_BC_S_COND:
401 402
		case INET_DIAG_BC_D_COND: {
			struct inet_diag_hostcond *cond;
A
Al Viro 已提交
403
			__be32 *addr;
L
Linus Torvalds 已提交
404

405
			cond = (struct inet_diag_hostcond *)(op + 1);
L
Linus Torvalds 已提交
406
			if (cond->port != -1 &&
407
			    cond->port != (op->code == INET_DIAG_BC_S_COND ?
L
Linus Torvalds 已提交
408 409 410 411
					     entry->sport : entry->dport)) {
				yes = 0;
				break;
			}
412

L
Linus Torvalds 已提交
413 414 415
			if (cond->prefix_len == 0)
				break;

416
			if (op->code == INET_DIAG_BC_S_COND)
L
Linus Torvalds 已提交
417 418 419 420
				addr = entry->saddr;
			else
				addr = entry->daddr;

421 422
			if (bitstring_match(addr, cond->addr,
					    cond->prefix_len))
L
Linus Torvalds 已提交
423 424 425 426 427
				break;
			if (entry->family == AF_INET6 &&
			    cond->family == AF_INET) {
				if (addr[0] == 0 && addr[1] == 0 &&
				    addr[2] == htonl(0xffff) &&
428
				    bitstring_match(addr + 3, cond->addr,
429
						    cond->prefix_len))
L
Linus Torvalds 已提交
430 431 432 433 434 435 436
					break;
			}
			yes = 0;
			break;
		}
		}

437
		if (yes) {
L
Linus Torvalds 已提交
438 439 440 441 442 443 444
			len -= op->yes;
			bc += op->yes;
		} else {
			len -= op->no;
			bc += op->no;
		}
	}
E
Eric Dumazet 已提交
445
	return len == 0;
L
Linus Torvalds 已提交
446 447 448 449 450
}

static int valid_cc(const void *bc, int len, int cc)
{
	while (len >= 0) {
451
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
452 453 454 455 456

		if (cc > len)
			return 0;
		if (cc == len)
			return 1;
457
		if (op->yes < 4 || op->yes & 3)
L
Linus Torvalds 已提交
458 459 460 461 462 463 464
			return 0;
		len -= op->yes;
		bc  += op->yes;
	}
	return 0;
}

465
static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
L
Linus Torvalds 已提交
466
{
467
	const void *bc = bytecode;
L
Linus Torvalds 已提交
468 469 470
	int  len = bytecode_len;

	while (len > 0) {
471
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
472 473 474

//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len);
		switch (op->code) {
475 476 477 478 479 480 481 482
		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:
		case INET_DIAG_BC_JMP:
483
			if (op->no < 4 || op->no > len + 4 || op->no & 3)
L
Linus Torvalds 已提交
484 485
				return -EINVAL;
			if (op->no < len &&
486
			    !valid_cc(bytecode, bytecode_len, len - op->no))
L
Linus Torvalds 已提交
487 488
				return -EINVAL;
			break;
489
		case INET_DIAG_BC_NOP:
L
Linus Torvalds 已提交
490 491 492 493
			break;
		default:
			return -EINVAL;
		}
494 495
		if (op->yes < 4 || op->yes > len + 4 || op->yes & 3)
			return -EINVAL;
496
		bc  += op->yes;
L
Linus Torvalds 已提交
497 498 499 500 501
		len -= op->yes;
	}
	return len == 0 ? 0 : -EINVAL;
}

502 503
static int inet_csk_diag_dump(struct sock *sk,
			      struct sk_buff *skb,
504
			      struct netlink_callback *cb,
505
			      struct inet_diag_req *r,
506
			      const struct nlattr *bc)
L
Linus Torvalds 已提交
507
{
508
	if (bc != NULL) {
509
		struct inet_diag_entry entry;
L
Linus Torvalds 已提交
510 511 512
		struct inet_sock *inet = inet_sk(sk);

		entry.family = sk->sk_family;
513
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
514 515 516 517 518 519 520 521
		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
		{
E
Eric Dumazet 已提交
522 523
			entry.saddr = &inet->inet_rcv_saddr;
			entry.daddr = &inet->inet_daddr;
L
Linus Torvalds 已提交
524
		}
E
Eric Dumazet 已提交
525 526
		entry.sport = inet->inet_num;
		entry.dport = ntohs(inet->inet_dport);
L
Linus Torvalds 已提交
527 528
		entry.userlocks = sk->sk_userlocks;

529
		if (!inet_diag_bc_run(nla_data(bc), nla_len(bc), &entry))
L
Linus Torvalds 已提交
530 531 532
			return 0;
	}

533 534 535
	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 已提交
536 537
}

538 539
static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
			       struct sk_buff *skb,
540
			       struct netlink_callback *cb,
541
			       struct inet_diag_req *r,
542
			       const struct nlattr *bc)
543
{
544
	if (bc != NULL) {
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
		struct inet_diag_entry entry;

		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);
562
		entry.userlocks = 0;
563

564
		if (!inet_diag_bc_run(nla_data(bc), nla_len(bc), &entry))
565 566 567 568 569 570 571 572
			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);
}

573
static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
574 575
			      struct request_sock *req, u32 pid, u32 seq,
			      const struct nlmsghdr *unlh)
L
Linus Torvalds 已提交
576
{
577
	const struct inet_request_sock *ireq = inet_rsk(req);
L
Linus Torvalds 已提交
578
	struct inet_sock *inet = inet_sk(sk);
579
	unsigned char *b = skb_tail_pointer(skb);
580
	struct inet_diag_msg *r;
L
Linus Torvalds 已提交
581 582 583
	struct nlmsghdr *nlh;
	long tmo;

584
	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
L
Linus Torvalds 已提交
585 586 587
	nlh->nlmsg_flags = NLM_F_MULTI;
	r = NLMSG_DATA(nlh);

588 589 590 591
	r->idiag_family = sk->sk_family;
	r->idiag_state = TCP_SYN_RECV;
	r->idiag_timer = 1;
	r->idiag_retrans = req->retrans;
L
Linus Torvalds 已提交
592

593 594 595
	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 已提交
596 597 598 599 600

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

E
Eric Dumazet 已提交
601
	r->id.idiag_sport = inet->inet_sport;
602 603 604 605 606 607 608 609
	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;
610
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
611
	if (r->idiag_family == AF_INET6) {
A
Alexey Dobriyan 已提交
612 613
		*(struct in6_addr *)r->id.idiag_src = inet6_rsk(req)->loc_addr;
		*(struct in6_addr *)r->id.idiag_dst = inet6_rsk(req)->rmt_addr;
L
Linus Torvalds 已提交
614 615
	}
#endif
616
	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
L
Linus Torvalds 已提交
617 618 619 620

	return skb->len;

nlmsg_failure:
621
	nlmsg_trim(skb, b);
L
Linus Torvalds 已提交
622 623 624
	return -1;
}

625
static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
626
			       struct netlink_callback *cb,
627
			       struct inet_diag_req *r,
628
			       const struct nlattr *bc)
L
Linus Torvalds 已提交
629
{
630
	struct inet_diag_entry entry;
631
	struct inet_connection_sock *icsk = inet_csk(sk);
632
	struct listen_sock *lopt;
L
Linus Torvalds 已提交
633 634 635 636 637 638 639 640 641 642 643 644 645
	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;

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

648
	lopt = icsk->icsk_accept_queue.listen_opt;
L
Linus Torvalds 已提交
649 650 651
	if (!lopt || !lopt->qlen)
		goto out;

652
	if (bc != NULL) {
E
Eric Dumazet 已提交
653
		entry.sport = inet->inet_num;
L
Linus Torvalds 已提交
654 655 656
		entry.userlocks = sk->sk_userlocks;
	}

657
	for (j = s_j; j < lopt->nr_table_entries; j++) {
658
		struct request_sock *req, *head = lopt->syn_table[j];
L
Linus Torvalds 已提交
659 660 661

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

L
Linus Torvalds 已提交
664 665
			if (reqnum < s_reqnum)
				continue;
666 667
			if (r->id.idiag_dport != ireq->rmt_port &&
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
668 669 670 671
				continue;

			if (bc) {
				entry.saddr =
672
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
673
					(entry.family == AF_INET6) ?
674
					inet6_rsk(req)->loc_addr.s6_addr32 :
L
Linus Torvalds 已提交
675
#endif
676
					&ireq->loc_addr;
677
				entry.daddr =
678
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
L
Linus Torvalds 已提交
679
					(entry.family == AF_INET6) ?
680
					inet6_rsk(req)->rmt_addr.s6_addr32 :
L
Linus Torvalds 已提交
681
#endif
682 683
					&ireq->rmt_addr;
				entry.dport = ntohs(ireq->rmt_port);
L
Linus Torvalds 已提交
684

685 686
				if (!inet_diag_bc_run(nla_data(bc),
						      nla_len(bc), &entry))
L
Linus Torvalds 已提交
687 688 689
					continue;
			}

690
			err = inet_diag_fill_req(skb, sk, req,
L
Linus Torvalds 已提交
691
					       NETLINK_CB(cb->skb).pid,
692
					       cb->nlh->nlmsg_seq, cb->nlh);
L
Linus Torvalds 已提交
693 694 695 696 697 698 699 700 701 702 703
			if (err < 0) {
				cb->args[3] = j + 1;
				cb->args[4] = reqnum;
				goto out;
			}
		}

		s_reqnum = 0;
	}

out:
704
	read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
L
Linus Torvalds 已提交
705 706 707 708

	return err;
}

709 710
static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
		struct inet_diag_req *r, struct nlattr *bc)
L
Linus Torvalds 已提交
711 712 713
{
	int i, num;
	int s_i, s_num;
714
	const struct inet_diag_handler *handler;
715
	struct inet_hashinfo *hashinfo;
716

717
	handler = inet_diag_lock_handler(r->sdiag_protocol);
718 719
	if (IS_ERR(handler))
		goto unlock;
720

721
	hashinfo = handler->idiag_hashinfo;
722

L
Linus Torvalds 已提交
723 724
	s_i = cb->args[1];
	s_num = num = cb->args[2];
725

L
Linus Torvalds 已提交
726
	if (cb->args[0] == 0) {
727
		if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
L
Linus Torvalds 已提交
728
			goto skip_listen_ht;
729

730
		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
L
Linus Torvalds 已提交
731
			struct sock *sk;
732
			struct hlist_nulls_node *node;
733
			struct inet_listen_hashbucket *ilb;
L
Linus Torvalds 已提交
734 735

			num = 0;
736 737
			ilb = &hashinfo->listening_hash[i];
			spin_lock_bh(&ilb->lock);
738
			sk_nulls_for_each(sk, node, &ilb->head) {
L
Linus Torvalds 已提交
739 740 741 742 743 744 745
				struct inet_sock *inet = inet_sk(sk);

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

746 747 748 749
				if (r->sdiag_family != AF_UNSPEC &&
						sk->sk_family != r->sdiag_family)
					goto next_listen;

E
Eric Dumazet 已提交
750
				if (r->id.idiag_sport != inet->inet_sport &&
751
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
752 753
					goto next_listen;

754 755
				if (!(r->idiag_states & TCPF_LISTEN) ||
				    r->id.idiag_dport ||
L
Linus Torvalds 已提交
756 757 758
				    cb->args[3] > 0)
					goto syn_recv;

759
				if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
760
					spin_unlock_bh(&ilb->lock);
L
Linus Torvalds 已提交
761 762 763 764
					goto done;
				}

syn_recv:
765
				if (!(r->idiag_states & TCPF_SYN_RECV))
L
Linus Torvalds 已提交
766 767
					goto next_listen;

768
				if (inet_diag_dump_reqs(skb, sk, cb, r, bc) < 0) {
769
					spin_unlock_bh(&ilb->lock);
L
Linus Torvalds 已提交
770 771 772 773 774 775 776 777
					goto done;
				}

next_listen:
				cb->args[3] = 0;
				cb->args[4] = 0;
				++num;
			}
778
			spin_unlock_bh(&ilb->lock);
L
Linus Torvalds 已提交
779 780 781 782 783 784 785 786 787 788

			s_num = 0;
			cb->args[3] = 0;
			cb->args[4] = 0;
		}
skip_listen_ht:
		cb->args[0] = 1;
		s_i = num = s_num = 0;
	}

789
	if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
790
		goto unlock;
L
Linus Torvalds 已提交
791

792
	for (i = s_i; i <= hashinfo->ehash_mask; i++) {
793
		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
794
		spinlock_t *lock = inet_ehash_lockp(hashinfo, i);
L
Linus Torvalds 已提交
795
		struct sock *sk;
796
		struct hlist_nulls_node *node;
L
Linus Torvalds 已提交
797

798 799
		num = 0;

800 801
		if (hlist_nulls_empty(&head->chain) &&
			hlist_nulls_empty(&head->twchain))
802 803
			continue;

L
Linus Torvalds 已提交
804 805 806
		if (i > s_i)
			s_num = 0;

807
		spin_lock_bh(lock);
808
		sk_nulls_for_each(sk, node, &head->chain) {
L
Linus Torvalds 已提交
809 810 811 812
			struct inet_sock *inet = inet_sk(sk);

			if (num < s_num)
				goto next_normal;
813
			if (!(r->idiag_states & (1 << sk->sk_state)))
L
Linus Torvalds 已提交
814
				goto next_normal;
815 816 817
			if (r->sdiag_family != AF_UNSPEC &&
					sk->sk_family != r->sdiag_family)
				goto next_normal;
E
Eric Dumazet 已提交
818
			if (r->id.idiag_sport != inet->inet_sport &&
819
			    r->id.idiag_sport)
L
Linus Torvalds 已提交
820
				goto next_normal;
E
Eric Dumazet 已提交
821
			if (r->id.idiag_dport != inet->inet_dport &&
822
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
823
				goto next_normal;
824
			if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
825
				spin_unlock_bh(lock);
L
Linus Torvalds 已提交
826 827 828 829 830 831
				goto done;
			}
next_normal:
			++num;
		}

832
		if (r->idiag_states & TCPF_TIME_WAIT) {
833 834 835
			struct inet_timewait_sock *tw;

			inet_twsk_for_each(tw, node,
836
				    &head->twchain) {
L
Linus Torvalds 已提交
837 838 839

				if (num < s_num)
					goto next_dying;
840 841 842
				if (r->sdiag_family != AF_UNSPEC &&
						tw->tw_family != r->sdiag_family)
					goto next_dying;
843
				if (r->id.idiag_sport != tw->tw_sport &&
844
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
845
					goto next_dying;
846
				if (r->id.idiag_dport != tw->tw_dport &&
847
				    r->id.idiag_dport)
L
Linus Torvalds 已提交
848
					goto next_dying;
849
				if (inet_twsk_diag_dump(tw, skb, cb, r, bc) < 0) {
850
					spin_unlock_bh(lock);
L
Linus Torvalds 已提交
851 852 853 854 855 856
					goto done;
				}
next_dying:
				++num;
			}
		}
857
		spin_unlock_bh(lock);
L
Linus Torvalds 已提交
858 859 860 861 862
	}

done:
	cb->args[1] = i;
	cb->args[2] = num;
863 864
unlock:
	inet_diag_unlock_handler(handler);
L
Linus Torvalds 已提交
865 866 867
	return skb->len;
}

868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct nlattr *bc = NULL;
	int hdrlen = sizeof(struct inet_diag_req);

	if (nlmsg_attrlen(cb->nlh, hdrlen))
		bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE);

	return __inet_diag_dump(skb, cb, (struct inet_diag_req *)NLMSG_DATA(cb->nlh), bc);
}

static int inet_diag_dump_compat(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct inet_diag_req_compat *rc = NLMSG_DATA(cb->nlh);
	struct inet_diag_req req;
	struct nlattr *bc = NULL;
	int hdrlen = sizeof(struct inet_diag_req_compat);

886
	req.sdiag_family = AF_UNSPEC; /* compatibility */
887 888 889 890 891 892 893 894 895 896 897
	req.sdiag_protocol = inet_diag_type2proto(cb->nlh->nlmsg_type);
	req.idiag_ext = rc->idiag_ext;
	req.idiag_states = rc->idiag_states;
	req.id = rc->id;

	if (nlmsg_attrlen(cb->nlh, hdrlen))
		bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE);

	return __inet_diag_dump(skb, cb, &req, bc);
}

898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
static int inet_diag_get_exact_compat(struct sk_buff *in_skb,
			       const struct nlmsghdr *nlh)
{
	struct inet_diag_req_compat *rc = NLMSG_DATA(nlh);
	struct inet_diag_req req;

	req.sdiag_family = rc->idiag_family;
	req.sdiag_protocol = inet_diag_type2proto(nlh->nlmsg_type);
	req.idiag_ext = rc->idiag_ext;
	req.idiag_states = rc->idiag_states;
	req.id = rc->id;

	return inet_diag_get_exact(in_skb, nlh, &req);
}

913
static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
914
{
915
	int hdrlen = sizeof(struct inet_diag_req_compat);
L
Linus Torvalds 已提交
916

917 918 919
	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX ||
	    nlmsg_len(nlh) < hdrlen)
		return -EINVAL;
L
Linus Torvalds 已提交
920

921
	if (nlh->nlmsg_flags & NLM_F_DUMP) {
922 923
		if (nlmsg_attrlen(nlh, hdrlen)) {
			struct nlattr *attr;
L
Linus Torvalds 已提交
924

925 926 927 928 929 930 931
			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 已提交
932

933
		return netlink_dump_start(sdiagnl, skb, nlh,
934
					  inet_diag_dump_compat, NULL, 0);
L
Linus Torvalds 已提交
935
	}
936

937
	return inet_diag_get_exact_compat(skb, nlh);
L
Linus Torvalds 已提交
938 939
}

P
Pavel Emelyanov 已提交
940 941 942 943 944 945 946 947
static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
{
	int hdrlen = sizeof(struct inet_diag_req);

	if (nlmsg_len(h) < hdrlen)
		return -EINVAL;

	if (h->nlmsg_flags & NLM_F_DUMP) {
948 949 950 951 952 953 954 955 956 957 958 959
		if (nlmsg_attrlen(h, hdrlen)) {
			struct nlattr *attr;
			attr = nlmsg_find_attr(h, 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;
		}

		return netlink_dump_start(sdiagnl, skb, h,
					  inet_diag_dump, NULL, 0);
P
Pavel Emelyanov 已提交
960 961
	}

962
	return inet_diag_get_exact(skb, h, (struct inet_diag_req *)NLMSG_DATA(h));
P
Pavel Emelyanov 已提交
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
}

static struct sock_diag_handler inet_diag_handler = {
	.family = AF_INET,
	.dump = inet_diag_handler_dump,
};

static struct sock_diag_handler inet6_diag_handler = {
	.family = AF_INET6,
	.dump = inet_diag_handler_dump,
};

static struct sock_diag_handler *sock_diag_handlers[AF_MAX];
static DEFINE_MUTEX(sock_diag_table_mutex);

int sock_diag_register(struct sock_diag_handler *hndl)
{
	int err = 0;

	if (hndl->family > AF_MAX)
		return -EINVAL;

	mutex_lock(&sock_diag_table_mutex);
	if (sock_diag_handlers[hndl->family])
		err = -EBUSY;
	else
		sock_diag_handlers[hndl->family] = hndl;
	mutex_unlock(&sock_diag_table_mutex);

	return err;
}

void sock_diag_unregister(struct sock_diag_handler *hnld)
{
	int family = hnld->family;

	if (family > AF_MAX)
		return;

	mutex_lock(&sock_diag_table_mutex);
	BUG_ON(sock_diag_handlers[family] != hnld);
	sock_diag_handlers[family] = NULL;
	mutex_unlock(&sock_diag_table_mutex);
}

static inline struct sock_diag_handler *sock_diag_lock_handler(int family)
{
	mutex_lock(&sock_diag_table_mutex);
	return sock_diag_handlers[family];
}

static inline void sock_diag_unlock_handler(struct sock_diag_handler *h)
{
	mutex_unlock(&sock_diag_table_mutex);
}

1019 1020
static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
P
Pavel Emelyanov 已提交
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
	int err;
	struct sock_diag_req *req = NLMSG_DATA(nlh);
	struct sock_diag_handler *hndl;

	if (nlmsg_len(nlh) < sizeof(*req))
		return -EINVAL;

	hndl = sock_diag_lock_handler(req->sdiag_family);
	if (hndl == NULL)
		err = -ENOENT;
	else
		err = hndl->dump(skb, nlh);
	sock_diag_unlock_handler(hndl);

	return err;
1036 1037
}

1038 1039
static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
1040 1041 1042 1043 1044 1045 1046 1047 1048
	switch (nlh->nlmsg_type) {
	case TCPDIAG_GETSOCK:
	case DCCPDIAG_GETSOCK:
		return inet_diag_rcv_msg_compat(skb, nlh);
	case SOCK_DIAG_BY_FAMILY:
		return __sock_diag_rcv_msg(skb, nlh);
	default:
		return -EINVAL;
	}
1049 1050 1051
}

static DEFINE_MUTEX(sock_diag_mutex);
1052

1053
static void sock_diag_rcv(struct sk_buff *skb)
L
Linus Torvalds 已提交
1054
{
1055 1056 1057
	mutex_lock(&sock_diag_mutex);
	netlink_rcv_skb(skb, &sock_diag_rcv_msg);
	mutex_unlock(&sock_diag_mutex);
L
Linus Torvalds 已提交
1058 1059
}

1060 1061 1062 1063 1064
int inet_diag_register(const struct inet_diag_handler *h)
{
	const __u16 type = h->idiag_type;
	int err = -EINVAL;

1065
	if (type >= IPPROTO_MAX)
1066 1067
		goto out;

1068
	mutex_lock(&inet_diag_table_mutex);
1069 1070 1071 1072 1073
	err = -EEXIST;
	if (inet_diag_table[type] == NULL) {
		inet_diag_table[type] = h;
		err = 0;
	}
1074
	mutex_unlock(&inet_diag_table_mutex);
1075 1076 1077 1078 1079 1080 1081 1082 1083
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;

1084
	if (type >= IPPROTO_MAX)
1085 1086
		return;

1087
	mutex_lock(&inet_diag_table_mutex);
1088
	inet_diag_table[type] = NULL;
1089
	mutex_unlock(&inet_diag_table_mutex);
1090 1091 1092
}
EXPORT_SYMBOL_GPL(inet_diag_unregister);

1093
static int __init inet_diag_init(void)
L
Linus Torvalds 已提交
1094
{
1095
	const int inet_diag_table_size = (IPPROTO_MAX *
1096 1097 1098
					  sizeof(struct inet_diag_handler *));
	int err = -ENOMEM;

1099
	inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL);
1100 1101 1102
	if (!inet_diag_table)
		goto out;

1103 1104 1105
	sdiagnl = netlink_kernel_create(&init_net, NETLINK_SOCK_DIAG, 0,
					sock_diag_rcv, NULL, THIS_MODULE);
	if (sdiagnl == NULL)
1106
		goto out_free_table;
P
Pavel Emelyanov 已提交
1107 1108 1109 1110 1111 1112 1113 1114 1115

	err = sock_diag_register(&inet_diag_handler);
	if (err)
		goto out_free_nl;

	err = sock_diag_register(&inet6_diag_handler);
	if (err)
		goto out_free_inet;

1116 1117
out:
	return err;
P
Pavel Emelyanov 已提交
1118 1119 1120 1121 1122

out_free_inet:
	sock_diag_unregister(&inet_diag_handler);
out_free_nl:
	netlink_kernel_release(sdiagnl);
1123 1124 1125
out_free_table:
	kfree(inet_diag_table);
	goto out;
L
Linus Torvalds 已提交
1126 1127
}

1128
static void __exit inet_diag_exit(void)
L
Linus Torvalds 已提交
1129
{
P
Pavel Emelyanov 已提交
1130 1131
	sock_diag_unregister(&inet6_diag_handler);
	sock_diag_unregister(&inet_diag_handler);
1132
	netlink_kernel_release(sdiagnl);
1133
	kfree(inet_diag_table);
L
Linus Torvalds 已提交
1134 1135
}

1136 1137
module_init(inet_diag_init);
module_exit(inet_diag_exit);
L
Linus Torvalds 已提交
1138
MODULE_LICENSE("GPL");
1139
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_SOCK_DIAG);