blob: 39e44c98176a71b5f85110fb4eebc47f9ff2826a [file] [log] [blame]
Pavel Emelyanov22931d32011-12-15 02:44:35 +00001#include <linux/types.h>
2#include <linux/spinlock.h>
3#include <linux/sock_diag.h>
4#include <linux/unix_diag.h>
5#include <linux/skbuff.h>
Cyrill Gorcunov2ea744a2011-12-20 04:33:03 +00006#include <linux/module.h>
Pavel Emelyanov22931d32011-12-15 02:44:35 +00007#include <net/netlink.h>
8#include <net/af_unix.h>
9#include <net/tcp_states.h>
10
11#define UNIX_DIAG_PUT(skb, attrtype, attrlen) \
12 RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
13
Pavel Emelyanovf5248b42011-12-15 02:45:24 +000014static int sk_diag_dump_name(struct sock *sk, struct sk_buff *nlskb)
15{
16 struct unix_address *addr = unix_sk(sk)->addr;
17 char *s;
18
19 if (addr) {
20 s = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_NAME, addr->len - sizeof(short));
21 memcpy(s, addr->name->sun_path, addr->len - sizeof(short));
22 }
23
24 return 0;
25
26rtattr_failure:
27 return -EMSGSIZE;
28}
29
Pavel Emelyanov5f7b0562011-12-15 02:45:43 +000030static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb)
31{
32 struct dentry *dentry = unix_sk(sk)->dentry;
33 struct unix_diag_vfs *uv;
34
35 if (dentry) {
36 uv = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_VFS, sizeof(*uv));
37 uv->udiag_vfs_ino = dentry->d_inode->i_ino;
38 uv->udiag_vfs_dev = dentry->d_sb->s_dev;
39 }
40
41 return 0;
42
43rtattr_failure:
44 return -EMSGSIZE;
45}
46
Pavel Emelyanovac02be82011-12-15 02:45:58 +000047static int sk_diag_dump_peer(struct sock *sk, struct sk_buff *nlskb)
48{
49 struct sock *peer;
50 int ino;
51
52 peer = unix_peer_get(sk);
53 if (peer) {
54 unix_state_lock(peer);
55 ino = sock_i_ino(peer);
56 unix_state_unlock(peer);
57 sock_put(peer);
58
59 RTA_PUT_U32(nlskb, UNIX_DIAG_PEER, ino);
60 }
61
62 return 0;
63rtattr_failure:
64 return -EMSGSIZE;
65}
66
Pavel Emelyanov2aac7a22011-12-15 02:46:14 +000067static int sk_diag_dump_icons(struct sock *sk, struct sk_buff *nlskb)
68{
69 struct sk_buff *skb;
70 u32 *buf;
71 int i;
72
73 if (sk->sk_state == TCP_LISTEN) {
74 spin_lock(&sk->sk_receive_queue.lock);
Pavel Emelyanov3b0723c2011-12-26 14:08:47 -050075 buf = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_ICONS,
76 sk->sk_receive_queue.qlen * sizeof(u32));
Pavel Emelyanov2aac7a22011-12-15 02:46:14 +000077 i = 0;
78 skb_queue_walk(&sk->sk_receive_queue, skb) {
79 struct sock *req, *peer;
80
81 req = skb->sk;
82 /*
83 * The state lock is outer for the same sk's
84 * queue lock. With the other's queue locked it's
85 * OK to lock the state.
86 */
87 unix_state_lock_nested(req);
88 peer = unix_sk(req)->peer;
89 if (peer)
90 buf[i++] = sock_i_ino(peer);
91 unix_state_unlock(req);
92 }
93 spin_unlock(&sk->sk_receive_queue.lock);
94 }
95
96 return 0;
97
98rtattr_failure:
99 spin_unlock(&sk->sk_receive_queue.lock);
100 return -EMSGSIZE;
101}
102
Pavel Emelyanovcbf39192011-12-15 02:46:31 +0000103static int sk_diag_show_rqlen(struct sock *sk, struct sk_buff *nlskb)
104{
105 RTA_PUT_U32(nlskb, UNIX_DIAG_RQLEN, sk->sk_receive_queue.qlen);
106 return 0;
107
108rtattr_failure:
109 return -EMSGSIZE;
110}
111
Pavel Emelyanov45a96b92011-12-15 02:44:52 +0000112static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
113 u32 pid, u32 seq, u32 flags, int sk_ino)
114{
115 unsigned char *b = skb_tail_pointer(skb);
116 struct nlmsghdr *nlh;
117 struct unix_diag_msg *rep;
118
119 nlh = NLMSG_PUT(skb, pid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep));
120 nlh->nlmsg_flags = flags;
121
122 rep = NLMSG_DATA(nlh);
123
124 rep->udiag_family = AF_UNIX;
125 rep->udiag_type = sk->sk_type;
126 rep->udiag_state = sk->sk_state;
127 rep->udiag_ino = sk_ino;
128 sock_diag_save_cookie(sk, rep->udiag_cookie);
129
Pavel Emelyanovf5248b42011-12-15 02:45:24 +0000130 if ((req->udiag_show & UDIAG_SHOW_NAME) &&
131 sk_diag_dump_name(sk, skb))
132 goto nlmsg_failure;
133
Pavel Emelyanov5f7b0562011-12-15 02:45:43 +0000134 if ((req->udiag_show & UDIAG_SHOW_VFS) &&
135 sk_diag_dump_vfs(sk, skb))
136 goto nlmsg_failure;
137
Pavel Emelyanovac02be82011-12-15 02:45:58 +0000138 if ((req->udiag_show & UDIAG_SHOW_PEER) &&
139 sk_diag_dump_peer(sk, skb))
140 goto nlmsg_failure;
141
Pavel Emelyanov2aac7a22011-12-15 02:46:14 +0000142 if ((req->udiag_show & UDIAG_SHOW_ICONS) &&
143 sk_diag_dump_icons(sk, skb))
144 goto nlmsg_failure;
145
Pavel Emelyanovcbf39192011-12-15 02:46:31 +0000146 if ((req->udiag_show & UDIAG_SHOW_RQLEN) &&
147 sk_diag_show_rqlen(sk, skb))
148 goto nlmsg_failure;
149
Pavel Emelyanov45a96b92011-12-15 02:44:52 +0000150 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
151 return skb->len;
152
153nlmsg_failure:
154 nlmsg_trim(skb, b);
155 return -EMSGSIZE;
156}
157
158static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
159 u32 pid, u32 seq, u32 flags)
160{
161 int sk_ino;
162
163 unix_state_lock(sk);
164 sk_ino = sock_i_ino(sk);
165 unix_state_unlock(sk);
166
167 if (!sk_ino)
168 return 0;
169
170 return sk_diag_fill(sk, skb, req, pid, seq, flags, sk_ino);
171}
172
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000173static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
174{
Pavel Emelyanov45a96b92011-12-15 02:44:52 +0000175 struct unix_diag_req *req;
176 int num, s_num, slot, s_slot;
177
178 req = NLMSG_DATA(cb->nlh);
179
180 s_slot = cb->args[0];
181 num = s_num = cb->args[1];
182
183 spin_lock(&unix_table_lock);
184 for (slot = s_slot; slot <= UNIX_HASH_SIZE; s_num = 0, slot++) {
185 struct sock *sk;
186 struct hlist_node *node;
187
188 num = 0;
189 sk_for_each(sk, node, &unix_socket_table[slot]) {
190 if (num < s_num)
191 goto next;
192 if (!(req->udiag_states & (1 << sk->sk_state)))
193 goto next;
194 if (sk_diag_dump(sk, skb, req,
195 NETLINK_CB(cb->skb).pid,
196 cb->nlh->nlmsg_seq,
197 NLM_F_MULTI) < 0)
198 goto done;
199next:
200 num++;
201 }
202 }
203done:
204 spin_unlock(&unix_table_lock);
205 cb->args[0] = slot;
206 cb->args[1] = num;
207
208 return skb->len;
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000209}
210
Pavel Emelyanov5d3cae82011-12-15 02:45:07 +0000211static struct sock *unix_lookup_by_ino(int ino)
212{
213 int i;
214 struct sock *sk;
215
216 spin_lock(&unix_table_lock);
217 for (i = 0; i <= UNIX_HASH_SIZE; i++) {
218 struct hlist_node *node;
219
220 sk_for_each(sk, node, &unix_socket_table[i])
221 if (ino == sock_i_ino(sk)) {
222 sock_hold(sk);
223 spin_unlock(&unix_table_lock);
224
225 return sk;
226 }
227 }
228
229 spin_unlock(&unix_table_lock);
230 return NULL;
231}
232
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000233static int unix_diag_get_exact(struct sk_buff *in_skb,
234 const struct nlmsghdr *nlh,
235 struct unix_diag_req *req)
236{
Pavel Emelyanov5d3cae82011-12-15 02:45:07 +0000237 int err = -EINVAL;
238 struct sock *sk;
239 struct sk_buff *rep;
240 unsigned int extra_len;
241
242 if (req->udiag_ino == 0)
243 goto out_nosk;
244
245 sk = unix_lookup_by_ino(req->udiag_ino);
246 err = -ENOENT;
247 if (sk == NULL)
248 goto out_nosk;
249
250 err = sock_diag_check_cookie(sk, req->udiag_cookie);
251 if (err)
252 goto out;
253
254 extra_len = 256;
255again:
256 err = -ENOMEM;
257 rep = alloc_skb(NLMSG_SPACE((sizeof(struct unix_diag_msg) + extra_len)),
258 GFP_KERNEL);
259 if (!rep)
260 goto out;
261
262 err = sk_diag_fill(sk, rep, req, NETLINK_CB(in_skb).pid,
263 nlh->nlmsg_seq, 0, req->udiag_ino);
264 if (err < 0) {
265 kfree_skb(rep);
266 extra_len += 256;
267 if (extra_len >= PAGE_SIZE)
268 goto out;
269
270 goto again;
271 }
272 err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid,
273 MSG_DONTWAIT);
274 if (err > 0)
275 err = 0;
276out:
277 if (sk)
278 sock_put(sk);
279out_nosk:
280 return err;
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000281}
282
283static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
284{
285 int hdrlen = sizeof(struct unix_diag_req);
286
287 if (nlmsg_len(h) < hdrlen)
288 return -EINVAL;
289
290 if (h->nlmsg_flags & NLM_F_DUMP)
291 return netlink_dump_start(sock_diag_nlsk, skb, h,
292 unix_diag_dump, NULL, 0);
293 else
294 return unix_diag_get_exact(skb, h, (struct unix_diag_req *)NLMSG_DATA(h));
295}
296
297static struct sock_diag_handler unix_diag_handler = {
298 .family = AF_UNIX,
299 .dump = unix_diag_handler_dump,
300};
301
302static int __init unix_diag_init(void)
303{
304 return sock_diag_register(&unix_diag_handler);
305}
306
307static void __exit unix_diag_exit(void)
308{
309 sock_diag_unregister(&unix_diag_handler);
310}
311
312module_init(unix_diag_init);
313module_exit(unix_diag_exit);
314MODULE_LICENSE("GPL");
315MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 1 /* AF_LOCAL */);