1 /* $NetBSD: channel.c,v 1.3 2009/05/12 21:08:30 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.3 2009/05/12 21:08:30 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) < 0) { 316 log_err("Could not add watchdog event: %m"); 317 exit(EXIT_FAILURE); 318 } 319 } 320