xref: /dragonfly/sys/netbt/l2cap_socket.c (revision c443c74f)
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