diff --git a/net/respondd-airtime/Makefile b/net/respondd-airtime/Makefile new file mode 100644 index 0000000..9d094d3 --- /dev/null +++ b/net/respondd-airtime/Makefile @@ -0,0 +1,40 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=respondd-airtime +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) +PKG_BUILD_DEPENDS := respondd + +include $(GLUONDIR)/include/package.mk + +define Package/respondd-airtime + SECTION:=net + CATEGORY:=Network + TITLE:=Add airtime to respondd + DEPENDS:=+respondd +libnl-tiny +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Configure +endef + + +TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny + +define Build/Compile + CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) +endef + + +define Package/respondd-airtime/install + $(INSTALL_DIR) $(1)/lib/gluon/respondd + $(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/airtime.so +endef + +$(eval $(call BuildPackage,respondd-airtime)) diff --git a/net/respondd-airtime/src/.gitignore b/net/respondd-airtime/src/.gitignore new file mode 100644 index 0000000..53e01c8 --- /dev/null +++ b/net/respondd-airtime/src/.gitignore @@ -0,0 +1,2 @@ +*.so +demo diff --git a/net/respondd-airtime/src/Makefile b/net/respondd-airtime/src/Makefile new file mode 100644 index 0000000..1ec8013 --- /dev/null +++ b/net/respondd-airtime/src/Makefile @@ -0,0 +1,22 @@ +ifeq ($(origin CC),default) +CC = gcc +endif + +# standard compliance +CFLAGS += -std=c99 + +# warnings +CFLAGS += -Wall -Wextra -Wformat=2 -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS += -pedantic + +all: respondd.so + +# sudo apt install libnl-3-dev +demo: demo.c airtime.c airtime.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -fPIC -D_GNU_SOURCE -lnl-tiny -o $@ airtime.c demo.c $(LDLIBS) + +respondd.so: respondd.c airtime.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -DGLUON -lnl-tiny -o $@ airtime.c respondd.c $(LDLIBS) + +clean: + rm -rf *.so demo diff --git a/net/respondd-airtime/src/airtime.c b/net/respondd-airtime/src/airtime.c new file mode 100644 index 0000000..cd1246d --- /dev/null +++ b/net/respondd-airtime/src/airtime.c @@ -0,0 +1,170 @@ +/* + Copyright (c) 2016, Julian Kornberger + Martin Müller + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "airtime.h" + +static struct airtime cur_airtime = { + { .frequency = 0 }, + { .frequency = 0 }, +}; + +/* + * Excerpt from nl80211.h: + * enum nl80211_survey_info - survey information + * + * These attribute types are used with %NL80211_ATTR_SURVEY_INFO + * when getting information about a survey. + * + * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved + * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel + * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) + * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used + * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio + * spent on this channel + * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * channel was sensed busy (either due to activity or energy detect) + * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * channel was sensed busy + * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent + * receiving data + * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent + * transmitting data + * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number + * currently defined + * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use + */ + +static int survey_airtime_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh; + struct airtime_result *result; + + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + }; + + result = (struct airtime_result *) arg; + +#define CHECK(x) { if (!(x)) goto abort; } + + CHECK(!(gnlh = nlmsg_data(nlmsg_hdr(msg)))); + CHECK(nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),genlmsg_attrlen(gnlh, 0), NULL)); + CHECK(!tb[NL80211_ATTR_SURVEY_INFO]); + CHECK(nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, tb[NL80211_ATTR_SURVEY_INFO], survey_policy)); + + // Channel active? + CHECK(!sinfo[NL80211_SURVEY_INFO_IN_USE]); + +#undef CHECK + + uint64_t frequency = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + + if (frequency != result->frequency) { + // channel changed, restart at zero + result->frequency = frequency; + result->active_time.offset = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]); + result->busy_time.offset = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]); + result->rx_time.offset = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]); + result->tx_time.offset = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]); + result->active_time.current = 0; + result->busy_time.current = 0; + result->rx_time.current = 0; + result->tx_time.current = 0; + } else { + result->active_time.current = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) - result->active_time.offset; + result->busy_time.current = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) - result->busy_time.offset; + result->rx_time.current = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) - result->rx_time.offset; + result->tx_time.current = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) - result->tx_time.offset; + } + + result->noise = nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + +abort: + return NL_SKIP; +} + +static int get_airtime_for_interface(struct airtime_result *result, const char *interface) { + int error = 0; + int ctrl, ifx, flags; + struct nl_sock *sk = NULL; + struct nl_msg *msg = NULL; + enum nl80211_commands cmd; + +#define CHECK(x) { if (!(x)) { printf("airtime.c: error on line %d\n", __LINE__); error = 1; goto out; } } + + CHECK(sk = nl_socket_alloc()); + CHECK(genl_connect(sk) >= 0); + + CHECK(ctrl = genl_ctrl_resolve(sk, NL80211_GENL_NAME)); + CHECK(nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, survey_airtime_handler, result) == 0); + CHECK(msg = nlmsg_alloc()); + + /* device does not exist */ + if (!(ifx = if_nametoindex(interface))){ + error = -1; + goto out; + } + + cmd = NL80211_CMD_GET_SURVEY; + flags = 0; + flags |= NLM_F_DUMP; + + /* TODO: check return? */ + genlmsg_put(msg, 0, 0, ctrl, 0, flags, cmd, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifx); + + CHECK(nl_send_auto_complete(sk, msg) >= 0); + CHECK(nl_recvmsgs_default(sk) >= 0); + +#undef CHECK + +nla_put_failure: +out: + if (msg) + nlmsg_free(msg); + + if (sk) + nl_socket_free(sk); + + return error; +} + +struct airtime* get_airtime(const char *wifi_0_dev, const char *wifi_1_dev) { + get_airtime_for_interface(&cur_airtime.radio0, wifi_0_dev); + get_airtime_for_interface(&cur_airtime.radio1, wifi_1_dev); + + return &cur_airtime; +} diff --git a/net/respondd-airtime/src/airtime.h b/net/respondd-airtime/src/airtime.h new file mode 100644 index 0000000..c6cba16 --- /dev/null +++ b/net/respondd-airtime/src/airtime.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +struct airtime_time { + uint64_t current; + uint64_t offset; +}; + +struct airtime_result { + uint32_t frequency; + uint8_t noise; + struct airtime_time active_time; + struct airtime_time busy_time; + struct airtime_time rx_time; + struct airtime_time tx_time; +}; + +struct airtime { + struct airtime_result radio0; + struct airtime_result radio1; +}; + +struct airtime* get_airtime(const char *radio0, const char *radio1); diff --git a/net/respondd-airtime/src/demo.c b/net/respondd-airtime/src/demo.c new file mode 100644 index 0000000..2cd3b7a --- /dev/null +++ b/net/respondd-airtime/src/demo.c @@ -0,0 +1,37 @@ +#include +#include /* sleep */ + +#include "airtime.h" +#if GLUON +static const char const *wifi_0_dev = "client0"; +static const char const *wifi_1_dev = "client1"; + +#else +static const char const *wifi_0_dev = "wlan0"; +static const char const *wifi_1_dev = "wlan1"; + +#endif /* GLUON */ + +void print_result(struct airtime_result *); + +int main() { + struct airtime *a; + + while (1) { + a = get_airtime(wifi_0_dev,wifi_1_dev); + print_result(&a->radio0); + print_result(&a->radio1); + sleep(1); + } +} + +void print_result(struct airtime_result *result){ + printf("freq=%d\tnoise=%d\tbusy=%lld\tactive=%lld\trx=%lld\ttx=%lld\n", + result->frequency, + result->noise, + result->busy_time.current, + result->active_time.current, + result->rx_time.current, + result->tx_time.current + ); +} diff --git a/net/respondd-airtime/src/respondd.c b/net/respondd-airtime/src/respondd.c new file mode 100644 index 0000000..494add5 --- /dev/null +++ b/net/respondd-airtime/src/respondd.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include + +#include "airtime.h" + +#if GLUON +static const char const *wifi_0_dev = "client0"; +static const char const *wifi_1_dev = "client1"; + +#else +static const char const *wifi_0_dev = "wlan0"; +static const char const *wifi_1_dev = "wlan1"; + +#endif /* GLUON */ + + +void fill_airtime_json(struct airtime_result *air, struct json_object* wireless){ + struct json_object *ret = NULL, *obj = NULL; + + obj = json_object_new_object(); + if(!obj) + goto error; +#define JSON_ADD_INT64(value,key) {ret = json_object_new_int64(value); json_object_object_add(obj,key,ret);} + ret = json_object_new_int(air->frequency); + if(!ret) + goto error; + json_object_object_add(obj,"frequency",ret); + + JSON_ADD_INT64(air->active_time.current,"active") + JSON_ADD_INT64(air->busy_time.current,"busy") + JSON_ADD_INT64(air->rx_time.current,"rx") + JSON_ADD_INT64(air->tx_time.current,"tx") + + ret = json_object_new_int(air->noise); + json_object_object_add(obj,"noise",ret); + +error: + if(air->frequency >= 2400 && air->frequency < 2500) + json_object_object_add(wireless, "airtime24", obj); + else if (air->frequency >= 5000 && air->frequency < 6000) + json_object_object_add(wireless, "airtime5", obj); +} + +static struct json_object *respondd_provider_statistics(void) { + struct airtime *a = NULL; + struct json_object *ret = NULL, *wireless = NULL; + + wireless = json_object_new_object(); + if (!wireless) + return NULL; + + ret = json_object_new_object(); + if (!ret) + return NULL; + + a = get_airtime(wifi_0_dev,wifi_1_dev); + if (!a) + goto end; + + if (a->radio0.frequency) + fill_airtime_json(&a->radio0,wireless); + + if (a->radio1.frequency) + fill_airtime_json(&a->radio1,wireless); + +end: + json_object_object_add(ret, "wireless", wireless); + return ret; +} + +const struct respondd_provider_info respondd_providers[] = { + {"statistics", respondd_provider_statistics}, + {0, 0}, +};