From d60429933c3139134d45885b386c6832bc7fcccf Mon Sep 17 00:00:00 2001 From: Martin/Geno Date: Wed, 10 Apr 2019 09:53:47 +0200 Subject: [PATCH] create independent respondd-wifi package --- net/respondd-wifi/Makefile | 27 ++++++++ net/respondd-wifi/README.md | 26 ++++++++ net/respondd-wifi/src/Makefile | 16 +++++ net/respondd-wifi/src/clients.c | 16 +++++ net/respondd-wifi/src/clients.h | 5 ++ net/respondd-wifi/src/ifaces.c | 41 ++++++++++++ net/respondd-wifi/src/ifaces.h | 13 ++++ net/respondd-wifi/src/neighbours.c | 86 ++++++++++++++++++++++++ net/respondd-wifi/src/neighbours.h | 6 ++ net/respondd-wifi/src/netlink.c | 73 ++++++++++++++++++++ net/respondd-wifi/src/netlink.h | 8 +++ net/respondd-wifi/src/respondd.c | 104 +++++++++++++++++++++++++++++ 12 files changed, 421 insertions(+) create mode 100644 net/respondd-wifi/Makefile create mode 100644 net/respondd-wifi/README.md create mode 100644 net/respondd-wifi/src/Makefile create mode 100644 net/respondd-wifi/src/clients.c create mode 100644 net/respondd-wifi/src/clients.h create mode 100644 net/respondd-wifi/src/ifaces.c create mode 100644 net/respondd-wifi/src/ifaces.h create mode 100644 net/respondd-wifi/src/neighbours.c create mode 100644 net/respondd-wifi/src/neighbours.h create mode 100644 net/respondd-wifi/src/netlink.c create mode 100644 net/respondd-wifi/src/netlink.h create mode 100644 net/respondd-wifi/src/respondd.c diff --git a/net/respondd-wifi/Makefile b/net/respondd-wifi/Makefile new file mode 100644 index 0000000..adfdf74 --- /dev/null +++ b/net/respondd-wifi/Makefile @@ -0,0 +1,27 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=respondd-wifi +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_LICENSE:=BSD-2-Clause + +PKG_BUILD_DEPENDS := respondd + +include $(INCLUDE_DIR)/package.mk + +TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny + +define Package/respondd-wifi/Default + SECTION:=net + CATEGORY:=Network + TITLE:=Add wifi statistics to respondd + DEPENDS:=+respondd +libnl-tiny +endef + +define Package/respondd-wifi/install + $(INSTALL_DIR) $(1)/usr/lib/respondd + $(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/usr/lib/respondd/wifi.so +endef + +$(eval $(call BuildPackage,respondd-wifi)) diff --git a/net/respondd-wifi/README.md b/net/respondd-wifi/README.md new file mode 100644 index 0000000..73c4a20 --- /dev/null +++ b/net/respondd-wifi/README.md @@ -0,0 +1,26 @@ +This module adds a respondd wifi usage statistics provider. +The format is the following: + +```json +{ + "statistics": { + "clients":{ + "wifi24": 3, + "wifi5": 7 + }, + }, + "neighbours": { + "wifi":{ + "00:11:22:33:44:55:66":{ + "frequency": 5220, + "neighbours":{ + "33:22:33:11:22:44":{ + "signal": 191, + "inactive": 50 + } + } + } + } + } +} +``` diff --git a/net/respondd-wifi/src/Makefile b/net/respondd-wifi/src/Makefile new file mode 100644 index 0000000..8afb817 --- /dev/null +++ b/net/respondd-wifi/src/Makefile @@ -0,0 +1,16 @@ +# standard compliance +CFLAGS += -std=c99 + +# warnings +CFLAGS += -Wall -Wextra -Wformat=2 -Wshadow -Wpointer-arith +CFLAGS += -pedantic + +all: respondd.so + +%.c: %.h + +respondd.so: netlink.c ifaces.c airtime.c clients.c neighbours.c respondd.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -lnl-tiny -o $@ $^ $(LDLIBS) + +clean: + rm -rf *.so diff --git a/net/respondd-wifi/src/clients.c b/net/respondd-wifi/src/clients.c new file mode 100644 index 0000000..f87433a --- /dev/null +++ b/net/respondd-wifi/src/clients.c @@ -0,0 +1,16 @@ +#include +#include + +#include "netlink.h" +#include "clients.h" + +static int station_client_handler(struct nl_msg *msg, void *arg) { + int *count = (int *) arg; + + (*count)++; + + return NL_SKIP; +} +bool get_client_counts(int *count, int ifx) { + return nl_send_dump(station_client_handler, count, NL80211_CMD_GET_STATION, ifx); +} diff --git a/net/respondd-wifi/src/clients.h b/net/respondd-wifi/src/clients.h new file mode 100644 index 0000000..d946aef --- /dev/null +++ b/net/respondd-wifi/src/clients.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +__attribute__((visibility("hidden"))) bool get_client_counts(int *count, int ifx); diff --git a/net/respondd-wifi/src/ifaces.c b/net/respondd-wifi/src/ifaces.c new file mode 100644 index 0000000..8a3d06d --- /dev/null +++ b/net/respondd-wifi/src/ifaces.c @@ -0,0 +1,41 @@ +#include +#include + +#include "ifaces.h" +#include "netlink.h" + + +static int iface_dump_handler(struct nl_msg *msg, void *arg) { + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct iface_list **last_next; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_WIPHY] || !tb[NL80211_ATTR_IFINDEX]) + goto skip; + + // TODO fix add to head list - instatt find last item + for (last_next = arg; *last_next != NULL; last_next = &(*last_next)->next) {} + + *last_next = malloc(sizeof(**last_next)); + if (!*last_next) + goto skip; + (*last_next)->next = NULL; + (*last_next)->ifx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + (*last_next)->frequency = tb[NL80211_ATTR_WIPHY_FREQ] ? nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]) : 0; + (*last_next)->type = tb[NL80211_ATTR_IFTYPE] ? nla_get_u32(tb[NL80211_ATTR_IFTYPE]) : 0; + + if(tb[NL80211_ATTR_MAC]) { + mac_addr_n2a((*last_next)->mac_addr, nla_data(tb[NL80211_ATTR_MAC])); + } + +skip: + return NL_SKIP; +} + +struct iface_list *get_ifaces() { + struct iface_list *ifaces = NULL; + nl_send_dump(&iface_dump_handler, &ifaces, NL80211_CMD_GET_INTERFACE, 0); + return ifaces; +} diff --git a/net/respondd-wifi/src/ifaces.h b/net/respondd-wifi/src/ifaces.h new file mode 100644 index 0000000..33bc53c --- /dev/null +++ b/net/respondd-wifi/src/ifaces.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +struct iface_list { + int ifx; + char mac_addr[20]; + int type; + int frequency; + struct iface_list *next; +}; + +__attribute__((visibility("hidden"))) struct iface_list *get_ifaces(); diff --git a/net/respondd-wifi/src/neighbours.c b/net/respondd-wifi/src/neighbours.c new file mode 100644 index 0000000..89b7791 --- /dev/null +++ b/net/respondd-wifi/src/neighbours.c @@ -0,0 +1,86 @@ +#include +#include + +#include "netlink.h" +#include "neighbours.h" + +static const char * neighbours_names[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_SIGNAL] = "signal", + [NL80211_STA_INFO_INACTIVE_TIME] = "inactive", +}; + +static int station_neighbours_handler(struct nl_msg *msg, void *arg) { + struct json_object *neighbour, *json = (struct json_object *) arg; + + neighbour = json_object_new_object(); + if (!neighbour) + goto abort; + + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *station_info = nla_find(genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NL80211_ATTR_STA_INFO); + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + + if (!station_info) { + fputs("respondd-wifi: station data missing in netlink message\n", stderr); + json_object_put(neighbour); + goto abort; + } + + char mac_addr[20]; + + if (!tb[NL80211_ATTR_MAC]) { + json_object_put(neighbour); + goto abort; + } + mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MAC])); + + int rem; + struct nlattr *nla; + nla_for_each_nested(nla, station_info, rem) { + int type = nla_type(nla); + + if (type > NL80211_STA_INFO_MAX) + continue; + + if (!neighbours_names[type]) + continue; + + struct json_object *data_json = NULL; + switch (nla_len(nla)) { + case sizeof(uint64_t): + data_json = json_object_new_int64(nla_get_u64(nla)); + break; + case sizeof(uint32_t): + data_json = json_object_new_int(nla_get_u32(nla)); + break; + case sizeof(uint8_t): + data_json = json_object_new_int(nla_get_u8(nla)); + break; + default: + fprintf(stderr, "respondd-wifi: Unexpected NL attribute length: %d\n", nla_len(nla)); + } + if (data_json) + json_object_object_add(neighbour, neighbours_names[type], data_json); + } + json_object_object_add(json, mac_addr, neighbour); + +abort: + return NL_SKIP; +} + +bool get_neighbours(struct json_object *result, int ifx) { + struct json_object *neighbours; + neighbours = json_object_new_object(); + if (!neighbours) + return false; + if(!nl_send_dump(station_neighbours_handler, neighbours, NL80211_CMD_GET_STATION, ifx)) { + json_object_put(neighbours); + return false; + } + json_object_object_add(result, "neighbours", neighbours); + return true; + +} diff --git a/net/respondd-wifi/src/neighbours.h b/net/respondd-wifi/src/neighbours.h new file mode 100644 index 0000000..28dcf49 --- /dev/null +++ b/net/respondd-wifi/src/neighbours.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +__attribute__((visibility("hidden"))) bool get_neighbours(struct json_object *result, int ifx); diff --git a/net/respondd-wifi/src/netlink.c b/net/respondd-wifi/src/netlink.c new file mode 100644 index 0000000..833802a --- /dev/null +++ b/net/respondd-wifi/src/netlink.c @@ -0,0 +1,73 @@ +#include + +#include +#include +#include +#include + +#include "netlink.h" + + +void mac_addr_n2a(char *mac_addr, unsigned char *arg) { + sprintf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]); +} + + +bool nl_send_dump(nl_recvmsg_msg_cb_t cb, void *cb_arg, int cmd, uint32_t cmd_arg) { + bool ok = false; + int ret; + int ctrl; + struct nl_sock *sk = NULL; + struct nl_msg *msg = NULL; + + +#define ERR(...) { fprintf(stderr, "respondd-wifi: " __VA_ARGS__); goto out; } + + sk = nl_socket_alloc(); + if (!sk) + ERR("nl_socket_alloc() failed\n"); + + ret = genl_connect(sk); + if (ret < 0) + ERR("genl_connect() returned %d\n", ret); + + ctrl = genl_ctrl_resolve(sk, NL80211_GENL_NAME); + if (ctrl < 0) + ERR("genl_ctrl_resolve() returned %d\n", ctrl); + + ret = nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, cb, cb_arg); + if (ret != 0) + ERR("nl_socket_modify_cb() returned %d\n", ret); + + msg = nlmsg_alloc(); + if (!msg) + ERR("nlmsg_alloc() failed\n"); + + if (!genlmsg_put(msg, 0, 0, ctrl, 0, NLM_F_DUMP, cmd, 0)) + ERR("genlmsg_put() failed while putting cmd %d\n", ret, cmd); + + if (cmd_arg != 0) + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, cmd_arg); + + ret = nl_send_auto_complete(sk, msg); + if (ret < 0) + ERR("nl_send_auto() returned %d while sending cmd %d with cmd_arg=%"PRIu32"\n", ret, cmd, cmd_arg); + + ret = nl_recvmsgs_default(sk); + if (ret < 0) + ERR("nl_recv_msgs_default() returned %d while receiving cmd %d with cmd_arg=%"PRIu32"\n", ret, cmd, cmd_arg); + +#undef ERR + + ok = true; + +nla_put_failure: +out: + if (msg) + nlmsg_free(msg); + + if (sk) + nl_socket_free(sk); + + return ok; +} diff --git a/net/respondd-wifi/src/netlink.h b/net/respondd-wifi/src/netlink.h new file mode 100644 index 0000000..c8f6898 --- /dev/null +++ b/net/respondd-wifi/src/netlink.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include + +__attribute__((visibility("hidden"))) void mac_addr_n2a(char *mac_addr, unsigned char *arg); +__attribute__((visibility("hidden"))) bool nl_send_dump(nl_recvmsg_msg_cb_t cb, void *cb_arg, int cmd, uint32_t cmd_arg); diff --git a/net/respondd-wifi/src/respondd.c b/net/respondd-wifi/src/respondd.c new file mode 100644 index 0000000..e3abff0 --- /dev/null +++ b/net/respondd-wifi/src/respondd.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include + +#include + +#include "clients.h" +#include "neighbours.h" +#include "ifaces.h" + +static struct json_object *respondd_provider_statistics(void) { + struct json_object *result, *clients; + struct iface_list *ifaces; + int wifi24 = 0, wifi5 = 0, clients_count; + + result = json_object_new_object(); + if (!result) + return NULL; + + clients = json_object_new_object(); + if (!clients) { + json_object_put(wireless); + json_object_put(result); + return NULL; + } + + ifaces = get_ifaces(); + while (ifaces != NULL) { + if(ifaces->type != NL80211_IFTYPE_AP) + goto next_statistics; + + clients_count = 0; + get_client_counts(&clients_count, ifaces->ifx); + if (ifaces->frequency < 5000) + wifi24 += clients_count; + if (ifaces->frequency > 5000) + wifi5 += clients_count; +next_statistics: ; + void *freeptr = ifaces; + ifaces = ifaces->next; + free(freeptr); + } + + //TODO maybe skip: if (wifi24 > 0 || wifi5 > 0) { + json_object_object_add(clients, "wifi24", json_object_new_int(wifi24)); + json_object_object_add(clients, "wifi5", json_object_new_int(wifi5)); + json_object_object_add(result, "clients", clients); + //} + + return result; +} + +static struct json_object *respondd_provider_neighbours(void) { + struct json_object *result, *wireless, *station; + struct iface_list *ifaces; + + result = json_object_new_object(); + if (!result) + return NULL; + + wireless = json_object_new_object(); + if (!wireless) { + json_object_put(result); + return NULL; + } + + ifaces = get_ifaces(); + + while (ifaces != NULL) { + + if(ifaces->type != NL80211_IFTYPE_ADHOC) + goto next_neighbours; + + station = json_object_new_object(); + if (!station) + goto next_neighbours; + + if (!get_neighbours(station, ifaces->ifx)) { + json_object_put(station); + goto next_neighbours; + } + + if (ifaces->frequency) + json_object_object_add(station, "frequency", json_object_new_int(ifaces->frequency)); + + json_object_object_add(wireless, ifaces->mac_addr, station); + +next_neighbours: ; + void *freeptr = ifaces; + ifaces = ifaces->next; + free(freeptr); + } + + + json_object_object_add(result, "wifi", wireless); + return result; +} + +const struct respondd_provider_info respondd_providers[] = { + {"statistics", respondd_provider_statistics}, + {"neighbours", respondd_provider_neighbours}, + {0, 0}, +};