Blame view

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