1 1.4 plunky /* $NetBSD: channel.c,v 1.4 2011/01/27 11:13:59 plunky Exp $ */ 2 1.1 plunky 3 1.1 plunky /*- 4 1.1 plunky * Copyright (c) 2008 Iain Hibbert 5 1.1 plunky * All rights reserved. 6 1.1 plunky * 7 1.1 plunky * Redistribution and use in source and binary forms, with or without 8 1.1 plunky * modification, are permitted provided that the following conditions 9 1.1 plunky * are met: 10 1.1 plunky * 1. Redistributions of source code must retain the above copyright 11 1.1 plunky * notice, this list of conditions and the following disclaimer. 12 1.1 plunky * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 plunky * notice, this list of conditions and the following disclaimer in the 14 1.1 plunky * documentation and/or other materials provided with the distribution. 15 1.1 plunky * 16 1.1 plunky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 1.1 plunky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 1.1 plunky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 1.1 plunky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 1.1 plunky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 1.1 plunky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 1.1 plunky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 1.1 plunky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 1.1 plunky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 1.1 plunky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 1.1 plunky */ 27 1.1 plunky 28 1.1 plunky #include <sys/cdefs.h> 29 1.4 plunky __RCSID("$NetBSD: channel.c,v 1.4 2011/01/27 11:13:59 plunky Exp $"); 30 1.1 plunky 31 1.1 plunky #include <sys/ioctl.h> 32 1.1 plunky 33 1.1 plunky #include <unistd.h> 34 1.1 plunky 35 1.1 plunky #include "btpand.h" 36 1.1 plunky 37 1.1 plunky static struct chlist channel_list; 38 1.1 plunky static int channel_tick; 39 1.1 plunky 40 1.1 plunky static void channel_start(int, short, void *); 41 1.1 plunky static void channel_read(int, short, void *); 42 1.1 plunky static void channel_dispatch(packet_t *); 43 1.1 plunky static void channel_watchdog(int, short, void *); 44 1.1 plunky 45 1.1 plunky void 46 1.1 plunky channel_init(void) 47 1.1 plunky { 48 1.1 plunky 49 1.1 plunky LIST_INIT(&channel_list); 50 1.1 plunky } 51 1.1 plunky 52 1.1 plunky channel_t * 53 1.1 plunky channel_alloc(void) 54 1.1 plunky { 55 1.1 plunky channel_t *chan; 56 1.1 plunky 57 1.1 plunky chan = malloc(sizeof(channel_t)); 58 1.1 plunky if (chan == NULL) { 59 1.1 plunky log_err("%s() failed: %m", __func__); 60 1.1 plunky return NULL; 61 1.1 plunky } 62 1.1 plunky 63 1.1 plunky memset(chan, 0, sizeof(channel_t)); 64 1.1 plunky STAILQ_INIT(&chan->pktlist); 65 1.1 plunky chan->state = CHANNEL_CLOSED; 66 1.1 plunky LIST_INSERT_HEAD(&channel_list, chan, next); 67 1.1 plunky 68 1.1 plunky return chan; 69 1.1 plunky } 70 1.1 plunky 71 1.1 plunky bool 72 1.1 plunky channel_open(channel_t *chan, int fd) 73 1.1 plunky { 74 1.1 plunky int n; 75 1.1 plunky 76 1.2 plunky assert(chan->refcnt == 0); 77 1.2 plunky assert(chan->state != CHANNEL_CLOSED); 78 1.3 plunky assert(chan->send != NULL); 79 1.3 plunky assert(chan->recv != NULL); 80 1.3 plunky assert(chan->down != NULL); 81 1.1 plunky 82 1.1 plunky if (chan->mtu > 0) { 83 1.1 plunky chan->sendbuf = malloc(chan->mtu); 84 1.1 plunky if (chan->sendbuf == NULL) { 85 1.1 plunky log_err("Could not malloc channel sendbuf: %m"); 86 1.1 plunky return false; 87 1.1 plunky } 88 1.1 plunky } 89 1.1 plunky 90 1.1 plunky n = 1; 91 1.1 plunky if (ioctl(fd, FIONBIO, &n) == -1) { 92 1.1 plunky log_err("Could not set non-blocking IO: %m"); 93 1.1 plunky return false; 94 1.1 plunky } 95 1.1 plunky 96 1.1 plunky event_set(&chan->rd_ev, fd, EV_READ | EV_PERSIST, channel_read, chan); 97 1.1 plunky if (event_add(&chan->rd_ev, NULL) == -1) { 98 1.1 plunky log_err("Could not add channel read event: %m"); 99 1.1 plunky return false; 100 1.1 plunky } 101 1.1 plunky 102 1.1 plunky event_set(&chan->wr_ev, fd, EV_WRITE, channel_start, chan); 103 1.1 plunky 104 1.1 plunky chan->refcnt++; 105 1.1 plunky chan->fd = fd; 106 1.1 plunky 107 1.1 plunky log_debug("(fd#%d)", chan->fd); 108 1.1 plunky 109 1.1 plunky return true; 110 1.1 plunky } 111 1.1 plunky 112 1.1 plunky void 113 1.1 plunky channel_close(channel_t *chan) 114 1.1 plunky { 115 1.1 plunky pkthdr_t *ph; 116 1.1 plunky 117 1.2 plunky assert(chan->state != CHANNEL_CLOSED); 118 1.1 plunky 119 1.1 plunky log_debug("(fd#%d)", chan->fd); 120 1.1 plunky 121 1.1 plunky chan->state = CHANNEL_CLOSED; 122 1.1 plunky event_del(&chan->rd_ev); 123 1.1 plunky event_del(&chan->wr_ev); 124 1.1 plunky close(chan->fd); 125 1.1 plunky chan->refcnt--; 126 1.1 plunky chan->tick = 0; 127 1.1 plunky 128 1.1 plunky while ((ph = STAILQ_FIRST(&chan->pktlist)) != NULL) { 129 1.1 plunky STAILQ_REMOVE_HEAD(&chan->pktlist, next); 130 1.1 plunky pkthdr_free(ph); 131 1.1 plunky chan->qlen--; 132 1.1 plunky } 133 1.1 plunky 134 1.1 plunky if (chan->refcnt == 0) 135 1.1 plunky channel_free(chan); 136 1.1 plunky } 137 1.1 plunky 138 1.1 plunky void 139 1.1 plunky channel_free(channel_t *chan) 140 1.1 plunky { 141 1.1 plunky 142 1.2 plunky assert(chan->refcnt == 0); 143 1.2 plunky assert(chan->state == CHANNEL_CLOSED); 144 1.2 plunky assert(chan->qlen == 0); 145 1.2 plunky assert(STAILQ_EMPTY(&chan->pktlist)); 146 1.1 plunky 147 1.1 plunky LIST_REMOVE(chan, next); 148 1.1 plunky free(chan->pfilter); 149 1.1 plunky free(chan->mfilter); 150 1.1 plunky free(chan->sendbuf); 151 1.1 plunky free(chan); 152 1.1 plunky } 153 1.1 plunky 154 1.1 plunky static void 155 1.1 plunky channel_start(int fd, short ev, void *arg) 156 1.1 plunky { 157 1.1 plunky channel_t *chan = arg; 158 1.1 plunky pkthdr_t *ph; 159 1.1 plunky 160 1.1 plunky chan->oactive = true; 161 1.1 plunky 162 1.1 plunky while (chan->qlen > 0) { 163 1.1 plunky ph = STAILQ_FIRST(&chan->pktlist); 164 1.1 plunky 165 1.1 plunky channel_timeout(chan, 10); 166 1.1 plunky if (chan->send(chan, ph->data) == false) { 167 1.1 plunky if (event_add(&chan->wr_ev, NULL) == -1) { 168 1.1 plunky log_err("Could not add channel write event: %m"); 169 1.3 plunky chan->down(chan); 170 1.1 plunky } 171 1.1 plunky return; 172 1.1 plunky } 173 1.1 plunky 174 1.1 plunky STAILQ_REMOVE_HEAD(&chan->pktlist, next); 175 1.1 plunky pkthdr_free(ph); 176 1.1 plunky chan->qlen--; 177 1.1 plunky } 178 1.1 plunky 179 1.1 plunky channel_timeout(chan, 0); 180 1.1 plunky chan->oactive = false; 181 1.1 plunky } 182 1.1 plunky 183 1.1 plunky static void 184 1.1 plunky channel_read(int fd, short ev, void *arg) 185 1.1 plunky { 186 1.1 plunky channel_t *chan = arg; 187 1.1 plunky packet_t *pkt; 188 1.1 plunky ssize_t nr; 189 1.1 plunky 190 1.1 plunky pkt = packet_alloc(chan); 191 1.1 plunky if (pkt == NULL) { 192 1.3 plunky chan->down(chan); 193 1.1 plunky return; 194 1.1 plunky } 195 1.1 plunky 196 1.1 plunky nr = read(fd, pkt->buf, chan->mru); 197 1.1 plunky if (nr == -1) { 198 1.1 plunky log_err("channel read error: %m"); 199 1.1 plunky packet_free(pkt); 200 1.3 plunky chan->down(chan); 201 1.1 plunky return; 202 1.1 plunky } 203 1.1 plunky if (nr == 0) { /* EOF */ 204 1.1 plunky log_debug("(fd#%d) EOF", fd); 205 1.1 plunky packet_free(pkt); 206 1.3 plunky chan->down(chan); 207 1.1 plunky return; 208 1.1 plunky } 209 1.1 plunky pkt->len = nr; 210 1.1 plunky 211 1.1 plunky if (chan->recv(pkt) == true) 212 1.1 plunky channel_dispatch(pkt); 213 1.1 plunky 214 1.1 plunky packet_free(pkt); 215 1.1 plunky } 216 1.1 plunky 217 1.1 plunky static void 218 1.1 plunky channel_dispatch(packet_t *pkt) 219 1.1 plunky { 220 1.1 plunky channel_t *chan; 221 1.1 plunky 222 1.1 plunky /* 223 1.1 plunky * This is simple routing. I'm not sure if its allowed by 224 1.1 plunky * the PAN or BNEP specifications, but it seems logical 225 1.1 plunky * to send unicast packets to connected destinations where 226 1.1 plunky * possible. 227 1.1 plunky */ 228 1.1 plunky if (!ETHER_IS_MULTICAST(pkt->dst)) { 229 1.1 plunky LIST_FOREACH(chan, &channel_list, next) { 230 1.1 plunky if (chan == pkt->chan 231 1.1 plunky || chan->state != CHANNEL_OPEN) 232 1.1 plunky continue; 233 1.1 plunky 234 1.1 plunky if (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) == 0) { 235 1.1 plunky if (chan->qlen > CHANNEL_MAXQLEN) 236 1.1 plunky log_notice("Queue overflow"); 237 1.1 plunky else 238 1.1 plunky channel_put(chan, pkt); 239 1.1 plunky 240 1.1 plunky return; 241 1.1 plunky } 242 1.1 plunky } 243 1.1 plunky } 244 1.1 plunky 245 1.1 plunky LIST_FOREACH(chan, &channel_list, next) { 246 1.1 plunky if (chan == pkt->chan 247 1.1 plunky || chan->state != CHANNEL_OPEN) 248 1.1 plunky continue; 249 1.1 plunky 250 1.1 plunky if (chan->qlen > CHANNEL_MAXQLEN) { 251 1.1 plunky log_notice("Queue overflow"); 252 1.1 plunky continue; 253 1.1 plunky } 254 1.1 plunky 255 1.1 plunky channel_put(chan, pkt); 256 1.1 plunky } 257 1.1 plunky } 258 1.1 plunky 259 1.1 plunky void 260 1.1 plunky channel_put(channel_t *chan, packet_t *pkt) 261 1.1 plunky { 262 1.1 plunky pkthdr_t *ph; 263 1.1 plunky 264 1.1 plunky ph = pkthdr_alloc(pkt); 265 1.1 plunky if (ph == NULL) 266 1.1 plunky return; 267 1.1 plunky 268 1.1 plunky chan->qlen++; 269 1.1 plunky STAILQ_INSERT_TAIL(&chan->pktlist, ph, next); 270 1.1 plunky 271 1.1 plunky if (!chan->oactive) 272 1.1 plunky channel_start(chan->fd, EV_WRITE, chan); 273 1.1 plunky } 274 1.1 plunky 275 1.1 plunky /* 276 1.1 plunky * Simple watchdog timer, only ticks when it is required and 277 1.1 plunky * closes the channel down if it times out. 278 1.1 plunky */ 279 1.1 plunky void 280 1.1 plunky channel_timeout(channel_t *chan, int to) 281 1.1 plunky { 282 1.1 plunky static struct event ev; 283 1.1 plunky 284 1.1 plunky if (to == 0) 285 1.1 plunky chan->tick = 0; 286 1.1 plunky else 287 1.1 plunky chan->tick = (channel_tick + to) % 60; 288 1.1 plunky 289 1.1 plunky if (channel_tick == 0) { 290 1.1 plunky evtimer_set(&ev, channel_watchdog, &ev); 291 1.1 plunky channel_watchdog(0, 0, &ev); 292 1.1 plunky } 293 1.1 plunky } 294 1.1 plunky 295 1.1 plunky static void 296 1.1 plunky channel_watchdog(int fd, short ev, void *arg) 297 1.1 plunky { 298 1.1 plunky static struct timeval tv = { .tv_sec = 1 }; 299 1.1 plunky channel_t *chan, *next; 300 1.1 plunky int tick; 301 1.1 plunky 302 1.1 plunky tick = (channel_tick % 60) + 1; 303 1.1 plunky channel_tick = 0; 304 1.1 plunky 305 1.1 plunky next = LIST_FIRST(&channel_list); 306 1.1 plunky while ((chan = next) != NULL) { 307 1.1 plunky next = LIST_NEXT(chan, next); 308 1.1 plunky 309 1.1 plunky if (chan->tick == tick) 310 1.3 plunky chan->down(chan); 311 1.1 plunky else if (chan->tick != 0) 312 1.1 plunky channel_tick = tick; 313 1.1 plunky } 314 1.1 plunky 315 1.4 plunky if (channel_tick != 0 && evtimer_add(arg, &tv) == -1) { 316 1.1 plunky log_err("Could not add watchdog event: %m"); 317 1.1 plunky exit(EXIT_FAILURE); 318 1.1 plunky } 319 1.1 plunky } 320