Blame view

net/phonet/socket.c 17.9 KB
ba113a94b   Remi Denis-Courmont   Phonet: common so...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /*
   * File: socket.c
   *
   * Phonet sockets
   *
   * Copyright (C) 2008 Nokia Corporation.
   *
   * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com>
   * Original author: Sakari Ailus <sakari.ailus@nokia.com>
   *
   * 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
57
58
59
60
  } pnsocks;
  
  void __init pn_sock_init(void)
  {
  	unsigned i;
  
  	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
75
76
77
78
79
  {
  	struct hlist_node *node;
  	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...
80
  	struct hlist_head *hlist = pn_hash_list(obj);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
81

44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
82
83
  	rcu_read_lock();
  	sk_for_each_rcu(sknode, node, hlist) {
ba113a94b   Remi Denis-Courmont   Phonet: common so...
84
85
  		struct pn_sock *pn = pn_sk(sknode);
  		BUG_ON(!pn->sobject); /* unbound socket */
524048819   Rémi Denis-Courmont   Phonet: basic net...
86
87
  		if (!net_eq(sock_net(sknode), net))
  			continue;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
88
89
90
91
92
93
94
95
96
  		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 ...
97
98
  		if (pn_addr(pn->sobject) &&
  		    pn_addr(pn->sobject) != pn_addr(obj))
ba113a94b   Remi Denis-Courmont   Phonet: common so...
99
100
101
102
103
104
  			continue;
  
  		rval = sknode;
  		sock_hold(sknode);
  		break;
  	}
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
105
  	rcu_read_unlock();
ba113a94b   Remi Denis-Courmont   Phonet: common so...
106
107
  
  	return rval;
f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
108
109
110
111
112
  }
  
  /* 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...
113
114
  	struct hlist_head *hlist = pnsocks.hlist;
  	unsigned h;
f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
115

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

6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
121
122
  		sk_for_each(sknode, node, hlist) {
  			struct sk_buff *clone;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
123

6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
124
125
126
127
128
129
130
131
132
133
  			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...
134
  		}
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
135
  		hlist++;
f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
136
  	}
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
137
  	rcu_read_unlock();
ba113a94b   Remi Denis-Courmont   Phonet: common so...
138
139
140
141
  }
  
  void pn_sock_hash(struct sock *sk)
  {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
142
  	struct hlist_head *hlist = pn_hash_list(pn_sk(sk)->sobject);
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
143
144
145
  	mutex_lock(&pnsocks.lock);
  	sk_add_node_rcu(sk, hlist);
  	mutex_unlock(&pnsocks.lock);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
146
147
148
149
150
  }
  EXPORT_SYMBOL(pn_sock_hash);
  
  void pn_sock_unhash(struct sock *sk)
  {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
151
152
153
  	mutex_lock(&pnsocks.lock);
  	sk_del_node_init_rcu(sk);
  	mutex_unlock(&pnsocks.lock);
7417fa83c   Rémi Denis-Courmont   Phonet: hook reso...
154
  	pn_sock_unbind_all_res(sk);
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
155
  	synchronize_rcu();
ba113a94b   Remi Denis-Courmont   Phonet: common so...
156
157
  }
  EXPORT_SYMBOL(pn_sock_unhash);
582b0b611   Rémi Denis-Courmont   Phonet: fix race ...
158
  static DEFINE_MUTEX(port_mutex);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  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...
178
  	if (saddr && phonet_address_lookup(sock_net(sk), saddr))
ba113a94b   Remi Denis-Courmont   Phonet: common so...
179
180
181
182
183
184
185
  		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 ...
186
187
  	WARN_ON(sk_hashed(sk));
  	mutex_lock(&port_mutex);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
188
189
  	err = sk->sk_prot->get_port(sk, pn_port(handle));
  	if (err)
582b0b611   Rémi Denis-Courmont   Phonet: fix race ...
190
  		goto out_port;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
191
192
193
194
195
196
197
  
  	/* 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 ...
198
199
  out_port:
  	mutex_unlock(&port_mutex);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  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'...
219
220
221
222
  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...
223
  	struct pn_sock *pn = pn_sk(sk);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
224
  	struct sockaddr_pn *spn = (struct sockaddr_pn *)addr;
297edb600   Rémi Denis-Courmont   Phonet: support a...
225
226
  	struct task_struct *tsk = current;
  	long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
227
  	int err;
297edb600   Rémi Denis-Courmont   Phonet: support a...
228
229
  	if (pn_socket_autobind(sock))
  		return -ENOBUFS;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
230
231
232
233
234
235
236
237
238
  	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...
239
  		if (sk->sk_state != TCP_CLOSE) {
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
240
241
  			err = -EISCONN;
  			goto out;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
242
243
  		}
  		break;
297edb600   Rémi Denis-Courmont   Phonet: support a...
244
245
246
247
248
249
  	case SS_CONNECTING:
  		err = -EALREADY;
  		goto out;
  	default:
  		err = -EISCONN;
  		goto out;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
250
  	}
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
251

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

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

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

297edb600   Rémi Denis-Courmont   Phonet: support a...
274
275
276
277
278
279
  		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'...
280
  	}
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
281

297edb600   Rémi Denis-Courmont   Phonet: support a...
282
283
284
285
286
287
288
  	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'...
289
290
291
292
  out:
  	release_sock(sk);
  	return err;
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
293

9995a32b4   Rémi Denis-Courmont   Phonet: connected...
294
295
296
297
298
299
  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 ...
300
301
  	if (unlikely(sk->sk_state != TCP_LISTEN))
  		return -EINVAL;
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
302
303
304
305
306
307
308
309
310
311
  	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...
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  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...
327
328
329
330
331
332
  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...
333
  	poll_wait(file, sk_sleep(sk), wait);
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
334

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
335
  	if (sk->sk_state == TCP_CLOSE)
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
336
  		return POLLERR;
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
337
338
  	if (!skb_queue_empty(&sk->sk_receive_queue))
  		mask |= POLLIN | POLLRDNORM;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
339
340
341
  	if (!skb_queue_empty(&pn->ctrlreq_queue))
  		mask |= POLLPRI;
  	if (!mask && sk->sk_state == TCP_CLOSE_WAIT)
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
342
  		return POLLHUP;
01b38606b   Rémi Denis-Courmont   Phonet: do not se...
343
344
345
  	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...
346
347
348
349
  		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
  
  	return mask;
  }
ba113a94b   Remi Denis-Courmont   Phonet: common so...
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
385
386
  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...
387
388
389
390
  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...
391
392
393
394
  	if (pn_socket_autobind(sock))
  		return -ENOBUFS;
  
  	lock_sock(sk);
96241544c   Rémi Denis-Courmont   Phonet: allow mul...
395
  	if (sock->state != SS_UNCONNECTED) {
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
396
397
398
  		err = -EINVAL;
  		goto out;
  	}
96241544c   Rémi Denis-Courmont   Phonet: allow mul...
399
400
401
402
  	if (sk->sk_state != TCP_LISTEN) {
  		sk->sk_state = TCP_LISTEN;
  		sk->sk_ack_backlog = 0;
  	}
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
403
404
405
406
407
  	sk->sk_max_ack_backlog = backlog;
  out:
  	release_sock(sk);
  	return err;
  }
ba113a94b   Remi Denis-Courmont   Phonet: common so...
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
441
442
  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...
443
444
445
446
447
  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'...
448
  	.connect	= pn_socket_connect,
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
449
450
451
452
453
454
455
  	.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...
456
457
  	.setsockopt	= sock_common_setsockopt,
  	.getsockopt	= sock_common_getsockopt,
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
458
  #ifdef CONFIG_COMPAT
02a47617c   Rémi Denis-Courmont   Phonet: implement...
459
460
  	.compat_setsockopt = compat_sock_common_setsockopt,
  	.compat_getsockopt = compat_sock_common_getsockopt,
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
461
462
463
464
465
466
467
  #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...
468
469
470
471
  /* 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...
472
  	struct net *net = sock_net(sk);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
473
474
475
476
477
478
  	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 ...
479
  	WARN_ON(!mutex_is_locked(&port_mutex));
ba113a94b   Remi Denis-Courmont   Phonet: common so...
480
481
  	if (!sport) {
  		/* search free port */
87ab4e20b   Remi Denis-Courmont   Phonet: proc inte...
482
  		int port, pmin, pmax;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
483

87ab4e20b   Remi Denis-Courmont   Phonet: proc inte...
484
  		phonet_get_local_port_range(&pmin, &pmax);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
485
486
487
488
489
490
  		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...
491
  			tmpsk = pn_find_sock_by_sa(net, &try_sa);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
492
493
494
495
496
497
498
499
500
  			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...
501
  		tmpsk = pn_find_sock_by_sa(net, &try_sa);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
502
503
504
505
506
507
  		if (tmpsk == NULL)
  			/* No sock there! We can use that port... */
  			goto found;
  		else
  			sock_put(tmpsk);
  	}
ba113a94b   Remi Denis-Courmont   Phonet: common so...
508
509
510
511
  	/* the port must be in use already */
  	return -EADDRINUSE;
  
  found:
ba113a94b   Remi Denis-Courmont   Phonet: common so...
512
513
514
515
  	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...
516

ae6e2aef6   Randy Dunlap   phonet: fix build...
517
  #ifdef CONFIG_PROC_FS
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
518
519
520
  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...
521
  	struct hlist_head *hlist = pnsocks.hlist;
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
522
523
  	struct hlist_node *node;
  	struct sock *sknode;
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
524
  	unsigned h;
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
525

6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
526
  	for (h = 0; h < PN_HASHSIZE; h++) {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
527
  		sk_for_each_rcu(sknode, node, hlist) {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
528
529
530
531
532
533
534
  			if (!net_eq(net, sock_net(sknode)))
  				continue;
  			if (!pos)
  				return sknode;
  			pos--;
  		}
  		hlist++;
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
  	}
  	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...
551
  	__acquires(rcu)
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
552
  {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
553
  	rcu_read_lock();
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
  	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...
570
  	__releases(rcu)
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
571
  {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
572
  	rcu_read_unlock();
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
573
574
575
576
577
578
579
580
581
582
583
584
585
586
  }
  
  static int pn_sock_seq_show(struct seq_file *seq, void *v)
  {
  	int len;
  
  	if (v == SEQ_START_TOKEN)
  		seq_printf(seq, "%s%n", "pt  loc  rem rs st tx_queue rx_queue "
  			"  uid inode ref pointer drops", &len);
  	else {
  		struct sock *sk = v;
  		struct pn_sock *pn = pn_sk(sk);
  
  		seq_printf(seq, "%2d %04X:%04X:%02X %02X %08X:%08X %5d %lu "
71338aa7d   Dan Rosenberg   net: convert %p u...
587
  			"%d %pK %d%n",
a8059512b   Rémi Denis-Courmont   Phonet: implement...
588
589
  			sk->sk_protocol, pn->sobject, pn->dobject,
  			pn->resource, sk->sk_state,
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
  			sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk),
  			sock_i_uid(sk), sock_i_ino(sk),
  			atomic_read(&sk->sk_refcnt), sk,
  			atomic_read(&sk->sk_drops), &len);
  	}
  	seq_printf(seq, "%*s
  ", 127 - len, "");
  	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...
609
610
  	return seq_open_net(inode, file, &pn_sock_seq_ops,
  				sizeof(struct seq_net_private));
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
611
612
613
614
615
616
617
  }
  
  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...
618
  	.release = seq_release_net,
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
619
  };
ae6e2aef6   Randy Dunlap   phonet: fix build...
620
  #endif
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
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
657
658
659
  
  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 ...
660
  		rcu_assign_pointer(pnres.sk[res], sk);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
  		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...
676
  		RCU_INIT_POINTER(pnres.sk[res], NULL);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
  		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)
  {
  	unsigned res, match = 0;
  
  	mutex_lock(&resource_mutex);
  	for (res = 0; res < 256; res++) {
  		if (pnres.sk[res] == sk) {
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
695
  			RCU_INIT_POINTER(pnres.sk[res], NULL);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
696
697
698
699
  			match++;
  		}
  	}
  	mutex_unlock(&resource_mutex);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
700
  	while (match > 0) {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
701
  		__sock_put(sk);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
702
703
  		match--;
  	}
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
704
  	/* Caller is responsible for RCU sync before final sock_put() */
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
705
  }
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
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
763
764
765
766
767
768
769
770
771
772
773
774
  
  #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);
  	unsigned i;
  
  	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);
  	unsigned i;
  
  	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)
  {
  	int len;
  
  	if (v == SEQ_START_TOKEN)
  		seq_printf(seq, "%s%n", "rs   uid inode", &len);
  	else {
  		struct sock **psk = v;
  		struct sock *sk = *psk;
  
  		seq_printf(seq, "%02X %5d %lu%n",
9e0064a54   David S. Miller   phonet: Fix build...
775
776
  			   (int) (psk - pnres.sk), sock_i_uid(sk),
  			   sock_i_ino(sk), &len);
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
  	}
  	seq_printf(seq, "%*s
  ", 63 - len, "");
  	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