Home | History | Annotate | Line # | Download | only in btpand
channel.c revision 1.1
      1 /*	$NetBSD: channel.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: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
     30 
     31 #include <sys/ioctl.h>
     32 
     33 #include <unistd.h>
     34 
     35 #include "btpand.h"
     36 
     37 static struct chlist	channel_list;
     38 static int		channel_count;
     39 static int		channel_tick;
     40 
     41 static void channel_start(int, short, void *);
     42 static void channel_read(int, short, void *);
     43 static void channel_dispatch(packet_t *);
     44 static void channel_watchdog(int, short, void *);
     45 
     46 void
     47 channel_init(void)
     48 {
     49 
     50 	LIST_INIT(&channel_list);
     51 }
     52 
     53 channel_t *
     54 channel_alloc(void)
     55 {
     56 	channel_t *chan;
     57 
     58 	chan = malloc(sizeof(channel_t));
     59 	if (chan == NULL) {
     60 		log_err("%s() failed: %m", __func__);
     61 		return NULL;
     62 	}
     63 
     64 	memset(chan, 0, sizeof(channel_t));
     65 	STAILQ_INIT(&chan->pktlist);
     66 	chan->state = CHANNEL_CLOSED;
     67 	LIST_INSERT_HEAD(&channel_list, chan, next);
     68 
     69 	server_update(++channel_count);
     70 
     71 	return chan;
     72 }
     73 
     74 bool
     75 channel_open(channel_t *chan, int fd)
     76 {
     77 	int n;
     78 
     79 	_DIAGASSERT(chan->refcnt == 0);
     80 	_DIAGASSERT(chan->state != CHANNEL_CLOSED);
     81 
     82 	if (chan->mtu > 0) {
     83 		chan->sendbuf = malloc(chan->mtu);
     84 		if (chan->sendbuf == NULL) {
     85 			log_err("Could not malloc channel sendbuf: %m");
     86 			return false;
     87 		}
     88 	}
     89 
     90 	n = 1;
     91 	if (ioctl(fd, FIONBIO, &n) == -1) {
     92 		log_err("Could not set non-blocking IO: %m");
     93 		return false;
     94 	}
     95 
     96 	event_set(&chan->rd_ev, fd, EV_READ | EV_PERSIST, channel_read, chan);
     97 	if (event_add(&chan->rd_ev, NULL) == -1) {
     98 		log_err("Could not add channel read event: %m");
     99 		return false;
    100 	}
    101 
    102 	event_set(&chan->wr_ev, fd, EV_WRITE, channel_start, chan);
    103 
    104 	chan->refcnt++;
    105 	chan->fd = fd;
    106 
    107 	log_debug("(fd#%d)", chan->fd);
    108 
    109 	return true;
    110 }
    111 
    112 void
    113 channel_close(channel_t *chan)
    114 {
    115 	pkthdr_t *ph;
    116 
    117 	_DIAGASSERT(chan->state != CHANNEL_CLOSED);
    118 
    119 	log_debug("(fd#%d)", chan->fd);
    120 
    121 	chan->state = CHANNEL_CLOSED;
    122 	event_del(&chan->rd_ev);
    123 	event_del(&chan->wr_ev);
    124 	close(chan->fd);
    125 	chan->refcnt--;
    126 	chan->tick = 0;
    127 
    128 	while ((ph = STAILQ_FIRST(&chan->pktlist)) != NULL) {
    129 		STAILQ_REMOVE_HEAD(&chan->pktlist, next);
    130 		pkthdr_free(ph);
    131 		chan->qlen--;
    132 	}
    133 
    134 	if (chan->refcnt == 0)
    135 		channel_free(chan);
    136 }
    137 
    138 void
    139 channel_free(channel_t *chan)
    140 {
    141 
    142 	_DIAGASSERT(chan->refcnt == 0);
    143 	_DIAGASSERT(chan->state == CHANNEL_CLOSED);
    144 	_DIAGASSERT(chan->qlen == 0);
    145 	_DIAGASSERT(STAILQ_EMPTY(&chan->pktlist));
    146 
    147 	LIST_REMOVE(chan, next);
    148 	free(chan->pfilter);
    149 	free(chan->mfilter);
    150 	free(chan->sendbuf);
    151 	free(chan);
    152 
    153 	server_update(--channel_count);
    154 
    155 	if (server_limit == 0) {
    156 		log_info("connection closed, exiting");
    157 		exit(EXIT_SUCCESS);
    158 	}
    159 }
    160 
    161 static void
    162 channel_start(int fd, short ev, void *arg)
    163 {
    164 	channel_t *chan = arg;
    165 	pkthdr_t *ph;
    166 
    167 	chan->oactive = true;
    168 
    169 	while (chan->qlen > 0) {
    170 		ph = STAILQ_FIRST(&chan->pktlist);
    171 
    172 		channel_timeout(chan, 10);
    173 		if (chan->send(chan, ph->data) == false) {
    174 			if (event_add(&chan->wr_ev, NULL) == -1) {
    175 				log_err("Could not add channel write event: %m");
    176 				channel_close(chan);
    177 			}
    178 			return;
    179 		}
    180 
    181 		STAILQ_REMOVE_HEAD(&chan->pktlist, next);
    182 		pkthdr_free(ph);
    183 		chan->qlen--;
    184 	}
    185 
    186 	channel_timeout(chan, 0);
    187 	chan->oactive = false;
    188 }
    189 
    190 static void
    191 channel_read(int fd, short ev, void *arg)
    192 {
    193 	channel_t *chan = arg;
    194 	packet_t *pkt;
    195 	ssize_t nr;
    196 
    197 	pkt = packet_alloc(chan);
    198 	if (pkt == NULL) {
    199 		channel_close(chan);
    200 		return;
    201 	}
    202 
    203 	nr = read(fd, pkt->buf, chan->mru);
    204 	if (nr == -1) {
    205 		log_err("channel read error: %m");
    206 		packet_free(pkt);
    207 		channel_close(chan);
    208 		return;
    209 	}
    210 	if (nr == 0) {	/* EOF */
    211 		log_debug("(fd#%d) EOF", fd);
    212 		packet_free(pkt);
    213 		channel_close(chan);
    214 		return;
    215 	}
    216 	pkt->len = nr;
    217 
    218 	if (chan->recv(pkt) == true)
    219 		channel_dispatch(pkt);
    220 
    221 	packet_free(pkt);
    222 }
    223 
    224 static void
    225 channel_dispatch(packet_t *pkt)
    226 {
    227 	channel_t *chan;
    228 
    229 	/*
    230 	 * This is simple routing. I'm not sure if its allowed by
    231 	 * the PAN or BNEP specifications, but it seems logical
    232 	 * to send unicast packets to connected destinations where
    233 	 * possible.
    234 	 */
    235 	if (!ETHER_IS_MULTICAST(pkt->dst)) {
    236 		LIST_FOREACH(chan, &channel_list, next) {
    237 			if (chan == pkt->chan
    238 			    || chan->state != CHANNEL_OPEN)
    239 				continue;
    240 
    241 			if (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) == 0) {
    242 				if (chan->qlen > CHANNEL_MAXQLEN)
    243 					log_notice("Queue overflow");
    244 				else
    245 					channel_put(chan, pkt);
    246 
    247 				return;
    248 			}
    249 		}
    250 	}
    251 
    252 	LIST_FOREACH(chan, &channel_list, next) {
    253 		if (chan == pkt->chan
    254 		    || chan->state != CHANNEL_OPEN)
    255 			continue;
    256 
    257 		if (chan->qlen > CHANNEL_MAXQLEN) {
    258 			log_notice("Queue overflow");
    259 			continue;
    260 		}
    261 
    262 		channel_put(chan, pkt);
    263 	}
    264 }
    265 
    266 void
    267 channel_put(channel_t *chan, packet_t *pkt)
    268 {
    269 	pkthdr_t *ph;
    270 
    271 	ph = pkthdr_alloc(pkt);
    272 	if (ph == NULL)
    273 		return;
    274 
    275 	chan->qlen++;
    276 	STAILQ_INSERT_TAIL(&chan->pktlist, ph, next);
    277 
    278 	if (!chan->oactive)
    279 		channel_start(chan->fd, EV_WRITE, chan);
    280 }
    281 
    282 /*
    283  * Simple watchdog timer, only ticks when it is required and
    284  * closes the channel down if it times out.
    285  */
    286 void
    287 channel_timeout(channel_t *chan, int to)
    288 {
    289 	static struct event ev;
    290 
    291 	if (to == 0)
    292 		chan->tick = 0;
    293 	else
    294 		chan->tick = (channel_tick + to) % 60;
    295 
    296 	if (channel_tick == 0) {
    297 		evtimer_set(&ev, channel_watchdog, &ev);
    298 		channel_watchdog(0, 0, &ev);
    299 	}
    300 }
    301 
    302 static void
    303 channel_watchdog(int fd, short ev, void *arg)
    304 {
    305 	static struct timeval tv = { .tv_sec = 1 };
    306 	channel_t *chan, *next;
    307 	int tick;
    308 
    309 	tick = (channel_tick % 60) + 1;
    310 	channel_tick = 0;
    311 
    312 	next = LIST_FIRST(&channel_list);
    313 	while ((chan = next) != NULL) {
    314 		next = LIST_NEXT(chan, next);
    315 
    316 		if (chan->tick == tick)
    317 			channel_close(chan);
    318 		else if (chan->tick != 0)
    319 			channel_tick = tick;
    320 	}
    321 
    322 	if (channel_tick != 0 && evtimer_add(arg, &tv) < 0) {
    323 		log_err("Could not add watchdog event: %m");
    324 		exit(EXIT_FAILURE);
    325 	}
    326 }
    327