xref: /dragonfly/sys/netbt/l2cap_socket.c (revision c443c74f42cc6a7f2385d56cc2b07c21939330d1)
1 /* $OpenBSD: l2cap_socket.c,v 1.1 2007/06/01 02:46:11 uwe Exp $ */
2 /* $NetBSD: l2cap_socket.c,v 1.7 2007/04/21 06:15:23 plunky Exp $ */
3 
4 /*-
5  * Copyright (c) 2005 Iain Hibbert.
6  * Copyright (c) 2006 Itronix Inc.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of Itronix Inc. may not be used to endorse
18  *    or promote products derived from this software without specific
19  *    prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28  * ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /* load symbolic names */
35 #ifdef BLUETOOTH_DEBUG
36 #define PRUREQUESTS
37 #define PRCOREQUESTS
38 #endif
39 
40 #include <sys/param.h>
41 #include <sys/domain.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>       /* for M_NOWAIT */
44 #include <sys/mbuf.h>
45 #include <sys/proc.h>
46 #include <sys/protosw.h>
47 #include <sys/socket.h>
48 #include <sys/socketvar.h>
49 #include <sys/systm.h>
50 
51 #include <sys/msgport2.h>
52 
53 #include <vm/vm_zone.h>
54 
55 #include <netbt/bluetooth.h>
56 #include <netbt/hci.h>                  /* XXX for EPASSTHROUGH */
57 #include <netbt/l2cap.h>
58 
59 /*
60  * L2CAP Sockets
61  *
62  *        SOCK_SEQPACKET - normal L2CAP connection
63  *
64  *        SOCK_DGRAM - connectionless L2CAP - XXX not yet
65  */
66 
67 static void l2cap_connecting(void *);
68 static void l2cap_connected(void *);
69 static void l2cap_disconnected(void *, int);
70 static void *l2cap_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
71 static void l2cap_complete(void *, int);
72 static void l2cap_linkmode(void *, int);
73 static void l2cap_input(void *, struct mbuf *);
74 
75 static const struct btproto l2cap_proto = {
76           l2cap_connecting,
77           l2cap_connected,
78           l2cap_disconnected,
79           l2cap_newconn,
80           l2cap_complete,
81           l2cap_linkmode,
82           l2cap_input,
83 };
84 
85 /* sysctl variables */
86 int l2cap_sendspace = 4096;
87 int l2cap_recvspace = 4096;
88 
89 /*
90  * l2cap_ctloutput(request, socket, level, optname, opt)
91  *
92  *        Apply configuration commands to channel. This corresponds to
93  *        "Reconfigure Channel Request" in the L2CAP specification.
94  */
95 void
l2cap_ctloutput(netmsg_t msg)96 l2cap_ctloutput(netmsg_t msg)
97 {
98           struct socket *so = msg->ctloutput.base.nm_so;
99           struct sockopt *sopt = msg->ctloutput.nm_sopt;
100           struct l2cap_channel *pcb = (struct l2cap_channel *) so->so_pcb;
101           struct mbuf *m;
102           int error = 0;
103 
104 #ifdef notyet                           /* XXX */
105           DPRINTFN(2, "%s\n", prcorequests[req]);
106 #endif
107 
108           if (pcb == NULL) {
109                     error = EINVAL;
110                     goto out;
111           }
112 
113           if (sopt->sopt_level != BTPROTO_L2CAP) {
114                     error = ENOPROTOOPT;
115                     goto out;
116           }
117 
118           switch(sopt->sopt_dir) {
119           case PRCO_GETOPT:
120                     m = m_get(M_NOWAIT, MT_DATA);
121                     if (m == NULL) {
122                         error = ENOMEM;
123                         break;
124                     }
125                     m->m_len = l2cap_getopt(pcb, sopt->sopt_name, mtod(m, void *));
126                     if (m->m_len == 0) {
127                               m_freem(m);
128                               m = NULL;
129                               error = ENOPROTOOPT;
130                     }
131                     soopt_from_kbuf(sopt, mtod(m, void *), m->m_len);
132                     break;
133 
134           case PRCO_SETOPT:
135                     error = l2cap_setopt2(pcb, sopt->sopt_name, so, sopt);
136                     break;
137 
138           default:
139                     error = ENOPROTOOPT;
140                     break;
141           }
142 out:
143           lwkt_replymsg(&msg->ctloutput.base.lmsg, error);
144 }
145 
146 /**********************************************************************
147  *
148  *        L2CAP Protocol socket callbacks
149  *
150  */
151 
152 static void
l2cap_connecting(void * arg)153 l2cap_connecting(void *arg)
154 {
155           struct socket *so = arg;
156 
157           DPRINTF("Connecting\n");
158           soisconnecting(so);
159 }
160 
161 static void
l2cap_connected(void * arg)162 l2cap_connected(void *arg)
163 {
164           struct socket *so = arg;
165 
166           DPRINTF("Connected\n");
167           soisconnected(so);
168 }
169 
170 static void
l2cap_disconnected(void * arg,int err)171 l2cap_disconnected(void *arg, int err)
172 {
173           struct socket *so = arg;
174 
175           DPRINTF("Disconnected (%d)\n", err);
176 
177           so->so_error = err;
178           soisdisconnected(so);
179 }
180 
181 static void *
l2cap_newconn(void * arg,struct sockaddr_bt * laddr,struct sockaddr_bt * raddr)182 l2cap_newconn(void *arg, struct sockaddr_bt *laddr,
183     struct sockaddr_bt *raddr)
184 {
185           struct socket *so = arg;
186 
187           DPRINTF("New Connection\n");
188           so = sonewconn(so, 0);
189           if (so == NULL)
190                     return NULL;
191 
192           soisconnecting(so);
193 
194           return so->so_pcb;
195 }
196 
197 static void
l2cap_complete(void * arg,int count)198 l2cap_complete(void *arg, int count)
199 {
200           struct socket *so = arg;
201 
202           while (count-- > 0)
203                     sbdroprecord(&so->so_snd.sb);
204 
205           sowwakeup(so);
206 }
207 
208 static void
l2cap_linkmode(void * arg,int new)209 l2cap_linkmode(void *arg, int new)
210 {
211           struct socket *so = arg;
212           int mode;
213 
214           DPRINTF("auth %s, encrypt %s, secure %s\n",
215                     (new & L2CAP_LM_AUTH ? "on" : "off"),
216                     (new & L2CAP_LM_ENCRYPT ? "on" : "off"),
217                     (new & L2CAP_LM_SECURE ? "on" : "off"));
218 
219           (void)l2cap_getopt(so->so_pcb, SO_L2CAP_LM, &mode);
220           if (((mode & L2CAP_LM_AUTH) && !(new & L2CAP_LM_AUTH))
221               || ((mode & L2CAP_LM_ENCRYPT) && !(new & L2CAP_LM_ENCRYPT))
222               || ((mode & L2CAP_LM_SECURE) && !(new & L2CAP_LM_SECURE)))
223                     l2cap_disconnect(so->so_pcb, 0);
224 }
225 
226 static void
l2cap_input(void * arg,struct mbuf * m)227 l2cap_input(void *arg, struct mbuf *m)
228 {
229           struct socket *so = arg;
230 
231           if (m->m_pkthdr.len > sbspace(&so->so_rcv)) {
232                     kprintf("%s: packet (%d bytes) dropped (socket buffer full)\n",
233                               __func__, m->m_pkthdr.len);
234                     m_freem(m);
235                     return;
236           }
237 
238           DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len);
239 
240           sbappendrecord(&so->so_rcv.sb, m);
241           sorwakeup(so);
242 }
243 
244 
245 /*
246  * Implementation of usrreqs.
247  */
248 static void
l2cap_sdetach(netmsg_t msg)249 l2cap_sdetach(netmsg_t msg)
250 {
251           struct socket *so = msg->detach.base.nm_so;
252           int error;
253 
254           error = l2cap_detach((struct l2cap_channel **)&so->so_pcb);
255           lwkt_replymsg(&msg->detach.base.lmsg, error);
256 }
257 
258 /*
259  * NOTE: (so) is referenced from soabort*() and netmsg_pru_abort()
260  *         will sofree() it when we return.
261  */
262 static void
l2cap_sabort(netmsg_t msg)263 l2cap_sabort(netmsg_t msg)
264 {
265           struct socket *so = msg->abort.base.nm_so;
266           struct l2cap_channel *pcb = so->so_pcb;
267 
268           l2cap_disconnect(pcb, 0);
269           soisdisconnected(so);
270 
271           l2cap_sdetach(msg);
272           /* msg invalid now */
273 }
274 
275 static void
l2cap_sdisconnect(netmsg_t msg)276 l2cap_sdisconnect(netmsg_t msg)
277 {
278           struct socket *so = msg->disconnect.base.nm_so;
279           struct l2cap_channel *pcb = so->so_pcb;
280           int error;
281 
282           soisdisconnecting(so);
283 
284           error = l2cap_disconnect(pcb, so->so_linger);
285           lwkt_replymsg(&msg->disconnect.base.lmsg, error);
286 }
287 
288 static void
l2cap_scontrol(netmsg_t msg)289 l2cap_scontrol(netmsg_t msg)
290 {
291           lwkt_replymsg(&msg->control.base.lmsg, EPASSTHROUGH);
292 }
293 
294 static void
l2cap_sattach(netmsg_t msg)295 l2cap_sattach(netmsg_t msg)
296 {
297           struct socket *so = msg->attach.base.nm_so;
298           struct l2cap_channel *pcb = so->so_pcb;
299           int error;
300 
301           if (pcb != NULL) {
302                     error = EINVAL;
303                     goto out;
304           }
305 
306           /*
307            * For L2CAP socket PCB we just use an l2cap_channel structure
308            * since we have nothing to add..
309            */
310           error = soreserve(so, l2cap_sendspace, l2cap_recvspace, NULL);
311           if (error == 0) {
312                     error = l2cap_attach((struct l2cap_channel **)&so->so_pcb,
313                                              &l2cap_proto, so);
314           }
315 out:
316           lwkt_replymsg(&msg->attach.base.lmsg, error);
317 }
318 
319 static void
l2cap_sbind(netmsg_t msg)320 l2cap_sbind (netmsg_t msg)
321 {
322           struct socket *so = msg->bind.base.nm_so;
323           struct sockaddr *nam = msg->bind.nm_nam;
324           struct l2cap_channel *pcb = so->so_pcb;
325           struct sockaddr_bt *sa;
326           int error;
327 
328           KKASSERT(nam != NULL);
329           sa = (struct sockaddr_bt *)nam;
330 
331           if (sa->bt_len != sizeof(struct sockaddr_bt)) {
332                     error = EINVAL;
333           } else if (sa->bt_family != AF_BLUETOOTH) {
334                     error = EAFNOSUPPORT;
335           } else {
336                     error = l2cap_bind(pcb, sa);
337           }
338           lwkt_replymsg(&msg->bind.base.lmsg, error);
339 }
340 
341 static void
l2cap_sconnect(netmsg_t msg)342 l2cap_sconnect(netmsg_t msg)
343 {
344           struct socket *so = msg->connect.base.nm_so;
345           struct sockaddr *nam = msg->connect.nm_nam;
346           struct l2cap_channel *pcb = so->so_pcb;
347           struct sockaddr_bt *sa;
348           int error;
349 
350           KKASSERT(nam != NULL);
351           sa = (struct sockaddr_bt *)nam;
352 
353           if (sa->bt_len != sizeof(struct sockaddr_bt)) {
354                     error = EINVAL;
355           } else if (sa->bt_family != AF_BLUETOOTH) {
356                     error = EAFNOSUPPORT;
357           } else {
358                     soisconnecting(so);
359                     error = l2cap_connect(pcb, sa);
360           }
361           lwkt_replymsg(&msg->connect.base.lmsg, error);
362 }
363 
364 static void
l2cap_speeraddr(netmsg_t msg)365 l2cap_speeraddr(netmsg_t msg)
366 {
367           struct socket *so = msg->peeraddr.base.nm_so;
368           struct sockaddr **nam = msg->peeraddr.nm_nam;
369           struct l2cap_channel *pcb = so->so_pcb;
370           struct sockaddr_bt *sa, ssa;
371           int error;
372 
373           sa = &ssa;
374           bzero(sa, sizeof *sa);
375           sa->bt_len = sizeof(struct sockaddr_bt);
376           sa->bt_family = AF_BLUETOOTH;
377           error = l2cap_peeraddr(pcb, sa);
378           *nam = dup_sockaddr((struct sockaddr *)sa);
379 
380           lwkt_replymsg(&msg->peeraddr.base.lmsg, error);
381 }
382 
383 static void
l2cap_ssockaddr(netmsg_t msg)384 l2cap_ssockaddr(netmsg_t msg)
385 {
386           struct socket *so = msg->sockaddr.base.nm_so;
387           struct sockaddr **nam = msg->sockaddr.nm_nam;
388           struct l2cap_channel *pcb = so->so_pcb;
389           struct sockaddr_bt *sa, ssa;
390           int error;
391 
392           sa = &ssa;
393           bzero(sa, sizeof *sa);
394           sa->bt_len = sizeof(struct sockaddr_bt);
395           sa->bt_family = AF_BLUETOOTH;
396           error = l2cap_sockaddr(pcb, sa);
397           *nam = dup_sockaddr((struct sockaddr *)sa);
398 
399           lwkt_replymsg(&msg->sockaddr.base.lmsg, error);
400 }
401 
402 static void
l2cap_sshutdown(netmsg_t msg)403 l2cap_sshutdown(netmsg_t msg)
404 {
405           struct socket *so = msg->shutdown.base.nm_so;
406 
407           socantsendmore(so);
408 
409           lwkt_replymsg(&msg->shutdown.base.lmsg, 0);
410 }
411 
412 static void
l2cap_ssend(netmsg_t msg)413 l2cap_ssend(netmsg_t msg)
414 {
415           struct socket *so = msg->send.base.nm_so;
416           struct mbuf *m = msg->send.nm_m;
417           struct mbuf *control = msg->send.nm_control;
418           struct l2cap_channel *pcb = so->so_pcb;
419           struct mbuf *m0;
420           int error;
421 
422           KKASSERT(m != NULL);
423           if (m->m_pkthdr.len == 0) {
424                     error = 0;
425                     goto out;
426           }
427 
428           if (m->m_pkthdr.len > pcb->lc_omtu) {
429                     error = EMSGSIZE;
430                     goto out;
431           }
432 
433           m0 = m_copym(m, 0, M_COPYALL, M_NOWAIT);
434           if (m0 == NULL) {
435                     error = ENOMEM;
436                     goto out;
437           }
438 
439           m0 = m_copym(m, 0, M_COPYALL, M_NOWAIT);
440           if (m0 == NULL) {
441                     error = ENOMEM;
442                     goto out;
443           }
444 
445           /* no use for that */
446           if (control) {
447                     m_freem(control);
448                     control = NULL;
449           }
450           sbappendrecord(&so->so_snd.sb, m);
451           error = l2cap_send(pcb, m0);
452           m = NULL;
453 out:
454           if (m)
455                     m_freem(m);
456           if (control)
457                     m_freem(control);
458 
459           lwkt_replymsg(&msg->send.base.lmsg, error);
460 }
461 
462 static void
l2cap_saccept(netmsg_t msg)463 l2cap_saccept(netmsg_t msg)
464 {
465           struct socket *so = msg->accept.base.nm_so;
466           struct sockaddr **nam = msg->accept.nm_nam;
467           struct l2cap_channel *pcb = so->so_pcb;
468           struct sockaddr_bt sa;
469           int error;
470 
471           KKASSERT(nam != NULL);
472 
473           bzero(&sa, sizeof (sa) );
474           sa.bt_len = sizeof(struct sockaddr_bt);
475           sa.bt_family = AF_BLUETOOTH;
476 
477           error = l2cap_peeraddr(pcb, &sa);
478           *nam = dup_sockaddr((struct sockaddr *)&sa);
479 
480           lwkt_replymsg(&msg->accept.base.lmsg, error);
481 }
482 
483 static void
l2cap_slisten(netmsg_t msg)484 l2cap_slisten(netmsg_t msg)
485 {
486           struct socket *so = msg->listen.base.nm_so;
487           struct l2cap_channel *pcb = so->so_pcb;
488           int error;
489 
490           error = l2cap_listen(pcb);
491           lwkt_replymsg(&msg->accept.base.lmsg, error);
492 }
493 
494 
495 struct pr_usrreqs l2cap_usrreqs = {
496         .pru_abort = l2cap_sabort,
497         .pru_accept = l2cap_saccept,
498         .pru_attach = l2cap_sattach,
499         .pru_bind = l2cap_sbind,
500         .pru_connect = l2cap_sconnect,
501         .pru_connect2 = pr_generic_notsupp,
502         .pru_control = l2cap_scontrol,
503         .pru_detach = l2cap_sdetach,
504         .pru_disconnect = l2cap_sdisconnect,
505         .pru_listen = l2cap_slisten,
506         .pru_peeraddr = l2cap_speeraddr,
507         .pru_rcvd = pr_generic_notsupp,
508         .pru_rcvoob = pr_generic_notsupp,
509         .pru_send = l2cap_ssend,
510         .pru_sense = pru_sense_null,
511         .pru_shutdown = l2cap_sshutdown,
512         .pru_sockaddr = l2cap_ssockaddr,
513         .pru_sosend = sosend,
514         .pru_soreceive = soreceive
515 };
516