]> git.karo-electronics.de Git - mv-sheeva.git/blob - net/unix/diag.c
83799ef19b49a4d5b66f80c62c7ec71f95b9e255
[mv-sheeva.git] / net / unix / diag.c
1 #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
13 static 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
25 rtattr_failure:
26         return -EMSGSIZE;
27 }
28
29 static 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
42 rtattr_failure:
43         return -EMSGSIZE;
44 }
45
46 static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
47                 u32 pid, u32 seq, u32 flags, int sk_ino)
48 {
49         unsigned char *b = skb_tail_pointer(skb);
50         struct nlmsghdr *nlh;
51         struct unix_diag_msg *rep;
52
53         nlh = NLMSG_PUT(skb, pid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep));
54         nlh->nlmsg_flags = flags;
55
56         rep = NLMSG_DATA(nlh);
57
58         rep->udiag_family = AF_UNIX;
59         rep->udiag_type = sk->sk_type;
60         rep->udiag_state = sk->sk_state;
61         rep->udiag_ino = sk_ino;
62         sock_diag_save_cookie(sk, rep->udiag_cookie);
63
64         if ((req->udiag_show & UDIAG_SHOW_NAME) &&
65                         sk_diag_dump_name(sk, skb))
66                 goto nlmsg_failure;
67
68         if ((req->udiag_show & UDIAG_SHOW_VFS) &&
69                         sk_diag_dump_vfs(sk, skb))
70                 goto nlmsg_failure;
71
72         nlh->nlmsg_len = skb_tail_pointer(skb) - b;
73         return skb->len;
74
75 nlmsg_failure:
76         nlmsg_trim(skb, b);
77         return -EMSGSIZE;
78 }
79
80 static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
81                 u32 pid, u32 seq, u32 flags)
82 {
83         int sk_ino;
84
85         unix_state_lock(sk);
86         sk_ino = sock_i_ino(sk);
87         unix_state_unlock(sk);
88
89         if (!sk_ino)
90                 return 0;
91
92         return sk_diag_fill(sk, skb, req, pid, seq, flags, sk_ino);
93 }
94
95 static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
96 {
97         struct unix_diag_req *req;
98         int num, s_num, slot, s_slot;
99
100         req = NLMSG_DATA(cb->nlh);
101
102         s_slot = cb->args[0];
103         num = s_num = cb->args[1];
104
105         spin_lock(&unix_table_lock);
106         for (slot = s_slot; slot <= UNIX_HASH_SIZE; s_num = 0, slot++) {
107                 struct sock *sk;
108                 struct hlist_node *node;
109
110                 num = 0;
111                 sk_for_each(sk, node, &unix_socket_table[slot]) {
112                         if (num < s_num)
113                                 goto next;
114                         if (!(req->udiag_states & (1 << sk->sk_state)))
115                                 goto next;
116                         if (sk_diag_dump(sk, skb, req,
117                                                 NETLINK_CB(cb->skb).pid,
118                                                 cb->nlh->nlmsg_seq,
119                                                 NLM_F_MULTI) < 0)
120                                 goto done;
121 next:
122                         num++;
123                 }
124         }
125 done:
126         spin_unlock(&unix_table_lock);
127         cb->args[0] = slot;
128         cb->args[1] = num;
129
130         return skb->len;
131 }
132
133 static struct sock *unix_lookup_by_ino(int ino)
134 {
135         int i;
136         struct sock *sk;
137
138         spin_lock(&unix_table_lock);
139         for (i = 0; i <= UNIX_HASH_SIZE; i++) {
140                 struct hlist_node *node;
141
142                 sk_for_each(sk, node, &unix_socket_table[i])
143                         if (ino == sock_i_ino(sk)) {
144                                 sock_hold(sk);
145                                 spin_unlock(&unix_table_lock);
146
147                                 return sk;
148                         }
149         }
150
151         spin_unlock(&unix_table_lock);
152         return NULL;
153 }
154
155 static int unix_diag_get_exact(struct sk_buff *in_skb,
156                                const struct nlmsghdr *nlh,
157                                struct unix_diag_req *req)
158 {
159         int err = -EINVAL;
160         struct sock *sk;
161         struct sk_buff *rep;
162         unsigned int extra_len;
163
164         if (req->udiag_ino == 0)
165                 goto out_nosk;
166
167         sk = unix_lookup_by_ino(req->udiag_ino);
168         err = -ENOENT;
169         if (sk == NULL)
170                 goto out_nosk;
171
172         err = sock_diag_check_cookie(sk, req->udiag_cookie);
173         if (err)
174                 goto out;
175
176         extra_len = 256;
177 again:
178         err = -ENOMEM;
179         rep = alloc_skb(NLMSG_SPACE((sizeof(struct unix_diag_msg) + extra_len)),
180                         GFP_KERNEL);
181         if (!rep)
182                 goto out;
183
184         err = sk_diag_fill(sk, rep, req, NETLINK_CB(in_skb).pid,
185                            nlh->nlmsg_seq, 0, req->udiag_ino);
186         if (err < 0) {
187                 kfree_skb(rep);
188                 extra_len += 256;
189                 if (extra_len >= PAGE_SIZE)
190                         goto out;
191
192                 goto again;
193         }
194         err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid,
195                               MSG_DONTWAIT);
196         if (err > 0)
197                 err = 0;
198 out:
199         if (sk)
200                 sock_put(sk);
201 out_nosk:
202         return err;
203 }
204
205 static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
206 {
207         int hdrlen = sizeof(struct unix_diag_req);
208
209         if (nlmsg_len(h) < hdrlen)
210                 return -EINVAL;
211
212         if (h->nlmsg_flags & NLM_F_DUMP)
213                 return netlink_dump_start(sock_diag_nlsk, skb, h,
214                                           unix_diag_dump, NULL, 0);
215         else
216                 return unix_diag_get_exact(skb, h, (struct unix_diag_req *)NLMSG_DATA(h));
217 }
218
219 static struct sock_diag_handler unix_diag_handler = {
220         .family = AF_UNIX,
221         .dump = unix_diag_handler_dump,
222 };
223
224 static int __init unix_diag_init(void)
225 {
226         return sock_diag_register(&unix_diag_handler);
227 }
228
229 static void __exit unix_diag_exit(void)
230 {
231         sock_diag_unregister(&unix_diag_handler);
232 }
233
234 module_init(unix_diag_init);
235 module_exit(unix_diag_exit);
236 MODULE_LICENSE("GPL");
237 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 1 /* AF_LOCAL */);