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