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