Home | History | Annotate | Line # | Download | only in btpand
      1 /*	$NetBSD: channel.c,v 1.4 2011/01/27 11:13:59 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.4 2011/01/27 11:13:59 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_tick;
     39 
     40 static void channel_start(int, short, void *);
     41 static void channel_read(int, short, void *);
     42 static void channel_dispatch(packet_t *);
     43 static void channel_watchdog(int, short, void *);
     44 
     45 void
     46 channel_init(void)
     47 {
     48 
     49 	LIST_INIT(&channel_list);
     50 }
     51 
     52 channel_t *
     53 channel_alloc(void)
     54 {
     55 	channel_t *chan;
     56 
     57 	chan = malloc(sizeof(channel_t));
     58 	if (chan == NULL) {
     59 		log_err("%s() failed: %m", __func__);
     60 		return NULL;
     61 	}
     62 
     63 	memset(chan, 0, sizeof(channel_t));
     64 	STAILQ_INIT(&chan->pktlist);
     65 	chan->state = CHANNEL_CLOSED;
     66 	LIST_INSERT_HEAD(&channel_list, chan, next);
     67 
     68 	return chan;
     69 }
     70 
     71 bool
     72 channel_open(channel_t *chan, int fd)
     73 {
     74 	int n;
     75 
     76 	assert(chan->refcnt == 0);
     77 	assert(chan->state != CHANNEL_CLOSED);
     78 	assert(chan->send != NULL);
     79 	assert(chan->recv != NULL);
     80 	assert(chan->down != NULL);
     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 	assert(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 	assert(chan->refcnt == 0);
    143 	assert(chan->state == CHANNEL_CLOSED);
    144 	assert(chan->qlen == 0);
    145 	assert(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 
    154 static void
    155 channel_start(int fd, short ev, void *arg)
    156 {
    157 	channel_t *chan = arg;
    158 	pkthdr_t *ph;
    159 
    160 	chan->oactive = true;
    161 
    162 	while (chan->qlen > 0) {
    163 		ph = STAILQ_FIRST(&chan->pktlist);
    164 
    165 		channel_timeout(chan, 10);
    166 		if (chan->send(chan, ph->data) == false) {
    167 			if (event_add(&chan->wr_ev, NULL) == -1) {
    168 				log_err("Could not add channel write event: %m");
    169 				chan->down(chan);
    170 			}
    171 			return;
    172 		}
    173 
    174 		STAILQ_REMOVE_HEAD(&chan->pktlist, next);
    175 		pkthdr_free(ph);
    176 		chan->qlen--;
    177 	}
    178 
    179 	channel_timeout(chan, 0);
    180 	chan->oactive = false;
    181 }
    182 
    183 static void
    184 channel_read(int fd, short ev, void *arg)
    185 {
    186 	channel_t *chan = arg;
    187 	packet_t *pkt;
    188 	ssize_t nr;
    189 
    190 	pkt = packet_alloc(chan);
    191 	if (pkt == NULL) {
    192 		chan->down(chan);
    193 		return;
    194 	}
    195 
    196 	nr = read(fd, pkt->buf, chan->mru);
    197 	if (nr == -1) {
    198 		log_err("channel read error: %m");
    199 		packet_free(pkt);
    200 		chan->down(chan);
    201 		return;
    202 	}
    203 	if (nr == 0) {	/* EOF */
    204 		log_debug("(fd#%d) EOF", fd);
    205 		packet_free(pkt);
    206 		chan->down(chan);
    207 		return;
    208 	}
    209 	pkt->len = nr;
    210 
    211 	if (chan->recv(pkt) == true)
    212 		channel_dispatch(pkt);
    213 
    214 	packet_free(pkt);
    215 }
    216 
    217 static void
    218 channel_dispatch(packet_t *pkt)
    219 {
    220 	channel_t *chan;
    221 
    222 	/*
    223 	 * This is simple routing. I'm not sure if its allowed by
    224 	 * the PAN or BNEP specifications, but it seems logical
    225 	 * to send unicast packets to connected destinations where
    226 	 * possible.
    227 	 */
    228 	if (!ETHER_IS_MULTICAST(pkt->dst)) {
    229 		LIST_FOREACH(chan, &channel_list, next) {
    230 			if (chan == pkt->chan
    231 			    || chan->state != CHANNEL_OPEN)
    232 				continue;
    233 
    234 			if (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) == 0) {
    235 				if (chan->qlen > CHANNEL_MAXQLEN)
    236 					log_notice("Queue overflow");
    237 				else
    238 					channel_put(chan, pkt);
    239 
    240 				return;
    241 			}
    242 		}
    243 	}
    244 
    245 	LIST_FOREACH(chan, &channel_list, next) {
    246 		if (chan == pkt->chan
    247 		    || chan->state != CHANNEL_OPEN)
    248 			continue;
    249 
    250 		if (chan->qlen > CHANNEL_MAXQLEN) {
    251 			log_notice("Queue overflow");
    252 			continue;
    253 		}
    254 
    255 		channel_put(chan, pkt);
    256 	}
    257 }
    258 
    259 void
    260 channel_put(channel_t *chan, packet_t *pkt)
    261 {
    262 	pkthdr_t *ph;
    263 
    264 	ph = pkthdr_alloc(pkt);
    265 	if (ph == NULL)
    266 		return;
    267 
    268 	chan->qlen++;
    269 	STAILQ_INSERT_TAIL(&chan->pktlist, ph, next);
    270 
    271 	if (!chan->oactive)
    272 		channel_start(chan->fd, EV_WRITE, chan);
    273 }
    274 
    275 /*
    276  * Simple watchdog timer, only ticks when it is required and
    277  * closes the channel down if it times out.
    278  */
    279 void
    280 channel_timeout(channel_t *chan, int to)
    281 {
    282 	static struct event ev;
    283 
    284 	if (to == 0)
    285 		chan->tick = 0;
    286 	else
    287 		chan->tick = (channel_tick + to) % 60;
    288 
    289 	if (channel_tick == 0) {
    290 		evtimer_set(&ev, channel_watchdog, &ev);
    291 		channel_watchdog(0, 0, &ev);
    292 	}
    293 }
    294 
    295 static void
    296 channel_watchdog(int fd, short ev, void *arg)
    297 {
    298 	static struct timeval tv = { .tv_sec = 1 };
    299 	channel_t *chan, *next;
    300 	int tick;
    301 
    302 	tick = (channel_tick % 60) + 1;
    303 	channel_tick = 0;
    304 
    305 	next = LIST_FIRST(&channel_list);
    306 	while ((chan = next) != NULL) {
    307 		next = LIST_NEXT(chan, next);
    308 
    309 		if (chan->tick == tick)
    310 			chan->down(chan);
    311 		else if (chan->tick != 0)
    312 			channel_tick = tick;
    313 	}
    314 
    315 	if (channel_tick != 0 && evtimer_add(arg, &tv) == -1) {
    316 		log_err("Could not add watchdog event: %m");
    317 		exit(EXIT_FAILURE);
    318 	}
    319 }
    320