]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - net/phonet/socket.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6-omap-h63xx.git] / net / phonet / socket.c
1 /*
2  * File: socket.c
3  *
4  * Phonet sockets
5  *
6  * Copyright (C) 2008 Nokia Corporation.
7  *
8  * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com>
9  * Original author: Sakari Ailus <sakari.ailus@nokia.com>
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * version 2 as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25
26 #include <linux/kernel.h>
27 #include <linux/net.h>
28 #include <linux/poll.h>
29 #include <net/sock.h>
30 #include <net/tcp_states.h>
31
32 #include <linux/phonet.h>
33 #include <net/phonet/phonet.h>
34 #include <net/phonet/pep.h>
35 #include <net/phonet/pn_dev.h>
36
37 static int pn_socket_release(struct socket *sock)
38 {
39         struct sock *sk = sock->sk;
40
41         if (sk) {
42                 sock->sk = NULL;
43                 sk->sk_prot->close(sk, 0);
44         }
45         return 0;
46 }
47
48 static struct  {
49         struct hlist_head hlist;
50         spinlock_t lock;
51 } pnsocks = {
52         .hlist = HLIST_HEAD_INIT,
53         .lock = __SPIN_LOCK_UNLOCKED(pnsocks.lock),
54 };
55
56 /*
57  * Find address based on socket address, match only certain fields.
58  * Also grab sock if it was found. Remember to sock_put it later.
59  */
60 struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *spn)
61 {
62         struct hlist_node *node;
63         struct sock *sknode;
64         struct sock *rval = NULL;
65         u16 obj = pn_sockaddr_get_object(spn);
66         u8 res = spn->spn_resource;
67
68         spin_lock_bh(&pnsocks.lock);
69
70         sk_for_each(sknode, node, &pnsocks.hlist) {
71                 struct pn_sock *pn = pn_sk(sknode);
72                 BUG_ON(!pn->sobject); /* unbound socket */
73
74                 if (pn_port(obj)) {
75                         /* Look up socket by port */
76                         if (pn_port(pn->sobject) != pn_port(obj))
77                                 continue;
78                 } else {
79                         /* If port is zero, look up by resource */
80                         if (pn->resource != res)
81                                 continue;
82                 }
83                 if (pn_addr(pn->sobject)
84                  && pn_addr(pn->sobject) != pn_addr(obj))
85                         continue;
86
87                 rval = sknode;
88                 sock_hold(sknode);
89                 break;
90         }
91
92         spin_unlock_bh(&pnsocks.lock);
93
94         return rval;
95
96 }
97
98 void pn_sock_hash(struct sock *sk)
99 {
100         spin_lock_bh(&pnsocks.lock);
101         sk_add_node(sk, &pnsocks.hlist);
102         spin_unlock_bh(&pnsocks.lock);
103 }
104 EXPORT_SYMBOL(pn_sock_hash);
105
106 void pn_sock_unhash(struct sock *sk)
107 {
108         spin_lock_bh(&pnsocks.lock);
109         sk_del_node_init(sk);
110         spin_unlock_bh(&pnsocks.lock);
111 }
112 EXPORT_SYMBOL(pn_sock_unhash);
113
114 static int pn_socket_bind(struct socket *sock, struct sockaddr *addr, int len)
115 {
116         struct sock *sk = sock->sk;
117         struct pn_sock *pn = pn_sk(sk);
118         struct sockaddr_pn *spn = (struct sockaddr_pn *)addr;
119         int err;
120         u16 handle;
121         u8 saddr;
122
123         if (sk->sk_prot->bind)
124                 return sk->sk_prot->bind(sk, addr, len);
125
126         if (len < sizeof(struct sockaddr_pn))
127                 return -EINVAL;
128         if (spn->spn_family != AF_PHONET)
129                 return -EAFNOSUPPORT;
130
131         handle = pn_sockaddr_get_object((struct sockaddr_pn *)addr);
132         saddr = pn_addr(handle);
133         if (saddr && phonet_address_lookup(saddr))
134                 return -EADDRNOTAVAIL;
135
136         lock_sock(sk);
137         if (sk->sk_state != TCP_CLOSE || pn_port(pn->sobject)) {
138                 err = -EINVAL; /* attempt to rebind */
139                 goto out;
140         }
141         err = sk->sk_prot->get_port(sk, pn_port(handle));
142         if (err)
143                 goto out;
144
145         /* get_port() sets the port, bind() sets the address if applicable */
146         pn->sobject = pn_object(saddr, pn_port(pn->sobject));
147         pn->resource = spn->spn_resource;
148
149         /* Enable RX on the socket */
150         sk->sk_prot->hash(sk);
151 out:
152         release_sock(sk);
153         return err;
154 }
155
156 static int pn_socket_autobind(struct socket *sock)
157 {
158         struct sockaddr_pn sa;
159         int err;
160
161         memset(&sa, 0, sizeof(sa));
162         sa.spn_family = AF_PHONET;
163         err = pn_socket_bind(sock, (struct sockaddr *)&sa,
164                                 sizeof(struct sockaddr_pn));
165         if (err != -EINVAL)
166                 return err;
167         BUG_ON(!pn_port(pn_sk(sock->sk)->sobject));
168         return 0; /* socket was already bound */
169 }
170
171 static int pn_socket_accept(struct socket *sock, struct socket *newsock,
172                                 int flags)
173 {
174         struct sock *sk = sock->sk;
175         struct sock *newsk;
176         int err;
177
178         newsk = sk->sk_prot->accept(sk, flags, &err);
179         if (!newsk)
180                 return err;
181
182         lock_sock(newsk);
183         sock_graft(newsk, newsock);
184         newsock->state = SS_CONNECTED;
185         release_sock(newsk);
186         return 0;
187 }
188
189 static int pn_socket_getname(struct socket *sock, struct sockaddr *addr,
190                                 int *sockaddr_len, int peer)
191 {
192         struct sock *sk = sock->sk;
193         struct pn_sock *pn = pn_sk(sk);
194
195         memset(addr, 0, sizeof(struct sockaddr_pn));
196         addr->sa_family = AF_PHONET;
197         if (!peer) /* Race with bind() here is userland's problem. */
198                 pn_sockaddr_set_object((struct sockaddr_pn *)addr,
199                                         pn->sobject);
200
201         *sockaddr_len = sizeof(struct sockaddr_pn);
202         return 0;
203 }
204
205 static unsigned int pn_socket_poll(struct file *file, struct socket *sock,
206                                         poll_table *wait)
207 {
208         struct sock *sk = sock->sk;
209         struct pep_sock *pn = pep_sk(sk);
210         unsigned int mask = 0;
211
212         poll_wait(file, &sock->wait, wait);
213
214         switch (sk->sk_state) {
215         case TCP_LISTEN:
216                 return hlist_empty(&pn->ackq) ? 0 : POLLIN;
217         case TCP_CLOSE:
218                 return POLLERR;
219         }
220
221         if (!skb_queue_empty(&sk->sk_receive_queue))
222                 mask |= POLLIN | POLLRDNORM;
223         if (!skb_queue_empty(&pn->ctrlreq_queue))
224                 mask |= POLLPRI;
225         if (!mask && sk->sk_state == TCP_CLOSE_WAIT)
226                 return POLLHUP;
227
228         if (sk->sk_state == TCP_ESTABLISHED && pn->tx_credits)
229                 mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
230
231         return mask;
232 }
233
234 static int pn_socket_ioctl(struct socket *sock, unsigned int cmd,
235                                 unsigned long arg)
236 {
237         struct sock *sk = sock->sk;
238         struct pn_sock *pn = pn_sk(sk);
239
240         if (cmd == SIOCPNGETOBJECT) {
241                 struct net_device *dev;
242                 u16 handle;
243                 u8 saddr;
244
245                 if (get_user(handle, (__u16 __user *)arg))
246                         return -EFAULT;
247
248                 lock_sock(sk);
249                 if (sk->sk_bound_dev_if)
250                         dev = dev_get_by_index(sock_net(sk),
251                                                 sk->sk_bound_dev_if);
252                 else
253                         dev = phonet_device_get(sock_net(sk));
254                 if (dev && (dev->flags & IFF_UP))
255                         saddr = phonet_address_get(dev, pn_addr(handle));
256                 else
257                         saddr = PN_NO_ADDR;
258                 release_sock(sk);
259
260                 if (dev)
261                         dev_put(dev);
262                 if (saddr == PN_NO_ADDR)
263                         return -EHOSTUNREACH;
264
265                 handle = pn_object(saddr, pn_port(pn->sobject));
266                 return put_user(handle, (__u16 __user *)arg);
267         }
268
269         return sk->sk_prot->ioctl(sk, cmd, arg);
270 }
271
272 static int pn_socket_listen(struct socket *sock, int backlog)
273 {
274         struct sock *sk = sock->sk;
275         int err = 0;
276
277         if (sock->state != SS_UNCONNECTED)
278                 return -EINVAL;
279         if (pn_socket_autobind(sock))
280                 return -ENOBUFS;
281
282         lock_sock(sk);
283         if (sk->sk_state != TCP_CLOSE) {
284                 err = -EINVAL;
285                 goto out;
286         }
287
288         sk->sk_state = TCP_LISTEN;
289         sk->sk_ack_backlog = 0;
290         sk->sk_max_ack_backlog = backlog;
291 out:
292         release_sock(sk);
293         return err;
294 }
295
296 static int pn_socket_sendmsg(struct kiocb *iocb, struct socket *sock,
297                                 struct msghdr *m, size_t total_len)
298 {
299         struct sock *sk = sock->sk;
300
301         if (pn_socket_autobind(sock))
302                 return -EAGAIN;
303
304         return sk->sk_prot->sendmsg(iocb, sk, m, total_len);
305 }
306
307 const struct proto_ops phonet_dgram_ops = {
308         .family         = AF_PHONET,
309         .owner          = THIS_MODULE,
310         .release        = pn_socket_release,
311         .bind           = pn_socket_bind,
312         .connect        = sock_no_connect,
313         .socketpair     = sock_no_socketpair,
314         .accept         = sock_no_accept,
315         .getname        = pn_socket_getname,
316         .poll           = datagram_poll,
317         .ioctl          = pn_socket_ioctl,
318         .listen         = sock_no_listen,
319         .shutdown       = sock_no_shutdown,
320         .setsockopt     = sock_no_setsockopt,
321         .getsockopt     = sock_no_getsockopt,
322 #ifdef CONFIG_COMPAT
323         .compat_setsockopt = sock_no_setsockopt,
324         .compat_getsockopt = sock_no_getsockopt,
325 #endif
326         .sendmsg        = pn_socket_sendmsg,
327         .recvmsg        = sock_common_recvmsg,
328         .mmap           = sock_no_mmap,
329         .sendpage       = sock_no_sendpage,
330 };
331
332 const struct proto_ops phonet_stream_ops = {
333         .family         = AF_PHONET,
334         .owner          = THIS_MODULE,
335         .release        = pn_socket_release,
336         .bind           = pn_socket_bind,
337         .connect        = sock_no_connect,
338         .socketpair     = sock_no_socketpair,
339         .accept         = pn_socket_accept,
340         .getname        = pn_socket_getname,
341         .poll           = pn_socket_poll,
342         .ioctl          = pn_socket_ioctl,
343         .listen         = pn_socket_listen,
344         .shutdown       = sock_no_shutdown,
345         .setsockopt     = sock_common_setsockopt,
346         .getsockopt     = sock_common_getsockopt,
347 #ifdef CONFIG_COMPAT
348         .compat_setsockopt = compat_sock_common_setsockopt,
349         .compat_getsockopt = compat_sock_common_getsockopt,
350 #endif
351         .sendmsg        = pn_socket_sendmsg,
352         .recvmsg        = sock_common_recvmsg,
353         .mmap           = sock_no_mmap,
354         .sendpage       = sock_no_sendpage,
355 };
356 EXPORT_SYMBOL(phonet_stream_ops);
357
358 static DEFINE_MUTEX(port_mutex);
359
360 /* allocate port for a socket */
361 int pn_sock_get_port(struct sock *sk, unsigned short sport)
362 {
363         static int port_cur;
364         struct pn_sock *pn = pn_sk(sk);
365         struct sockaddr_pn try_sa;
366         struct sock *tmpsk;
367
368         memset(&try_sa, 0, sizeof(struct sockaddr_pn));
369         try_sa.spn_family = AF_PHONET;
370
371         mutex_lock(&port_mutex);
372
373         if (!sport) {
374                 /* search free port */
375                 int port, pmin, pmax;
376
377                 phonet_get_local_port_range(&pmin, &pmax);
378                 for (port = pmin; port <= pmax; port++) {
379                         port_cur++;
380                         if (port_cur < pmin || port_cur > pmax)
381                                 port_cur = pmin;
382
383                         pn_sockaddr_set_port(&try_sa, port_cur);
384                         tmpsk = pn_find_sock_by_sa(&try_sa);
385                         if (tmpsk == NULL) {
386                                 sport = port_cur;
387                                 goto found;
388                         } else
389                                 sock_put(tmpsk);
390                 }
391         } else {
392                 /* try to find specific port */
393                 pn_sockaddr_set_port(&try_sa, sport);
394                 tmpsk = pn_find_sock_by_sa(&try_sa);
395                 if (tmpsk == NULL)
396                         /* No sock there! We can use that port... */
397                         goto found;
398                 else
399                         sock_put(tmpsk);
400         }
401         mutex_unlock(&port_mutex);
402
403         /* the port must be in use already */
404         return -EADDRINUSE;
405
406 found:
407         mutex_unlock(&port_mutex);
408         pn->sobject = pn_object(pn_addr(pn->sobject), sport);
409         return 0;
410 }
411 EXPORT_SYMBOL(pn_sock_get_port);