inet_diag.c 27.9 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
	u16 sport;
	u16 dport;
	u16 family;
	u16 userlocks;
47 48 49 50
#if IS_ENABLED(CONFIG_IPV6)
	struct in6_addr saddr_storage;	/* for IPv4-mapped-IPv6 addresses */
	struct in6_addr daddr_storage;	/* for IPv4-mapped-IPv6 addresses */
#endif
L
Linus Torvalds 已提交
51 52
};

53 54
static DEFINE_MUTEX(inet_diag_table_mutex);

55
static const struct inet_diag_handler *inet_diag_lock_handler(int proto)
56
{
57
	if (!inet_diag_table[proto])
58 59
		request_module("net-pf-%d-proto-%d-type-%d-%d", PF_NETLINK,
			       NETLINK_SOCK_DIAG, AF_INET, proto);
60 61

	mutex_lock(&inet_diag_table_mutex);
62
	if (!inet_diag_table[proto])
63 64
		return ERR_PTR(-ENOENT);

65
	return inet_diag_table[proto];
66 67 68 69 70 71 72 73
}

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

74
int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
75
			      struct sk_buff *skb, struct inet_diag_req_v2 *req,
76
			      struct user_namespace *user_ns,		      	
77
			      u32 portid, u32 seq, u16 nlmsg_flags,
78
			      const struct nlmsghdr *unlh)
L
Linus Torvalds 已提交
79
{
80
	const struct inet_sock *inet = inet_sk(sk);
81
	struct inet_diag_msg *r;
L
Linus Torvalds 已提交
82
	struct nlmsghdr  *nlh;
83
	struct nlattr *attr;
84 85
	void *info = NULL;
	const struct inet_diag_handler *handler;
86
	int ext = req->idiag_ext;
87

88
	handler = inet_diag_table[req->sdiag_protocol];
89
	BUG_ON(handler == NULL);
L
Linus Torvalds 已提交
90

91
	nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
92 93
			nlmsg_flags);
	if (!nlh)
94
		return -EMSGSIZE;
95

96
	r = nlmsg_data(nlh);
97 98
	BUG_ON(sk->sk_state == TCP_TIME_WAIT);

99 100 101 102
	r->idiag_family = sk->sk_family;
	r->idiag_state = sk->sk_state;
	r->idiag_timer = 0;
	r->idiag_retrans = 0;
L
Linus Torvalds 已提交
103

104
	r->id.idiag_if = sk->sk_bound_dev_if;
105
	sock_diag_save_cookie(sk, r->id.idiag_cookie);
L
Linus Torvalds 已提交
106

E
Eric Dumazet 已提交
107 108 109 110
	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 已提交
111

112 113 114 115
	/* 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)))
116 117
		if (nla_put_u8(skb, INET_DIAG_TOS, inet->tos) < 0)
			goto errout;
118

E
Eric Dumazet 已提交
119
#if IS_ENABLED(CONFIG_IPV6)
120
	if (r->idiag_family == AF_INET6) {
121
		const struct ipv6_pinfo *np = inet6_sk(sk);
L
Linus Torvalds 已提交
122

A
Alexey Dobriyan 已提交
123 124
		*(struct in6_addr *)r->id.idiag_src = np->rcv_saddr;
		*(struct in6_addr *)r->id.idiag_dst = np->daddr;
125

126
		if (ext & (1 << (INET_DIAG_TCLASS - 1)))
127 128
			if (nla_put_u8(skb, INET_DIAG_TCLASS, np->tclass) < 0)
				goto errout;
L
Linus Torvalds 已提交
129 130 131
	}
#endif

132
	r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
133 134
	r->idiag_inode = sock_i_ino(sk);

135 136 137 138 139 140 141 142 143 144
	if (ext & (1 << (INET_DIAG_MEMINFO - 1))) {
		struct inet_diag_meminfo minfo = {
			.idiag_rmem = sk_rmem_alloc_get(sk),
			.idiag_wmem = sk->sk_wmem_queued,
			.idiag_fmem = sk->sk_forward_alloc,
			.idiag_tmem = sk_wmem_alloc_get(sk),
		};

		if (nla_put(skb, INET_DIAG_MEMINFO, sizeof(minfo), &minfo) < 0)
			goto errout;
145 146
	}

147 148
	if (ext & (1 << (INET_DIAG_SKMEMINFO - 1)))
		if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO))
149
			goto errout;
150

151
	if (icsk == NULL) {
152
		handler->idiag_get_info(sk, r, NULL);
153 154 155
		goto out;
	}

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

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

176 177 178 179 180
	if (ext & (1 << (INET_DIAG_INFO - 1))) {
		attr = nla_reserve(skb, INET_DIAG_INFO,
				   sizeof(struct tcp_info));
		if (!attr)
			goto errout;
181

182
		info = nla_data(attr);
L
Linus Torvalds 已提交
183 184
	}

185 186 187 188 189
	if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops)
		if (nla_put_string(skb, INET_DIAG_CONG,
				   icsk->icsk_ca_ops->name) < 0)
			goto errout;

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

192 193 194
	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 已提交
195

196
out:
197
	return nlmsg_end(skb, nlh);
L
Linus Torvalds 已提交
198

199 200
errout:
	nlmsg_cancel(skb, nlh);
201
	return -EMSGSIZE;
L
Linus Torvalds 已提交
202
}
203 204 205
EXPORT_SYMBOL_GPL(inet_sk_diag_fill);

static int inet_csk_diag_fill(struct sock *sk,
206
			      struct sk_buff *skb, struct inet_diag_req_v2 *req,
207
			      struct user_namespace *user_ns,
208
			      u32 portid, u32 seq, u16 nlmsg_flags,
209 210 211
			      const struct nlmsghdr *unlh)
{
	return inet_sk_diag_fill(sk, inet_csk(sk),
212
			skb, req, user_ns, portid, seq, nlmsg_flags, unlh);
213
}
L
Linus Torvalds 已提交
214

215
static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
216
			       struct sk_buff *skb, struct inet_diag_req_v2 *req,
217
			       u32 portid, u32 seq, u16 nlmsg_flags,
218 219 220 221
			       const struct nlmsghdr *unlh)
{
	long tmo;
	struct inet_diag_msg *r;
222
	struct nlmsghdr *nlh;
223

224
	nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
225 226
			nlmsg_flags);
	if (!nlh)
227
		return -EMSGSIZE;
228

229
	r = nlmsg_data(nlh);
230 231 232 233 234 235 236 237 238
	BUG_ON(tw->tw_state != TCP_TIME_WAIT);

	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;
239
	sock_diag_save_cookie(tw, r->id.idiag_cookie);
240 241 242 243 244 245
	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;
246
	r->idiag_expires      = DIV_ROUND_UP(tmo * 1000, HZ);
247 248 249 250
	r->idiag_rqueue	      = 0;
	r->idiag_wqueue	      = 0;
	r->idiag_uid	      = 0;
	r->idiag_inode	      = 0;
E
Eric Dumazet 已提交
251
#if IS_ENABLED(CONFIG_IPV6)
252 253 254 255
	if (tw->tw_family == AF_INET6) {
		const struct inet6_timewait_sock *tw6 =
						inet6_twsk((struct sock *)tw);

A
Alexey Dobriyan 已提交
256 257
		*(struct in6_addr *)r->id.idiag_src = tw6->tw_v6_rcv_saddr;
		*(struct in6_addr *)r->id.idiag_dst = tw6->tw_v6_daddr;
258 259
	}
#endif
260 261

	return nlmsg_end(skb, nlh);
262 263
}

264
static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
265 266
			struct inet_diag_req_v2 *r,
			struct user_namespace *user_ns,
267
			u32 portid, u32 seq, u16 nlmsg_flags,
268 269 270 271
			const struct nlmsghdr *unlh)
{
	if (sk->sk_state == TCP_TIME_WAIT)
		return inet_twsk_diag_fill((struct inet_timewait_sock *)sk,
272
					   skb, r, portid, seq, nlmsg_flags,
273
					   unlh);
274
	return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq, nlmsg_flags, unlh);
275 276
}

277
int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_skb,
278
		const struct nlmsghdr *nlh, struct inet_diag_req_v2 *req)
L
Linus Torvalds 已提交
279 280 281 282
{
	int err;
	struct sock *sk;
	struct sk_buff *rep;
A
Andrey Vagin 已提交
283
	struct net *net = sock_net(in_skb->sk);
284 285

	err = -EINVAL;
286
	if (req->sdiag_family == AF_INET) {
A
Andrey Vagin 已提交
287
		sk = inet_lookup(net, hashinfo, req->id.idiag_dst[0],
288 289
				 req->id.idiag_dport, req->id.idiag_src[0],
				 req->id.idiag_sport, req->id.idiag_if);
L
Linus Torvalds 已提交
290
	}
E
Eric Dumazet 已提交
291
#if IS_ENABLED(CONFIG_IPV6)
292
	else if (req->sdiag_family == AF_INET6) {
A
Andrey Vagin 已提交
293
		sk = inet6_lookup(net, hashinfo,
294 295 296 297 298
				  (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 已提交
299 300 301
	}
#endif
	else {
302
		goto out_nosk;
L
Linus Torvalds 已提交
303 304
	}

305
	err = -ENOENT;
L
Linus Torvalds 已提交
306
	if (sk == NULL)
307
		goto out_nosk;
L
Linus Torvalds 已提交
308

309
	err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
310
	if (err)
L
Linus Torvalds 已提交
311 312
		goto out;

313 314 315 316 317
	rep = nlmsg_new(sizeof(struct inet_diag_msg) +
			sizeof(struct inet_diag_meminfo) +
			sizeof(struct tcp_info) + 64, GFP_KERNEL);
	if (!rep) {
		err = -ENOMEM;
L
Linus Torvalds 已提交
318
		goto out;
319
	}
L
Linus Torvalds 已提交
320

321
	err = sk_diag_fill(sk, rep, req,
322
			   sk_user_ns(NETLINK_CB(in_skb).ssk),
323
			   NETLINK_CB(in_skb).portid,
324 325 326
			   nlh->nlmsg_seq, 0, nlh);
	if (err < 0) {
		WARN_ON(err == -EMSGSIZE);
327
		nlmsg_free(rep);
328 329
		goto out;
	}
330
	err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid,
331
			      MSG_DONTWAIT);
L
Linus Torvalds 已提交
332 333 334 335 336 337
	if (err > 0)
		err = 0;

out:
	if (sk) {
		if (sk->sk_state == TCP_TIME_WAIT)
338
			inet_twsk_put((struct inet_timewait_sock *)sk);
L
Linus Torvalds 已提交
339 340 341
		else
			sock_put(sk);
	}
342 343 344
out_nosk:
	return err;
}
345
EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk);
346 347 348

static int inet_diag_get_exact(struct sk_buff *in_skb,
			       const struct nlmsghdr *nlh,
349
			       struct inet_diag_req_v2 *req)
350 351 352 353 354 355 356 357
{
	const struct inet_diag_handler *handler;
	int err;

	handler = inet_diag_lock_handler(req->sdiag_protocol);
	if (IS_ERR(handler))
		err = PTR_ERR(handler);
	else
358
		err = handler->dump_one(in_skb, nlh, req);
359
	inet_diag_unlock_handler(handler);
360

L
Linus Torvalds 已提交
361 362 363
	return err;
}

A
Al Viro 已提交
364
static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits)
L
Linus Torvalds 已提交
365 366 367 368 369 370 371 372 373 374
{
	int words = bits >> 5;

	bits &= 0x1f;

	if (words) {
		if (memcmp(a1, a2, words << 2))
			return 0;
	}
	if (bits) {
A
Al Viro 已提交
375 376
		__be32 w1, w2;
		__be32 mask;
L
Linus Torvalds 已提交
377 378 379 380 381 382 383 384 385 386 387 388 389 390

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

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

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

	return 1;
}


391 392
static int inet_diag_bc_run(const struct nlattr *_bc,
		const struct inet_diag_entry *entry)
L
Linus Torvalds 已提交
393
{
394 395 396
	const void *bc = nla_data(_bc);
	int len = nla_len(_bc);

L
Linus Torvalds 已提交
397 398
	while (len > 0) {
		int yes = 1;
399
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
400 401

		switch (op->code) {
402
		case INET_DIAG_BC_NOP:
L
Linus Torvalds 已提交
403
			break;
404
		case INET_DIAG_BC_JMP:
L
Linus Torvalds 已提交
405 406
			yes = 0;
			break;
407
		case INET_DIAG_BC_S_GE:
L
Linus Torvalds 已提交
408 409
			yes = entry->sport >= op[1].no;
			break;
410
		case INET_DIAG_BC_S_LE:
411
			yes = entry->sport <= op[1].no;
L
Linus Torvalds 已提交
412
			break;
413
		case INET_DIAG_BC_D_GE:
L
Linus Torvalds 已提交
414 415
			yes = entry->dport >= op[1].no;
			break;
416
		case INET_DIAG_BC_D_LE:
L
Linus Torvalds 已提交
417 418
			yes = entry->dport <= op[1].no;
			break;
419
		case INET_DIAG_BC_AUTO:
L
Linus Torvalds 已提交
420 421
			yes = !(entry->userlocks & SOCK_BINDPORT_LOCK);
			break;
422
		case INET_DIAG_BC_S_COND:
423 424
		case INET_DIAG_BC_D_COND: {
			struct inet_diag_hostcond *cond;
A
Al Viro 已提交
425
			__be32 *addr;
L
Linus Torvalds 已提交
426

427
			cond = (struct inet_diag_hostcond *)(op + 1);
L
Linus Torvalds 已提交
428
			if (cond->port != -1 &&
429
			    cond->port != (op->code == INET_DIAG_BC_S_COND ?
L
Linus Torvalds 已提交
430 431 432 433
					     entry->sport : entry->dport)) {
				yes = 0;
				break;
			}
434

L
Linus Torvalds 已提交
435 436 437
			if (cond->prefix_len == 0)
				break;

438
			if (op->code == INET_DIAG_BC_S_COND)
L
Linus Torvalds 已提交
439 440 441 442
				addr = entry->saddr;
			else
				addr = entry->daddr;

443 444
			if (bitstring_match(addr, cond->addr,
					    cond->prefix_len))
L
Linus Torvalds 已提交
445 446 447 448 449
				break;
			if (entry->family == AF_INET6 &&
			    cond->family == AF_INET) {
				if (addr[0] == 0 && addr[1] == 0 &&
				    addr[2] == htonl(0xffff) &&
450
				    bitstring_match(addr + 3, cond->addr,
451
						    cond->prefix_len))
L
Linus Torvalds 已提交
452 453 454 455 456 457 458
					break;
			}
			yes = 0;
			break;
		}
		}

459
		if (yes) {
L
Linus Torvalds 已提交
460 461 462 463 464 465 466
			len -= op->yes;
			bc += op->yes;
		} else {
			len -= op->no;
			bc += op->no;
		}
	}
E
Eric Dumazet 已提交
467
	return len == 0;
L
Linus Torvalds 已提交
468 469
}

470 471 472 473 474 475 476 477 478
int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk)
{
	struct inet_diag_entry entry;
	struct inet_sock *inet = inet_sk(sk);

	if (bc == NULL)
		return 1;

	entry.family = sk->sk_family;
E
Eric Dumazet 已提交
479
#if IS_ENABLED(CONFIG_IPV6)
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
	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->inet_rcv_saddr;
		entry.daddr = &inet->inet_daddr;
	}
	entry.sport = inet->inet_num;
	entry.dport = ntohs(inet->inet_dport);
	entry.userlocks = sk->sk_userlocks;

	return inet_diag_bc_run(bc, &entry);
}
EXPORT_SYMBOL_GPL(inet_diag_bc_sk);

L
Linus Torvalds 已提交
499 500 501
static int valid_cc(const void *bc, int len, int cc)
{
	while (len >= 0) {
502
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
503 504 505 506 507

		if (cc > len)
			return 0;
		if (cc == len)
			return 1;
508
		if (op->yes < 4 || op->yes & 3)
L
Linus Torvalds 已提交
509 510 511 512 513 514 515
			return 0;
		len -= op->yes;
		bc  += op->yes;
	}
	return 0;
}

516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
/* Validate an inet_diag_hostcond. */
static bool valid_hostcond(const struct inet_diag_bc_op *op, int len,
			   int *min_len)
{
	int addr_len;
	struct inet_diag_hostcond *cond;

	/* Check hostcond space. */
	*min_len += sizeof(struct inet_diag_hostcond);
	if (len < *min_len)
		return false;
	cond = (struct inet_diag_hostcond *)(op + 1);

	/* Check address family and address length. */
	switch (cond->family) {
	case AF_UNSPEC:
		addr_len = 0;
		break;
	case AF_INET:
		addr_len = sizeof(struct in_addr);
		break;
	case AF_INET6:
		addr_len = sizeof(struct in6_addr);
		break;
	default:
		return false;
	}
	*min_len += addr_len;
	if (len < *min_len)
		return false;

	/* Check prefix length (in bits) vs address length (in bytes). */
	if (cond->prefix_len > 8 * addr_len)
		return false;

	return true;
}

554
static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
L
Linus Torvalds 已提交
555
{
556
	const void *bc = bytecode;
L
Linus Torvalds 已提交
557 558 559
	int  len = bytecode_len;

	while (len > 0) {
560
		const struct inet_diag_bc_op *op = bc;
561
		int min_len = sizeof(struct inet_diag_bc_op);
L
Linus Torvalds 已提交
562 563 564

//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len);
		switch (op->code) {
565 566
		case INET_DIAG_BC_S_COND:
		case INET_DIAG_BC_D_COND:
567 568 569 570
			if (!valid_hostcond(bc, len, &min_len))
				return -EINVAL;
			/* fall through */
		case INET_DIAG_BC_AUTO:
571 572 573 574 575
		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:
576
			if (op->no < min_len || op->no > len + 4 || op->no & 3)
L
Linus Torvalds 已提交
577 578
				return -EINVAL;
			if (op->no < len &&
579
			    !valid_cc(bytecode, bytecode_len, len - op->no))
L
Linus Torvalds 已提交
580 581
				return -EINVAL;
			break;
582
		case INET_DIAG_BC_NOP:
L
Linus Torvalds 已提交
583 584 585 586
			break;
		default:
			return -EINVAL;
		}
587
		if (op->yes < min_len || op->yes > len + 4 || op->yes & 3)
588
			return -EINVAL;
589
		bc  += op->yes;
L
Linus Torvalds 已提交
590 591 592 593 594
		len -= op->yes;
	}
	return len == 0 ? 0 : -EINVAL;
}

595 596
static int inet_csk_diag_dump(struct sock *sk,
			      struct sk_buff *skb,
597
			      struct netlink_callback *cb,
598
			      struct inet_diag_req_v2 *r,
599
			      const struct nlattr *bc)
L
Linus Torvalds 已提交
600
{
601 602
	if (!inet_diag_bc_sk(bc, sk))
		return 0;
L
Linus Torvalds 已提交
603

604
	return inet_csk_diag_fill(sk, skb, r,
605
				  sk_user_ns(NETLINK_CB(cb->skb).ssk),
606
				  NETLINK_CB(cb->skb).portid,
607
				  cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
L
Linus Torvalds 已提交
608 609
}

610 611
static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
			       struct sk_buff *skb,
612
			       struct netlink_callback *cb,
613
			       struct inet_diag_req_v2 *r,
614
			       const struct nlattr *bc)
615
{
616
	if (bc != NULL) {
617 618 619
		struct inet_diag_entry entry;

		entry.family = tw->tw_family;
E
Eric Dumazet 已提交
620
#if IS_ENABLED(CONFIG_IPV6)
621 622 623 624 625 626 627 628 629 630 631 632 633
		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);
634
		entry.userlocks = 0;
635

636
		if (!inet_diag_bc_run(bc, &entry))
637 638 639
			return 0;
	}

640
	return inet_twsk_diag_fill(tw, skb, r,
641
				   NETLINK_CB(cb->skb).portid,
642 643 644
				   cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
}

645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
/* Get the IPv4, IPv6, or IPv4-mapped-IPv6 local and remote addresses
 * from a request_sock. For IPv4-mapped-IPv6 we must map IPv4 to IPv6.
 */
static inline void inet_diag_req_addrs(const struct sock *sk,
				       const struct request_sock *req,
				       struct inet_diag_entry *entry)
{
	struct inet_request_sock *ireq = inet_rsk(req);

#if IS_ENABLED(CONFIG_IPV6)
	if (sk->sk_family == AF_INET6) {
		if (req->rsk_ops->family == AF_INET6) {
			entry->saddr = inet6_rsk(req)->loc_addr.s6_addr32;
			entry->daddr = inet6_rsk(req)->rmt_addr.s6_addr32;
		} else if (req->rsk_ops->family == AF_INET) {
			ipv6_addr_set_v4mapped(ireq->loc_addr,
					       &entry->saddr_storage);
			ipv6_addr_set_v4mapped(ireq->rmt_addr,
					       &entry->daddr_storage);
			entry->saddr = entry->saddr_storage.s6_addr32;
			entry->daddr = entry->daddr_storage.s6_addr32;
		}
	} else
#endif
	{
		entry->saddr = &ireq->loc_addr;
		entry->daddr = &ireq->rmt_addr;
	}
}

675
static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
676 677
			      struct request_sock *req,
			      struct user_namespace *user_ns,
678
			      u32 portid, u32 seq,
679
			      const struct nlmsghdr *unlh)
L
Linus Torvalds 已提交
680
{
681
	const struct inet_request_sock *ireq = inet_rsk(req);
L
Linus Torvalds 已提交
682
	struct inet_sock *inet = inet_sk(sk);
683
	struct inet_diag_msg *r;
L
Linus Torvalds 已提交
684 685 686
	struct nlmsghdr *nlh;
	long tmo;

687
	nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
688 689 690
			NLM_F_MULTI);
	if (!nlh)
		return -EMSGSIZE;
L
Linus Torvalds 已提交
691

692
	r = nlmsg_data(nlh);
693 694 695 696
	r->idiag_family = sk->sk_family;
	r->idiag_state = TCP_SYN_RECV;
	r->idiag_timer = 1;
	r->idiag_retrans = req->retrans;
L
Linus Torvalds 已提交
697

698
	r->id.idiag_if = sk->sk_bound_dev_if;
699
	sock_diag_save_cookie(req, r->id.idiag_cookie);
L
Linus Torvalds 已提交
700 701 702 703 704

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

E
Eric Dumazet 已提交
705
	r->id.idiag_sport = inet->inet_sport;
706 707 708 709 710 711
	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;
712
	r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
713
	r->idiag_inode = 0;
E
Eric Dumazet 已提交
714
#if IS_ENABLED(CONFIG_IPV6)
715
	if (r->idiag_family == AF_INET6) {
716 717 718 719
		struct inet_diag_entry entry;
		inet_diag_req_addrs(sk, req, &entry);
		memcpy(r->id.idiag_src, entry.saddr, sizeof(struct in6_addr));
		memcpy(r->id.idiag_dst, entry.daddr, sizeof(struct in6_addr));
L
Linus Torvalds 已提交
720 721 722
	}
#endif

723
	return nlmsg_end(skb, nlh);
L
Linus Torvalds 已提交
724 725
}

726
static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
727
			       struct netlink_callback *cb,
728
			       struct inet_diag_req_v2 *r,
729
			       const struct nlattr *bc)
L
Linus Torvalds 已提交
730
{
731
	struct inet_diag_entry entry;
732
	struct inet_connection_sock *icsk = inet_csk(sk);
733
	struct listen_sock *lopt;
L
Linus Torvalds 已提交
734 735 736 737 738 739 740 741 742 743 744 745 746
	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;

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

749
	lopt = icsk->icsk_accept_queue.listen_opt;
L
Linus Torvalds 已提交
750 751 752
	if (!lopt || !lopt->qlen)
		goto out;

753
	if (bc != NULL) {
E
Eric Dumazet 已提交
754
		entry.sport = inet->inet_num;
L
Linus Torvalds 已提交
755 756 757
		entry.userlocks = sk->sk_userlocks;
	}

758
	for (j = s_j; j < lopt->nr_table_entries; j++) {
759
		struct request_sock *req, *head = lopt->syn_table[j];
L
Linus Torvalds 已提交
760 761 762

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

L
Linus Torvalds 已提交
765 766
			if (reqnum < s_reqnum)
				continue;
767 768
			if (r->id.idiag_dport != ireq->rmt_port &&
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
769 770 771
				continue;

			if (bc) {
772
				inet_diag_req_addrs(sk, req, &entry);
773
				entry.dport = ntohs(ireq->rmt_port);
L
Linus Torvalds 已提交
774

775
				if (!inet_diag_bc_run(bc, &entry))
L
Linus Torvalds 已提交
776 777 778
					continue;
			}

779
			err = inet_diag_fill_req(skb, sk, req,
780
					       sk_user_ns(NETLINK_CB(cb->skb).ssk),
781
					       NETLINK_CB(cb->skb).portid,
782
					       cb->nlh->nlmsg_seq, cb->nlh);
L
Linus Torvalds 已提交
783 784 785 786 787 788 789 790 791 792 793
			if (err < 0) {
				cb->args[3] = j + 1;
				cb->args[4] = reqnum;
				goto out;
			}
		}

		s_reqnum = 0;
	}

out:
794
	read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
L
Linus Torvalds 已提交
795 796 797 798

	return err;
}

799
void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
800
		struct netlink_callback *cb, struct inet_diag_req_v2 *r, struct nlattr *bc)
L
Linus Torvalds 已提交
801 802 803
{
	int i, num;
	int s_i, s_num;
A
Andrey Vagin 已提交
804
	struct net *net = sock_net(skb->sk);
805

L
Linus Torvalds 已提交
806 807
	s_i = cb->args[1];
	s_num = num = cb->args[2];
808

L
Linus Torvalds 已提交
809
	if (cb->args[0] == 0) {
810
		if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
L
Linus Torvalds 已提交
811
			goto skip_listen_ht;
812

813
		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
L
Linus Torvalds 已提交
814
			struct sock *sk;
815
			struct hlist_nulls_node *node;
816
			struct inet_listen_hashbucket *ilb;
L
Linus Torvalds 已提交
817 818

			num = 0;
819 820
			ilb = &hashinfo->listening_hash[i];
			spin_lock_bh(&ilb->lock);
821
			sk_nulls_for_each(sk, node, &ilb->head) {
L
Linus Torvalds 已提交
822 823
				struct inet_sock *inet = inet_sk(sk);

A
Andrey Vagin 已提交
824 825 826
				if (!net_eq(sock_net(sk), net))
					continue;

L
Linus Torvalds 已提交
827 828 829 830 831
				if (num < s_num) {
					num++;
					continue;
				}

832 833 834 835
				if (r->sdiag_family != AF_UNSPEC &&
						sk->sk_family != r->sdiag_family)
					goto next_listen;

E
Eric Dumazet 已提交
836
				if (r->id.idiag_sport != inet->inet_sport &&
837
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
838 839
					goto next_listen;

840 841
				if (!(r->idiag_states & TCPF_LISTEN) ||
				    r->id.idiag_dport ||
L
Linus Torvalds 已提交
842 843 844
				    cb->args[3] > 0)
					goto syn_recv;

845
				if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
846
					spin_unlock_bh(&ilb->lock);
L
Linus Torvalds 已提交
847 848 849 850
					goto done;
				}

syn_recv:
851
				if (!(r->idiag_states & TCPF_SYN_RECV))
L
Linus Torvalds 已提交
852 853
					goto next_listen;

854
				if (inet_diag_dump_reqs(skb, sk, cb, r, bc) < 0) {
855
					spin_unlock_bh(&ilb->lock);
L
Linus Torvalds 已提交
856 857 858 859 860 861 862 863
					goto done;
				}

next_listen:
				cb->args[3] = 0;
				cb->args[4] = 0;
				++num;
			}
864
			spin_unlock_bh(&ilb->lock);
L
Linus Torvalds 已提交
865 866 867 868 869 870 871 872 873 874

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

875
	if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
876
		goto out;
L
Linus Torvalds 已提交
877

878
	for (i = s_i; i <= hashinfo->ehash_mask; i++) {
879
		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
880
		spinlock_t *lock = inet_ehash_lockp(hashinfo, i);
L
Linus Torvalds 已提交
881
		struct sock *sk;
882
		struct hlist_nulls_node *node;
L
Linus Torvalds 已提交
883

884 885
		num = 0;

886 887
		if (hlist_nulls_empty(&head->chain) &&
			hlist_nulls_empty(&head->twchain))
888 889
			continue;

L
Linus Torvalds 已提交
890 891 892
		if (i > s_i)
			s_num = 0;

893
		spin_lock_bh(lock);
894
		sk_nulls_for_each(sk, node, &head->chain) {
L
Linus Torvalds 已提交
895 896
			struct inet_sock *inet = inet_sk(sk);

A
Andrey Vagin 已提交
897 898
			if (!net_eq(sock_net(sk), net))
				continue;
L
Linus Torvalds 已提交
899 900
			if (num < s_num)
				goto next_normal;
901
			if (!(r->idiag_states & (1 << sk->sk_state)))
L
Linus Torvalds 已提交
902
				goto next_normal;
903 904 905
			if (r->sdiag_family != AF_UNSPEC &&
					sk->sk_family != r->sdiag_family)
				goto next_normal;
E
Eric Dumazet 已提交
906
			if (r->id.idiag_sport != inet->inet_sport &&
907
			    r->id.idiag_sport)
L
Linus Torvalds 已提交
908
				goto next_normal;
E
Eric Dumazet 已提交
909
			if (r->id.idiag_dport != inet->inet_dport &&
910
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
911
				goto next_normal;
912
			if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
913
				spin_unlock_bh(lock);
L
Linus Torvalds 已提交
914 915 916 917 918 919
				goto done;
			}
next_normal:
			++num;
		}

920
		if (r->idiag_states & TCPF_TIME_WAIT) {
921 922 923
			struct inet_timewait_sock *tw;

			inet_twsk_for_each(tw, node,
924
				    &head->twchain) {
A
Andrey Vagin 已提交
925 926
				if (!net_eq(twsk_net(tw), net))
					continue;
L
Linus Torvalds 已提交
927 928 929

				if (num < s_num)
					goto next_dying;
930 931 932
				if (r->sdiag_family != AF_UNSPEC &&
						tw->tw_family != r->sdiag_family)
					goto next_dying;
933
				if (r->id.idiag_sport != tw->tw_sport &&
934
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
935
					goto next_dying;
936
				if (r->id.idiag_dport != tw->tw_dport &&
937
				    r->id.idiag_dport)
L
Linus Torvalds 已提交
938
					goto next_dying;
939
				if (inet_twsk_diag_dump(tw, skb, cb, r, bc) < 0) {
940
					spin_unlock_bh(lock);
L
Linus Torvalds 已提交
941 942 943 944 945 946
					goto done;
				}
next_dying:
				++num;
			}
		}
947
		spin_unlock_bh(lock);
L
Linus Torvalds 已提交
948 949 950 951 952
	}

done:
	cb->args[1] = i;
	cb->args[2] = num;
953 954 955
out:
	;
}
956
EXPORT_SYMBOL_GPL(inet_diag_dump_icsk);
957 958

static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
959
		struct inet_diag_req_v2 *r, struct nlattr *bc)
960 961
{
	const struct inet_diag_handler *handler;
962
	int err = 0;
963 964 965

	handler = inet_diag_lock_handler(r->sdiag_protocol);
	if (!IS_ERR(handler))
966
		handler->dump(skb, cb, r, bc);
967 968
	else
		err = PTR_ERR(handler);
969
	inet_diag_unlock_handler(handler);
970

971
	return err ? : skb->len;
L
Linus Torvalds 已提交
972 973
}

974 975 976
static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct nlattr *bc = NULL;
977
	int hdrlen = sizeof(struct inet_diag_req_v2);
978 979 980 981

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

982
	return __inet_diag_dump(skb, cb, nlmsg_data(cb->nlh), bc);
983 984
}

985 986 987 988 989 990 991 992 993 994 995 996
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;
	}
}

997 998
static int inet_diag_dump_compat(struct sk_buff *skb, struct netlink_callback *cb)
{
999
	struct inet_diag_req *rc = nlmsg_data(cb->nlh);
1000
	struct inet_diag_req_v2 req;
1001
	struct nlattr *bc = NULL;
1002
	int hdrlen = sizeof(struct inet_diag_req);
1003

1004
	req.sdiag_family = AF_UNSPEC; /* compatibility */
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
	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);
}

1016 1017 1018
static int inet_diag_get_exact_compat(struct sk_buff *in_skb,
			       const struct nlmsghdr *nlh)
{
1019
	struct inet_diag_req *rc = nlmsg_data(nlh);
1020
	struct inet_diag_req_v2 req;
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030

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

1031
static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
1032
{
1033
	int hdrlen = sizeof(struct inet_diag_req);
A
Andrey Vagin 已提交
1034
	struct net *net = sock_net(skb->sk);
L
Linus Torvalds 已提交
1035

1036 1037 1038
	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX ||
	    nlmsg_len(nlh) < hdrlen)
		return -EINVAL;
L
Linus Torvalds 已提交
1039

1040
	if (nlh->nlmsg_flags & NLM_F_DUMP) {
1041 1042
		if (nlmsg_attrlen(nlh, hdrlen)) {
			struct nlattr *attr;
L
Linus Torvalds 已提交
1043

1044 1045 1046 1047 1048 1049 1050
			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;
		}
1051 1052 1053 1054
		{
			struct netlink_dump_control c = {
				.dump = inet_diag_dump_compat,
			};
A
Andrey Vagin 已提交
1055
			return netlink_dump_start(net->diag_nlsk, skb, nlh, &c);
1056
		}
L
Linus Torvalds 已提交
1057
	}
1058

1059
	return inet_diag_get_exact_compat(skb, nlh);
L
Linus Torvalds 已提交
1060 1061
}

P
Pavel Emelyanov 已提交
1062 1063
static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
{
1064
	int hdrlen = sizeof(struct inet_diag_req_v2);
A
Andrey Vagin 已提交
1065
	struct net *net = sock_net(skb->sk);
P
Pavel Emelyanov 已提交
1066 1067 1068 1069 1070

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

	if (h->nlmsg_flags & NLM_F_DUMP) {
1071 1072 1073 1074 1075 1076 1077 1078 1079
		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;
		}
1080 1081 1082 1083
		{
			struct netlink_dump_control c = {
				.dump = inet_diag_dump,
			};
A
Andrey Vagin 已提交
1084
			return netlink_dump_start(net->diag_nlsk, skb, h, &c);
1085
		}
P
Pavel Emelyanov 已提交
1086 1087
	}

1088
	return inet_diag_get_exact(skb, h, nlmsg_data(h));
P
Pavel Emelyanov 已提交
1089 1090
}

1091
static const struct sock_diag_handler inet_diag_handler = {
P
Pavel Emelyanov 已提交
1092 1093 1094 1095
	.family = AF_INET,
	.dump = inet_diag_handler_dump,
};

1096
static const struct sock_diag_handler inet6_diag_handler = {
P
Pavel Emelyanov 已提交
1097 1098 1099 1100
	.family = AF_INET6,
	.dump = inet_diag_handler_dump,
};

1101 1102 1103 1104 1105
int inet_diag_register(const struct inet_diag_handler *h)
{
	const __u16 type = h->idiag_type;
	int err = -EINVAL;

1106
	if (type >= IPPROTO_MAX)
1107 1108
		goto out;

1109
	mutex_lock(&inet_diag_table_mutex);
1110 1111 1112 1113 1114
	err = -EEXIST;
	if (inet_diag_table[type] == NULL) {
		inet_diag_table[type] = h;
		err = 0;
	}
1115
	mutex_unlock(&inet_diag_table_mutex);
1116 1117 1118 1119 1120 1121 1122 1123 1124
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;

1125
	if (type >= IPPROTO_MAX)
1126 1127
		return;

1128
	mutex_lock(&inet_diag_table_mutex);
1129
	inet_diag_table[type] = NULL;
1130
	mutex_unlock(&inet_diag_table_mutex);
1131 1132 1133
}
EXPORT_SYMBOL_GPL(inet_diag_unregister);

1134
static int __init inet_diag_init(void)
L
Linus Torvalds 已提交
1135
{
1136
	const int inet_diag_table_size = (IPPROTO_MAX *
1137 1138 1139
					  sizeof(struct inet_diag_handler *));
	int err = -ENOMEM;

1140
	inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL);
1141 1142 1143
	if (!inet_diag_table)
		goto out;

P
Pavel Emelyanov 已提交
1144 1145 1146 1147 1148 1149 1150 1151
	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;

1152
	sock_diag_register_inet_compat(inet_diag_rcv_msg_compat);
1153 1154
out:
	return err;
P
Pavel Emelyanov 已提交
1155 1156 1157 1158

out_free_inet:
	sock_diag_unregister(&inet_diag_handler);
out_free_nl:
1159 1160
	kfree(inet_diag_table);
	goto out;
L
Linus Torvalds 已提交
1161 1162
}

1163
static void __exit inet_diag_exit(void)
L
Linus Torvalds 已提交
1164
{
P
Pavel Emelyanov 已提交
1165 1166
	sock_diag_unregister(&inet6_diag_handler);
	sock_diag_unregister(&inet_diag_handler);
1167
	sock_diag_unregister_inet_compat(inet_diag_rcv_msg_compat);
1168
	kfree(inet_diag_table);
L
Linus Torvalds 已提交
1169 1170
}

1171 1172
module_init(inet_diag_init);
module_exit(inet_diag_exit);
L
Linus Torvalds 已提交
1173
MODULE_LICENSE("GPL");
1174 1175
MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2 /* AF_INET */);
MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10 /* AF_INET6 */);