1 /* $NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */
4 * SPDX-License-Identifier: BSD-2-Clause-NetBSD
6 * Copyright (c) 2008 Iain Hibbert
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
35 #include <sys/param.h>
36 #include <sys/ioctl.h>
40 #define L2CAP_SOCKET_CHECKED
43 static struct chlist channel_list;
44 static int channel_count;
45 static int channel_tick;
47 static void channel_start(int, short, void *);
48 static void channel_read(int, short, void *);
49 static void channel_dispatch(packet_t *);
50 static void channel_watchdog(int, short, void *);
56 LIST_INIT(&channel_list);
64 chan = malloc(sizeof(channel_t));
66 log_err("%s() failed: %m", __func__);
70 memset(chan, 0, sizeof(channel_t));
71 STAILQ_INIT(&chan->pktlist);
72 chan->state = CHANNEL_CLOSED;
73 LIST_INSERT_HEAD(&channel_list, chan, next);
75 server_update(++channel_count);
81 channel_open(channel_t *chan, int fd)
85 assert(chan->refcnt == 0);
86 assert(chan->state != CHANNEL_CLOSED);
89 chan->sendbuf = malloc(chan->mtu);
90 if (chan->sendbuf == NULL) {
91 log_err("Could not malloc channel sendbuf: %m");
97 if (ioctl(fd, FIONBIO, &n) == -1) {
98 log_err("Could not set non-blocking IO: %m");
102 event_set(&chan->rd_ev, fd, EV_READ | EV_PERSIST, channel_read, chan);
103 if (event_add(&chan->rd_ev, NULL) == -1) {
104 log_err("Could not add channel read event: %m");
108 event_set(&chan->wr_ev, fd, EV_WRITE, channel_start, chan);
113 log_debug("(fd#%d)", chan->fd);
119 channel_close(channel_t *chan)
123 assert(chan->state != CHANNEL_CLOSED);
125 log_debug("(fd#%d)", chan->fd);
127 chan->state = CHANNEL_CLOSED;
128 event_del(&chan->rd_ev);
129 event_del(&chan->wr_ev);
134 while ((ph = STAILQ_FIRST(&chan->pktlist)) != NULL) {
135 STAILQ_REMOVE_HEAD(&chan->pktlist, next);
140 if (chan->pfh != NULL) {
141 pidfile_remove(chan->pfh);
145 if (chan->refcnt == 0)
150 channel_free(channel_t *chan)
153 assert(chan->refcnt == 0);
154 assert(chan->state == CHANNEL_CLOSED);
155 assert(chan->qlen == 0);
156 assert(STAILQ_EMPTY(&chan->pktlist));
158 LIST_REMOVE(chan, next);
164 server_update(--channel_count);
166 if (server_limit == 0) {
167 log_info("connection closed, exiting");
173 channel_start(int fd, short ev, void *arg)
175 channel_t *chan = arg;
178 chan->oactive = true;
180 while (chan->qlen > 0) {
181 ph = STAILQ_FIRST(&chan->pktlist);
183 channel_timeout(chan, 10);
184 if (chan->send(chan, ph->data) == false) {
185 if (event_add(&chan->wr_ev, NULL) == -1) {
186 log_err("Could not add channel write event: %m");
192 STAILQ_REMOVE_HEAD(&chan->pktlist, next);
197 channel_timeout(chan, 0);
198 chan->oactive = false;
202 channel_read(int fd, short ev, void *arg)
204 channel_t *chan = arg;
208 pkt = packet_alloc(chan);
214 nr = read(fd, pkt->buf, chan->mru);
216 log_err("channel read error: %m");
221 if (nr == 0) { /* EOF */
222 log_debug("(fd#%d) EOF", fd);
229 if (chan->recv(pkt) == true)
230 channel_dispatch(pkt);
236 channel_dispatch(packet_t *pkt)
241 * This is simple routing. I'm not sure if its allowed by
242 * the PAN or BNEP specifications, but it seems logical
243 * to send unicast packets to connected destinations where
246 if (!ETHER_IS_MULTICAST(pkt->dst)) {
247 LIST_FOREACH(chan, &channel_list, next) {
248 if (chan == pkt->chan
249 || chan->state != CHANNEL_OPEN)
252 if (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) == 0) {
253 if (chan->qlen > CHANNEL_MAXQLEN)
254 log_notice("Queue overflow");
256 channel_put(chan, pkt);
263 LIST_FOREACH(chan, &channel_list, next) {
264 if (chan == pkt->chan
265 || chan->state != CHANNEL_OPEN)
268 if (chan->qlen > CHANNEL_MAXQLEN) {
269 log_notice("Queue overflow");
273 channel_put(chan, pkt);
278 channel_put(channel_t *chan, packet_t *pkt)
282 ph = pkthdr_alloc(pkt);
287 STAILQ_INSERT_TAIL(&chan->pktlist, ph, next);
290 channel_start(chan->fd, EV_WRITE, chan);
294 * Simple watchdog timer, only ticks when it is required and
295 * closes the channel down if it times out.
298 channel_timeout(channel_t *chan, int to)
300 static struct event ev;
305 chan->tick = (channel_tick + to) % 60;
307 if (channel_tick == 0) {
308 evtimer_set(&ev, channel_watchdog, &ev);
309 channel_watchdog(0, 0, &ev);
314 channel_watchdog(int fd, short ev, void *arg)
316 static struct timeval tv = { .tv_sec = 1 };
317 channel_t *chan, *next;
320 tick = (channel_tick % 60) + 1;
323 next = LIST_FIRST(&channel_list);
324 while ((chan = next) != NULL) {
325 next = LIST_NEXT(chan, next);
327 if (chan->tick == tick)
329 else if (chan->tick != 0)
333 if (channel_tick != 0 && evtimer_add(arg, &tv) < 0) {
334 log_err("Could not add watchdog event: %m");