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