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