Blame view

net/phonet/socket.c 17.8 KB
ba113a94b   Remi Denis-Courmont   Phonet: common so...
1
2
3
4
5
6
7
  /*
   * File: socket.c
   *
   * Phonet sockets
   *
   * Copyright (C) 2008 Nokia Corporation.
   *
31fdc5553   Rémi Denis-Courmont   net: remove my fu...
8
9
   * Authors: Sakari Ailus <sakari.ailus@nokia.com>
   *          Rémi Denis-Courmont
ba113a94b   Remi Denis-Courmont   Phonet: common so...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   * 02110-1301 USA
   */
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/gfp.h>
ba113a94b   Remi Denis-Courmont   Phonet: common so...
26
27
  #include <linux/kernel.h>
  #include <linux/net.h>
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
28
  #include <linux/poll.h>
ba113a94b   Remi Denis-Courmont   Phonet: common so...
29
30
31
32
  #include <net/sock.h>
  #include <net/tcp_states.h>
  
  #include <linux/phonet.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
33
  #include <linux/export.h>
ba113a94b   Remi Denis-Courmont   Phonet: common so...
34
  #include <net/phonet/phonet.h>
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
35
  #include <net/phonet/pep.h>
ba113a94b   Remi Denis-Courmont   Phonet: common so...
36
37
38
39
40
41
42
43
44
45
46
47
  #include <net/phonet/pn_dev.h>
  
  static int pn_socket_release(struct socket *sock)
  {
  	struct sock *sk = sock->sk;
  
  	if (sk) {
  		sock->sk = NULL;
  		sk->sk_prot->close(sk, 0);
  	}
  	return 0;
  }
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
48
49
  #define PN_HASHSIZE	16
  #define PN_HASHMASK	(PN_HASHSIZE-1)
ba113a94b   Remi Denis-Courmont   Phonet: common so...
50
  static struct  {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
51
  	struct hlist_head hlist[PN_HASHSIZE];
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
52
  	struct mutex lock;
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
53
54
55
56
  } pnsocks;
  
  void __init pn_sock_init(void)
  {
95c961747   Eric Dumazet   net: cleanup unsi...
57
  	unsigned int i;
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
58
59
60
  
  	for (i = 0; i < PN_HASHSIZE; i++)
  		INIT_HLIST_HEAD(pnsocks.hlist + i);
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
61
  	mutex_init(&pnsocks.lock);
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
62
63
64
65
66
67
  }
  
  static struct hlist_head *pn_hash_list(u16 obj)
  {
  	return pnsocks.hlist + (obj & PN_HASHMASK);
  }
ba113a94b   Remi Denis-Courmont   Phonet: common so...
68
69
70
71
72
  
  /*
   * Find address based on socket address, match only certain fields.
   * Also grab sock if it was found. Remember to sock_put it later.
   */
524048819   Rémi Denis-Courmont   Phonet: basic net...
73
  struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *spn)
ba113a94b   Remi Denis-Courmont   Phonet: common so...
74
  {
ba113a94b   Remi Denis-Courmont   Phonet: common so...
75
76
77
78
  	struct sock *sknode;
  	struct sock *rval = NULL;
  	u16 obj = pn_sockaddr_get_object(spn);
  	u8 res = spn->spn_resource;
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
79
  	struct hlist_head *hlist = pn_hash_list(obj);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
80

44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
81
  	rcu_read_lock();
b67bfe0d4   Sasha Levin   hlist: drop the n...
82
  	sk_for_each_rcu(sknode, hlist) {
ba113a94b   Remi Denis-Courmont   Phonet: common so...
83
84
  		struct pn_sock *pn = pn_sk(sknode);
  		BUG_ON(!pn->sobject); /* unbound socket */
524048819   Rémi Denis-Courmont   Phonet: basic net...
85
86
  		if (!net_eq(sock_net(sknode), net))
  			continue;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
87
88
89
90
91
92
93
94
95
  		if (pn_port(obj)) {
  			/* Look up socket by port */
  			if (pn_port(pn->sobject) != pn_port(obj))
  				continue;
  		} else {
  			/* If port is zero, look up by resource */
  			if (pn->resource != res)
  				continue;
  		}
f64f9e719   Joe Perches   net: Move && and ...
96
97
  		if (pn_addr(pn->sobject) &&
  		    pn_addr(pn->sobject) != pn_addr(obj))
ba113a94b   Remi Denis-Courmont   Phonet: common so...
98
99
100
101
102
103
  			continue;
  
  		rval = sknode;
  		sock_hold(sknode);
  		break;
  	}
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
104
  	rcu_read_unlock();
ba113a94b   Remi Denis-Courmont   Phonet: common so...
105
106
  
  	return rval;
f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
107
108
109
110
111
  }
  
  /* Deliver a broadcast packet (only in bottom-half) */
  void pn_deliver_sock_broadcast(struct net *net, struct sk_buff *skb)
  {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
112
  	struct hlist_head *hlist = pnsocks.hlist;
95c961747   Eric Dumazet   net: cleanup unsi...
113
  	unsigned int h;
f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
114

44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
115
  	rcu_read_lock();
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
116
  	for (h = 0; h < PN_HASHSIZE; h++) {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
117
  		struct sock *sknode;
f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
118

b67bfe0d4   Sasha Levin   hlist: drop the n...
119
  		sk_for_each(sknode, hlist) {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
120
  			struct sk_buff *clone;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
121

6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
122
123
124
125
126
127
128
129
130
131
  			if (!net_eq(sock_net(sknode), net))
  				continue;
  			if (!sock_flag(sknode, SOCK_BROADCAST))
  				continue;
  
  			clone = skb_clone(skb, GFP_ATOMIC);
  			if (clone) {
  				sock_hold(sknode);
  				sk_receive_skb(sknode, clone, 0);
  			}
21912d1ca   Rémi Denis-Courmont   Phonet: hold sock...
132
  		}
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
133
  		hlist++;
f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
134
  	}
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
135
  	rcu_read_unlock();
ba113a94b   Remi Denis-Courmont   Phonet: common so...
136
137
138
139
  }
  
  void pn_sock_hash(struct sock *sk)
  {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
140
  	struct hlist_head *hlist = pn_hash_list(pn_sk(sk)->sobject);
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
141
142
143
  	mutex_lock(&pnsocks.lock);
  	sk_add_node_rcu(sk, hlist);
  	mutex_unlock(&pnsocks.lock);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
144
145
146
147
148
  }
  EXPORT_SYMBOL(pn_sock_hash);
  
  void pn_sock_unhash(struct sock *sk)
  {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
149
150
151
  	mutex_lock(&pnsocks.lock);
  	sk_del_node_init_rcu(sk);
  	mutex_unlock(&pnsocks.lock);
7417fa83c   Rémi Denis-Courmont   Phonet: hook reso...
152
  	pn_sock_unbind_all_res(sk);
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
153
  	synchronize_rcu();
ba113a94b   Remi Denis-Courmont   Phonet: common so...
154
155
  }
  EXPORT_SYMBOL(pn_sock_unhash);
582b0b611   Rémi Denis-Courmont   Phonet: fix race ...
156
  static DEFINE_MUTEX(port_mutex);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  static int pn_socket_bind(struct socket *sock, struct sockaddr *addr, int len)
  {
  	struct sock *sk = sock->sk;
  	struct pn_sock *pn = pn_sk(sk);
  	struct sockaddr_pn *spn = (struct sockaddr_pn *)addr;
  	int err;
  	u16 handle;
  	u8 saddr;
  
  	if (sk->sk_prot->bind)
  		return sk->sk_prot->bind(sk, addr, len);
  
  	if (len < sizeof(struct sockaddr_pn))
  		return -EINVAL;
  	if (spn->spn_family != AF_PHONET)
  		return -EAFNOSUPPORT;
  
  	handle = pn_sockaddr_get_object((struct sockaddr_pn *)addr);
  	saddr = pn_addr(handle);
524048819   Rémi Denis-Courmont   Phonet: basic net...
176
  	if (saddr && phonet_address_lookup(sock_net(sk), saddr))
ba113a94b   Remi Denis-Courmont   Phonet: common so...
177
178
179
180
181
182
183
  		return -EADDRNOTAVAIL;
  
  	lock_sock(sk);
  	if (sk->sk_state != TCP_CLOSE || pn_port(pn->sobject)) {
  		err = -EINVAL; /* attempt to rebind */
  		goto out;
  	}
582b0b611   Rémi Denis-Courmont   Phonet: fix race ...
184
185
  	WARN_ON(sk_hashed(sk));
  	mutex_lock(&port_mutex);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
186
187
  	err = sk->sk_prot->get_port(sk, pn_port(handle));
  	if (err)
582b0b611   Rémi Denis-Courmont   Phonet: fix race ...
188
  		goto out_port;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
189
190
191
192
193
194
195
  
  	/* get_port() sets the port, bind() sets the address if applicable */
  	pn->sobject = pn_object(saddr, pn_port(pn->sobject));
  	pn->resource = spn->spn_resource;
  
  	/* Enable RX on the socket */
  	sk->sk_prot->hash(sk);
582b0b611   Rémi Denis-Courmont   Phonet: fix race ...
196
197
  out_port:
  	mutex_unlock(&port_mutex);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  out:
  	release_sock(sk);
  	return err;
  }
  
  static int pn_socket_autobind(struct socket *sock)
  {
  	struct sockaddr_pn sa;
  	int err;
  
  	memset(&sa, 0, sizeof(sa));
  	sa.spn_family = AF_PHONET;
  	err = pn_socket_bind(sock, (struct sockaddr *)&sa,
  				sizeof(struct sockaddr_pn));
  	if (err != -EINVAL)
  		return err;
  	BUG_ON(!pn_port(pn_sk(sock->sk)->sobject));
  	return 0; /* socket was already bound */
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
217
218
219
220
  static int pn_socket_connect(struct socket *sock, struct sockaddr *addr,
  		int len, int flags)
  {
  	struct sock *sk = sock->sk;
297edb600   Rémi Denis-Courmont   Phonet: support a...
221
  	struct pn_sock *pn = pn_sk(sk);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
222
  	struct sockaddr_pn *spn = (struct sockaddr_pn *)addr;
297edb600   Rémi Denis-Courmont   Phonet: support a...
223
224
  	struct task_struct *tsk = current;
  	long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
225
  	int err;
297edb600   Rémi Denis-Courmont   Phonet: support a...
226
227
  	if (pn_socket_autobind(sock))
  		return -ENOBUFS;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
228
229
230
231
232
233
234
235
236
  	if (len < sizeof(struct sockaddr_pn))
  		return -EINVAL;
  	if (spn->spn_family != AF_PHONET)
  		return -EAFNOSUPPORT;
  
  	lock_sock(sk);
  
  	switch (sock->state) {
  	case SS_UNCONNECTED:
297edb600   Rémi Denis-Courmont   Phonet: support a...
237
  		if (sk->sk_state != TCP_CLOSE) {
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
238
239
  			err = -EISCONN;
  			goto out;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
240
241
  		}
  		break;
297edb600   Rémi Denis-Courmont   Phonet: support a...
242
243
244
245
246
247
  	case SS_CONNECTING:
  		err = -EALREADY;
  		goto out;
  	default:
  		err = -EISCONN;
  		goto out;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
248
  	}
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
249

297edb600   Rémi Denis-Courmont   Phonet: support a...
250
251
  	pn->dobject = pn_sockaddr_get_object(spn);
  	pn->resource = pn_sockaddr_get_resource(spn);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
252
  	sock->state = SS_CONNECTING;
297edb600   Rémi Denis-Courmont   Phonet: support a...
253

b3d625538   Kumar Sanghvi   Phonet: 'connect'...
254
  	err = sk->sk_prot->connect(sk, addr, len);
297edb600   Rémi Denis-Courmont   Phonet: support a...
255
  	if (err) {
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
256
  		sock->state = SS_UNCONNECTED;
297edb600   Rémi Denis-Courmont   Phonet: support a...
257
  		pn->dobject = 0;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
258
259
  		goto out;
  	}
297edb600   Rémi Denis-Courmont   Phonet: support a...
260
261
  	while (sk->sk_state == TCP_SYN_SENT) {
  		DEFINE_WAIT(wait);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
262

297edb600   Rémi Denis-Courmont   Phonet: support a...
263
264
265
266
267
268
269
270
  		if (!timeo) {
  			err = -EINPROGRESS;
  			goto out;
  		}
  		if (signal_pending(tsk)) {
  			err = sock_intr_errno(timeo);
  			goto out;
  		}
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
271

297edb600   Rémi Denis-Courmont   Phonet: support a...
272
273
274
275
276
277
  		prepare_to_wait_exclusive(sk_sleep(sk), &wait,
  						TASK_INTERRUPTIBLE);
  		release_sock(sk);
  		timeo = schedule_timeout(timeo);
  		lock_sock(sk);
  		finish_wait(sk_sleep(sk), &wait);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
278
  	}
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
279

297edb600   Rémi Denis-Courmont   Phonet: support a...
280
281
282
283
284
285
286
  	if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED))
  		err = 0;
  	else if (sk->sk_state == TCP_CLOSE_WAIT)
  		err = -ECONNRESET;
  	else
  		err = -ECONNREFUSED;
  	sock->state = err ? SS_UNCONNECTED : SS_CONNECTED;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
287
288
289
290
  out:
  	release_sock(sk);
  	return err;
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
291

9995a32b4   Rémi Denis-Courmont   Phonet: connected...
292
293
294
295
296
297
  static int pn_socket_accept(struct socket *sock, struct socket *newsock,
  				int flags)
  {
  	struct sock *sk = sock->sk;
  	struct sock *newsk;
  	int err;
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
298
299
  	if (unlikely(sk->sk_state != TCP_LISTEN))
  		return -EINVAL;
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
300
301
302
303
304
305
306
307
308
309
  	newsk = sk->sk_prot->accept(sk, flags, &err);
  	if (!newsk)
  		return err;
  
  	lock_sock(newsk);
  	sock_graft(newsk, newsock);
  	newsock->state = SS_CONNECTED;
  	release_sock(newsk);
  	return 0;
  }
ba113a94b   Remi Denis-Courmont   Phonet: common so...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
  static int pn_socket_getname(struct socket *sock, struct sockaddr *addr,
  				int *sockaddr_len, int peer)
  {
  	struct sock *sk = sock->sk;
  	struct pn_sock *pn = pn_sk(sk);
  
  	memset(addr, 0, sizeof(struct sockaddr_pn));
  	addr->sa_family = AF_PHONET;
  	if (!peer) /* Race with bind() here is userland's problem. */
  		pn_sockaddr_set_object((struct sockaddr_pn *)addr,
  					pn->sobject);
  
  	*sockaddr_len = sizeof(struct sockaddr_pn);
  	return 0;
  }
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
325
326
327
328
329
330
  static unsigned int pn_socket_poll(struct file *file, struct socket *sock,
  					poll_table *wait)
  {
  	struct sock *sk = sock->sk;
  	struct pep_sock *pn = pep_sk(sk);
  	unsigned int mask = 0;
438154823   Eric Dumazet   net: sock_def_rea...
331
  	poll_wait(file, sk_sleep(sk), wait);
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
332

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
333
  	if (sk->sk_state == TCP_CLOSE)
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
334
  		return POLLERR;
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
335
336
  	if (!skb_queue_empty(&sk->sk_receive_queue))
  		mask |= POLLIN | POLLRDNORM;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
337
338
339
  	if (!skb_queue_empty(&pn->ctrlreq_queue))
  		mask |= POLLPRI;
  	if (!mask && sk->sk_state == TCP_CLOSE_WAIT)
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
340
  		return POLLHUP;
01b38606b   Rémi Denis-Courmont   Phonet: do not se...
341
342
343
  	if (sk->sk_state == TCP_ESTABLISHED &&
  		atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf &&
  		atomic_read(&pn->tx_credits))
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
344
345
346
347
  		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
  
  	return mask;
  }
ba113a94b   Remi Denis-Courmont   Phonet: common so...
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
  static int pn_socket_ioctl(struct socket *sock, unsigned int cmd,
  				unsigned long arg)
  {
  	struct sock *sk = sock->sk;
  	struct pn_sock *pn = pn_sk(sk);
  
  	if (cmd == SIOCPNGETOBJECT) {
  		struct net_device *dev;
  		u16 handle;
  		u8 saddr;
  
  		if (get_user(handle, (__u16 __user *)arg))
  			return -EFAULT;
  
  		lock_sock(sk);
  		if (sk->sk_bound_dev_if)
  			dev = dev_get_by_index(sock_net(sk),
  						sk->sk_bound_dev_if);
  		else
  			dev = phonet_device_get(sock_net(sk));
  		if (dev && (dev->flags & IFF_UP))
  			saddr = phonet_address_get(dev, pn_addr(handle));
  		else
  			saddr = PN_NO_ADDR;
  		release_sock(sk);
  
  		if (dev)
  			dev_put(dev);
  		if (saddr == PN_NO_ADDR)
  			return -EHOSTUNREACH;
  
  		handle = pn_object(saddr, pn_port(pn->sobject));
  		return put_user(handle, (__u16 __user *)arg);
  	}
  
  	return sk->sk_prot->ioctl(sk, cmd, arg);
  }
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
385
386
387
388
  static int pn_socket_listen(struct socket *sock, int backlog)
  {
  	struct sock *sk = sock->sk;
  	int err = 0;
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
389
390
391
392
  	if (pn_socket_autobind(sock))
  		return -ENOBUFS;
  
  	lock_sock(sk);
96241544c   Rémi Denis-Courmont   Phonet: allow mul...
393
  	if (sock->state != SS_UNCONNECTED) {
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
394
395
396
  		err = -EINVAL;
  		goto out;
  	}
96241544c   Rémi Denis-Courmont   Phonet: allow mul...
397
398
399
400
  	if (sk->sk_state != TCP_LISTEN) {
  		sk->sk_state = TCP_LISTEN;
  		sk->sk_ack_backlog = 0;
  	}
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
401
402
403
404
405
  	sk->sk_max_ack_backlog = backlog;
  out:
  	release_sock(sk);
  	return err;
  }
ba113a94b   Remi Denis-Courmont   Phonet: common so...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
  static int pn_socket_sendmsg(struct kiocb *iocb, struct socket *sock,
  				struct msghdr *m, size_t total_len)
  {
  	struct sock *sk = sock->sk;
  
  	if (pn_socket_autobind(sock))
  		return -EAGAIN;
  
  	return sk->sk_prot->sendmsg(iocb, sk, m, total_len);
  }
  
  const struct proto_ops phonet_dgram_ops = {
  	.family		= AF_PHONET,
  	.owner		= THIS_MODULE,
  	.release	= pn_socket_release,
  	.bind		= pn_socket_bind,
  	.connect	= sock_no_connect,
  	.socketpair	= sock_no_socketpair,
  	.accept		= sock_no_accept,
  	.getname	= pn_socket_getname,
  	.poll		= datagram_poll,
  	.ioctl		= pn_socket_ioctl,
  	.listen		= sock_no_listen,
  	.shutdown	= sock_no_shutdown,
  	.setsockopt	= sock_no_setsockopt,
  	.getsockopt	= sock_no_getsockopt,
  #ifdef CONFIG_COMPAT
  	.compat_setsockopt = sock_no_setsockopt,
  	.compat_getsockopt = sock_no_getsockopt,
  #endif
  	.sendmsg	= pn_socket_sendmsg,
  	.recvmsg	= sock_common_recvmsg,
  	.mmap		= sock_no_mmap,
  	.sendpage	= sock_no_sendpage,
  };
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
441
442
443
444
445
  const struct proto_ops phonet_stream_ops = {
  	.family		= AF_PHONET,
  	.owner		= THIS_MODULE,
  	.release	= pn_socket_release,
  	.bind		= pn_socket_bind,
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
446
  	.connect	= pn_socket_connect,
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
447
448
449
450
451
452
453
  	.socketpair	= sock_no_socketpair,
  	.accept		= pn_socket_accept,
  	.getname	= pn_socket_getname,
  	.poll		= pn_socket_poll,
  	.ioctl		= pn_socket_ioctl,
  	.listen		= pn_socket_listen,
  	.shutdown	= sock_no_shutdown,
02a47617c   Rémi Denis-Courmont   Phonet: implement...
454
455
  	.setsockopt	= sock_common_setsockopt,
  	.getsockopt	= sock_common_getsockopt,
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
456
  #ifdef CONFIG_COMPAT
02a47617c   Rémi Denis-Courmont   Phonet: implement...
457
458
  	.compat_setsockopt = compat_sock_common_setsockopt,
  	.compat_getsockopt = compat_sock_common_getsockopt,
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
459
460
461
462
463
464
465
  #endif
  	.sendmsg	= pn_socket_sendmsg,
  	.recvmsg	= sock_common_recvmsg,
  	.mmap		= sock_no_mmap,
  	.sendpage	= sock_no_sendpage,
  };
  EXPORT_SYMBOL(phonet_stream_ops);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
466
467
468
469
  /* allocate port for a socket */
  int pn_sock_get_port(struct sock *sk, unsigned short sport)
  {
  	static int port_cur;
524048819   Rémi Denis-Courmont   Phonet: basic net...
470
  	struct net *net = sock_net(sk);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
471
472
473
474
475
476
  	struct pn_sock *pn = pn_sk(sk);
  	struct sockaddr_pn try_sa;
  	struct sock *tmpsk;
  
  	memset(&try_sa, 0, sizeof(struct sockaddr_pn));
  	try_sa.spn_family = AF_PHONET;
582b0b611   Rémi Denis-Courmont   Phonet: fix race ...
477
  	WARN_ON(!mutex_is_locked(&port_mutex));
ba113a94b   Remi Denis-Courmont   Phonet: common so...
478
479
  	if (!sport) {
  		/* search free port */
87ab4e20b   Remi Denis-Courmont   Phonet: proc inte...
480
  		int port, pmin, pmax;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
481

87ab4e20b   Remi Denis-Courmont   Phonet: proc inte...
482
  		phonet_get_local_port_range(&pmin, &pmax);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
483
484
485
486
487
488
  		for (port = pmin; port <= pmax; port++) {
  			port_cur++;
  			if (port_cur < pmin || port_cur > pmax)
  				port_cur = pmin;
  
  			pn_sockaddr_set_port(&try_sa, port_cur);
524048819   Rémi Denis-Courmont   Phonet: basic net...
489
  			tmpsk = pn_find_sock_by_sa(net, &try_sa);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
490
491
492
493
494
495
496
497
498
  			if (tmpsk == NULL) {
  				sport = port_cur;
  				goto found;
  			} else
  				sock_put(tmpsk);
  		}
  	} else {
  		/* try to find specific port */
  		pn_sockaddr_set_port(&try_sa, sport);
524048819   Rémi Denis-Courmont   Phonet: basic net...
499
  		tmpsk = pn_find_sock_by_sa(net, &try_sa);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
500
501
502
503
504
505
  		if (tmpsk == NULL)
  			/* No sock there! We can use that port... */
  			goto found;
  		else
  			sock_put(tmpsk);
  	}
ba113a94b   Remi Denis-Courmont   Phonet: common so...
506
507
508
509
  	/* the port must be in use already */
  	return -EADDRINUSE;
  
  found:
ba113a94b   Remi Denis-Courmont   Phonet: common so...
510
511
512
513
  	pn->sobject = pn_object(pn_addr(pn->sobject), sport);
  	return 0;
  }
  EXPORT_SYMBOL(pn_sock_get_port);
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
514

ae6e2aef6   Randy Dunlap   phonet: fix build...
515
  #ifdef CONFIG_PROC_FS
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
516
517
518
  static struct sock *pn_sock_get_idx(struct seq_file *seq, loff_t pos)
  {
  	struct net *net = seq_file_net(seq);
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
519
  	struct hlist_head *hlist = pnsocks.hlist;
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
520
  	struct sock *sknode;
95c961747   Eric Dumazet   net: cleanup unsi...
521
  	unsigned int h;
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
522

6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
523
  	for (h = 0; h < PN_HASHSIZE; h++) {
b67bfe0d4   Sasha Levin   hlist: drop the n...
524
  		sk_for_each_rcu(sknode, hlist) {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
525
526
527
528
529
530
531
  			if (!net_eq(net, sock_net(sknode)))
  				continue;
  			if (!pos)
  				return sknode;
  			pos--;
  		}
  		hlist++;
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
  	}
  	return NULL;
  }
  
  static struct sock *pn_sock_get_next(struct seq_file *seq, struct sock *sk)
  {
  	struct net *net = seq_file_net(seq);
  
  	do
  		sk = sk_next(sk);
  	while (sk && !net_eq(net, sock_net(sk)));
  
  	return sk;
  }
  
  static void *pn_sock_seq_start(struct seq_file *seq, loff_t *pos)
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
548
  	__acquires(rcu)
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
549
  {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
550
  	rcu_read_lock();
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
  	return *pos ? pn_sock_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
  }
  
  static void *pn_sock_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct sock *sk;
  
  	if (v == SEQ_START_TOKEN)
  		sk = pn_sock_get_idx(seq, 0);
  	else
  		sk = pn_sock_get_next(seq, v);
  	(*pos)++;
  	return sk;
  }
  
  static void pn_sock_seq_stop(struct seq_file *seq, void *v)
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
567
  	__releases(rcu)
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
568
  {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
569
  	rcu_read_unlock();
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
570
571
572
573
  }
  
  static int pn_sock_seq_show(struct seq_file *seq, void *v)
  {
652586df9   Tetsuo Handa   seq_file: remove ...
574
  	seq_setwidth(seq, 127);
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
575
  	if (v == SEQ_START_TOKEN)
652586df9   Tetsuo Handa   seq_file: remove ...
576
577
  		seq_puts(seq, "pt  loc  rem rs st tx_queue rx_queue "
  			"  uid inode ref pointer drops");
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
578
579
580
581
582
  	else {
  		struct sock *sk = v;
  		struct pn_sock *pn = pn_sk(sk);
  
  		seq_printf(seq, "%2d %04X:%04X:%02X %02X %08X:%08X %5d %lu "
652586df9   Tetsuo Handa   seq_file: remove ...
583
  			"%d %pK %d",
a8059512b   Rémi Denis-Courmont   Phonet: implement...
584
585
  			sk->sk_protocol, pn->sobject, pn->dobject,
  			pn->resource, sk->sk_state,
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
586
  			sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk),
a7cb5a49b   Eric W. Biederman   userns: Print out...
587
588
  			from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
  			sock_i_ino(sk),
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
589
  			atomic_read(&sk->sk_refcnt), sk,
652586df9   Tetsuo Handa   seq_file: remove ...
590
  			atomic_read(&sk->sk_drops));
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
591
  	}
652586df9   Tetsuo Handa   seq_file: remove ...
592
593
  	seq_pad(seq, '
  ');
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
594
595
596
597
598
599
600
601
602
603
604
605
  	return 0;
  }
  
  static const struct seq_operations pn_sock_seq_ops = {
  	.start = pn_sock_seq_start,
  	.next = pn_sock_seq_next,
  	.stop = pn_sock_seq_stop,
  	.show = pn_sock_seq_show,
  };
  
  static int pn_sock_open(struct inode *inode, struct file *file)
  {
cb7d9e7f9   Rémi Denis-Courmont   Phonet: fix /proc...
606
607
  	return seq_open_net(inode, file, &pn_sock_seq_ops,
  				sizeof(struct seq_net_private));
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
608
609
610
611
612
613
614
  }
  
  const struct file_operations pn_sock_seq_fops = {
  	.owner = THIS_MODULE,
  	.open = pn_sock_open,
  	.read = seq_read,
  	.llseek = seq_lseek,
cb7d9e7f9   Rémi Denis-Courmont   Phonet: fix /proc...
615
  	.release = seq_release_net,
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
616
  };
ae6e2aef6   Randy Dunlap   phonet: fix build...
617
  #endif
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
  
  static struct  {
  	struct sock *sk[256];
  } pnres;
  
  /*
   * Find and hold socket based on resource.
   */
  struct sock *pn_find_sock_by_res(struct net *net, u8 res)
  {
  	struct sock *sk;
  
  	if (!net_eq(net, &init_net))
  		return NULL;
  
  	rcu_read_lock();
  	sk = rcu_dereference(pnres.sk[res]);
  	if (sk)
  		sock_hold(sk);
  	rcu_read_unlock();
  	return sk;
  }
  
  static DEFINE_MUTEX(resource_mutex);
  
  int pn_sock_bind_res(struct sock *sk, u8 res)
  {
  	int ret = -EADDRINUSE;
  
  	if (!net_eq(sock_net(sk), &init_net))
  		return -ENOIOCTLCMD;
  	if (!capable(CAP_SYS_ADMIN))
  		return -EPERM;
  	if (pn_socket_autobind(sk->sk_socket))
  		return -EAGAIN;
  
  	mutex_lock(&resource_mutex);
  	if (pnres.sk[res] == NULL) {
  		sock_hold(sk);
cf778b00e   Eric Dumazet   net: reintroduce ...
657
  		rcu_assign_pointer(pnres.sk[res], sk);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
  		ret = 0;
  	}
  	mutex_unlock(&resource_mutex);
  	return ret;
  }
  
  int pn_sock_unbind_res(struct sock *sk, u8 res)
  {
  	int ret = -ENOENT;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EPERM;
  
  	mutex_lock(&resource_mutex);
  	if (pnres.sk[res] == sk) {
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
673
  		RCU_INIT_POINTER(pnres.sk[res], NULL);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
674
675
676
677
678
679
680
681
682
683
684
685
686
  		ret = 0;
  	}
  	mutex_unlock(&resource_mutex);
  
  	if (ret == 0) {
  		synchronize_rcu();
  		sock_put(sk);
  	}
  	return ret;
  }
  
  void pn_sock_unbind_all_res(struct sock *sk)
  {
95c961747   Eric Dumazet   net: cleanup unsi...
687
  	unsigned int res, match = 0;
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
688
689
690
691
  
  	mutex_lock(&resource_mutex);
  	for (res = 0; res < 256; res++) {
  		if (pnres.sk[res] == sk) {
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
692
  			RCU_INIT_POINTER(pnres.sk[res], NULL);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
693
694
695
696
  			match++;
  		}
  	}
  	mutex_unlock(&resource_mutex);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
697
  	while (match > 0) {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
698
  		__sock_put(sk);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
699
700
  		match--;
  	}
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
701
  	/* Caller is responsible for RCU sync before final sock_put() */
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
702
  }
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
703
704
705
706
707
  
  #ifdef CONFIG_PROC_FS
  static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos)
  {
  	struct net *net = seq_file_net(seq);
95c961747   Eric Dumazet   net: cleanup unsi...
708
  	unsigned int i;
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
  
  	if (!net_eq(net, &init_net))
  		return NULL;
  
  	for (i = 0; i < 256; i++) {
  		if (pnres.sk[i] == NULL)
  			continue;
  		if (!pos)
  			return pnres.sk + i;
  		pos--;
  	}
  	return NULL;
  }
  
  static struct sock **pn_res_get_next(struct seq_file *seq, struct sock **sk)
  {
  	struct net *net = seq_file_net(seq);
95c961747   Eric Dumazet   net: cleanup unsi...
726
  	unsigned int i;
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
  
  	BUG_ON(!net_eq(net, &init_net));
  
  	for (i = (sk - pnres.sk) + 1; i < 256; i++)
  		if (pnres.sk[i])
  			return pnres.sk + i;
  	return NULL;
  }
  
  static void *pn_res_seq_start(struct seq_file *seq, loff_t *pos)
  	__acquires(resource_mutex)
  {
  	mutex_lock(&resource_mutex);
  	return *pos ? pn_res_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
  }
  
  static void *pn_res_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct sock **sk;
  
  	if (v == SEQ_START_TOKEN)
  		sk = pn_res_get_idx(seq, 0);
  	else
  		sk = pn_res_get_next(seq, v);
  	(*pos)++;
  	return sk;
  }
  
  static void pn_res_seq_stop(struct seq_file *seq, void *v)
  	__releases(resource_mutex)
  {
  	mutex_unlock(&resource_mutex);
  }
  
  static int pn_res_seq_show(struct seq_file *seq, void *v)
  {
652586df9   Tetsuo Handa   seq_file: remove ...
763
  	seq_setwidth(seq, 63);
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
764
  	if (v == SEQ_START_TOKEN)
652586df9   Tetsuo Handa   seq_file: remove ...
765
  		seq_puts(seq, "rs   uid inode");
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
766
767
768
  	else {
  		struct sock **psk = v;
  		struct sock *sk = *psk;
652586df9   Tetsuo Handa   seq_file: remove ...
769
  		seq_printf(seq, "%02X %5u %lu",
a7cb5a49b   Eric W. Biederman   userns: Print out...
770
771
  			   (int) (psk - pnres.sk),
  			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
652586df9   Tetsuo Handa   seq_file: remove ...
772
  			   sock_i_ino(sk));
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
773
  	}
652586df9   Tetsuo Handa   seq_file: remove ...
774
775
  	seq_pad(seq, '
  ');
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
  	return 0;
  }
  
  static const struct seq_operations pn_res_seq_ops = {
  	.start = pn_res_seq_start,
  	.next = pn_res_seq_next,
  	.stop = pn_res_seq_stop,
  	.show = pn_res_seq_show,
  };
  
  static int pn_res_open(struct inode *inode, struct file *file)
  {
  	return seq_open_net(inode, file, &pn_res_seq_ops,
  				sizeof(struct seq_net_private));
  }
  
  const struct file_operations pn_res_seq_fops = {
  	.owner = THIS_MODULE,
  	.open = pn_res_open,
  	.read = seq_read,
  	.llseek = seq_lseek,
  	.release = seq_release_net,
  };
  #endif