blob: 711bdefe775357fecfe6e80c819e49dc5bf27e9d [file] [log] [blame]
Pavel Emelyanov8ef874b2011-12-06 07:59:52 +00001#include <linux/mutex.h>
2#include <linux/socket.h>
3#include <linux/skbuff.h>
4#include <net/netlink.h>
5#include <net/net_namespace.h>
6#include <linux/module.h>
7
8#include <linux/inet_diag.h>
9#include <linux/sock_diag.h>
10
11static struct sock_diag_handler *sock_diag_handlers[AF_MAX];
12static int (*inet_rcv_compat)(struct sk_buff *skb, struct nlmsghdr *nlh);
13static DEFINE_MUTEX(sock_diag_table_mutex);
14
Pavel Emelyanovf65c1b52011-12-15 02:43:44 +000015int sock_diag_check_cookie(void *sk, __u32 *cookie)
16{
17 if ((cookie[0] != INET_DIAG_NOCOOKIE ||
18 cookie[1] != INET_DIAG_NOCOOKIE) &&
19 ((u32)(unsigned long)sk != cookie[0] ||
20 (u32)((((unsigned long)sk) >> 31) >> 1) != cookie[1]))
21 return -ESTALE;
22 else
23 return 0;
24}
25EXPORT_SYMBOL_GPL(sock_diag_check_cookie);
26
27void sock_diag_save_cookie(void *sk, __u32 *cookie)
28{
29 cookie[0] = (u32)(unsigned long)sk;
30 cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1);
31}
32EXPORT_SYMBOL_GPL(sock_diag_save_cookie);
33
Pavel Emelyanov8ef874b2011-12-06 07:59:52 +000034void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh))
35{
36 mutex_lock(&sock_diag_table_mutex);
37 inet_rcv_compat = fn;
38 mutex_unlock(&sock_diag_table_mutex);
39}
40EXPORT_SYMBOL_GPL(sock_diag_register_inet_compat);
41
42void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh))
43{
44 mutex_lock(&sock_diag_table_mutex);
45 inet_rcv_compat = NULL;
46 mutex_unlock(&sock_diag_table_mutex);
47}
48EXPORT_SYMBOL_GPL(sock_diag_unregister_inet_compat);
49
50int sock_diag_register(struct sock_diag_handler *hndl)
51{
52 int err = 0;
53
Dan Carpenter6f8e4ad2011-12-07 20:49:38 +000054 if (hndl->family >= AF_MAX)
Pavel Emelyanov8ef874b2011-12-06 07:59:52 +000055 return -EINVAL;
56
57 mutex_lock(&sock_diag_table_mutex);
58 if (sock_diag_handlers[hndl->family])
59 err = -EBUSY;
60 else
61 sock_diag_handlers[hndl->family] = hndl;
62 mutex_unlock(&sock_diag_table_mutex);
63
64 return err;
65}
66EXPORT_SYMBOL_GPL(sock_diag_register);
67
68void sock_diag_unregister(struct sock_diag_handler *hnld)
69{
70 int family = hnld->family;
71
Dan Carpenter6f8e4ad2011-12-07 20:49:38 +000072 if (family >= AF_MAX)
Pavel Emelyanov8ef874b2011-12-06 07:59:52 +000073 return;
74
75 mutex_lock(&sock_diag_table_mutex);
76 BUG_ON(sock_diag_handlers[family] != hnld);
77 sock_diag_handlers[family] = NULL;
78 mutex_unlock(&sock_diag_table_mutex);
79}
80EXPORT_SYMBOL_GPL(sock_diag_unregister);
81
82static inline struct sock_diag_handler *sock_diag_lock_handler(int family)
83{
84 if (sock_diag_handlers[family] == NULL)
85 request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
Pavel Emelyanovaec8dc62011-12-15 02:43:27 +000086 NETLINK_SOCK_DIAG, family);
Pavel Emelyanov8ef874b2011-12-06 07:59:52 +000087
88 mutex_lock(&sock_diag_table_mutex);
89 return sock_diag_handlers[family];
90}
91
92static inline void sock_diag_unlock_handler(struct sock_diag_handler *h)
93{
94 mutex_unlock(&sock_diag_table_mutex);
95}
96
97static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
98{
99 int err;
100 struct sock_diag_req *req = NLMSG_DATA(nlh);
101 struct sock_diag_handler *hndl;
102
103 if (nlmsg_len(nlh) < sizeof(*req))
104 return -EINVAL;
105
106 hndl = sock_diag_lock_handler(req->sdiag_family);
107 if (hndl == NULL)
108 err = -ENOENT;
109 else
110 err = hndl->dump(skb, nlh);
111 sock_diag_unlock_handler(hndl);
112
113 return err;
114}
115
116static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
117{
118 int ret;
119
120 switch (nlh->nlmsg_type) {
121 case TCPDIAG_GETSOCK:
122 case DCCPDIAG_GETSOCK:
123 if (inet_rcv_compat == NULL)
124 request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
Pavel Emelyanovaec8dc62011-12-15 02:43:27 +0000125 NETLINK_SOCK_DIAG, AF_INET);
Pavel Emelyanov8ef874b2011-12-06 07:59:52 +0000126
127 mutex_lock(&sock_diag_table_mutex);
128 if (inet_rcv_compat != NULL)
129 ret = inet_rcv_compat(skb, nlh);
130 else
131 ret = -EOPNOTSUPP;
132 mutex_unlock(&sock_diag_table_mutex);
133
134 return ret;
135 case SOCK_DIAG_BY_FAMILY:
136 return __sock_diag_rcv_msg(skb, nlh);
137 default:
138 return -EINVAL;
139 }
140}
141
142static DEFINE_MUTEX(sock_diag_mutex);
143
144static void sock_diag_rcv(struct sk_buff *skb)
145{
146 mutex_lock(&sock_diag_mutex);
147 netlink_rcv_skb(skb, &sock_diag_rcv_msg);
148 mutex_unlock(&sock_diag_mutex);
149}
150
151struct sock *sock_diag_nlsk;
152EXPORT_SYMBOL_GPL(sock_diag_nlsk);
153
154static int __init sock_diag_init(void)
155{
156 sock_diag_nlsk = netlink_kernel_create(&init_net, NETLINK_SOCK_DIAG, 0,
157 sock_diag_rcv, NULL, THIS_MODULE);
158 return sock_diag_nlsk == NULL ? -ENOMEM : 0;
159}
160
161static void __exit sock_diag_exit(void)
162{
163 netlink_kernel_release(sock_diag_nlsk);
164}
165
166module_init(sock_diag_init);
167module_exit(sock_diag_exit);
168MODULE_LICENSE("GPL");
169MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_SOCK_DIAG);