1 /* $NetBSD: channel.c,v 1.2 2009/05/02 20:07:51 plunky Exp $ */
4 * Copyright (c) 2008 Iain Hibbert
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
28 #include <sys/cdefs.h>
29 __RCSID("$NetBSD: channel.c,v 1.2 2009/05/02 20:07:51 plunky Exp $");
31 #include <sys/ioctl.h>
37 static struct chlist channel_list
;
38 static int channel_tick
;
40 static void channel_start(int, short, void *);
41 static void channel_read(int, short, void *);
42 static void channel_dispatch(packet_t
*);
43 static void channel_watchdog(int, short, void *);
49 LIST_INIT(&channel_list
);
57 chan
= malloc(sizeof(channel_t
));
59 log_err("%s() failed: %m", __func__
);
63 memset(chan
, 0, sizeof(channel_t
));
64 STAILQ_INIT(&chan
->pktlist
);
65 chan
->state
= CHANNEL_CLOSED
;
66 LIST_INSERT_HEAD(&channel_list
, chan
, next
);
72 channel_open(channel_t
*chan
, int fd
)
76 assert(chan
->refcnt
== 0);
77 assert(chan
->state
!= CHANNEL_CLOSED
);
78 assert(chan
->send
!= NULL
);
79 assert(chan
->recv
!= NULL
);
80 assert(chan
->down
!= NULL
);
83 chan
->sendbuf
= malloc(chan
->mtu
);
84 if (chan
->sendbuf
== NULL
) {
85 log_err("Could not malloc channel sendbuf: %m");
91 if (ioctl(fd
, FIONBIO
, &n
) == -1) {
92 log_err("Could not set non-blocking IO: %m");
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");
102 event_set(&chan
->wr_ev
, fd
, EV_WRITE
, channel_start
, chan
);
107 log_debug("(fd#%d)", chan
->fd
);
113 channel_close(channel_t
*chan
)
117 assert(chan
->state
!= CHANNEL_CLOSED
);
119 log_debug("(fd#%d)", chan
->fd
);
121 chan
->state
= CHANNEL_CLOSED
;
122 event_del(&chan
->rd_ev
);
123 event_del(&chan
->wr_ev
);
128 while ((ph
= STAILQ_FIRST(&chan
->pktlist
)) != NULL
) {
129 STAILQ_REMOVE_HEAD(&chan
->pktlist
, next
);
134 if (chan
->refcnt
== 0)
139 channel_free(channel_t
*chan
)
142 assert(chan
->refcnt
== 0);
143 assert(chan
->state
== CHANNEL_CLOSED
);
144 assert(chan
->qlen
== 0);
145 assert(STAILQ_EMPTY(&chan
->pktlist
));
147 LIST_REMOVE(chan
, next
);
155 channel_start(int fd
, short ev
, void *arg
)
157 channel_t
*chan
= arg
;
160 chan
->oactive
= true;
162 while (chan
->qlen
> 0) {
163 ph
= STAILQ_FIRST(&chan
->pktlist
);
165 channel_timeout(chan
, 10);
166 if (chan
->send(chan
, ph
->data
) == false) {
167 if (event_add(&chan
->wr_ev
, NULL
) == -1) {
168 log_err("Could not add channel write event: %m");
174 STAILQ_REMOVE_HEAD(&chan
->pktlist
, next
);
179 channel_timeout(chan
, 0);
180 chan
->oactive
= false;
184 channel_read(int fd
, short ev
, void *arg
)
186 channel_t
*chan
= arg
;
190 pkt
= packet_alloc(chan
);
196 nr
= read(fd
, pkt
->buf
, chan
->mru
);
198 log_err("channel read error: %m");
203 if (nr
== 0) { /* EOF */
204 log_debug("(fd#%d) EOF", fd
);
211 if (chan
->recv(pkt
) == true)
212 channel_dispatch(pkt
);
218 channel_dispatch(packet_t
*pkt
)
223 * This is simple routing. I'm not sure if its allowed by
224 * the PAN or BNEP specifications, but it seems logical
225 * to send unicast packets to connected destinations where
228 if (!ETHER_IS_MULTICAST(pkt
->dst
)) {
229 LIST_FOREACH(chan
, &channel_list
, next
) {
230 if (chan
== pkt
->chan
231 || chan
->state
!= CHANNEL_OPEN
)
234 if (memcmp(pkt
->dst
, chan
->raddr
, ETHER_ADDR_LEN
) == 0) {
235 if (chan
->qlen
> CHANNEL_MAXQLEN
)
236 log_notice("Queue overflow");
238 channel_put(chan
, pkt
);
245 LIST_FOREACH(chan
, &channel_list
, next
) {
246 if (chan
== pkt
->chan
247 || chan
->state
!= CHANNEL_OPEN
)
250 if (chan
->qlen
> CHANNEL_MAXQLEN
) {
251 log_notice("Queue overflow");
255 channel_put(chan
, pkt
);
260 channel_put(channel_t
*chan
, packet_t
*pkt
)
264 ph
= pkthdr_alloc(pkt
);
269 STAILQ_INSERT_TAIL(&chan
->pktlist
, ph
, next
);
272 channel_start(chan
->fd
, EV_WRITE
, chan
);
276 * Simple watchdog timer, only ticks when it is required and
277 * closes the channel down if it times out.
280 channel_timeout(channel_t
*chan
, int to
)
282 static struct event ev
;
287 chan
->tick
= (channel_tick
+ to
) % 60;
289 if (channel_tick
== 0) {
290 evtimer_set(&ev
, channel_watchdog
, &ev
);
291 channel_watchdog(0, 0, &ev
);
296 channel_watchdog(int fd
, short ev
, void *arg
)
298 static struct timeval tv
= { .tv_sec
= 1 };
299 channel_t
*chan
, *next
;
302 tick
= (channel_tick
% 60) + 1;
305 next
= LIST_FIRST(&channel_list
);
306 while ((chan
= next
) != NULL
) {
307 next
= LIST_NEXT(chan
, next
);
309 if (chan
->tick
== tick
)
311 else if (chan
->tick
!= 0)
315 if (channel_tick
!= 0 && evtimer_add(arg
, &tv
) < 0) {
316 log_err("Could not add watchdog event: %m");