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