1 | /* $NetBSD: l2cap_socket.c,v 1.35 2015/05/02 17:18:03 rtr Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2005 Iain Hibbert. |
5 | * Copyright (c) 2006 Itronix Inc. |
6 | * All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. The name of Itronix Inc. may not be used to endorse |
17 | * or promote products derived from this software without specific |
18 | * prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY |
24 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
27 | * ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: l2cap_socket.c,v 1.35 2015/05/02 17:18:03 rtr Exp $" ); |
35 | |
36 | /* load symbolic names */ |
37 | #ifdef BLUETOOTH_DEBUG |
38 | #define PRUREQUESTS |
39 | #define PRCOREQUESTS |
40 | #endif |
41 | |
42 | #include <sys/param.h> |
43 | #include <sys/domain.h> |
44 | #include <sys/kernel.h> |
45 | #include <sys/mbuf.h> |
46 | #include <sys/proc.h> |
47 | #include <sys/protosw.h> |
48 | #include <sys/socket.h> |
49 | #include <sys/socketvar.h> |
50 | #include <sys/systm.h> |
51 | |
52 | #include <netbt/bluetooth.h> |
53 | #include <netbt/l2cap.h> |
54 | |
55 | /* |
56 | * L2CAP Sockets |
57 | * |
58 | * SOCK_SEQPACKET - normal L2CAP connection |
59 | * |
60 | * SOCK_DGRAM - connectionless L2CAP - XXX not yet |
61 | */ |
62 | |
63 | static void l2cap_connecting(void *); |
64 | static void l2cap_connected(void *); |
65 | static void l2cap_disconnected(void *, int); |
66 | static void *l2cap_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); |
67 | static void l2cap_complete(void *, int); |
68 | static void l2cap_linkmode(void *, int); |
69 | static void l2cap_input(void *, struct mbuf *); |
70 | |
71 | static const struct btproto l2cap_proto = { |
72 | l2cap_connecting, |
73 | l2cap_connected, |
74 | l2cap_disconnected, |
75 | l2cap_newconn, |
76 | l2cap_complete, |
77 | l2cap_linkmode, |
78 | l2cap_input, |
79 | }; |
80 | |
81 | /* sysctl variables */ |
82 | int l2cap_sendspace = 4096; |
83 | int l2cap_recvspace = 4096; |
84 | |
85 | static int |
86 | l2cap_attach(struct socket *so, int proto) |
87 | { |
88 | int error; |
89 | |
90 | KASSERT(so->so_pcb == NULL); |
91 | |
92 | if (so->so_lock == NULL) { |
93 | mutex_obj_hold(bt_lock); |
94 | so->so_lock = bt_lock; |
95 | solock(so); |
96 | } |
97 | KASSERT(solocked(so)); |
98 | |
99 | /* |
100 | * For L2CAP socket PCB we just use an l2cap_channel structure |
101 | * since we have nothing to add.. |
102 | */ |
103 | error = soreserve(so, l2cap_sendspace, l2cap_recvspace); |
104 | if (error) |
105 | return error; |
106 | |
107 | return l2cap_attach_pcb((struct l2cap_channel **)&so->so_pcb, |
108 | &l2cap_proto, so); |
109 | } |
110 | |
111 | static void |
112 | l2cap_detach(struct socket *so) |
113 | { |
114 | KASSERT(so->so_pcb != NULL); |
115 | l2cap_detach_pcb((struct l2cap_channel **)&so->so_pcb); |
116 | KASSERT(so->so_pcb == NULL); |
117 | } |
118 | |
119 | static int |
120 | l2cap_accept(struct socket *so, struct sockaddr *nam) |
121 | { |
122 | struct l2cap_channel *pcb = so->so_pcb; |
123 | |
124 | KASSERT(solocked(so)); |
125 | KASSERT(nam != NULL); |
126 | |
127 | if (pcb == NULL) |
128 | return EINVAL; |
129 | |
130 | return l2cap_peeraddr_pcb(pcb, (struct sockaddr_bt *)nam); |
131 | } |
132 | |
133 | static int |
134 | l2cap_bind(struct socket *so, struct sockaddr *nam, struct lwp *l) |
135 | { |
136 | struct l2cap_channel *pcb = so->so_pcb; |
137 | struct sockaddr_bt *sa = (struct sockaddr_bt *)nam; |
138 | |
139 | KASSERT(solocked(so)); |
140 | KASSERT(nam != NULL); |
141 | |
142 | if (pcb == NULL) |
143 | return EINVAL; |
144 | |
145 | if (sa->bt_len != sizeof(struct sockaddr_bt)) |
146 | return EINVAL; |
147 | |
148 | if (sa->bt_family != AF_BLUETOOTH) |
149 | return EAFNOSUPPORT; |
150 | |
151 | return l2cap_bind_pcb(pcb, sa); |
152 | } |
153 | |
154 | static int |
155 | l2cap_listen(struct socket *so, struct lwp *l) |
156 | { |
157 | struct l2cap_channel *pcb = so->so_pcb; |
158 | |
159 | KASSERT(solocked(so)); |
160 | |
161 | if (pcb == NULL) |
162 | return EINVAL; |
163 | |
164 | return l2cap_listen_pcb(pcb); |
165 | } |
166 | |
167 | static int |
168 | l2cap_connect(struct socket *so, struct sockaddr *nam, struct lwp *l) |
169 | { |
170 | struct l2cap_channel *pcb = so->so_pcb; |
171 | struct sockaddr_bt *sa = (struct sockaddr_bt *)nam; |
172 | |
173 | KASSERT(solocked(so)); |
174 | KASSERT(nam != NULL); |
175 | |
176 | if (pcb == NULL) |
177 | return EINVAL; |
178 | |
179 | if (sa->bt_len != sizeof(struct sockaddr_bt)) |
180 | return EINVAL; |
181 | |
182 | if (sa->bt_family != AF_BLUETOOTH) |
183 | return EAFNOSUPPORT; |
184 | |
185 | soisconnecting(so); |
186 | return l2cap_connect_pcb(pcb, sa); |
187 | } |
188 | |
189 | static int |
190 | l2cap_connect2(struct socket *so, struct socket *so2) |
191 | { |
192 | KASSERT(solocked(so)); |
193 | |
194 | if (so->so_pcb == NULL) |
195 | return EINVAL; |
196 | |
197 | return EOPNOTSUPP; |
198 | } |
199 | |
200 | static int |
201 | l2cap_disconnect(struct socket *so) |
202 | { |
203 | struct l2cap_channel *pcb = so->so_pcb; |
204 | |
205 | KASSERT(solocked(so)); |
206 | |
207 | if (pcb == NULL) |
208 | return EINVAL; |
209 | |
210 | soisdisconnecting(so); |
211 | return l2cap_disconnect_pcb(pcb, so->so_linger); |
212 | } |
213 | |
214 | static int |
215 | l2cap_shutdown(struct socket *so) |
216 | { |
217 | KASSERT(solocked(so)); |
218 | |
219 | socantsendmore(so); |
220 | return 0; |
221 | } |
222 | |
223 | static int |
224 | l2cap_abort(struct socket *so) |
225 | { |
226 | struct l2cap_channel *pcb = so->so_pcb; |
227 | |
228 | KASSERT(solocked(so)); |
229 | |
230 | if (pcb == NULL) |
231 | return EINVAL; |
232 | |
233 | l2cap_disconnect_pcb(pcb, 0); |
234 | soisdisconnected(so); |
235 | l2cap_detach(so); |
236 | return 0; |
237 | } |
238 | |
239 | static int |
240 | l2cap_ioctl(struct socket *so, u_long cmd, void *nam, struct ifnet *ifp) |
241 | { |
242 | return EPASSTHROUGH; |
243 | } |
244 | |
245 | static int |
246 | l2cap_stat(struct socket *so, struct stat *ub) |
247 | { |
248 | KASSERT(solocked(so)); |
249 | |
250 | return 0; |
251 | } |
252 | |
253 | static int |
254 | l2cap_peeraddr(struct socket *so, struct sockaddr *nam) |
255 | { |
256 | struct l2cap_channel *pcb = so->so_pcb; |
257 | |
258 | KASSERT(solocked(so)); |
259 | KASSERT(pcb != NULL); |
260 | KASSERT(nam != NULL); |
261 | |
262 | return l2cap_peeraddr_pcb(pcb, (struct sockaddr_bt *)nam); |
263 | } |
264 | |
265 | static int |
266 | l2cap_sockaddr(struct socket *so, struct sockaddr *nam) |
267 | { |
268 | struct l2cap_channel *pcb = so->so_pcb; |
269 | |
270 | KASSERT(solocked(so)); |
271 | KASSERT(pcb != NULL); |
272 | KASSERT(nam != NULL); |
273 | |
274 | return l2cap_sockaddr_pcb(pcb, (struct sockaddr_bt *)nam); |
275 | } |
276 | |
277 | static int |
278 | l2cap_rcvd(struct socket *so, int flags, struct lwp *l) |
279 | { |
280 | KASSERT(solocked(so)); |
281 | |
282 | return EOPNOTSUPP; |
283 | } |
284 | |
285 | static int |
286 | l2cap_recvoob(struct socket *so, struct mbuf *m, int flags) |
287 | { |
288 | KASSERT(solocked(so)); |
289 | |
290 | return EOPNOTSUPP; |
291 | } |
292 | |
293 | static int |
294 | l2cap_send(struct socket *so, struct mbuf *m, struct sockaddr *nam, |
295 | struct mbuf *control, struct lwp *l) |
296 | { |
297 | struct l2cap_channel *pcb = so->so_pcb; |
298 | struct mbuf *m0; |
299 | int error = 0; |
300 | |
301 | KASSERT(solocked(so)); |
302 | KASSERT(m != NULL); |
303 | |
304 | if (control) |
305 | m_freem(control); |
306 | |
307 | if (pcb == NULL) { |
308 | error = EINVAL; |
309 | goto release; |
310 | } |
311 | |
312 | if (m->m_pkthdr.len == 0) |
313 | goto release; |
314 | |
315 | if (m->m_pkthdr.len > pcb->lc_omtu) { |
316 | error = EMSGSIZE; |
317 | goto release; |
318 | } |
319 | |
320 | m0 = m_copypacket(m, M_DONTWAIT); |
321 | if (m0 == NULL) { |
322 | error = ENOMEM; |
323 | goto release; |
324 | } |
325 | |
326 | sbappendrecord(&so->so_snd, m); |
327 | return l2cap_send_pcb(pcb, m0); |
328 | |
329 | release: |
330 | if (m) |
331 | m_freem(m); |
332 | |
333 | return error; |
334 | } |
335 | |
336 | static int |
337 | l2cap_sendoob(struct socket *so, struct mbuf *m, struct mbuf *control) |
338 | { |
339 | KASSERT(solocked(so)); |
340 | |
341 | if (m) |
342 | m_freem(m); |
343 | if (control) |
344 | m_freem(control); |
345 | |
346 | return EOPNOTSUPP; |
347 | } |
348 | |
349 | static int |
350 | l2cap_purgeif(struct socket *so, struct ifnet *ifp) |
351 | { |
352 | |
353 | return EOPNOTSUPP; |
354 | } |
355 | |
356 | /* |
357 | * l2cap_ctloutput(req, socket, sockopt) |
358 | * |
359 | * Apply configuration commands to channel. This corresponds to |
360 | * "Reconfigure Channel Request" in the L2CAP specification. |
361 | */ |
362 | int |
363 | l2cap_ctloutput(int req, struct socket *so, struct sockopt *sopt) |
364 | { |
365 | struct l2cap_channel *pcb = so->so_pcb; |
366 | int err = 0; |
367 | |
368 | DPRINTFN(2, "%s\n" , prcorequests[req]); |
369 | |
370 | if (pcb == NULL) |
371 | return EINVAL; |
372 | |
373 | if (sopt->sopt_level != BTPROTO_L2CAP) |
374 | return ENOPROTOOPT; |
375 | |
376 | switch(req) { |
377 | case PRCO_GETOPT: |
378 | err = l2cap_getopt(pcb, sopt); |
379 | break; |
380 | |
381 | case PRCO_SETOPT: |
382 | err = l2cap_setopt(pcb, sopt); |
383 | break; |
384 | |
385 | default: |
386 | err = ENOPROTOOPT; |
387 | break; |
388 | } |
389 | |
390 | return err; |
391 | } |
392 | |
393 | /********************************************************************** |
394 | * |
395 | * L2CAP Protocol socket callbacks |
396 | * |
397 | */ |
398 | |
399 | static void |
400 | l2cap_connecting(void *arg) |
401 | { |
402 | struct socket *so = arg; |
403 | |
404 | DPRINTF("Connecting\n" ); |
405 | soisconnecting(so); |
406 | } |
407 | |
408 | static void |
409 | l2cap_connected(void *arg) |
410 | { |
411 | struct socket *so = arg; |
412 | |
413 | DPRINTF("Connected\n" ); |
414 | soisconnected(so); |
415 | } |
416 | |
417 | static void |
418 | l2cap_disconnected(void *arg, int err) |
419 | { |
420 | struct socket *so = arg; |
421 | |
422 | DPRINTF("Disconnected (%d)\n" , err); |
423 | |
424 | so->so_error = err; |
425 | soisdisconnected(so); |
426 | } |
427 | |
428 | static void * |
429 | l2cap_newconn(void *arg, struct sockaddr_bt *laddr, |
430 | struct sockaddr_bt *raddr) |
431 | { |
432 | struct socket *so = arg; |
433 | |
434 | DPRINTF("New Connection\n" ); |
435 | so = sonewconn(so, false); |
436 | if (so == NULL) |
437 | return NULL; |
438 | |
439 | soisconnecting(so); |
440 | |
441 | return so->so_pcb; |
442 | } |
443 | |
444 | static void |
445 | l2cap_complete(void *arg, int count) |
446 | { |
447 | struct socket *so = arg; |
448 | |
449 | while (count-- > 0) |
450 | sbdroprecord(&so->so_snd); |
451 | |
452 | sowwakeup(so); |
453 | } |
454 | |
455 | static void |
456 | l2cap_linkmode(void *arg, int new) |
457 | { |
458 | struct socket *so = arg; |
459 | struct sockopt sopt; |
460 | int mode; |
461 | |
462 | DPRINTF("auth %s, encrypt %s, secure %s\n" , |
463 | (new & L2CAP_LM_AUTH ? "on" : "off" ), |
464 | (new & L2CAP_LM_ENCRYPT ? "on" : "off" ), |
465 | (new & L2CAP_LM_SECURE ? "on" : "off" )); |
466 | |
467 | sockopt_init(&sopt, BTPROTO_L2CAP, SO_L2CAP_LM, 0); |
468 | (void)l2cap_getopt(so->so_pcb, &sopt); |
469 | (void)sockopt_getint(&sopt, &mode); |
470 | sockopt_destroy(&sopt); |
471 | |
472 | if (((mode & L2CAP_LM_AUTH) && !(new & L2CAP_LM_AUTH)) |
473 | || ((mode & L2CAP_LM_ENCRYPT) && !(new & L2CAP_LM_ENCRYPT)) |
474 | || ((mode & L2CAP_LM_SECURE) && !(new & L2CAP_LM_SECURE))) |
475 | l2cap_disconnect_pcb(so->so_pcb, 0); |
476 | } |
477 | |
478 | static void |
479 | l2cap_input(void *arg, struct mbuf *m) |
480 | { |
481 | struct socket *so = arg; |
482 | |
483 | if (m->m_pkthdr.len > sbspace(&so->so_rcv)) { |
484 | printf("%s: packet (%d bytes) dropped (socket buffer full)\n" , |
485 | __func__, m->m_pkthdr.len); |
486 | m_freem(m); |
487 | return; |
488 | } |
489 | |
490 | DPRINTFN(10, "received %d bytes\n" , m->m_pkthdr.len); |
491 | |
492 | sbappendrecord(&so->so_rcv, m); |
493 | sorwakeup(so); |
494 | } |
495 | |
496 | PR_WRAP_USRREQS(l2cap) |
497 | |
498 | #define l2cap_attach l2cap_attach_wrapper |
499 | #define l2cap_detach l2cap_detach_wrapper |
500 | #define l2cap_accept l2cap_accept_wrapper |
501 | #define l2cap_bind l2cap_bind_wrapper |
502 | #define l2cap_listen l2cap_listen_wrapper |
503 | #define l2cap_connect l2cap_connect_wrapper |
504 | #define l2cap_connect2 l2cap_connect2_wrapper |
505 | #define l2cap_disconnect l2cap_disconnect_wrapper |
506 | #define l2cap_shutdown l2cap_shutdown_wrapper |
507 | #define l2cap_abort l2cap_abort_wrapper |
508 | #define l2cap_ioctl l2cap_ioctl_wrapper |
509 | #define l2cap_stat l2cap_stat_wrapper |
510 | #define l2cap_peeraddr l2cap_peeraddr_wrapper |
511 | #define l2cap_sockaddr l2cap_sockaddr_wrapper |
512 | #define l2cap_rcvd l2cap_rcvd_wrapper |
513 | #define l2cap_recvoob l2cap_recvoob_wrapper |
514 | #define l2cap_send l2cap_send_wrapper |
515 | #define l2cap_sendoob l2cap_sendoob_wrapper |
516 | #define l2cap_purgeif l2cap_purgeif_wrapper |
517 | |
518 | const struct pr_usrreqs l2cap_usrreqs = { |
519 | .pr_attach = l2cap_attach, |
520 | .pr_detach = l2cap_detach, |
521 | .pr_accept = l2cap_accept, |
522 | .pr_bind = l2cap_bind, |
523 | .pr_listen = l2cap_listen, |
524 | .pr_connect = l2cap_connect, |
525 | .pr_connect2 = l2cap_connect2, |
526 | .pr_disconnect = l2cap_disconnect, |
527 | .pr_shutdown = l2cap_shutdown, |
528 | .pr_abort = l2cap_abort, |
529 | .pr_ioctl = l2cap_ioctl, |
530 | .pr_stat = l2cap_stat, |
531 | .pr_peeraddr = l2cap_peeraddr, |
532 | .pr_sockaddr = l2cap_sockaddr, |
533 | .pr_rcvd = l2cap_rcvd, |
534 | .pr_recvoob = l2cap_recvoob, |
535 | .pr_send = l2cap_send, |
536 | .pr_sendoob = l2cap_sendoob, |
537 | .pr_purgeif = l2cap_purgeif, |
538 | }; |
539 | |