]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - net/phonet/socket.c
Phonet: common socket glue
[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 <net/sock.h>
29 #include <net/tcp_states.h>
30
31 #include <linux/phonet.h>
32 #include <net/phonet/phonet.h>
33 #include <net/phonet/pn_dev.h>
34
35 static int pn_socket_release(struct socket *sock)
36 {
37         struct sock *sk = sock->sk;
38
39         if (sk) {
40                 sock->sk = NULL;
41                 sk->sk_prot->close(sk, 0);
42         }
43         return 0;
44 }
45
46 static struct  {
47         struct hlist_head hlist;
48         spinlock_t lock;
49 } pnsocks = {
50         .hlist = HLIST_HEAD_INIT,
51         .lock = __SPIN_LOCK_UNLOCKED(pnsocks.lock),
52 };
53
54 /*
55  * Find address based on socket address, match only certain fields.
56  * Also grab sock if it was found. Remember to sock_put it later.
57  */
58 struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *spn)
59 {
60         struct hlist_node *node;
61         struct sock *sknode;
62         struct sock *rval = NULL;
63         u16 obj = pn_sockaddr_get_object(spn);
64         u8 res = spn->spn_resource;
65
66         spin_lock_bh(&pnsocks.lock);
67
68         sk_for_each(sknode, node, &pnsocks.hlist) {
69                 struct pn_sock *pn = pn_sk(sknode);
70                 BUG_ON(!pn->sobject); /* unbound socket */
71
72                 if (pn_port(obj)) {
73                         /* Look up socket by port */
74                         if (pn_port(pn->sobject) != pn_port(obj))
75                                 continue;
76                 } else {
77                         /* If port is zero, look up by resource */
78                         if (pn->resource != res)
79                                 continue;
80                 }
81                 if (pn_addr(pn->sobject)
82                  && pn_addr(pn->sobject) != pn_addr(obj))
83                         continue;
84
85                 rval = sknode;
86                 sock_hold(sknode);
87                 break;
88         }
89
90         spin_unlock_bh(&pnsocks.lock);
91
92         return rval;
93
94 }
95
96 void pn_sock_hash(struct sock *sk)
97 {
98         spin_lock_bh(&pnsocks.lock);
99         sk_add_node(sk, &pnsocks.hlist);
100         spin_unlock_bh(&pnsocks.lock);
101 }
102 EXPORT_SYMBOL(pn_sock_hash);
103
104 void pn_sock_unhash(struct sock *sk)
105 {
106         spin_lock_bh(&pnsocks.lock);
107         sk_del_node_init(sk);
108         spin_unlock_bh(&pnsocks.lock);
109 }
110 EXPORT_SYMBOL(pn_sock_unhash);
111
112 static int pn_socket_bind(struct socket *sock, struct sockaddr *addr, int len)
113 {
114         struct sock *sk = sock->sk;
115         struct pn_sock *pn = pn_sk(sk);
116         struct sockaddr_pn *spn = (struct sockaddr_pn *)addr;
117         int err;
118         u16 handle;
119         u8 saddr;
120
121         if (sk->sk_prot->bind)
122                 return sk->sk_prot->bind(sk, addr, len);
123
124         if (len < sizeof(struct sockaddr_pn))
125                 return -EINVAL;
126         if (spn->spn_family != AF_PHONET)
127                 return -EAFNOSUPPORT;
128
129         handle = pn_sockaddr_get_object((struct sockaddr_pn *)addr);
130         saddr = pn_addr(handle);
131         if (saddr && phonet_address_lookup(saddr))
132                 return -EADDRNOTAVAIL;
133
134         lock_sock(sk);
135         if (sk->sk_state != TCP_CLOSE || pn_port(pn->sobject)) {
136                 err = -EINVAL; /* attempt to rebind */
137                 goto out;
138         }
139         err = sk->sk_prot->get_port(sk, pn_port(handle));
140         if (err)
141                 goto out;
142
143         /* get_port() sets the port, bind() sets the address if applicable */
144         pn->sobject = pn_object(saddr, pn_port(pn->sobject));
145         pn->resource = spn->spn_resource;
146
147         /* Enable RX on the socket */
148         sk->sk_prot->hash(sk);
149 out:
150         release_sock(sk);
151         return err;
152 }
153
154 static int pn_socket_autobind(struct socket *sock)
155 {
156         struct sockaddr_pn sa;
157         int err;
158
159         memset(&sa, 0, sizeof(sa));
160         sa.spn_family = AF_PHONET;
161         err = pn_socket_bind(sock, (struct sockaddr *)&sa,
162                                 sizeof(struct sockaddr_pn));
163         if (err != -EINVAL)
164                 return err;
165         BUG_ON(!pn_port(pn_sk(sock->sk)->sobject));
166         return 0; /* socket was already bound */
167 }
168
169 static int pn_socket_getname(struct socket *sock, struct sockaddr *addr,
170                                 int *sockaddr_len, int peer)
171 {
172         struct sock *sk = sock->sk;
173         struct pn_sock *pn = pn_sk(sk);
174
175         memset(addr, 0, sizeof(struct sockaddr_pn));
176         addr->sa_family = AF_PHONET;
177         if (!peer) /* Race with bind() here is userland's problem. */
178                 pn_sockaddr_set_object((struct sockaddr_pn *)addr,
179                                         pn->sobject);
180
181         *sockaddr_len = sizeof(struct sockaddr_pn);
182         return 0;
183 }
184
185 static int pn_socket_ioctl(struct socket *sock, unsigned int cmd,
186                                 unsigned long arg)
187 {
188         struct sock *sk = sock->sk;
189         struct pn_sock *pn = pn_sk(sk);
190
191         if (cmd == SIOCPNGETOBJECT) {
192                 struct net_device *dev;
193                 u16 handle;
194                 u8 saddr;
195
196                 if (get_user(handle, (__u16 __user *)arg))
197                         return -EFAULT;
198
199                 lock_sock(sk);
200                 if (sk->sk_bound_dev_if)
201                         dev = dev_get_by_index(sock_net(sk),
202                                                 sk->sk_bound_dev_if);
203                 else
204                         dev = phonet_device_get(sock_net(sk));
205                 if (dev && (dev->flags & IFF_UP))
206                         saddr = phonet_address_get(dev, pn_addr(handle));
207                 else
208                         saddr = PN_NO_ADDR;
209                 release_sock(sk);
210
211                 if (dev)
212                         dev_put(dev);
213                 if (saddr == PN_NO_ADDR)
214                         return -EHOSTUNREACH;
215
216                 handle = pn_object(saddr, pn_port(pn->sobject));
217                 return put_user(handle, (__u16 __user *)arg);
218         }
219
220         return sk->sk_prot->ioctl(sk, cmd, arg);
221 }
222
223 static int pn_socket_sendmsg(struct kiocb *iocb, struct socket *sock,
224                                 struct msghdr *m, size_t total_len)
225 {
226         struct sock *sk = sock->sk;
227
228         if (pn_socket_autobind(sock))
229                 return -EAGAIN;
230
231         return sk->sk_prot->sendmsg(iocb, sk, m, total_len);
232 }
233
234 const struct proto_ops phonet_dgram_ops = {
235         .family         = AF_PHONET,
236         .owner          = THIS_MODULE,
237         .release        = pn_socket_release,
238         .bind           = pn_socket_bind,
239         .connect        = sock_no_connect,
240         .socketpair     = sock_no_socketpair,
241         .accept         = sock_no_accept,
242         .getname        = pn_socket_getname,
243         .poll           = datagram_poll,
244         .ioctl          = pn_socket_ioctl,
245         .listen         = sock_no_listen,
246         .shutdown       = sock_no_shutdown,
247         .setsockopt     = sock_no_setsockopt,
248         .getsockopt     = sock_no_getsockopt,
249 #ifdef CONFIG_COMPAT
250         .compat_setsockopt = sock_no_setsockopt,
251         .compat_getsockopt = sock_no_getsockopt,
252 #endif
253         .sendmsg        = pn_socket_sendmsg,
254         .recvmsg        = sock_common_recvmsg,
255         .mmap           = sock_no_mmap,
256         .sendpage       = sock_no_sendpage,
257 };
258
259 static DEFINE_MUTEX(port_mutex);
260
261 /* allocate port for a socket */
262 int pn_sock_get_port(struct sock *sk, unsigned short sport)
263 {
264         static int port_cur;
265         struct pn_sock *pn = pn_sk(sk);
266         struct sockaddr_pn try_sa;
267         struct sock *tmpsk;
268
269         memset(&try_sa, 0, sizeof(struct sockaddr_pn));
270         try_sa.spn_family = AF_PHONET;
271
272         mutex_lock(&port_mutex);
273
274         if (!sport) {
275                 /* search free port */
276                 int port, pmin = 0x40, pmax = 0x7f;
277
278                 for (port = pmin; port <= pmax; port++) {
279                         port_cur++;
280                         if (port_cur < pmin || port_cur > pmax)
281                                 port_cur = pmin;
282
283                         pn_sockaddr_set_port(&try_sa, port_cur);
284                         tmpsk = pn_find_sock_by_sa(&try_sa);
285                         if (tmpsk == NULL) {
286                                 sport = port_cur;
287                                 goto found;
288                         } else
289                                 sock_put(tmpsk);
290                 }
291         } else {
292                 /* try to find specific port */
293                 pn_sockaddr_set_port(&try_sa, sport);
294                 tmpsk = pn_find_sock_by_sa(&try_sa);
295                 if (tmpsk == NULL)
296                         /* No sock there! We can use that port... */
297                         goto found;
298                 else
299                         sock_put(tmpsk);
300         }
301         mutex_unlock(&port_mutex);
302
303         /* the port must be in use already */
304         return -EADDRINUSE;
305
306 found:
307         mutex_unlock(&port_mutex);
308         pn->sobject = pn_object(pn_addr(pn->sobject), sport);
309         return 0;
310 }
311 EXPORT_SYMBOL(pn_sock_get_port);