Blame view

net/phonet/socket.c 17.9 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>
174cd4b1e   Ingo Molnar   sched/headers: Pr...
29
  #include <linux/sched/signal.h>
ba113a94b   Remi Denis-Courmont   Phonet: common so...
30
31
32
33
  #include <net/sock.h>
  #include <net/tcp_states.h>
  
  #include <linux/phonet.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
34
  #include <linux/export.h>
ba113a94b   Remi Denis-Courmont   Phonet: common so...
35
  #include <net/phonet/phonet.h>
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
36
  #include <net/phonet/pep.h>
ba113a94b   Remi Denis-Courmont   Phonet: common so...
37
38
39
40
41
42
43
44
45
46
47
48
  #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...
49
50
  #define PN_HASHSIZE	16
  #define PN_HASHMASK	(PN_HASHSIZE-1)
ba113a94b   Remi Denis-Courmont   Phonet: common so...
51
  static struct  {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
52
  	struct hlist_head hlist[PN_HASHSIZE];
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
53
  	struct mutex lock;
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
54
55
56
57
  } pnsocks;
  
  void __init pn_sock_init(void)
  {
95c961747   Eric Dumazet   net: cleanup unsi...
58
  	unsigned int i;
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
59
60
61
  
  	for (i = 0; i < PN_HASHSIZE; i++)
  		INIT_HLIST_HEAD(pnsocks.hlist + i);
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
62
  	mutex_init(&pnsocks.lock);
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
63
64
65
66
67
68
  }
  
  static struct hlist_head *pn_hash_list(u16 obj)
  {
  	return pnsocks.hlist + (obj & PN_HASHMASK);
  }
ba113a94b   Remi Denis-Courmont   Phonet: common so...
69
70
71
72
73
  
  /*
   * 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...
74
  struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *spn)
ba113a94b   Remi Denis-Courmont   Phonet: common so...
75
  {
ba113a94b   Remi Denis-Courmont   Phonet: common so...
76
77
78
79
  	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
  	rcu_read_lock();
b67bfe0d4   Sasha Levin   hlist: drop the n...
83
  	sk_for_each_rcu(sknode, 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
  	struct hlist_head *hlist = pnsocks.hlist;
95c961747   Eric Dumazet   net: cleanup unsi...
114
  	unsigned int 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
  	for (h = 0; h < PN_HASHSIZE; h++) {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
118
  		struct sock *sknode;
f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
119

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

6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
123
124
125
126
127
128
129
130
131
132
  			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...
133
  		}
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
134
  		hlist++;
f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
135
  	}
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
136
  	rcu_read_unlock();
ba113a94b   Remi Denis-Courmont   Phonet: common so...
137
  }
086c653f5   Craig Gallek   sock: struct prot...
138
  int pn_sock_hash(struct sock *sk)
ba113a94b   Remi Denis-Courmont   Phonet: common so...
139
  {
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);
086c653f5   Craig Gallek   sock: struct prot...
144
145
  
  	return 0;
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
  
  	/* 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 */
086c653f5   Craig Gallek   sock: struct prot...
197
  	err = 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
  static int pn_socket_accept(struct socket *sock, struct socket *newsock,
cdfbabfb2   David Howells   net: Work around ...
295
  			    int flags, bool kern)
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
296
297
298
299
  {
  	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;
cdfbabfb2   David Howells   net: Work around ...
302
  	newsk = sk->sk_prot->accept(sk, flags, &err, kern);
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
303
304
305
306
307
308
309
310
311
  	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
  	if (sk->sk_state == TCP_ESTABLISHED &&
14afee4b6   Reshetova, Elena   net: convert sock...
344
  		refcount_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf &&
01b38606b   Rémi Denis-Courmont   Phonet: do not se...
345
  		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;
  }
1b7841404   Ying Xue   net: Remove iocb ...
408
409
  static int pn_socket_sendmsg(struct socket *sock, struct msghdr *m,
  			     size_t total_len)
ba113a94b   Remi Denis-Courmont   Phonet: common so...
410
411
412
413
414
  {
  	struct sock *sk = sock->sk;
  
  	if (pn_socket_autobind(sock))
  		return -EAGAIN;
1b7841404   Ying Xue   net: Remove iocb ...
415
  	return sk->sk_prot->sendmsg(sk, m, total_len);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
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
  }
  
  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...
442
443
444
445
446
  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'...
447
  	.connect	= pn_socket_connect,
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
448
449
450
451
452
453
454
  	.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...
455
456
  	.setsockopt	= sock_common_setsockopt,
  	.getsockopt	= sock_common_getsockopt,
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
457
  #ifdef CONFIG_COMPAT
02a47617c   Rémi Denis-Courmont   Phonet: implement...
458
459
  	.compat_setsockopt = compat_sock_common_setsockopt,
  	.compat_getsockopt = compat_sock_common_getsockopt,
9995a32b4   Rémi Denis-Courmont   Phonet: connected...
460
461
462
463
464
465
466
  #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...
467
468
469
470
  /* 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...
471
  	struct net *net = sock_net(sk);
ba113a94b   Remi Denis-Courmont   Phonet: common so...
472
473
474
475
476
477
  	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 ...
478
  	WARN_ON(!mutex_is_locked(&port_mutex));
ba113a94b   Remi Denis-Courmont   Phonet: common so...
479
480
  	if (!sport) {
  		/* search free port */
87ab4e20b   Remi Denis-Courmont   Phonet: proc inte...
481
  		int port, pmin, pmax;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
482

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

ae6e2aef6   Randy Dunlap   phonet: fix build...
516
  #ifdef CONFIG_PROC_FS
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
517
518
519
  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...
520
  	struct hlist_head *hlist = pnsocks.hlist;
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
521
  	struct sock *sknode;
95c961747   Eric Dumazet   net: cleanup unsi...
522
  	unsigned int h;
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
523

6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
524
  	for (h = 0; h < PN_HASHSIZE; h++) {
b67bfe0d4   Sasha Levin   hlist: drop the n...
525
  		sk_for_each_rcu(sknode, hlist) {
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
526
527
528
529
530
531
532
  			if (!net_eq(net, sock_net(sknode)))
  				continue;
  			if (!pos)
  				return sknode;
  			pos--;
  		}
  		hlist++;
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
  	}
  	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...
549
  	__acquires(rcu)
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
550
  {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
551
  	rcu_read_lock();
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  	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...
568
  	__releases(rcu)
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
569
  {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
570
  	rcu_read_unlock();
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
571
572
573
574
  }
  
  static int pn_sock_seq_show(struct seq_file *seq, void *v)
  {
652586df9   Tetsuo Handa   seq_file: remove ...
575
  	seq_setwidth(seq, 127);
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
576
  	if (v == SEQ_START_TOKEN)
652586df9   Tetsuo Handa   seq_file: remove ...
577
578
  		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...
579
580
581
582
583
  	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 ...
584
  			"%d %pK %d",
a8059512b   Rémi Denis-Courmont   Phonet: implement...
585
586
  			sk->sk_protocol, pn->sobject, pn->dobject,
  			pn->resource, sk->sk_state,
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
587
  			sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk),
a7cb5a49b   Eric W. Biederman   userns: Print out...
588
589
  			from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
  			sock_i_ino(sk),
41c6d650f   Reshetova, Elena   net: convert sock...
590
  			refcount_read(&sk->sk_refcnt), sk,
652586df9   Tetsuo Handa   seq_file: remove ...
591
  			atomic_read(&sk->sk_drops));
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
592
  	}
652586df9   Tetsuo Handa   seq_file: remove ...
593
594
  	seq_pad(seq, '
  ');
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
595
596
597
598
599
600
601
602
603
604
605
606
  	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...
607
608
  	return seq_open_net(inode, file, &pn_sock_seq_ops,
  				sizeof(struct seq_net_private));
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
609
610
611
612
613
614
615
  }
  
  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...
616
  	.release = seq_release_net,
c1dc13e9d   Rémi Denis-Courmont   Phonet: sockets l...
617
  };
ae6e2aef6   Randy Dunlap   phonet: fix build...
618
  #endif
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
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
657
  
  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 ...
658
  		rcu_assign_pointer(pnres.sk[res], sk);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
  		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...
674
  		RCU_INIT_POINTER(pnres.sk[res], NULL);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
675
676
677
678
679
680
681
682
683
684
685
686
687
  		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...
688
  	unsigned int res, match = 0;
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
689
690
691
692
  
  	mutex_lock(&resource_mutex);
  	for (res = 0; res < 256; res++) {
  		if (pnres.sk[res] == sk) {
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
693
  			RCU_INIT_POINTER(pnres.sk[res], NULL);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
694
695
696
697
  			match++;
  		}
  	}
  	mutex_unlock(&resource_mutex);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
698
  	while (match > 0) {
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
699
  		__sock_put(sk);
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
700
701
  		match--;
  	}
44f4d5a27   Rémi Denis-Courmont   Phonet: convert b...
702
  	/* Caller is responsible for RCU sync before final sock_put() */
4e3d16ce5   Rémi Denis-Courmont   Phonet: resource ...
703
  }
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
704
705
706
707
708
  
  #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...
709
  	unsigned int i;
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
  
  	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...
727
  	unsigned int i;
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
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
  
  	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 ...
764
  	seq_setwidth(seq, 63);
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
765
  	if (v == SEQ_START_TOKEN)
652586df9   Tetsuo Handa   seq_file: remove ...
766
  		seq_puts(seq, "rs   uid inode");
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
767
768
769
  	else {
  		struct sock **psk = v;
  		struct sock *sk = *psk;
652586df9   Tetsuo Handa   seq_file: remove ...
770
  		seq_printf(seq, "%02X %5u %lu",
a7cb5a49b   Eric W. Biederman   userns: Print out...
771
772
  			   (int) (psk - pnres.sk),
  			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
652586df9   Tetsuo Handa   seq_file: remove ...
773
  			   sock_i_ino(sk));
507215f8d   Rémi Denis-Courmont   Phonet: list subs...
774
  	}
652586df9   Tetsuo Handa   seq_file: remove ...
775
776
  	seq_pad(seq, '
  ');
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
  	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