inet_diag.c 28.0 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

435
			if (op->code == INET_DIAG_BC_S_COND)
L
Linus Torvalds 已提交
436 437 438 439
				addr = entry->saddr;
			else
				addr = entry->daddr;

440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
			if (cond->family != AF_UNSPEC &&
			    cond->family != entry->family) {
				if (entry->family == AF_INET6 &&
				    cond->family == AF_INET) {
					if (addr[0] == 0 && addr[1] == 0 &&
					    addr[2] == htonl(0xffff) &&
					    bitstring_match(addr + 3,
							    cond->addr,
							    cond->prefix_len))
						break;
				}
				yes = 0;
				break;
			}

			if (cond->prefix_len == 0)
				break;
457 458
			if (bitstring_match(addr, cond->addr,
					    cond->prefix_len))
L
Linus Torvalds 已提交
459 460 461 462 463 464
				break;
			yes = 0;
			break;
		}
		}

465
		if (yes) {
L
Linus Torvalds 已提交
466 467 468 469 470 471 472
			len -= op->yes;
			bc += op->yes;
		} else {
			len -= op->no;
			bc += op->no;
		}
	}
E
Eric Dumazet 已提交
473
	return len == 0;
L
Linus Torvalds 已提交
474 475
}

476 477 478 479 480 481 482 483 484
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 已提交
485
#if IS_ENABLED(CONFIG_IPV6)
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
	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 已提交
505 506 507
static int valid_cc(const void *bc, int len, int cc)
{
	while (len >= 0) {
508
		const struct inet_diag_bc_op *op = bc;
L
Linus Torvalds 已提交
509 510 511 512 513

		if (cc > len)
			return 0;
		if (cc == len)
			return 1;
514
		if (op->yes < 4 || op->yes & 3)
L
Linus Torvalds 已提交
515 516 517 518 519 520 521
			return 0;
		len -= op->yes;
		bc  += op->yes;
	}
	return 0;
}

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 554 555 556 557 558 559
/* 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;
}

560
static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
L
Linus Torvalds 已提交
561
{
562
	const void *bc = bytecode;
L
Linus Torvalds 已提交
563 564 565
	int  len = bytecode_len;

	while (len > 0) {
566
		const struct inet_diag_bc_op *op = bc;
567
		int min_len = sizeof(struct inet_diag_bc_op);
L
Linus Torvalds 已提交
568 569 570

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

601 602
static int inet_csk_diag_dump(struct sock *sk,
			      struct sk_buff *skb,
603
			      struct netlink_callback *cb,
604
			      struct inet_diag_req_v2 *r,
605
			      const struct nlattr *bc)
L
Linus Torvalds 已提交
606
{
607 608
	if (!inet_diag_bc_sk(bc, sk))
		return 0;
L
Linus Torvalds 已提交
609

610
	return inet_csk_diag_fill(sk, skb, r,
611
				  sk_user_ns(NETLINK_CB(cb->skb).ssk),
612
				  NETLINK_CB(cb->skb).portid,
613
				  cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
L
Linus Torvalds 已提交
614 615
}

616 617
static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
			       struct sk_buff *skb,
618
			       struct netlink_callback *cb,
619
			       struct inet_diag_req_v2 *r,
620
			       const struct nlattr *bc)
621
{
622
	if (bc != NULL) {
623 624 625
		struct inet_diag_entry entry;

		entry.family = tw->tw_family;
E
Eric Dumazet 已提交
626
#if IS_ENABLED(CONFIG_IPV6)
627 628 629 630 631 632 633 634 635 636 637 638 639
		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);
640
		entry.userlocks = 0;
641

642
		if (!inet_diag_bc_run(bc, &entry))
643 644 645
			return 0;
	}

646
	return inet_twsk_diag_fill(tw, skb, r,
647
				   NETLINK_CB(cb->skb).portid,
648 649 650
				   cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
}

651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
/* 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;
	}
}

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

693
	nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
694 695 696
			NLM_F_MULTI);
	if (!nlh)
		return -EMSGSIZE;
L
Linus Torvalds 已提交
697

698
	r = nlmsg_data(nlh);
699 700 701 702
	r->idiag_family = sk->sk_family;
	r->idiag_state = TCP_SYN_RECV;
	r->idiag_timer = 1;
	r->idiag_retrans = req->retrans;
L
Linus Torvalds 已提交
703

704
	r->id.idiag_if = sk->sk_bound_dev_if;
705
	sock_diag_save_cookie(req, r->id.idiag_cookie);
L
Linus Torvalds 已提交
706 707 708 709 710

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

E
Eric Dumazet 已提交
711
	r->id.idiag_sport = inet->inet_sport;
712 713 714 715 716 717
	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;
718
	r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
719
	r->idiag_inode = 0;
E
Eric Dumazet 已提交
720
#if IS_ENABLED(CONFIG_IPV6)
721
	if (r->idiag_family == AF_INET6) {
722 723 724 725
		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 已提交
726 727 728
	}
#endif

729
	return nlmsg_end(skb, nlh);
L
Linus Torvalds 已提交
730 731
}

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

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

755
	lopt = icsk->icsk_accept_queue.listen_opt;
L
Linus Torvalds 已提交
756 757 758
	if (!lopt || !lopt->qlen)
		goto out;

759
	if (bc != NULL) {
E
Eric Dumazet 已提交
760
		entry.sport = inet->inet_num;
L
Linus Torvalds 已提交
761 762 763
		entry.userlocks = sk->sk_userlocks;
	}

764
	for (j = s_j; j < lopt->nr_table_entries; j++) {
765
		struct request_sock *req, *head = lopt->syn_table[j];
L
Linus Torvalds 已提交
766 767 768

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

L
Linus Torvalds 已提交
771 772
			if (reqnum < s_reqnum)
				continue;
773 774
			if (r->id.idiag_dport != ireq->rmt_port &&
			    r->id.idiag_dport)
L
Linus Torvalds 已提交
775 776 777
				continue;

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

781
				if (!inet_diag_bc_run(bc, &entry))
L
Linus Torvalds 已提交
782 783 784
					continue;
			}

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

		s_reqnum = 0;
	}

out:
800
	read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
L
Linus Torvalds 已提交
801 802 803 804

	return err;
}

805
void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
806
		struct netlink_callback *cb, struct inet_diag_req_v2 *r, struct nlattr *bc)
L
Linus Torvalds 已提交
807 808 809
{
	int i, num;
	int s_i, s_num;
A
Andrey Vagin 已提交
810
	struct net *net = sock_net(skb->sk);
811

L
Linus Torvalds 已提交
812 813
	s_i = cb->args[1];
	s_num = num = cb->args[2];
814

L
Linus Torvalds 已提交
815
	if (cb->args[0] == 0) {
816
		if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
L
Linus Torvalds 已提交
817
			goto skip_listen_ht;
818

819
		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
L
Linus Torvalds 已提交
820
			struct sock *sk;
821
			struct hlist_nulls_node *node;
822
			struct inet_listen_hashbucket *ilb;
L
Linus Torvalds 已提交
823 824

			num = 0;
825 826
			ilb = &hashinfo->listening_hash[i];
			spin_lock_bh(&ilb->lock);
827
			sk_nulls_for_each(sk, node, &ilb->head) {
L
Linus Torvalds 已提交
828 829
				struct inet_sock *inet = inet_sk(sk);

A
Andrey Vagin 已提交
830 831 832
				if (!net_eq(sock_net(sk), net))
					continue;

L
Linus Torvalds 已提交
833 834 835 836 837
				if (num < s_num) {
					num++;
					continue;
				}

838 839 840 841
				if (r->sdiag_family != AF_UNSPEC &&
						sk->sk_family != r->sdiag_family)
					goto next_listen;

E
Eric Dumazet 已提交
842
				if (r->id.idiag_sport != inet->inet_sport &&
843
				    r->id.idiag_sport)
L
Linus Torvalds 已提交
844 845
					goto next_listen;

846 847
				if (!(r->idiag_states & TCPF_LISTEN) ||
				    r->id.idiag_dport ||
L
Linus Torvalds 已提交
848 849 850
				    cb->args[3] > 0)
					goto syn_recv;

851
				if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
852
					spin_unlock_bh(&ilb->lock);
L
Linus Torvalds 已提交
853 854 855 856
					goto done;
				}

syn_recv:
857
				if (!(r->idiag_states & TCPF_SYN_RECV))
L
Linus Torvalds 已提交
858 859
					goto next_listen;

860
				if (inet_diag_dump_reqs(skb, sk, cb, r, bc) < 0) {
861
					spin_unlock_bh(&ilb->lock);
L
Linus Torvalds 已提交
862 863 864 865 866 867 868 869
					goto done;
				}

next_listen:
				cb->args[3] = 0;
				cb->args[4] = 0;
				++num;
			}
870
			spin_unlock_bh(&ilb->lock);
L
Linus Torvalds 已提交
871 872 873 874 875 876 877 878 879 880

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

881
	if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
882
		goto out;
L
Linus Torvalds 已提交
883

884
	for (i = s_i; i <= hashinfo->ehash_mask; i++) {
885
		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
886
		spinlock_t *lock = inet_ehash_lockp(hashinfo, i);
L
Linus Torvalds 已提交
887
		struct sock *sk;
888
		struct hlist_nulls_node *node;
L
Linus Torvalds 已提交
889

890 891
		num = 0;

892 893
		if (hlist_nulls_empty(&head->chain) &&
			hlist_nulls_empty(&head->twchain))
894 895
			continue;

L
Linus Torvalds 已提交
896 897 898
		if (i > s_i)
			s_num = 0;

899
		spin_lock_bh(lock);
900
		sk_nulls_for_each(sk, node, &head->chain) {
L
Linus Torvalds 已提交
901 902
			struct inet_sock *inet = inet_sk(sk);

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

926
		if (r->idiag_states & TCPF_TIME_WAIT) {
927 928 929
			struct inet_timewait_sock *tw;

			inet_twsk_for_each(tw, node,
930
				    &head->twchain) {
A
Andrey Vagin 已提交
931 932
				if (!net_eq(twsk_net(tw), net))
					continue;
L
Linus Torvalds 已提交
933 934 935

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

done:
	cb->args[1] = i;
	cb->args[2] = num;
959 960 961
out:
	;
}
962
EXPORT_SYMBOL_GPL(inet_diag_dump_icsk);
963 964

static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
965
		struct inet_diag_req_v2 *r, struct nlattr *bc)
966 967
{
	const struct inet_diag_handler *handler;
968
	int err = 0;
969 970 971

	handler = inet_diag_lock_handler(r->sdiag_protocol);
	if (!IS_ERR(handler))
972
		handler->dump(skb, cb, r, bc);
973 974
	else
		err = PTR_ERR(handler);
975
	inet_diag_unlock_handler(handler);
976

977
	return err ? : skb->len;
L
Linus Torvalds 已提交
978 979
}

980 981 982
static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct nlattr *bc = NULL;
983
	int hdrlen = sizeof(struct inet_diag_req_v2);
984 985 986 987

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

988
	return __inet_diag_dump(skb, cb, nlmsg_data(cb->nlh), bc);
989 990
}

991 992 993 994 995 996 997 998 999 1000 1001 1002
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;
	}
}

1003 1004
static int inet_diag_dump_compat(struct sk_buff *skb, struct netlink_callback *cb)
{
1005
	struct inet_diag_req *rc = nlmsg_data(cb->nlh);
1006
	struct inet_diag_req_v2 req;
1007
	struct nlattr *bc = NULL;
1008
	int hdrlen = sizeof(struct inet_diag_req);
1009

1010
	req.sdiag_family = AF_UNSPEC; /* compatibility */
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
	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);
}

1022 1023 1024
static int inet_diag_get_exact_compat(struct sk_buff *in_skb,
			       const struct nlmsghdr *nlh)
{
1025
	struct inet_diag_req *rc = nlmsg_data(nlh);
1026
	struct inet_diag_req_v2 req;
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036

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

1037
static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
L
Linus Torvalds 已提交
1038
{
1039
	int hdrlen = sizeof(struct inet_diag_req);
A
Andrey Vagin 已提交
1040
	struct net *net = sock_net(skb->sk);
L
Linus Torvalds 已提交
1041

1042 1043 1044
	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX ||
	    nlmsg_len(nlh) < hdrlen)
		return -EINVAL;
L
Linus Torvalds 已提交
1045

1046
	if (nlh->nlmsg_flags & NLM_F_DUMP) {
1047 1048
		if (nlmsg_attrlen(nlh, hdrlen)) {
			struct nlattr *attr;
L
Linus Torvalds 已提交
1049

1050 1051 1052 1053 1054 1055 1056
			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;
		}
1057 1058 1059 1060
		{
			struct netlink_dump_control c = {
				.dump = inet_diag_dump_compat,
			};
A
Andrey Vagin 已提交
1061
			return netlink_dump_start(net->diag_nlsk, skb, nlh, &c);
1062
		}
L
Linus Torvalds 已提交
1063
	}
1064

1065
	return inet_diag_get_exact_compat(skb, nlh);
L
Linus Torvalds 已提交
1066 1067
}

P
Pavel Emelyanov 已提交
1068 1069
static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
{
1070
	int hdrlen = sizeof(struct inet_diag_req_v2);
A
Andrey Vagin 已提交
1071
	struct net *net = sock_net(skb->sk);
P
Pavel Emelyanov 已提交
1072 1073 1074 1075 1076

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

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

1094
	return inet_diag_get_exact(skb, h, nlmsg_data(h));
P
Pavel Emelyanov 已提交
1095 1096
}

1097
static const struct sock_diag_handler inet_diag_handler = {
P
Pavel Emelyanov 已提交
1098 1099 1100 1101
	.family = AF_INET,
	.dump = inet_diag_handler_dump,
};

1102
static const struct sock_diag_handler inet6_diag_handler = {
P
Pavel Emelyanov 已提交
1103 1104 1105 1106
	.family = AF_INET6,
	.dump = inet_diag_handler_dump,
};

1107 1108 1109 1110 1111
int inet_diag_register(const struct inet_diag_handler *h)
{
	const __u16 type = h->idiag_type;
	int err = -EINVAL;

1112
	if (type >= IPPROTO_MAX)
1113 1114
		goto out;

1115
	mutex_lock(&inet_diag_table_mutex);
1116 1117 1118 1119 1120
	err = -EEXIST;
	if (inet_diag_table[type] == NULL) {
		inet_diag_table[type] = h;
		err = 0;
	}
1121
	mutex_unlock(&inet_diag_table_mutex);
1122 1123 1124 1125 1126 1127 1128 1129 1130
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;

1131
	if (type >= IPPROTO_MAX)
1132 1133
		return;

1134
	mutex_lock(&inet_diag_table_mutex);
1135
	inet_diag_table[type] = NULL;
1136
	mutex_unlock(&inet_diag_table_mutex);
1137 1138 1139
}
EXPORT_SYMBOL_GPL(inet_diag_unregister);

1140
static int __init inet_diag_init(void)
L
Linus Torvalds 已提交
1141
{
1142
	const int inet_diag_table_size = (IPPROTO_MAX *
1143 1144 1145
					  sizeof(struct inet_diag_handler *));
	int err = -ENOMEM;

1146
	inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL);
1147 1148 1149
	if (!inet_diag_table)
		goto out;

P
Pavel Emelyanov 已提交
1150 1151 1152 1153 1154 1155 1156 1157
	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;

1158
	sock_diag_register_inet_compat(inet_diag_rcv_msg_compat);
1159 1160
out:
	return err;
P
Pavel Emelyanov 已提交
1161 1162 1163 1164

out_free_inet:
	sock_diag_unregister(&inet_diag_handler);
out_free_nl:
1165 1166
	kfree(inet_diag_table);
	goto out;
L
Linus Torvalds 已提交
1167 1168
}

1169
static void __exit inet_diag_exit(void)
L
Linus Torvalds 已提交
1170
{
P
Pavel Emelyanov 已提交
1171 1172
	sock_diag_unregister(&inet6_diag_handler);
	sock_diag_unregister(&inet_diag_handler);
1173
	sock_diag_unregister_inet_compat(inet_diag_rcv_msg_compat);
1174
	kfree(inet_diag_table);
L
Linus Torvalds 已提交
1175 1176
}

1177 1178
module_init(inet_diag_init);
module_exit(inet_diag_exit);
L
Linus Torvalds 已提交
1179
MODULE_LICENSE("GPL");
1180 1181
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 */);