xref: /openbmc/linux/net/core/sock_diag.c (revision 96ba0fc3)
1b6191aeeSPaul Gortmaker /* License: GPL */
2b6191aeeSPaul Gortmaker 
3b6459415SJakub Kicinski #include <linux/filter.h>
48ef874bfSPavel Emelyanov #include <linux/mutex.h>
58ef874bfSPavel Emelyanov #include <linux/socket.h>
68ef874bfSPavel Emelyanov #include <linux/skbuff.h>
78ef874bfSPavel Emelyanov #include <net/netlink.h>
88ef874bfSPavel Emelyanov #include <net/net_namespace.h>
98ef874bfSPavel Emelyanov #include <linux/module.h>
105d2e5f27SPavel Emelyanov #include <net/sock.h>
11eb4cb008SCraig Gallek #include <linux/kernel.h>
12eb4cb008SCraig Gallek #include <linux/tcp.h>
13eb4cb008SCraig Gallek #include <linux/workqueue.h>
1466b51b0aSJeremy Cline #include <linux/nospec.h>
1592acdc58SDaniel Borkmann #include <linux/cookie.h>
168ef874bfSPavel Emelyanov #include <linux/inet_diag.h>
178ef874bfSPavel Emelyanov #include <linux/sock_diag.h>
188ef874bfSPavel Emelyanov 
198dcf01fcSShan Wei static const struct sock_diag_handler *sock_diag_handlers[AF_MAX];
208ef874bfSPavel Emelyanov static int (*inet_rcv_compat)(struct sk_buff *skb, struct nlmsghdr *nlh);
218ef874bfSPavel Emelyanov static DEFINE_MUTEX(sock_diag_table_mutex);
22eb4cb008SCraig Gallek static struct workqueue_struct *broadcast_wq;
238ef874bfSPavel Emelyanov 
2492acdc58SDaniel Borkmann DEFINE_COOKIE(sock_cookie);
2592acdc58SDaniel Borkmann 
__sock_gen_cookie(struct sock * sk)2692acdc58SDaniel Borkmann u64 __sock_gen_cookie(struct sock *sk)
27f65c1b53SPavel Emelyanov {
2833cf7c90SEric Dumazet 	u64 res = atomic64_read(&sk->sk_cookie);
2933cf7c90SEric Dumazet 
304ebf802cSEric Dumazet 	if (!res) {
314ebf802cSEric Dumazet 		u64 new = gen_cookie_next(&sock_cookie);
324ebf802cSEric Dumazet 
3332634819SEric Dumazet 		atomic64_cmpxchg(&sk->sk_cookie, res, new);
3432634819SEric Dumazet 
3532634819SEric Dumazet 		/* Another thread might have changed sk_cookie before us. */
3632634819SEric Dumazet 		res = atomic64_read(&sk->sk_cookie);
3733cf7c90SEric Dumazet 	}
384ebf802cSEric Dumazet 	return res;
3933cf7c90SEric Dumazet }
4033cf7c90SEric Dumazet 
sock_diag_check_cookie(struct sock * sk,const __u32 * cookie)4133cf7c90SEric Dumazet int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie)
4233cf7c90SEric Dumazet {
4333cf7c90SEric Dumazet 	u64 res;
4433cf7c90SEric Dumazet 
4533cf7c90SEric Dumazet 	if (cookie[0] == INET_DIAG_NOCOOKIE && cookie[1] == INET_DIAG_NOCOOKIE)
4633cf7c90SEric Dumazet 		return 0;
4733cf7c90SEric Dumazet 
4833cf7c90SEric Dumazet 	res = sock_gen_cookie(sk);
4933cf7c90SEric Dumazet 	if ((u32)res != cookie[0] || (u32)(res >> 32) != cookie[1])
50f65c1b53SPavel Emelyanov 		return -ESTALE;
5133cf7c90SEric Dumazet 
52f65c1b53SPavel Emelyanov 	return 0;
53f65c1b53SPavel Emelyanov }
54f65c1b53SPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_check_cookie);
55f65c1b53SPavel Emelyanov 
sock_diag_save_cookie(struct sock * sk,__u32 * cookie)5633cf7c90SEric Dumazet void sock_diag_save_cookie(struct sock *sk, __u32 *cookie)
57f65c1b53SPavel Emelyanov {
5833cf7c90SEric Dumazet 	u64 res = sock_gen_cookie(sk);
5933cf7c90SEric Dumazet 
6033cf7c90SEric Dumazet 	cookie[0] = (u32)res;
6133cf7c90SEric Dumazet 	cookie[1] = (u32)(res >> 32);
62f65c1b53SPavel Emelyanov }
63f65c1b53SPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_save_cookie);
64f65c1b53SPavel Emelyanov 
sock_diag_put_meminfo(struct sock * sk,struct sk_buff * skb,int attrtype)655d2e5f27SPavel Emelyanov int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attrtype)
665d2e5f27SPavel Emelyanov {
677b46866dSThomas Graf 	u32 mem[SK_MEMINFO_VARS];
685d2e5f27SPavel Emelyanov 
69a2d133b1SJosh Hunt 	sk_get_meminfo(sk, mem);
705d2e5f27SPavel Emelyanov 
717b46866dSThomas Graf 	return nla_put(skb, attrtype, sizeof(mem), &mem);
725d2e5f27SPavel Emelyanov }
735d2e5f27SPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_put_meminfo);
745d2e5f27SPavel Emelyanov 
sock_diag_put_filterinfo(bool may_report_filterinfo,struct sock * sk,struct sk_buff * skb,int attrtype)75a53b72c8SEric W. Biederman int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk,
76e8d9612cSNicolas Dichtel 			     struct sk_buff *skb, int attrtype)
77e8d9612cSNicolas Dichtel {
78a3ea269bSDaniel Borkmann 	struct sock_fprog_kern *fprog;
79e8d9612cSNicolas Dichtel 	struct sk_filter *filter;
80a3ea269bSDaniel Borkmann 	struct nlattr *attr;
81a3ea269bSDaniel Borkmann 	unsigned int flen;
82e8d9612cSNicolas Dichtel 	int err = 0;
83e8d9612cSNicolas Dichtel 
84a53b72c8SEric W. Biederman 	if (!may_report_filterinfo) {
85e8d9612cSNicolas Dichtel 		nla_reserve(skb, attrtype, 0);
86e8d9612cSNicolas Dichtel 		return 0;
87e8d9612cSNicolas Dichtel 	}
88e8d9612cSNicolas Dichtel 
89e8d9612cSNicolas Dichtel 	rcu_read_lock();
90e8d9612cSNicolas Dichtel 	filter = rcu_dereference(sk->sk_filter);
91a3ea269bSDaniel Borkmann 	if (!filter)
92a3ea269bSDaniel Borkmann 		goto out;
93e8d9612cSNicolas Dichtel 
947ae457c1SAlexei Starovoitov 	fprog = filter->prog->orig_prog;
95b382c086SDaniel Borkmann 	if (!fprog)
96b382c086SDaniel Borkmann 		goto out;
97b382c086SDaniel Borkmann 
98009937e7SAlexei Starovoitov 	flen = bpf_classic_proglen(fprog);
99a3ea269bSDaniel Borkmann 
100a3ea269bSDaniel Borkmann 	attr = nla_reserve(skb, attrtype, flen);
101e8d9612cSNicolas Dichtel 	if (attr == NULL) {
102e8d9612cSNicolas Dichtel 		err = -EMSGSIZE;
103e8d9612cSNicolas Dichtel 		goto out;
104e8d9612cSNicolas Dichtel 	}
105e8d9612cSNicolas Dichtel 
106a3ea269bSDaniel Borkmann 	memcpy(nla_data(attr), fprog->filter, flen);
107e8d9612cSNicolas Dichtel out:
108e8d9612cSNicolas Dichtel 	rcu_read_unlock();
109e8d9612cSNicolas Dichtel 	return err;
110e8d9612cSNicolas Dichtel }
111e8d9612cSNicolas Dichtel EXPORT_SYMBOL(sock_diag_put_filterinfo);
112e8d9612cSNicolas Dichtel 
113eb4cb008SCraig Gallek struct broadcast_sk {
114eb4cb008SCraig Gallek 	struct sock *sk;
115eb4cb008SCraig Gallek 	struct work_struct work;
116eb4cb008SCraig Gallek };
117eb4cb008SCraig Gallek 
sock_diag_nlmsg_size(void)118eb4cb008SCraig Gallek static size_t sock_diag_nlmsg_size(void)
119eb4cb008SCraig Gallek {
120eb4cb008SCraig Gallek 	return NLMSG_ALIGN(sizeof(struct inet_diag_msg)
121eb4cb008SCraig Gallek 	       + nla_total_size(sizeof(u8)) /* INET_DIAG_PROTOCOL */
1226ed46d12SNicolas Dichtel 	       + nla_total_size_64bit(sizeof(struct tcp_info))); /* INET_DIAG_INFO */
123eb4cb008SCraig Gallek }
124eb4cb008SCraig Gallek 
sock_diag_broadcast_destroy_work(struct work_struct * work)125eb4cb008SCraig Gallek static void sock_diag_broadcast_destroy_work(struct work_struct *work)
126eb4cb008SCraig Gallek {
127eb4cb008SCraig Gallek 	struct broadcast_sk *bsk =
128eb4cb008SCraig Gallek 		container_of(work, struct broadcast_sk, work);
129eb4cb008SCraig Gallek 	struct sock *sk = bsk->sk;
130eb4cb008SCraig Gallek 	const struct sock_diag_handler *hndl;
131eb4cb008SCraig Gallek 	struct sk_buff *skb;
132eb4cb008SCraig Gallek 	const enum sknetlink_groups group = sock_diag_destroy_group(sk);
133eb4cb008SCraig Gallek 	int err = -1;
134eb4cb008SCraig Gallek 
135eb4cb008SCraig Gallek 	WARN_ON(group == SKNLGRP_NONE);
136eb4cb008SCraig Gallek 
137eb4cb008SCraig Gallek 	skb = nlmsg_new(sock_diag_nlmsg_size(), GFP_KERNEL);
138eb4cb008SCraig Gallek 	if (!skb)
139eb4cb008SCraig Gallek 		goto out;
140eb4cb008SCraig Gallek 
141eb4cb008SCraig Gallek 	mutex_lock(&sock_diag_table_mutex);
142eb4cb008SCraig Gallek 	hndl = sock_diag_handlers[sk->sk_family];
143eb4cb008SCraig Gallek 	if (hndl && hndl->get_info)
144eb4cb008SCraig Gallek 		err = hndl->get_info(skb, sk);
145eb4cb008SCraig Gallek 	mutex_unlock(&sock_diag_table_mutex);
146eb4cb008SCraig Gallek 
147eb4cb008SCraig Gallek 	if (!err)
148eb4cb008SCraig Gallek 		nlmsg_multicast(sock_net(sk)->diag_nlsk, skb, 0, group,
149eb4cb008SCraig Gallek 				GFP_KERNEL);
150eb4cb008SCraig Gallek 	else
151eb4cb008SCraig Gallek 		kfree_skb(skb);
152eb4cb008SCraig Gallek out:
153eb4cb008SCraig Gallek 	sk_destruct(sk);
154eb4cb008SCraig Gallek 	kfree(bsk);
155eb4cb008SCraig Gallek }
156eb4cb008SCraig Gallek 
sock_diag_broadcast_destroy(struct sock * sk)157eb4cb008SCraig Gallek void sock_diag_broadcast_destroy(struct sock *sk)
158eb4cb008SCraig Gallek {
159eb4cb008SCraig Gallek 	/* Note, this function is often called from an interrupt context. */
160eb4cb008SCraig Gallek 	struct broadcast_sk *bsk =
161eb4cb008SCraig Gallek 		kmalloc(sizeof(struct broadcast_sk), GFP_ATOMIC);
162eb4cb008SCraig Gallek 	if (!bsk)
163eb4cb008SCraig Gallek 		return sk_destruct(sk);
164eb4cb008SCraig Gallek 	bsk->sk = sk;
165eb4cb008SCraig Gallek 	INIT_WORK(&bsk->work, sock_diag_broadcast_destroy_work);
166eb4cb008SCraig Gallek 	queue_work(broadcast_wq, &bsk->work);
167eb4cb008SCraig Gallek }
168eb4cb008SCraig Gallek 
sock_diag_register_inet_compat(int (* fn)(struct sk_buff * skb,struct nlmsghdr * nlh))1698ef874bfSPavel Emelyanov void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh))
1708ef874bfSPavel Emelyanov {
1718ef874bfSPavel Emelyanov 	mutex_lock(&sock_diag_table_mutex);
1728ef874bfSPavel Emelyanov 	inet_rcv_compat = fn;
1738ef874bfSPavel Emelyanov 	mutex_unlock(&sock_diag_table_mutex);
1748ef874bfSPavel Emelyanov }
1758ef874bfSPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_register_inet_compat);
1768ef874bfSPavel Emelyanov 
sock_diag_unregister_inet_compat(int (* fn)(struct sk_buff * skb,struct nlmsghdr * nlh))1778ef874bfSPavel Emelyanov void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh))
1788ef874bfSPavel Emelyanov {
1798ef874bfSPavel Emelyanov 	mutex_lock(&sock_diag_table_mutex);
1808ef874bfSPavel Emelyanov 	inet_rcv_compat = NULL;
1818ef874bfSPavel Emelyanov 	mutex_unlock(&sock_diag_table_mutex);
1828ef874bfSPavel Emelyanov }
1838ef874bfSPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_unregister_inet_compat);
1848ef874bfSPavel Emelyanov 
sock_diag_register(const struct sock_diag_handler * hndl)1858dcf01fcSShan Wei int sock_diag_register(const struct sock_diag_handler *hndl)
1868ef874bfSPavel Emelyanov {
1878ef874bfSPavel Emelyanov 	int err = 0;
1888ef874bfSPavel Emelyanov 
1896f8e4ad0SDan Carpenter 	if (hndl->family >= AF_MAX)
1908ef874bfSPavel Emelyanov 		return -EINVAL;
1918ef874bfSPavel Emelyanov 
1928ef874bfSPavel Emelyanov 	mutex_lock(&sock_diag_table_mutex);
1938ef874bfSPavel Emelyanov 	if (sock_diag_handlers[hndl->family])
1948ef874bfSPavel Emelyanov 		err = -EBUSY;
1958ef874bfSPavel Emelyanov 	else
196*96ba0fc3SEric Dumazet 		WRITE_ONCE(sock_diag_handlers[hndl->family], hndl);
1978ef874bfSPavel Emelyanov 	mutex_unlock(&sock_diag_table_mutex);
1988ef874bfSPavel Emelyanov 
1998ef874bfSPavel Emelyanov 	return err;
2008ef874bfSPavel Emelyanov }
2018ef874bfSPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_register);
2028ef874bfSPavel Emelyanov 
sock_diag_unregister(const struct sock_diag_handler * hnld)2038dcf01fcSShan Wei void sock_diag_unregister(const struct sock_diag_handler *hnld)
2048ef874bfSPavel Emelyanov {
2058ef874bfSPavel Emelyanov 	int family = hnld->family;
2068ef874bfSPavel Emelyanov 
2076f8e4ad0SDan Carpenter 	if (family >= AF_MAX)
2088ef874bfSPavel Emelyanov 		return;
2098ef874bfSPavel Emelyanov 
2108ef874bfSPavel Emelyanov 	mutex_lock(&sock_diag_table_mutex);
2118ef874bfSPavel Emelyanov 	BUG_ON(sock_diag_handlers[family] != hnld);
212*96ba0fc3SEric Dumazet 	WRITE_ONCE(sock_diag_handlers[family], NULL);
2138ef874bfSPavel Emelyanov 	mutex_unlock(&sock_diag_table_mutex);
2148ef874bfSPavel Emelyanov }
2158ef874bfSPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_unregister);
2168ef874bfSPavel Emelyanov 
__sock_diag_cmd(struct sk_buff * skb,struct nlmsghdr * nlh)21764be0aedSLorenzo Colitti static int __sock_diag_cmd(struct sk_buff *skb, struct nlmsghdr *nlh)
2188ef874bfSPavel Emelyanov {
2198ef874bfSPavel Emelyanov 	int err;
2207b46866dSThomas Graf 	struct sock_diag_req *req = nlmsg_data(nlh);
2218dcf01fcSShan Wei 	const struct sock_diag_handler *hndl;
2228ef874bfSPavel Emelyanov 
2238ef874bfSPavel Emelyanov 	if (nlmsg_len(nlh) < sizeof(*req))
2248ef874bfSPavel Emelyanov 		return -EINVAL;
2258ef874bfSPavel Emelyanov 
2266e601a53SMathias Krause 	if (req->sdiag_family >= AF_MAX)
2276e601a53SMathias Krause 		return -EINVAL;
22866b51b0aSJeremy Cline 	req->sdiag_family = array_index_nospec(req->sdiag_family, AF_MAX);
2296e601a53SMathias Krause 
230*96ba0fc3SEric Dumazet 	if (READ_ONCE(sock_diag_handlers[req->sdiag_family]) == NULL)
231bf2ae2e4SXin Long 		sock_load_diag_module(req->sdiag_family, 0);
2328e904550SMathias Krause 
2338e904550SMathias Krause 	mutex_lock(&sock_diag_table_mutex);
2348e904550SMathias Krause 	hndl = sock_diag_handlers[req->sdiag_family];
2358ef874bfSPavel Emelyanov 	if (hndl == NULL)
2368ef874bfSPavel Emelyanov 		err = -ENOENT;
23764be0aedSLorenzo Colitti 	else if (nlh->nlmsg_type == SOCK_DIAG_BY_FAMILY)
2388ef874bfSPavel Emelyanov 		err = hndl->dump(skb, nlh);
23964be0aedSLorenzo Colitti 	else if (nlh->nlmsg_type == SOCK_DESTROY && hndl->destroy)
24064be0aedSLorenzo Colitti 		err = hndl->destroy(skb, nlh);
24164be0aedSLorenzo Colitti 	else
24264be0aedSLorenzo Colitti 		err = -EOPNOTSUPP;
2438e904550SMathias Krause 	mutex_unlock(&sock_diag_table_mutex);
2448ef874bfSPavel Emelyanov 
2458ef874bfSPavel Emelyanov 	return err;
2468ef874bfSPavel Emelyanov }
2478ef874bfSPavel Emelyanov 
sock_diag_rcv_msg(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)2482d4bc933SJohannes Berg static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
2492d4bc933SJohannes Berg 			     struct netlink_ext_ack *extack)
2508ef874bfSPavel Emelyanov {
2518ef874bfSPavel Emelyanov 	int ret;
2528ef874bfSPavel Emelyanov 
2538ef874bfSPavel Emelyanov 	switch (nlh->nlmsg_type) {
2548ef874bfSPavel Emelyanov 	case TCPDIAG_GETSOCK:
2558ef874bfSPavel Emelyanov 	case DCCPDIAG_GETSOCK:
2568ef874bfSPavel Emelyanov 		if (inet_rcv_compat == NULL)
257bf2ae2e4SXin Long 			sock_load_diag_module(AF_INET, 0);
2588ef874bfSPavel Emelyanov 
2598ef874bfSPavel Emelyanov 		mutex_lock(&sock_diag_table_mutex);
2608ef874bfSPavel Emelyanov 		if (inet_rcv_compat != NULL)
2618ef874bfSPavel Emelyanov 			ret = inet_rcv_compat(skb, nlh);
2628ef874bfSPavel Emelyanov 		else
2638ef874bfSPavel Emelyanov 			ret = -EOPNOTSUPP;
2648ef874bfSPavel Emelyanov 		mutex_unlock(&sock_diag_table_mutex);
2658ef874bfSPavel Emelyanov 
2668ef874bfSPavel Emelyanov 		return ret;
2678ef874bfSPavel Emelyanov 	case SOCK_DIAG_BY_FAMILY:
26864be0aedSLorenzo Colitti 	case SOCK_DESTROY:
26964be0aedSLorenzo Colitti 		return __sock_diag_cmd(skb, nlh);
2708ef874bfSPavel Emelyanov 	default:
2718ef874bfSPavel Emelyanov 		return -EINVAL;
2728ef874bfSPavel Emelyanov 	}
2738ef874bfSPavel Emelyanov }
2748ef874bfSPavel Emelyanov 
2758ef874bfSPavel Emelyanov static DEFINE_MUTEX(sock_diag_mutex);
2768ef874bfSPavel Emelyanov 
sock_diag_rcv(struct sk_buff * skb)2778ef874bfSPavel Emelyanov static void sock_diag_rcv(struct sk_buff *skb)
2788ef874bfSPavel Emelyanov {
2798ef874bfSPavel Emelyanov 	mutex_lock(&sock_diag_mutex);
2808ef874bfSPavel Emelyanov 	netlink_rcv_skb(skb, &sock_diag_rcv_msg);
2818ef874bfSPavel Emelyanov 	mutex_unlock(&sock_diag_mutex);
2828ef874bfSPavel Emelyanov }
2838ef874bfSPavel Emelyanov 
sock_diag_bind(struct net * net,int group)284eb4cb008SCraig Gallek static int sock_diag_bind(struct net *net, int group)
285eb4cb008SCraig Gallek {
286eb4cb008SCraig Gallek 	switch (group) {
287eb4cb008SCraig Gallek 	case SKNLGRP_INET_TCP_DESTROY:
288eb4cb008SCraig Gallek 	case SKNLGRP_INET_UDP_DESTROY:
289*96ba0fc3SEric Dumazet 		if (!READ_ONCE(sock_diag_handlers[AF_INET]))
290bf2ae2e4SXin Long 			sock_load_diag_module(AF_INET, 0);
291eb4cb008SCraig Gallek 		break;
292eb4cb008SCraig Gallek 	case SKNLGRP_INET6_TCP_DESTROY:
293eb4cb008SCraig Gallek 	case SKNLGRP_INET6_UDP_DESTROY:
294*96ba0fc3SEric Dumazet 		if (!READ_ONCE(sock_diag_handlers[AF_INET6]))
295bf2ae2e4SXin Long 			sock_load_diag_module(AF_INET6, 0);
296eb4cb008SCraig Gallek 		break;
297eb4cb008SCraig Gallek 	}
298eb4cb008SCraig Gallek 	return 0;
299eb4cb008SCraig Gallek }
300eb4cb008SCraig Gallek 
sock_diag_destroy(struct sock * sk,int err)30164be0aedSLorenzo Colitti int sock_diag_destroy(struct sock *sk, int err)
30264be0aedSLorenzo Colitti {
30364be0aedSLorenzo Colitti 	if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
30464be0aedSLorenzo Colitti 		return -EPERM;
30564be0aedSLorenzo Colitti 
30664be0aedSLorenzo Colitti 	if (!sk->sk_prot->diag_destroy)
30764be0aedSLorenzo Colitti 		return -EOPNOTSUPP;
30864be0aedSLorenzo Colitti 
30964be0aedSLorenzo Colitti 	return sk->sk_prot->diag_destroy(sk, err);
31064be0aedSLorenzo Colitti }
31164be0aedSLorenzo Colitti EXPORT_SYMBOL_GPL(sock_diag_destroy);
31264be0aedSLorenzo Colitti 
diag_net_init(struct net * net)31351d7cccfSAndrey Vagin static int __net_init diag_net_init(struct net *net)
3148ef874bfSPavel Emelyanov {
315a31f2d17SPablo Neira Ayuso 	struct netlink_kernel_cfg cfg = {
316eb4cb008SCraig Gallek 		.groups	= SKNLGRP_MAX,
317a31f2d17SPablo Neira Ayuso 		.input	= sock_diag_rcv,
318eb4cb008SCraig Gallek 		.bind	= sock_diag_bind,
319eb4cb008SCraig Gallek 		.flags	= NL_CFG_F_NONROOT_RECV,
320a31f2d17SPablo Neira Ayuso 	};
321a31f2d17SPablo Neira Ayuso 
3229f00d977SPablo Neira Ayuso 	net->diag_nlsk = netlink_kernel_create(net, NETLINK_SOCK_DIAG, &cfg);
32351d7cccfSAndrey Vagin 	return net->diag_nlsk == NULL ? -ENOMEM : 0;
32451d7cccfSAndrey Vagin }
32551d7cccfSAndrey Vagin 
diag_net_exit(struct net * net)32651d7cccfSAndrey Vagin static void __net_exit diag_net_exit(struct net *net)
32751d7cccfSAndrey Vagin {
32851d7cccfSAndrey Vagin 	netlink_kernel_release(net->diag_nlsk);
32951d7cccfSAndrey Vagin 	net->diag_nlsk = NULL;
33051d7cccfSAndrey Vagin }
33151d7cccfSAndrey Vagin 
33251d7cccfSAndrey Vagin static struct pernet_operations diag_net_ops = {
33351d7cccfSAndrey Vagin 	.init = diag_net_init,
33451d7cccfSAndrey Vagin 	.exit = diag_net_exit,
33551d7cccfSAndrey Vagin };
33651d7cccfSAndrey Vagin 
sock_diag_init(void)33751d7cccfSAndrey Vagin static int __init sock_diag_init(void)
33851d7cccfSAndrey Vagin {
339eb4cb008SCraig Gallek 	broadcast_wq = alloc_workqueue("sock_diag_events", 0, 0);
340eb4cb008SCraig Gallek 	BUG_ON(!broadcast_wq);
34151d7cccfSAndrey Vagin 	return register_pernet_subsys(&diag_net_ops);
3428ef874bfSPavel Emelyanov }
343b6191aeeSPaul Gortmaker device_initcall(sock_diag_init);
344