Path: blob/main/usr.sbin/bluetooth/btpand/channel.c
107108 views
/* $NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */12/*-3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 2008 Iain Hibbert6* All rights reserved.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR18* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES19* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.20* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,21* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT22* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF26* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.27*/282930#include <sys/cdefs.h>31__RCSID("$NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");3233#include <sys/param.h>34#include <sys/ioctl.h>3536#include <libutil.h>37#include <unistd.h>38#define L2CAP_SOCKET_CHECKED39#include "btpand.h"4041static struct chlist channel_list;42static int channel_count;43static int channel_tick;4445static void channel_start(int, short, void *);46static void channel_read(int, short, void *);47static void channel_dispatch(packet_t *);48static void channel_watchdog(int, short, void *);4950void51channel_init(void)52{5354LIST_INIT(&channel_list);55}5657channel_t *58channel_alloc(void)59{60channel_t *chan;6162chan = malloc(sizeof(channel_t));63if (chan == NULL) {64log_err("%s() failed: %m", __func__);65return NULL;66}6768memset(chan, 0, sizeof(channel_t));69STAILQ_INIT(&chan->pktlist);70chan->state = CHANNEL_CLOSED;71LIST_INSERT_HEAD(&channel_list, chan, next);7273server_update(++channel_count);7475return chan;76}7778bool79channel_open(channel_t *chan, int fd)80{81int n;8283assert(chan->refcnt == 0);84assert(chan->state != CHANNEL_CLOSED);8586if (chan->mtu > 0) {87chan->sendbuf = malloc(chan->mtu);88if (chan->sendbuf == NULL) {89log_err("Could not malloc channel sendbuf: %m");90return false;91}92}9394n = 1;95if (ioctl(fd, FIONBIO, &n) == -1) {96log_err("Could not set non-blocking IO: %m");97return false;98}99100event_set(&chan->rd_ev, fd, EV_READ | EV_PERSIST, channel_read, chan);101if (event_add(&chan->rd_ev, NULL) == -1) {102log_err("Could not add channel read event: %m");103return false;104}105106event_set(&chan->wr_ev, fd, EV_WRITE, channel_start, chan);107108chan->refcnt++;109chan->fd = fd;110111log_debug("(fd#%d)", chan->fd);112113return true;114}115116void117channel_close(channel_t *chan)118{119pkthdr_t *ph;120121assert(chan->state != CHANNEL_CLOSED);122123log_debug("(fd#%d)", chan->fd);124125chan->state = CHANNEL_CLOSED;126event_del(&chan->rd_ev);127event_del(&chan->wr_ev);128close(chan->fd);129chan->refcnt--;130chan->tick = 0;131132while ((ph = STAILQ_FIRST(&chan->pktlist)) != NULL) {133STAILQ_REMOVE_HEAD(&chan->pktlist, next);134pkthdr_free(ph);135chan->qlen--;136}137138if (chan->pfh != NULL) {139pidfile_remove(chan->pfh);140chan->pfh = NULL;141}142143if (chan->refcnt == 0)144channel_free(chan);145}146147void148channel_free(channel_t *chan)149{150151assert(chan->refcnt == 0);152assert(chan->state == CHANNEL_CLOSED);153assert(chan->qlen == 0);154assert(STAILQ_EMPTY(&chan->pktlist));155156LIST_REMOVE(chan, next);157free(chan->pfilter);158free(chan->mfilter);159free(chan->sendbuf);160free(chan);161162server_update(--channel_count);163164if (server_limit == 0) {165log_info("connection closed, exiting");166exit(EXIT_SUCCESS);167}168}169170static void171channel_start(int fd, short ev, void *arg)172{173channel_t *chan = arg;174pkthdr_t *ph;175176chan->oactive = true;177178while (chan->qlen > 0) {179ph = STAILQ_FIRST(&chan->pktlist);180181channel_timeout(chan, 10);182if (chan->send(chan, ph->data) == false) {183if (event_add(&chan->wr_ev, NULL) == -1) {184log_err("Could not add channel write event: %m");185channel_close(chan);186}187return;188}189190STAILQ_REMOVE_HEAD(&chan->pktlist, next);191pkthdr_free(ph);192chan->qlen--;193}194195channel_timeout(chan, 0);196chan->oactive = false;197}198199static void200channel_read(int fd, short ev, void *arg)201{202channel_t *chan = arg;203packet_t *pkt;204ssize_t nr;205206pkt = packet_alloc(chan);207if (pkt == NULL) {208channel_close(chan);209return;210}211212nr = read(fd, pkt->buf, chan->mru);213if (nr == -1) {214log_err("channel read error: %m");215packet_free(pkt);216channel_close(chan);217return;218}219if (nr == 0) { /* EOF */220log_debug("(fd#%d) EOF", fd);221packet_free(pkt);222channel_close(chan);223return;224}225pkt->len = nr;226227if (chan->recv(pkt) == true)228channel_dispatch(pkt);229230packet_free(pkt);231}232233static void234channel_dispatch(packet_t *pkt)235{236channel_t *chan;237238/*239* This is simple routing. I'm not sure if its allowed by240* the PAN or BNEP specifications, but it seems logical241* to send unicast packets to connected destinations where242* possible.243*/244if (!ETHER_IS_MULTICAST(pkt->dst)) {245LIST_FOREACH(chan, &channel_list, next) {246if (chan == pkt->chan247|| chan->state != CHANNEL_OPEN)248continue;249250if (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) == 0) {251if (chan->qlen > CHANNEL_MAXQLEN)252log_notice("Queue overflow");253else254channel_put(chan, pkt);255256return;257}258}259}260261LIST_FOREACH(chan, &channel_list, next) {262if (chan == pkt->chan263|| chan->state != CHANNEL_OPEN)264continue;265266if (chan->qlen > CHANNEL_MAXQLEN) {267log_notice("Queue overflow");268continue;269}270271channel_put(chan, pkt);272}273}274275void276channel_put(channel_t *chan, packet_t *pkt)277{278pkthdr_t *ph;279280ph = pkthdr_alloc(pkt);281if (ph == NULL)282return;283284chan->qlen++;285STAILQ_INSERT_TAIL(&chan->pktlist, ph, next);286287if (!chan->oactive)288channel_start(chan->fd, EV_WRITE, chan);289}290291/*292* Simple watchdog timer, only ticks when it is required and293* closes the channel down if it times out.294*/295void296channel_timeout(channel_t *chan, int to)297{298static struct event ev;299300if (to == 0)301chan->tick = 0;302else303chan->tick = (channel_tick + to) % 60;304305if (channel_tick == 0) {306evtimer_set(&ev, channel_watchdog, &ev);307channel_watchdog(0, 0, &ev);308}309}310311static void312channel_watchdog(int fd, short ev, void *arg)313{314static struct timeval tv = { .tv_sec = 1 };315channel_t *chan, *next;316int tick;317318tick = (channel_tick % 60) + 1;319channel_tick = 0;320321next = LIST_FIRST(&channel_list);322while ((chan = next) != NULL) {323next = LIST_NEXT(chan, next);324325if (chan->tick == tick)326channel_close(chan);327else if (chan->tick != 0)328channel_tick = tick;329}330331if (channel_tick != 0 && evtimer_add(arg, &tv) < 0) {332log_err("Could not add watchdog event: %m");333exit(EXIT_FAILURE);334}335}336337338