blob: 161ce6c05e3140b1830195317ce6887a38a354f0 [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 Emelyanov45a96b92011-12-15 02:44:52 +000029static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
30 u32 pid, u32 seq, u32 flags, int sk_ino)
31{
32 unsigned char *b = skb_tail_pointer(skb);
33 struct nlmsghdr *nlh;
34 struct unix_diag_msg *rep;
35
36 nlh = NLMSG_PUT(skb, pid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep));
37 nlh->nlmsg_flags = flags;
38
39 rep = NLMSG_DATA(nlh);
40
41 rep->udiag_family = AF_UNIX;
42 rep->udiag_type = sk->sk_type;
43 rep->udiag_state = sk->sk_state;
44 rep->udiag_ino = sk_ino;
45 sock_diag_save_cookie(sk, rep->udiag_cookie);
46
Pavel Emelyanovf5248b42011-12-15 02:45:24 +000047 if ((req->udiag_show & UDIAG_SHOW_NAME) &&
48 sk_diag_dump_name(sk, skb))
49 goto nlmsg_failure;
50
Pavel Emelyanov45a96b92011-12-15 02:44:52 +000051 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
52 return skb->len;
53
54nlmsg_failure:
55 nlmsg_trim(skb, b);
56 return -EMSGSIZE;
57}
58
59static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
60 u32 pid, u32 seq, u32 flags)
61{
62 int sk_ino;
63
64 unix_state_lock(sk);
65 sk_ino = sock_i_ino(sk);
66 unix_state_unlock(sk);
67
68 if (!sk_ino)
69 return 0;
70
71 return sk_diag_fill(sk, skb, req, pid, seq, flags, sk_ino);
72}
73
Pavel Emelyanov22931d32011-12-15 02:44:35 +000074static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
75{
Pavel Emelyanov45a96b92011-12-15 02:44:52 +000076 struct unix_diag_req *req;
77 int num, s_num, slot, s_slot;
78
79 req = NLMSG_DATA(cb->nlh);
80
81 s_slot = cb->args[0];
82 num = s_num = cb->args[1];
83
84 spin_lock(&unix_table_lock);
85 for (slot = s_slot; slot <= UNIX_HASH_SIZE; s_num = 0, slot++) {
86 struct sock *sk;
87 struct hlist_node *node;
88
89 num = 0;
90 sk_for_each(sk, node, &unix_socket_table[slot]) {
91 if (num < s_num)
92 goto next;
93 if (!(req->udiag_states & (1 << sk->sk_state)))
94 goto next;
95 if (sk_diag_dump(sk, skb, req,
96 NETLINK_CB(cb->skb).pid,
97 cb->nlh->nlmsg_seq,
98 NLM_F_MULTI) < 0)
99 goto done;
100next:
101 num++;
102 }
103 }
104done:
105 spin_unlock(&unix_table_lock);
106 cb->args[0] = slot;
107 cb->args[1] = num;
108
109 return skb->len;
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000110}
111
Pavel Emelyanov5d3cae82011-12-15 02:45:07 +0000112static struct sock *unix_lookup_by_ino(int ino)
113{
114 int i;
115 struct sock *sk;
116
117 spin_lock(&unix_table_lock);
118 for (i = 0; i <= UNIX_HASH_SIZE; i++) {
119 struct hlist_node *node;
120
121 sk_for_each(sk, node, &unix_socket_table[i])
122 if (ino == sock_i_ino(sk)) {
123 sock_hold(sk);
124 spin_unlock(&unix_table_lock);
125
126 return sk;
127 }
128 }
129
130 spin_unlock(&unix_table_lock);
131 return NULL;
132}
133
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000134static int unix_diag_get_exact(struct sk_buff *in_skb,
135 const struct nlmsghdr *nlh,
136 struct unix_diag_req *req)
137{
Pavel Emelyanov5d3cae82011-12-15 02:45:07 +0000138 int err = -EINVAL;
139 struct sock *sk;
140 struct sk_buff *rep;
141 unsigned int extra_len;
142
143 if (req->udiag_ino == 0)
144 goto out_nosk;
145
146 sk = unix_lookup_by_ino(req->udiag_ino);
147 err = -ENOENT;
148 if (sk == NULL)
149 goto out_nosk;
150
151 err = sock_diag_check_cookie(sk, req->udiag_cookie);
152 if (err)
153 goto out;
154
155 extra_len = 256;
156again:
157 err = -ENOMEM;
158 rep = alloc_skb(NLMSG_SPACE((sizeof(struct unix_diag_msg) + extra_len)),
159 GFP_KERNEL);
160 if (!rep)
161 goto out;
162
163 err = sk_diag_fill(sk, rep, req, NETLINK_CB(in_skb).pid,
164 nlh->nlmsg_seq, 0, req->udiag_ino);
165 if (err < 0) {
166 kfree_skb(rep);
167 extra_len += 256;
168 if (extra_len >= PAGE_SIZE)
169 goto out;
170
171 goto again;
172 }
173 err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid,
174 MSG_DONTWAIT);
175 if (err > 0)
176 err = 0;
177out:
178 if (sk)
179 sock_put(sk);
180out_nosk:
181 return err;
Pavel Emelyanov22931d32011-12-15 02:44:35 +0000182}
183
184static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
185{
186 int hdrlen = sizeof(struct unix_diag_req);
187
188 if (nlmsg_len(h) < hdrlen)
189 return -EINVAL;
190
191 if (h->nlmsg_flags & NLM_F_DUMP)
192 return netlink_dump_start(sock_diag_nlsk, skb, h,
193 unix_diag_dump, NULL, 0);
194 else
195 return unix_diag_get_exact(skb, h, (struct unix_diag_req *)NLMSG_DATA(h));
196}
197
198static struct sock_diag_handler unix_diag_handler = {
199 .family = AF_UNIX,
200 .dump = unix_diag_handler_dump,
201};
202
203static int __init unix_diag_init(void)
204{
205 return sock_diag_register(&unix_diag_handler);
206}
207
208static void __exit unix_diag_exit(void)
209{
210 sock_diag_unregister(&unix_diag_handler);
211}
212
213module_init(unix_diag_init);
214module_exit(unix_diag_exit);
215MODULE_LICENSE("GPL");
216MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 1 /* AF_LOCAL */);