blob: 0e0fda786afeea2607051a644d90dd9613d930e0 [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>
6#include <net/netlink.h>
7#include <net/af_unix.h>
8#include <net/tcp_states.h>
9
10#define UNIX_DIAG_PUT(skb, attrtype, attrlen) \
11 RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
12
Pavel Emelyanovf5248b42011-12-15 02:45:24 +000013static int sk_diag_dump_name(struct sock *sk, struct sk_buff *nlskb)
14{
15 struct unix_address *addr = unix_sk(sk)->addr;
16 char *s;
17
18 if (addr) {
19 s = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_NAME, addr->len - sizeof(short));
20 memcpy(s, addr->name->sun_path, addr->len - sizeof(short));
21 }
22
23 return 0;
24
25rtattr_failure:
26 return -EMSGSIZE;
27}
28
Pavel Emelyanov5f7b0562011-12-15 02:45:43 +000029static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb)
30{
31 struct dentry *dentry = unix_sk(sk)->dentry;
32 struct unix_diag_vfs *uv;
33
34 if (dentry) {
35 uv = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_VFS, sizeof(*uv));
36 uv->udiag_vfs_ino = dentry->d_inode->i_ino;
37 uv->udiag_vfs_dev = dentry->d_sb->s_dev;
38 }
39
40 return 0;
41
42rtattr_failure:
43 return -EMSGSIZE;
44}
45
Pavel Emelyanovac02be82011-12-15 02:45:58 +000046static int sk_diag_dump_peer(struct sock *sk, struct sk_buff *nlskb)
47{
48 struct sock *peer;
49 int ino;
50
51 peer = unix_peer_get(sk);
52 if (peer) {
53 unix_state_lock(peer);
54 ino = sock_i_ino(peer);
55 unix_state_unlock(peer);
56 sock_put(peer);
57
58 RTA_PUT_U32(nlskb, UNIX_DIAG_PEER, ino);
59 }
60
61 return 0;
62rtattr_failure:
63 return -EMSGSIZE;
64}
65
Pavel Emelyanov45a96b92011-12-15 02:44:52 +000066static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
67 u32 pid, u32 seq, u32 flags, int sk_ino)
68{
69 unsigned char *b = skb_tail_pointer(skb);
70 struct nlmsghdr *nlh;
71 struct unix_diag_msg *rep;
72
73 nlh = NLMSG_PUT(skb, pid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep));
74 nlh->nlmsg_flags = flags;
75
76 rep = NLMSG_DATA(nlh);
77
78 rep->udiag_family = AF_UNIX;
79 rep->udiag_type = sk->sk_type;
80 rep->udiag_state = sk->sk_state;
81 rep->udiag_ino = sk_ino;
82 sock_diag_save_cookie(sk, rep->udiag_cookie);
83
Pavel Emelyanovf5248b42011-12-15 02:45:24 +000084 if ((req->udiag_show & UDIAG_SHOW_NAME) &&
85 sk_diag_dump_name(sk, skb))
86 goto nlmsg_failure;
87
Pavel Emelyanov5f7b0562011-12-15 02:45:43 +000088 if ((req->udiag_show & UDIAG_SHOW_VFS) &&
89 sk_diag_dump_vfs(sk, skb))
90 goto nlmsg_failure;
91
Pavel Emelyanovac02be82011-12-15 02:45:58 +000092 if ((req->udiag_show & UDIAG_SHOW_PEER) &&
93 sk_diag_dump_peer(sk, skb))
94 goto nlmsg_failure;
95
Pavel Emelyanov45a96b92011-12-15 02:44:52 +000096 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
97 return skb->len;
98
99nlmsg_failure:
100 nlmsg_trim(skb, b);
101 return -EMSGSIZE;
102}
103
104static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
105 u32 pid, u32 seq, u32 flags)
106{
107 int sk_ino;
108
109 unix_state_lock(sk);
110 sk_ino = sock_i_ino(sk);
111 unix_state_unlock(sk);
112
113 if (!sk_ino)
114 return 0;
115
116 return sk_diag_fill(sk, skb, req, pid, seq, flags, sk_ino);
117}
118
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000119static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
120{
Pavel Emelyanov45a96b92011-12-15 02:44:52 +0000121 struct unix_diag_req *req;
122 int num, s_num, slot, s_slot;
123
124 req = NLMSG_DATA(cb->nlh);
125
126 s_slot = cb->args[0];
127 num = s_num = cb->args[1];
128
129 spin_lock(&unix_table_lock);
130 for (slot = s_slot; slot <= UNIX_HASH_SIZE; s_num = 0, slot++) {
131 struct sock *sk;
132 struct hlist_node *node;
133
134 num = 0;
135 sk_for_each(sk, node, &unix_socket_table[slot]) {
136 if (num < s_num)
137 goto next;
138 if (!(req->udiag_states & (1 << sk->sk_state)))
139 goto next;
140 if (sk_diag_dump(sk, skb, req,
141 NETLINK_CB(cb->skb).pid,
142 cb->nlh->nlmsg_seq,
143 NLM_F_MULTI) < 0)
144 goto done;
145next:
146 num++;
147 }
148 }
149done:
150 spin_unlock(&unix_table_lock);
151 cb->args[0] = slot;
152 cb->args[1] = num;
153
154 return skb->len;
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000155}
156
Pavel Emelyanov5d3cae82011-12-15 02:45:07 +0000157static struct sock *unix_lookup_by_ino(int ino)
158{
159 int i;
160 struct sock *sk;
161
162 spin_lock(&unix_table_lock);
163 for (i = 0; i <= UNIX_HASH_SIZE; i++) {
164 struct hlist_node *node;
165
166 sk_for_each(sk, node, &unix_socket_table[i])
167 if (ino == sock_i_ino(sk)) {
168 sock_hold(sk);
169 spin_unlock(&unix_table_lock);
170
171 return sk;
172 }
173 }
174
175 spin_unlock(&unix_table_lock);
176 return NULL;
177}
178
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000179static int unix_diag_get_exact(struct sk_buff *in_skb,
180 const struct nlmsghdr *nlh,
181 struct unix_diag_req *req)
182{
Pavel Emelyanov5d3cae82011-12-15 02:45:07 +0000183 int err = -EINVAL;
184 struct sock *sk;
185 struct sk_buff *rep;
186 unsigned int extra_len;
187
188 if (req->udiag_ino == 0)
189 goto out_nosk;
190
191 sk = unix_lookup_by_ino(req->udiag_ino);
192 err = -ENOENT;
193 if (sk == NULL)
194 goto out_nosk;
195
196 err = sock_diag_check_cookie(sk, req->udiag_cookie);
197 if (err)
198 goto out;
199
200 extra_len = 256;
201again:
202 err = -ENOMEM;
203 rep = alloc_skb(NLMSG_SPACE((sizeof(struct unix_diag_msg) + extra_len)),
204 GFP_KERNEL);
205 if (!rep)
206 goto out;
207
208 err = sk_diag_fill(sk, rep, req, NETLINK_CB(in_skb).pid,
209 nlh->nlmsg_seq, 0, req->udiag_ino);
210 if (err < 0) {
211 kfree_skb(rep);
212 extra_len += 256;
213 if (extra_len >= PAGE_SIZE)
214 goto out;
215
216 goto again;
217 }
218 err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid,
219 MSG_DONTWAIT);
220 if (err > 0)
221 err = 0;
222out:
223 if (sk)
224 sock_put(sk);
225out_nosk:
226 return err;
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000227}
228
229static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
230{
231 int hdrlen = sizeof(struct unix_diag_req);
232
233 if (nlmsg_len(h) < hdrlen)
234 return -EINVAL;
235
236 if (h->nlmsg_flags & NLM_F_DUMP)
237 return netlink_dump_start(sock_diag_nlsk, skb, h,
238 unix_diag_dump, NULL, 0);
239 else
240 return unix_diag_get_exact(skb, h, (struct unix_diag_req *)NLMSG_DATA(h));
241}
242
243static struct sock_diag_handler unix_diag_handler = {
244 .family = AF_UNIX,
245 .dump = unix_diag_handler_dump,
246};
247
248static int __init unix_diag_init(void)
249{
250 return sock_diag_register(&unix_diag_handler);
251}
252
253static void __exit unix_diag_exit(void)
254{
255 sock_diag_unregister(&unix_diag_handler);
256}
257
258module_init(unix_diag_init);
259module_exit(unix_diag_exit);
260MODULE_LICENSE("GPL");
261MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 1 /* AF_LOCAL */);