Home | History | Annotate | Line # | Download | only in btpand
server.c revision 1.1
      1 /*	$NetBSD: server.c,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2008 Iain Hibbert
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __RCSID("$NetBSD: server.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
     30 
     31 #include <sys/ioctl.h>
     32 
     33 #include <bluetooth.h>
     34 #include <errno.h>
     35 #include <sdp.h>
     36 #include <unistd.h>
     37 
     38 #include "btpand.h"
     39 #include "bnep.h"
     40 
     41 static struct event	server_ev;
     42 static int		server_fd;
     43 static int		server_load;
     44 
     45 static void *		server_ss;
     46 static uint32_t		server_handle;
     47 
     48 static void server_open(void);
     49 static void server_close(void);
     50 static void server_read(int, short, void *);
     51 static void server_register(void);
     52 
     53 void
     54 server_init(void)
     55 {
     56 
     57 	server_fd = -1;
     58 }
     59 
     60 /*
     61  * The server_update() function is called whenever the channel count is
     62  * changed. We maintain the SDP record and open or close the server socket
     63  * as required.
     64  */
     65 void
     66 server_update(int count)
     67 {
     68 
     69 	if (server_limit == 0)
     70 		return;
     71 
     72 	log_debug("count %d", count);
     73 
     74 	server_load = (count - 1) * 100 / server_limit;
     75 	log_info("server_load: %d%%", server_load);
     76 
     77 	if (server_load > 99 && server_fd != -1)
     78 		server_close();
     79 
     80 	if (server_load < 100 && server_fd == -1)
     81 		server_open();
     82 
     83 	if (service_name)
     84 		server_register();
     85 }
     86 
     87 static void
     88 server_open(void)
     89 {
     90 	struct sockaddr_bt sa;
     91 	uint16_t mru;
     92 
     93 	server_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
     94 	if (server_fd == -1) {
     95 		log_err("Could not open L2CAP socket: %m");
     96 		exit(EXIT_FAILURE);
     97 	}
     98 
     99 	memset(&sa, 0, sizeof(sa));
    100 	sa.bt_family = AF_BLUETOOTH;
    101 	sa.bt_len = sizeof(sa);
    102 	sa.bt_psm = l2cap_psm;
    103 	bdaddr_copy(&sa.bt_bdaddr, &local_bdaddr);
    104 	if (bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
    105 		log_err("Could not bind server socket: %m");
    106 		exit(EXIT_FAILURE);
    107 	}
    108 
    109 	if (setsockopt(server_fd, BTPROTO_L2CAP,
    110 	    SO_L2CAP_LM, &l2cap_mode, sizeof(l2cap_mode)) == -1) {
    111 		log_err("Could not set link mode (0x%4.4x): %m", l2cap_mode);
    112 		exit(EXIT_FAILURE);
    113 	}
    114 
    115 	mru = BNEP_MTU_MIN;
    116 	if (setsockopt(server_fd, BTPROTO_L2CAP,
    117 	    SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) {
    118 		log_err("Could not set L2CAP IMTU (%d): %m", mru);
    119 		exit(EXIT_FAILURE);
    120 	}
    121 
    122 	if (listen(server_fd, 0) == -1) {
    123 		log_err("Could not listen on server socket: %m");
    124 		exit(EXIT_FAILURE);
    125 	}
    126 
    127 	event_set(&server_ev, server_fd, EV_READ | EV_PERSIST, server_read, NULL);
    128 	if (event_add(&server_ev, NULL) == -1) {
    129 		log_err("Could not add server event: %m");
    130 		exit(EXIT_FAILURE);
    131 	}
    132 
    133 	log_info("server socket open");
    134 }
    135 
    136 static void
    137 server_close(void)
    138 {
    139 
    140 	event_del(&server_ev);
    141 	close(server_fd);
    142 	server_fd = -1;
    143 
    144 	log_info("server socket closed");
    145 }
    146 
    147 /*
    148  * handle connection request
    149  */
    150 static void
    151 server_read(int s, short ev, void *arg)
    152 {
    153 	struct sockaddr_bt ra, la;
    154 	channel_t *chan;
    155 	socklen_t len;
    156 	int fd, n;
    157 	uint16_t mru, mtu;
    158 
    159 	len = sizeof(ra);
    160 	fd = accept(s, (struct sockaddr *)&ra, &len);
    161 	if (fd == -1)
    162 		return;
    163 
    164 	n = 1;
    165 	if (ioctl(fd, FIONBIO, &n) == -1) {
    166 		log_err("Could not set NonBlocking IO: %m");
    167 		close(fd);
    168 		return;
    169 	}
    170 
    171 	len = sizeof(mru);
    172 	if (getsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) {
    173 		log_err("Could not get L2CAP IMTU: %m");
    174 		close(fd);
    175 		return;
    176 	}
    177 	if(mru < BNEP_MTU_MIN) {
    178 		log_err("L2CAP IMTU too small (%d)", mru);
    179 		close(fd);
    180 		return;
    181 	}
    182 
    183 	len = sizeof(mtu);
    184 	if (getsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) {
    185 		log_err("Could not get L2CAP OMTU: %m");
    186 		close(fd);
    187 		return;
    188 	}
    189 	if (mtu < BNEP_MTU_MIN) {
    190 		log_err("L2CAP OMTU too small (%d)", mtu);
    191 		close(fd);
    192 		return;
    193 	}
    194 
    195 	len = sizeof(n);
    196 	if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) {
    197 		log_err("Could not get socket send buffer size: %m");
    198 		close(fd);
    199 		return;
    200 	}
    201 
    202 	if (n < (mtu * 2)) {
    203 		n = mtu * 2;
    204 		if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) {
    205 			log_err("Could not set socket send buffer size (%d): %m", n);
    206 			close(fd);
    207 			return;
    208 		}
    209 	}
    210 
    211 	n = mtu;
    212 	if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) {
    213 		log_err("Could not set socket low water mark (%d): %m", n);
    214 		close(fd);
    215 		return;
    216 	}
    217 
    218 	len = sizeof(la);
    219 	if (getsockname(fd, (struct sockaddr *)&la, &len) == -1) {
    220 		log_err("Could not get socket address: %m");
    221 		close(fd);
    222 		return;
    223 	}
    224 
    225 	log_info("Accepted connection from %s", bt_ntoa(&ra.bt_bdaddr, NULL));
    226 
    227 	chan = channel_alloc();
    228 	if (chan == NULL) {
    229 		close(fd);
    230 		return;
    231 	}
    232 
    233 	chan->send = bnep_send;
    234 	chan->recv = bnep_recv;
    235 	chan->mru = mru;
    236 	chan->mtu = mtu;
    237 	b2eaddr(chan->raddr, &ra.bt_bdaddr);
    238 	b2eaddr(chan->laddr, &la.bt_bdaddr);
    239 	chan->state = CHANNEL_WAIT_CONNECT_REQ;
    240 	channel_timeout(chan, 10);
    241 	if (!channel_open(chan, fd)) {
    242 		chan->state = CHANNEL_CLOSED;
    243 		channel_free(chan);
    244 		close(fd);
    245 		return;
    246 	}
    247 }
    248 
    249 static void
    250 server_register(void)
    251 {
    252 	sdp_nap_profile_t p;
    253 	int rv;
    254 
    255 	if (server_ss == NULL) {
    256 		server_ss = sdp_open_local(control_path);
    257 		if (server_ss == NULL || sdp_error(server_ss) != 0) {
    258 			log_err("failed to contact SDP server");
    259 			return;
    260 		}
    261 	}
    262 
    263 					memset(&p, 0, sizeof(p));
    264 
    265 					p.psm = l2cap_psm;
    266 
    267 	if (server_load < 1)		p.load_factor = 0;
    268 	else if (server_load <= 17)	p.load_factor = 1;
    269 	else if (server_load <= 33)	p.load_factor = 2;
    270 	else if (server_load <= 50)	p.load_factor = 3;
    271 	else if (server_load <= 67)	p.load_factor = 4;
    272 	else if (server_load <= 83)	p.load_factor = 5;
    273 	else if (server_load <= 99)	p.load_factor = 6;
    274 	else				p.load_factor = 7;
    275 
    276 	if (l2cap_mode != 0)		p.security_description = 0x0001;
    277 
    278 	if (server_handle)
    279 		rv = sdp_change_service(server_ss, server_handle,
    280 		    (uint8_t *)&p, sizeof(p));
    281 	else
    282 		rv = sdp_register_service(server_ss, service_class,
    283 		    &local_bdaddr, (uint8_t *)&p, sizeof(p), &server_handle);
    284 
    285 	if (rv != 0) {
    286 		errno = sdp_error(server_ss);
    287 		log_err("%s: %m", service_name);
    288 		exit(EXIT_FAILURE);
    289 	}
    290 }
    291