1905033dcSMaksim Yevmenkin /* $NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $ */ 27718ced0SMaksim Yevmenkin 37718ced0SMaksim Yevmenkin /*- 4*b61a5730SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 51de7b4b8SPedro F. Giffuni * 67718ced0SMaksim Yevmenkin * Copyright (c) 2008 Iain Hibbert 77718ced0SMaksim Yevmenkin * All rights reserved. 87718ced0SMaksim Yevmenkin * 97718ced0SMaksim Yevmenkin * Redistribution and use in source and binary forms, with or without 107718ced0SMaksim Yevmenkin * modification, are permitted provided that the following conditions 117718ced0SMaksim Yevmenkin * are met: 127718ced0SMaksim Yevmenkin * 1. Redistributions of source code must retain the above copyright 137718ced0SMaksim Yevmenkin * notice, this list of conditions and the following disclaimer. 147718ced0SMaksim Yevmenkin * 2. Redistributions in binary form must reproduce the above copyright 157718ced0SMaksim Yevmenkin * notice, this list of conditions and the following disclaimer in the 167718ced0SMaksim Yevmenkin * documentation and/or other materials provided with the distribution. 177718ced0SMaksim Yevmenkin * 187718ced0SMaksim Yevmenkin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 197718ced0SMaksim Yevmenkin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 207718ced0SMaksim Yevmenkin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 217718ced0SMaksim Yevmenkin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 227718ced0SMaksim Yevmenkin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 237718ced0SMaksim Yevmenkin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 247718ced0SMaksim Yevmenkin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 257718ced0SMaksim Yevmenkin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 267718ced0SMaksim Yevmenkin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 277718ced0SMaksim Yevmenkin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 287718ced0SMaksim Yevmenkin */ 297718ced0SMaksim Yevmenkin 307718ced0SMaksim Yevmenkin /* $FreeBSD$ */ 317718ced0SMaksim Yevmenkin 327718ced0SMaksim Yevmenkin #include <sys/cdefs.h> 33905033dcSMaksim Yevmenkin __RCSID("$NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $"); 347718ced0SMaksim Yevmenkin 357718ced0SMaksim Yevmenkin #include <sys/ioctl.h> 367718ced0SMaksim Yevmenkin 378d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED 387718ced0SMaksim Yevmenkin #include <bluetooth.h> 39905033dcSMaksim Yevmenkin #include <inttypes.h> 407718ced0SMaksim Yevmenkin #include <errno.h> 417718ced0SMaksim Yevmenkin #include <sdp.h> 427718ced0SMaksim Yevmenkin #include <unistd.h> 437718ced0SMaksim Yevmenkin 447718ced0SMaksim Yevmenkin #include "btpand.h" 457718ced0SMaksim Yevmenkin #include "bnep.h" 467718ced0SMaksim Yevmenkin 477718ced0SMaksim Yevmenkin static struct event server_ev; 487718ced0SMaksim Yevmenkin static int server_fd; 49905033dcSMaksim Yevmenkin static int server_avail; 507718ced0SMaksim Yevmenkin 517718ced0SMaksim Yevmenkin static void * server_ss; 527718ced0SMaksim Yevmenkin static uint32_t server_handle; 537718ced0SMaksim Yevmenkin 547718ced0SMaksim Yevmenkin static void server_open(void); 557718ced0SMaksim Yevmenkin static void server_close(void); 567718ced0SMaksim Yevmenkin static void server_read(int, short, void *); 577718ced0SMaksim Yevmenkin static void server_register(void); 587718ced0SMaksim Yevmenkin 597718ced0SMaksim Yevmenkin void 607718ced0SMaksim Yevmenkin server_init(void) 617718ced0SMaksim Yevmenkin { 627718ced0SMaksim Yevmenkin 637718ced0SMaksim Yevmenkin server_fd = -1; 647718ced0SMaksim Yevmenkin } 657718ced0SMaksim Yevmenkin 667718ced0SMaksim Yevmenkin /* 677718ced0SMaksim Yevmenkin * The server_update() function is called whenever the channel count is 687718ced0SMaksim Yevmenkin * changed. We maintain the SDP record and open or close the server socket 697718ced0SMaksim Yevmenkin * as required. 707718ced0SMaksim Yevmenkin */ 717718ced0SMaksim Yevmenkin void 727718ced0SMaksim Yevmenkin server_update(int count) 737718ced0SMaksim Yevmenkin { 747718ced0SMaksim Yevmenkin 757718ced0SMaksim Yevmenkin if (server_limit == 0) 767718ced0SMaksim Yevmenkin return; 777718ced0SMaksim Yevmenkin 787718ced0SMaksim Yevmenkin log_debug("count %d", count); 797718ced0SMaksim Yevmenkin 80905033dcSMaksim Yevmenkin server_avail = UINT8_MAX - (count - 1) * UINT8_MAX / server_limit; 81905033dcSMaksim Yevmenkin log_info("Service Availability: %d/%d", server_avail, UINT8_MAX); 827718ced0SMaksim Yevmenkin 83905033dcSMaksim Yevmenkin if (server_avail == 0 && server_fd != -1) 847718ced0SMaksim Yevmenkin server_close(); 857718ced0SMaksim Yevmenkin 86905033dcSMaksim Yevmenkin if (server_avail > 0 && server_fd == -1) 877718ced0SMaksim Yevmenkin server_open(); 887718ced0SMaksim Yevmenkin 897718ced0SMaksim Yevmenkin if (service_name) 907718ced0SMaksim Yevmenkin server_register(); 917718ced0SMaksim Yevmenkin } 927718ced0SMaksim Yevmenkin 937718ced0SMaksim Yevmenkin static void 947718ced0SMaksim Yevmenkin server_open(void) 957718ced0SMaksim Yevmenkin { 967718ced0SMaksim Yevmenkin struct sockaddr_l2cap sa; 977718ced0SMaksim Yevmenkin uint16_t mru; 987718ced0SMaksim Yevmenkin 997718ced0SMaksim Yevmenkin server_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); 1007718ced0SMaksim Yevmenkin if (server_fd == -1) { 1017718ced0SMaksim Yevmenkin log_err("Could not open L2CAP socket: %m"); 1027718ced0SMaksim Yevmenkin exit(EXIT_FAILURE); 1037718ced0SMaksim Yevmenkin } 1047718ced0SMaksim Yevmenkin 1057718ced0SMaksim Yevmenkin memset(&sa, 0, sizeof(sa)); 1067718ced0SMaksim Yevmenkin sa.l2cap_family = AF_BLUETOOTH; 1077718ced0SMaksim Yevmenkin sa.l2cap_len = sizeof(sa); 1087718ced0SMaksim Yevmenkin sa.l2cap_psm = htole16(l2cap_psm); 1098d6f425dSTakanori Watanabe sa.l2cap_bdaddr_type = BDADDR_BREDR; 1108d6f425dSTakanori Watanabe sa.l2cap_cid = 0; 1118d6f425dSTakanori Watanabe 1127718ced0SMaksim Yevmenkin bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr); 1137718ced0SMaksim Yevmenkin if (bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 1147718ced0SMaksim Yevmenkin log_err("Could not bind server socket: %m"); 1157718ced0SMaksim Yevmenkin exit(EXIT_FAILURE); 1167718ced0SMaksim Yevmenkin } 1177718ced0SMaksim Yevmenkin 1187718ced0SMaksim Yevmenkin mru = BNEP_MTU_MIN; 1197718ced0SMaksim Yevmenkin if (setsockopt(server_fd, SOL_L2CAP, 1207718ced0SMaksim Yevmenkin SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) { 1217718ced0SMaksim Yevmenkin log_err("Could not set L2CAP IMTU (%d): %m", mru); 1227718ced0SMaksim Yevmenkin exit(EXIT_FAILURE); 1237718ced0SMaksim Yevmenkin } 1247718ced0SMaksim Yevmenkin 1257718ced0SMaksim Yevmenkin if (listen(server_fd, 0) == -1) { 1267718ced0SMaksim Yevmenkin log_err("Could not listen on server socket: %m"); 1277718ced0SMaksim Yevmenkin exit(EXIT_FAILURE); 1287718ced0SMaksim Yevmenkin } 1297718ced0SMaksim Yevmenkin 1307718ced0SMaksim Yevmenkin event_set(&server_ev, server_fd, EV_READ | EV_PERSIST, server_read, NULL); 1317718ced0SMaksim Yevmenkin if (event_add(&server_ev, NULL) == -1) { 1327718ced0SMaksim Yevmenkin log_err("Could not add server event: %m"); 1337718ced0SMaksim Yevmenkin exit(EXIT_FAILURE); 1347718ced0SMaksim Yevmenkin } 1357718ced0SMaksim Yevmenkin 1367718ced0SMaksim Yevmenkin log_info("server socket open"); 1377718ced0SMaksim Yevmenkin } 1387718ced0SMaksim Yevmenkin 1397718ced0SMaksim Yevmenkin static void 1407718ced0SMaksim Yevmenkin server_close(void) 1417718ced0SMaksim Yevmenkin { 1427718ced0SMaksim Yevmenkin 1437718ced0SMaksim Yevmenkin event_del(&server_ev); 1447718ced0SMaksim Yevmenkin close(server_fd); 1457718ced0SMaksim Yevmenkin server_fd = -1; 1467718ced0SMaksim Yevmenkin 1477718ced0SMaksim Yevmenkin log_info("server socket closed"); 1487718ced0SMaksim Yevmenkin } 1497718ced0SMaksim Yevmenkin 1507718ced0SMaksim Yevmenkin /* 1517718ced0SMaksim Yevmenkin * handle connection request 1527718ced0SMaksim Yevmenkin */ 1537718ced0SMaksim Yevmenkin static void 1547718ced0SMaksim Yevmenkin server_read(int s, short ev, void *arg) 1557718ced0SMaksim Yevmenkin { 1567718ced0SMaksim Yevmenkin struct sockaddr_l2cap ra, la; 1577718ced0SMaksim Yevmenkin channel_t *chan; 1587718ced0SMaksim Yevmenkin socklen_t len; 1597718ced0SMaksim Yevmenkin int fd, n; 1607718ced0SMaksim Yevmenkin uint16_t mru, mtu; 1617718ced0SMaksim Yevmenkin 1627718ced0SMaksim Yevmenkin len = sizeof(ra); 1637718ced0SMaksim Yevmenkin fd = accept(s, (struct sockaddr *)&ra, &len); 1647718ced0SMaksim Yevmenkin if (fd == -1) 1657718ced0SMaksim Yevmenkin return; 1667718ced0SMaksim Yevmenkin 1677718ced0SMaksim Yevmenkin n = 1; 1687718ced0SMaksim Yevmenkin if (ioctl(fd, FIONBIO, &n) == -1) { 1697718ced0SMaksim Yevmenkin log_err("Could not set NonBlocking IO: %m"); 1707718ced0SMaksim Yevmenkin close(fd); 1717718ced0SMaksim Yevmenkin return; 1727718ced0SMaksim Yevmenkin } 1737718ced0SMaksim Yevmenkin 1747718ced0SMaksim Yevmenkin len = sizeof(mru); 1757718ced0SMaksim Yevmenkin if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) { 1767718ced0SMaksim Yevmenkin log_err("Could not get L2CAP IMTU: %m"); 1777718ced0SMaksim Yevmenkin close(fd); 1787718ced0SMaksim Yevmenkin return; 1797718ced0SMaksim Yevmenkin } 1807718ced0SMaksim Yevmenkin if(mru < BNEP_MTU_MIN) { 1817718ced0SMaksim Yevmenkin log_err("L2CAP IMTU too small (%d)", mru); 1827718ced0SMaksim Yevmenkin close(fd); 1837718ced0SMaksim Yevmenkin return; 1847718ced0SMaksim Yevmenkin } 1857718ced0SMaksim Yevmenkin 1862dcf7e97SMaksim Yevmenkin len = sizeof(n); 1872dcf7e97SMaksim Yevmenkin if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, &len) == -1) { 1882dcf7e97SMaksim Yevmenkin log_err("Could not read SO_RCVBUF"); 1892dcf7e97SMaksim Yevmenkin close(fd); 1902dcf7e97SMaksim Yevmenkin return; 1912dcf7e97SMaksim Yevmenkin } 1922dcf7e97SMaksim Yevmenkin if (n < (mru * 10)) { 1932dcf7e97SMaksim Yevmenkin n = mru * 10; 1942dcf7e97SMaksim Yevmenkin if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1) 1952dcf7e97SMaksim Yevmenkin log_info("Could not increase SO_RCVBUF (from %d)", n); 1962dcf7e97SMaksim Yevmenkin } 1972dcf7e97SMaksim Yevmenkin 1987718ced0SMaksim Yevmenkin len = sizeof(mtu); 1997718ced0SMaksim Yevmenkin if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) { 2007718ced0SMaksim Yevmenkin log_err("Could not get L2CAP OMTU: %m"); 2017718ced0SMaksim Yevmenkin close(fd); 2027718ced0SMaksim Yevmenkin return; 2037718ced0SMaksim Yevmenkin } 2047718ced0SMaksim Yevmenkin if (mtu < BNEP_MTU_MIN) { 2057718ced0SMaksim Yevmenkin log_err("L2CAP OMTU too small (%d)", mtu); 2067718ced0SMaksim Yevmenkin close(fd); 2077718ced0SMaksim Yevmenkin return; 2087718ced0SMaksim Yevmenkin } 2097718ced0SMaksim Yevmenkin 2107718ced0SMaksim Yevmenkin len = sizeof(n); 2117718ced0SMaksim Yevmenkin if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) { 2127718ced0SMaksim Yevmenkin log_err("Could not get socket send buffer size: %m"); 2137718ced0SMaksim Yevmenkin close(fd); 2147718ced0SMaksim Yevmenkin return; 2157718ced0SMaksim Yevmenkin } 2167718ced0SMaksim Yevmenkin 2177718ced0SMaksim Yevmenkin if (n < (mtu * 2)) { 2187718ced0SMaksim Yevmenkin n = mtu * 2; 2197718ced0SMaksim Yevmenkin if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) { 2207718ced0SMaksim Yevmenkin log_err("Could not set socket send buffer size (%d): %m", n); 2217718ced0SMaksim Yevmenkin close(fd); 2227718ced0SMaksim Yevmenkin return; 2237718ced0SMaksim Yevmenkin } 2247718ced0SMaksim Yevmenkin } 2257718ced0SMaksim Yevmenkin 2267718ced0SMaksim Yevmenkin n = mtu; 2277718ced0SMaksim Yevmenkin if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) { 2287718ced0SMaksim Yevmenkin log_err("Could not set socket low water mark (%d): %m", n); 2297718ced0SMaksim Yevmenkin close(fd); 2307718ced0SMaksim Yevmenkin return; 2317718ced0SMaksim Yevmenkin } 2327718ced0SMaksim Yevmenkin 2337718ced0SMaksim Yevmenkin len = sizeof(la); 2347718ced0SMaksim Yevmenkin if (getsockname(fd, (struct sockaddr *)&la, &len) == -1) { 2357718ced0SMaksim Yevmenkin log_err("Could not get socket address: %m"); 2367718ced0SMaksim Yevmenkin close(fd); 2377718ced0SMaksim Yevmenkin return; 2387718ced0SMaksim Yevmenkin } 2397718ced0SMaksim Yevmenkin 2407718ced0SMaksim Yevmenkin log_info("Accepted connection from %s", bt_ntoa(&ra.l2cap_bdaddr, NULL)); 2417718ced0SMaksim Yevmenkin 2427718ced0SMaksim Yevmenkin chan = channel_alloc(); 2437718ced0SMaksim Yevmenkin if (chan == NULL) { 2447718ced0SMaksim Yevmenkin close(fd); 2457718ced0SMaksim Yevmenkin return; 2467718ced0SMaksim Yevmenkin } 2477718ced0SMaksim Yevmenkin 2487718ced0SMaksim Yevmenkin chan->send = bnep_send; 2497718ced0SMaksim Yevmenkin chan->recv = bnep_recv; 2507718ced0SMaksim Yevmenkin chan->mru = mru; 2517718ced0SMaksim Yevmenkin chan->mtu = mtu; 2527718ced0SMaksim Yevmenkin b2eaddr(chan->raddr, &ra.l2cap_bdaddr); 2537718ced0SMaksim Yevmenkin b2eaddr(chan->laddr, &la.l2cap_bdaddr); 2547718ced0SMaksim Yevmenkin chan->state = CHANNEL_WAIT_CONNECT_REQ; 2557718ced0SMaksim Yevmenkin channel_timeout(chan, 10); 2567718ced0SMaksim Yevmenkin if (!channel_open(chan, fd)) { 2577718ced0SMaksim Yevmenkin chan->state = CHANNEL_CLOSED; 2587718ced0SMaksim Yevmenkin channel_free(chan); 2597718ced0SMaksim Yevmenkin close(fd); 2607718ced0SMaksim Yevmenkin return; 2617718ced0SMaksim Yevmenkin } 2627718ced0SMaksim Yevmenkin } 2637718ced0SMaksim Yevmenkin 2647718ced0SMaksim Yevmenkin static void 2657718ced0SMaksim Yevmenkin server_register(void) 2667718ced0SMaksim Yevmenkin { 2677718ced0SMaksim Yevmenkin sdp_nap_profile_t p; 2687718ced0SMaksim Yevmenkin int rv; 2697718ced0SMaksim Yevmenkin 2707718ced0SMaksim Yevmenkin if (server_ss == NULL) { 2717718ced0SMaksim Yevmenkin server_ss = sdp_open_local(control_path); 2727718ced0SMaksim Yevmenkin if (server_ss == NULL || sdp_error(server_ss) != 0) { 2737718ced0SMaksim Yevmenkin log_err("failed to contact SDP server"); 2747718ced0SMaksim Yevmenkin return; 2757718ced0SMaksim Yevmenkin } 2767718ced0SMaksim Yevmenkin } 2777718ced0SMaksim Yevmenkin 2787718ced0SMaksim Yevmenkin memset(&p, 0, sizeof(p)); 2797718ced0SMaksim Yevmenkin p.psm = l2cap_psm; 280905033dcSMaksim Yevmenkin p.load_factor = server_avail; 281905033dcSMaksim Yevmenkin p.security_description = (l2cap_mode == 0 ? 0x0000 : 0x0001); 2827718ced0SMaksim Yevmenkin 2837718ced0SMaksim Yevmenkin if (server_handle) 2847718ced0SMaksim Yevmenkin rv = sdp_change_service(server_ss, server_handle, 2857718ced0SMaksim Yevmenkin (uint8_t *)&p, sizeof(p)); 2867718ced0SMaksim Yevmenkin else 2877718ced0SMaksim Yevmenkin rv = sdp_register_service(server_ss, service_class, 2887718ced0SMaksim Yevmenkin &local_bdaddr, (uint8_t *)&p, sizeof(p), &server_handle); 2897718ced0SMaksim Yevmenkin 2907718ced0SMaksim Yevmenkin if (rv != 0) { 2917718ced0SMaksim Yevmenkin errno = sdp_error(server_ss); 2927718ced0SMaksim Yevmenkin log_err("%s: %m", service_name); 2937718ced0SMaksim Yevmenkin exit(EXIT_FAILURE); 2947718ced0SMaksim Yevmenkin } 2957718ced0SMaksim Yevmenkin } 296