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
19161d4fc0SEric Dumazet static const struct sock_diag_handler __rcu *sock_diag_handlers[AF_MAX];
20bba7266dSEric Dumazet
21*70530a2fSEric Dumazet static const struct sock_diag_inet_compat __rcu *inet_rcv_compat;
22bba7266dSEric Dumazet
23eb4cb008SCraig Gallek static struct workqueue_struct *broadcast_wq;
248ef874bfSPavel Emelyanov
2592acdc58SDaniel Borkmann DEFINE_COOKIE(sock_cookie);
2692acdc58SDaniel Borkmann
__sock_gen_cookie(struct sock * sk)2792acdc58SDaniel Borkmann u64 __sock_gen_cookie(struct sock *sk)
28f65c1b53SPavel Emelyanov {
2933cf7c90SEric Dumazet u64 res = atomic64_read(&sk->sk_cookie);
3033cf7c90SEric Dumazet
314ebf802cSEric Dumazet if (!res) {
324ebf802cSEric Dumazet u64 new = gen_cookie_next(&sock_cookie);
334ebf802cSEric Dumazet
3432634819SEric Dumazet atomic64_cmpxchg(&sk->sk_cookie, res, new);
3532634819SEric Dumazet
3632634819SEric Dumazet /* Another thread might have changed sk_cookie before us. */
3732634819SEric Dumazet res = atomic64_read(&sk->sk_cookie);
3833cf7c90SEric Dumazet }
394ebf802cSEric Dumazet return res;
4033cf7c90SEric Dumazet }
4133cf7c90SEric Dumazet
sock_diag_check_cookie(struct sock * sk,const __u32 * cookie)4233cf7c90SEric Dumazet int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie)
4333cf7c90SEric Dumazet {
4433cf7c90SEric Dumazet u64 res;
4533cf7c90SEric Dumazet
4633cf7c90SEric Dumazet if (cookie[0] == INET_DIAG_NOCOOKIE && cookie[1] == INET_DIAG_NOCOOKIE)
4733cf7c90SEric Dumazet return 0;
4833cf7c90SEric Dumazet
4933cf7c90SEric Dumazet res = sock_gen_cookie(sk);
5033cf7c90SEric Dumazet if ((u32)res != cookie[0] || (u32)(res >> 32) != cookie[1])
51f65c1b53SPavel Emelyanov return -ESTALE;
5233cf7c90SEric Dumazet
53f65c1b53SPavel Emelyanov return 0;
54f65c1b53SPavel Emelyanov }
55f65c1b53SPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_check_cookie);
56f65c1b53SPavel Emelyanov
sock_diag_save_cookie(struct sock * sk,__u32 * cookie)5733cf7c90SEric Dumazet void sock_diag_save_cookie(struct sock *sk, __u32 *cookie)
58f65c1b53SPavel Emelyanov {
5933cf7c90SEric Dumazet u64 res = sock_gen_cookie(sk);
6033cf7c90SEric Dumazet
6133cf7c90SEric Dumazet cookie[0] = (u32)res;
6233cf7c90SEric Dumazet cookie[1] = (u32)(res >> 32);
63f65c1b53SPavel Emelyanov }
64f65c1b53SPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_save_cookie);
65f65c1b53SPavel Emelyanov
sock_diag_put_meminfo(struct sock * sk,struct sk_buff * skb,int attrtype)665d2e5f27SPavel Emelyanov int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attrtype)
675d2e5f27SPavel Emelyanov {
687b46866dSThomas Graf u32 mem[SK_MEMINFO_VARS];
695d2e5f27SPavel Emelyanov
70a2d133b1SJosh Hunt sk_get_meminfo(sk, mem);
715d2e5f27SPavel Emelyanov
727b46866dSThomas Graf return nla_put(skb, attrtype, sizeof(mem), &mem);
735d2e5f27SPavel Emelyanov }
745d2e5f27SPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_put_meminfo);
755d2e5f27SPavel Emelyanov
sock_diag_put_filterinfo(bool may_report_filterinfo,struct sock * sk,struct sk_buff * skb,int attrtype)76a53b72c8SEric W. Biederman int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk,
77e8d9612cSNicolas Dichtel struct sk_buff *skb, int attrtype)
78e8d9612cSNicolas Dichtel {
79a3ea269bSDaniel Borkmann struct sock_fprog_kern *fprog;
80e8d9612cSNicolas Dichtel struct sk_filter *filter;
81a3ea269bSDaniel Borkmann struct nlattr *attr;
82a3ea269bSDaniel Borkmann unsigned int flen;
83e8d9612cSNicolas Dichtel int err = 0;
84e8d9612cSNicolas Dichtel
85a53b72c8SEric W. Biederman if (!may_report_filterinfo) {
86e8d9612cSNicolas Dichtel nla_reserve(skb, attrtype, 0);
87e8d9612cSNicolas Dichtel return 0;
88e8d9612cSNicolas Dichtel }
89e8d9612cSNicolas Dichtel
90e8d9612cSNicolas Dichtel rcu_read_lock();
91e8d9612cSNicolas Dichtel filter = rcu_dereference(sk->sk_filter);
92a3ea269bSDaniel Borkmann if (!filter)
93a3ea269bSDaniel Borkmann goto out;
94e8d9612cSNicolas Dichtel
957ae457c1SAlexei Starovoitov fprog = filter->prog->orig_prog;
96b382c086SDaniel Borkmann if (!fprog)
97b382c086SDaniel Borkmann goto out;
98b382c086SDaniel Borkmann
99009937e7SAlexei Starovoitov flen = bpf_classic_proglen(fprog);
100a3ea269bSDaniel Borkmann
101a3ea269bSDaniel Borkmann attr = nla_reserve(skb, attrtype, flen);
102e8d9612cSNicolas Dichtel if (attr == NULL) {
103e8d9612cSNicolas Dichtel err = -EMSGSIZE;
104e8d9612cSNicolas Dichtel goto out;
105e8d9612cSNicolas Dichtel }
106e8d9612cSNicolas Dichtel
107a3ea269bSDaniel Borkmann memcpy(nla_data(attr), fprog->filter, flen);
108e8d9612cSNicolas Dichtel out:
109e8d9612cSNicolas Dichtel rcu_read_unlock();
110e8d9612cSNicolas Dichtel return err;
111e8d9612cSNicolas Dichtel }
112e8d9612cSNicolas Dichtel EXPORT_SYMBOL(sock_diag_put_filterinfo);
113e8d9612cSNicolas Dichtel
114eb4cb008SCraig Gallek struct broadcast_sk {
115eb4cb008SCraig Gallek struct sock *sk;
116eb4cb008SCraig Gallek struct work_struct work;
117eb4cb008SCraig Gallek };
118eb4cb008SCraig Gallek
sock_diag_nlmsg_size(void)119eb4cb008SCraig Gallek static size_t sock_diag_nlmsg_size(void)
120eb4cb008SCraig Gallek {
121eb4cb008SCraig Gallek return NLMSG_ALIGN(sizeof(struct inet_diag_msg)
122eb4cb008SCraig Gallek + nla_total_size(sizeof(u8)) /* INET_DIAG_PROTOCOL */
1236ed46d12SNicolas Dichtel + nla_total_size_64bit(sizeof(struct tcp_info))); /* INET_DIAG_INFO */
124eb4cb008SCraig Gallek }
125eb4cb008SCraig Gallek
sock_diag_lock_handler(int family)126161d4fc0SEric Dumazet static const struct sock_diag_handler *sock_diag_lock_handler(int family)
127161d4fc0SEric Dumazet {
128161d4fc0SEric Dumazet const struct sock_diag_handler *handler;
129161d4fc0SEric Dumazet
130161d4fc0SEric Dumazet rcu_read_lock();
131161d4fc0SEric Dumazet handler = rcu_dereference(sock_diag_handlers[family]);
132161d4fc0SEric Dumazet if (handler && !try_module_get(handler->owner))
133161d4fc0SEric Dumazet handler = NULL;
134161d4fc0SEric Dumazet rcu_read_unlock();
135161d4fc0SEric Dumazet
136161d4fc0SEric Dumazet return handler;
137161d4fc0SEric Dumazet }
138161d4fc0SEric Dumazet
sock_diag_unlock_handler(const struct sock_diag_handler * handler)139161d4fc0SEric Dumazet static void sock_diag_unlock_handler(const struct sock_diag_handler *handler)
140161d4fc0SEric Dumazet {
141161d4fc0SEric Dumazet module_put(handler->owner);
142161d4fc0SEric Dumazet }
143161d4fc0SEric Dumazet
sock_diag_broadcast_destroy_work(struct work_struct * work)144eb4cb008SCraig Gallek static void sock_diag_broadcast_destroy_work(struct work_struct *work)
145eb4cb008SCraig Gallek {
146eb4cb008SCraig Gallek struct broadcast_sk *bsk =
147eb4cb008SCraig Gallek container_of(work, struct broadcast_sk, work);
148eb4cb008SCraig Gallek struct sock *sk = bsk->sk;
149eb4cb008SCraig Gallek const struct sock_diag_handler *hndl;
150eb4cb008SCraig Gallek struct sk_buff *skb;
151eb4cb008SCraig Gallek const enum sknetlink_groups group = sock_diag_destroy_group(sk);
152eb4cb008SCraig Gallek int err = -1;
153eb4cb008SCraig Gallek
154eb4cb008SCraig Gallek WARN_ON(group == SKNLGRP_NONE);
155eb4cb008SCraig Gallek
156eb4cb008SCraig Gallek skb = nlmsg_new(sock_diag_nlmsg_size(), GFP_KERNEL);
157eb4cb008SCraig Gallek if (!skb)
158eb4cb008SCraig Gallek goto out;
159eb4cb008SCraig Gallek
160161d4fc0SEric Dumazet hndl = sock_diag_lock_handler(sk->sk_family);
161161d4fc0SEric Dumazet if (hndl) {
162161d4fc0SEric Dumazet if (hndl->get_info)
163eb4cb008SCraig Gallek err = hndl->get_info(skb, sk);
164161d4fc0SEric Dumazet sock_diag_unlock_handler(hndl);
165161d4fc0SEric Dumazet }
166eb4cb008SCraig Gallek if (!err)
167eb4cb008SCraig Gallek nlmsg_multicast(sock_net(sk)->diag_nlsk, skb, 0, group,
168eb4cb008SCraig Gallek GFP_KERNEL);
169eb4cb008SCraig Gallek else
170eb4cb008SCraig Gallek kfree_skb(skb);
171eb4cb008SCraig Gallek out:
172eb4cb008SCraig Gallek sk_destruct(sk);
173eb4cb008SCraig Gallek kfree(bsk);
174eb4cb008SCraig Gallek }
175eb4cb008SCraig Gallek
sock_diag_broadcast_destroy(struct sock * sk)176eb4cb008SCraig Gallek void sock_diag_broadcast_destroy(struct sock *sk)
177eb4cb008SCraig Gallek {
178eb4cb008SCraig Gallek /* Note, this function is often called from an interrupt context. */
179eb4cb008SCraig Gallek struct broadcast_sk *bsk =
180eb4cb008SCraig Gallek kmalloc(sizeof(struct broadcast_sk), GFP_ATOMIC);
181eb4cb008SCraig Gallek if (!bsk)
182eb4cb008SCraig Gallek return sk_destruct(sk);
183eb4cb008SCraig Gallek bsk->sk = sk;
184eb4cb008SCraig Gallek INIT_WORK(&bsk->work, sock_diag_broadcast_destroy_work);
185eb4cb008SCraig Gallek queue_work(broadcast_wq, &bsk->work);
186eb4cb008SCraig Gallek }
187eb4cb008SCraig Gallek
sock_diag_register_inet_compat(const struct sock_diag_inet_compat * ptr)188bba7266dSEric Dumazet void sock_diag_register_inet_compat(const struct sock_diag_inet_compat *ptr)
1898ef874bfSPavel Emelyanov {
190*70530a2fSEric Dumazet xchg(&inet_rcv_compat, RCU_INITIALIZER(ptr));
1918ef874bfSPavel Emelyanov }
1928ef874bfSPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_register_inet_compat);
1938ef874bfSPavel Emelyanov
sock_diag_unregister_inet_compat(const struct sock_diag_inet_compat * ptr)194bba7266dSEric Dumazet void sock_diag_unregister_inet_compat(const struct sock_diag_inet_compat *ptr)
1958ef874bfSPavel Emelyanov {
196bba7266dSEric Dumazet const struct sock_diag_inet_compat *old;
197bba7266dSEric Dumazet
198*70530a2fSEric Dumazet old = unrcu_pointer(xchg(&inet_rcv_compat, NULL));
199bba7266dSEric Dumazet WARN_ON_ONCE(old != ptr);
2008ef874bfSPavel Emelyanov }
2018ef874bfSPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_unregister_inet_compat);
2028ef874bfSPavel Emelyanov
sock_diag_register(const struct sock_diag_handler * hndl)2038dcf01fcSShan Wei int sock_diag_register(const struct sock_diag_handler *hndl)
2048ef874bfSPavel Emelyanov {
205161d4fc0SEric Dumazet int family = hndl->family;
2068ef874bfSPavel Emelyanov
207161d4fc0SEric Dumazet if (family >= AF_MAX)
2088ef874bfSPavel Emelyanov return -EINVAL;
2098ef874bfSPavel Emelyanov
210161d4fc0SEric Dumazet return !cmpxchg((const struct sock_diag_handler **)
211161d4fc0SEric Dumazet &sock_diag_handlers[family],
212161d4fc0SEric Dumazet NULL, hndl) ? 0 : -EBUSY;
2138ef874bfSPavel Emelyanov }
2148ef874bfSPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_register);
2158ef874bfSPavel Emelyanov
sock_diag_unregister(const struct sock_diag_handler * hndl)216161d4fc0SEric Dumazet void sock_diag_unregister(const struct sock_diag_handler *hndl)
2178ef874bfSPavel Emelyanov {
218161d4fc0SEric Dumazet int family = hndl->family;
2198ef874bfSPavel Emelyanov
2206f8e4ad0SDan Carpenter if (family >= AF_MAX)
2218ef874bfSPavel Emelyanov return;
2228ef874bfSPavel Emelyanov
223161d4fc0SEric Dumazet xchg((const struct sock_diag_handler **)&sock_diag_handlers[family],
224161d4fc0SEric Dumazet NULL);
2258ef874bfSPavel Emelyanov }
2268ef874bfSPavel Emelyanov EXPORT_SYMBOL_GPL(sock_diag_unregister);
2278ef874bfSPavel Emelyanov
__sock_diag_cmd(struct sk_buff * skb,struct nlmsghdr * nlh)22864be0aedSLorenzo Colitti static int __sock_diag_cmd(struct sk_buff *skb, struct nlmsghdr *nlh)
2298ef874bfSPavel Emelyanov {
2308ef874bfSPavel Emelyanov int err;
2317b46866dSThomas Graf struct sock_diag_req *req = nlmsg_data(nlh);
2328dcf01fcSShan Wei const struct sock_diag_handler *hndl;
2338ef874bfSPavel Emelyanov
2348ef874bfSPavel Emelyanov if (nlmsg_len(nlh) < sizeof(*req))
2358ef874bfSPavel Emelyanov return -EINVAL;
2368ef874bfSPavel Emelyanov
2376e601a53SMathias Krause if (req->sdiag_family >= AF_MAX)
2386e601a53SMathias Krause return -EINVAL;
23966b51b0aSJeremy Cline req->sdiag_family = array_index_nospec(req->sdiag_family, AF_MAX);
2406e601a53SMathias Krause
241161d4fc0SEric Dumazet if (!rcu_access_pointer(sock_diag_handlers[req->sdiag_family]))
242bf2ae2e4SXin Long sock_load_diag_module(req->sdiag_family, 0);
2438e904550SMathias Krause
244161d4fc0SEric Dumazet hndl = sock_diag_lock_handler(req->sdiag_family);
2458ef874bfSPavel Emelyanov if (hndl == NULL)
246161d4fc0SEric Dumazet return -ENOENT;
247161d4fc0SEric Dumazet
248161d4fc0SEric Dumazet if (nlh->nlmsg_type == SOCK_DIAG_BY_FAMILY)
2498ef874bfSPavel Emelyanov err = hndl->dump(skb, nlh);
25064be0aedSLorenzo Colitti else if (nlh->nlmsg_type == SOCK_DESTROY && hndl->destroy)
25164be0aedSLorenzo Colitti err = hndl->destroy(skb, nlh);
25264be0aedSLorenzo Colitti else
25364be0aedSLorenzo Colitti err = -EOPNOTSUPP;
254161d4fc0SEric Dumazet sock_diag_unlock_handler(hndl);
2558ef874bfSPavel Emelyanov
2568ef874bfSPavel Emelyanov return err;
2578ef874bfSPavel Emelyanov }
2588ef874bfSPavel Emelyanov
sock_diag_rcv_msg(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)2592d4bc933SJohannes Berg static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
2602d4bc933SJohannes Berg struct netlink_ext_ack *extack)
2618ef874bfSPavel Emelyanov {
262bba7266dSEric Dumazet const struct sock_diag_inet_compat *ptr;
2638ef874bfSPavel Emelyanov int ret;
2648ef874bfSPavel Emelyanov
2658ef874bfSPavel Emelyanov switch (nlh->nlmsg_type) {
2668ef874bfSPavel Emelyanov case TCPDIAG_GETSOCK:
2678ef874bfSPavel Emelyanov case DCCPDIAG_GETSOCK:
268bba7266dSEric Dumazet
269bba7266dSEric Dumazet if (!rcu_access_pointer(inet_rcv_compat))
270bf2ae2e4SXin Long sock_load_diag_module(AF_INET, 0);
2718ef874bfSPavel Emelyanov
272bba7266dSEric Dumazet rcu_read_lock();
273bba7266dSEric Dumazet ptr = rcu_dereference(inet_rcv_compat);
274bba7266dSEric Dumazet if (ptr && !try_module_get(ptr->owner))
275bba7266dSEric Dumazet ptr = NULL;
276bba7266dSEric Dumazet rcu_read_unlock();
277bba7266dSEric Dumazet
2788ef874bfSPavel Emelyanov ret = -EOPNOTSUPP;
279bba7266dSEric Dumazet if (ptr) {
280bba7266dSEric Dumazet ret = ptr->fn(skb, nlh);
281bba7266dSEric Dumazet module_put(ptr->owner);
282bba7266dSEric Dumazet }
2838ef874bfSPavel Emelyanov
2848ef874bfSPavel Emelyanov return ret;
2858ef874bfSPavel Emelyanov case SOCK_DIAG_BY_FAMILY:
28664be0aedSLorenzo Colitti case SOCK_DESTROY:
28764be0aedSLorenzo Colitti return __sock_diag_cmd(skb, nlh);
2888ef874bfSPavel Emelyanov default:
2898ef874bfSPavel Emelyanov return -EINVAL;
2908ef874bfSPavel Emelyanov }
2918ef874bfSPavel Emelyanov }
2928ef874bfSPavel Emelyanov
2938ef874bfSPavel Emelyanov static DEFINE_MUTEX(sock_diag_mutex);
2948ef874bfSPavel Emelyanov
sock_diag_rcv(struct sk_buff * skb)2958ef874bfSPavel Emelyanov static void sock_diag_rcv(struct sk_buff *skb)
2968ef874bfSPavel Emelyanov {
2978ef874bfSPavel Emelyanov mutex_lock(&sock_diag_mutex);
2988ef874bfSPavel Emelyanov netlink_rcv_skb(skb, &sock_diag_rcv_msg);
2998ef874bfSPavel Emelyanov mutex_unlock(&sock_diag_mutex);
3008ef874bfSPavel Emelyanov }
3018ef874bfSPavel Emelyanov
sock_diag_bind(struct net * net,int group)302eb4cb008SCraig Gallek static int sock_diag_bind(struct net *net, int group)
303eb4cb008SCraig Gallek {
304eb4cb008SCraig Gallek switch (group) {
305eb4cb008SCraig Gallek case SKNLGRP_INET_TCP_DESTROY:
306eb4cb008SCraig Gallek case SKNLGRP_INET_UDP_DESTROY:
307161d4fc0SEric Dumazet if (!rcu_access_pointer(sock_diag_handlers[AF_INET]))
308bf2ae2e4SXin Long sock_load_diag_module(AF_INET, 0);
309eb4cb008SCraig Gallek break;
310eb4cb008SCraig Gallek case SKNLGRP_INET6_TCP_DESTROY:
311eb4cb008SCraig Gallek case SKNLGRP_INET6_UDP_DESTROY:
312161d4fc0SEric Dumazet if (!rcu_access_pointer(sock_diag_handlers[AF_INET6]))
313bf2ae2e4SXin Long sock_load_diag_module(AF_INET6, 0);
314eb4cb008SCraig Gallek break;
315eb4cb008SCraig Gallek }
316eb4cb008SCraig Gallek return 0;
317eb4cb008SCraig Gallek }
318eb4cb008SCraig Gallek
sock_diag_destroy(struct sock * sk,int err)31964be0aedSLorenzo Colitti int sock_diag_destroy(struct sock *sk, int err)
32064be0aedSLorenzo Colitti {
32164be0aedSLorenzo Colitti if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
32264be0aedSLorenzo Colitti return -EPERM;
32364be0aedSLorenzo Colitti
32464be0aedSLorenzo Colitti if (!sk->sk_prot->diag_destroy)
32564be0aedSLorenzo Colitti return -EOPNOTSUPP;
32664be0aedSLorenzo Colitti
32764be0aedSLorenzo Colitti return sk->sk_prot->diag_destroy(sk, err);
32864be0aedSLorenzo Colitti }
32964be0aedSLorenzo Colitti EXPORT_SYMBOL_GPL(sock_diag_destroy);
33064be0aedSLorenzo Colitti
diag_net_init(struct net * net)33151d7cccfSAndrey Vagin static int __net_init diag_net_init(struct net *net)
3328ef874bfSPavel Emelyanov {
333a31f2d17SPablo Neira Ayuso struct netlink_kernel_cfg cfg = {
334eb4cb008SCraig Gallek .groups = SKNLGRP_MAX,
335a31f2d17SPablo Neira Ayuso .input = sock_diag_rcv,
336eb4cb008SCraig Gallek .bind = sock_diag_bind,
337eb4cb008SCraig Gallek .flags = NL_CFG_F_NONROOT_RECV,
338a31f2d17SPablo Neira Ayuso };
339a31f2d17SPablo Neira Ayuso
3409f00d977SPablo Neira Ayuso net->diag_nlsk = netlink_kernel_create(net, NETLINK_SOCK_DIAG, &cfg);
34151d7cccfSAndrey Vagin return net->diag_nlsk == NULL ? -ENOMEM : 0;
34251d7cccfSAndrey Vagin }
34351d7cccfSAndrey Vagin
diag_net_exit(struct net * net)34451d7cccfSAndrey Vagin static void __net_exit diag_net_exit(struct net *net)
34551d7cccfSAndrey Vagin {
34651d7cccfSAndrey Vagin netlink_kernel_release(net->diag_nlsk);
34751d7cccfSAndrey Vagin net->diag_nlsk = NULL;
34851d7cccfSAndrey Vagin }
34951d7cccfSAndrey Vagin
35051d7cccfSAndrey Vagin static struct pernet_operations diag_net_ops = {
35151d7cccfSAndrey Vagin .init = diag_net_init,
35251d7cccfSAndrey Vagin .exit = diag_net_exit,
35351d7cccfSAndrey Vagin };
35451d7cccfSAndrey Vagin
sock_diag_init(void)35551d7cccfSAndrey Vagin static int __init sock_diag_init(void)
35651d7cccfSAndrey Vagin {
357eb4cb008SCraig Gallek broadcast_wq = alloc_workqueue("sock_diag_events", 0, 0);
358eb4cb008SCraig Gallek BUG_ON(!broadcast_wq);
35951d7cccfSAndrey Vagin return register_pernet_subsys(&diag_net_ops);
3608ef874bfSPavel Emelyanov }
361b6191aeeSPaul Gortmaker device_initcall(sock_diag_init);
362