hostapd: use new udebug ubus api to make debug rings configurable
[openwrt/staging/hauke.git] / package / network / services / hostapd / src / src / utils / ucode.c
index 896ef46e1f6f13e2b25b5c38c51c285fa9587efa..29c753c326905dcf0df18a98864f87f9f12bc472 100644 (file)
@@ -4,12 +4,71 @@
 #include "crypto/crypto.h"
 #include "crypto/sha1.h"
 #include "common/ieee802_11_common.h"
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#include <linux/nl80211.h>
 #include <libubox/uloop.h>
 #include <ucode/compiler.h>
+#include <udebug.h>
 
 static uc_value_t *registry;
 static uc_vm_t vm;
 static struct uloop_timeout gc_timer;
+static struct udebug ud;
+static struct udebug_buf ud_log, ud_nl[3];
+static const struct udebug_buf_meta meta_log = {
+       .name = "wpa_log",
+       .format = UDEBUG_FORMAT_STRING,
+};
+static const struct udebug_buf_meta meta_nl_ll = {
+       .name = "wpa_nl_ctrl",
+       .format = UDEBUG_FORMAT_PACKET,
+       .sub_format = UDEBUG_DLT_NETLINK,
+};
+static const struct udebug_buf_meta meta_nl_tx = {
+       .name = "wpa_nl_tx",
+       .format = UDEBUG_FORMAT_PACKET,
+       .sub_format = UDEBUG_DLT_NETLINK,
+};
+#define UDEBUG_FLAG_RX_FRAME   (1ULL << 0)
+static const struct udebug_buf_flag rx_flags[] = {
+       {  "rx_frame", UDEBUG_FLAG_RX_FRAME },
+};
+static const struct udebug_buf_meta meta_nl_rx = {
+       .name = "wpa_nl_rx",
+       .format = UDEBUG_FORMAT_PACKET,
+       .sub_format = UDEBUG_DLT_NETLINK,
+       .flags = rx_flags,
+       .n_flags = ARRAY_SIZE(rx_flags),
+};
+static struct udebug_ubus_ring udebug_rings[] = {
+       {
+               .buf = &ud_log,
+               .meta = &meta_log,
+               .default_entries = 1024,
+               .default_size = 64 * 1024
+       },
+       {
+               .buf = &ud_nl[0],
+               .meta = &meta_nl_rx,
+               .default_entries = 1024,
+               .default_size = 256 * 1024,
+       },
+       {
+               .buf = &ud_nl[1],
+               .meta = &meta_nl_tx,
+               .default_entries = 1024,
+               .default_size = 64 * 1024,
+       },
+       {
+               .buf = &ud_nl[2],
+               .meta = &meta_nl_ll,
+               .default_entries = 1024,
+               .default_size = 32 * 1024,
+       }
+};
+char *udebug_service;
+struct udebug_ubus ud_ubus;
 
 static void uc_gc_timer(struct uloop_timeout *timeout)
 {
@@ -251,6 +310,114 @@ int wpa_ucode_call_prepare(const char *fname)
        return 0;
 }
 
+static void udebug_printf_hook(int level, const char *fmt, va_list ap)
+{
+       udebug_entry_init(&ud_log);
+       udebug_entry_vprintf(&ud_log, fmt, ap);
+       udebug_entry_add(&ud_log);
+}
+
+static void udebug_hexdump_hook(int level, const char *title,
+                const void *data, size_t len)
+{
+       char *buf;
+
+       udebug_entry_init(&ud_log);
+       udebug_entry_printf(&ud_log, "%s - hexdump:", title);
+       buf = udebug_entry_append(&ud_log, NULL, 3 * len);
+       for (size_t i = 0; i < len; i++)
+               buf += sprintf(buf, " %02x", *(uint8_t *)(data + i));
+       udebug_entry_add(&ud_log);
+}
+
+static void udebug_netlink_hook(int tx, const void *data, size_t len)
+{
+       struct {
+               uint16_t pkttype;
+               uint16_t arphdr;
+               uint16_t _pad[5];
+               uint16_t proto;
+       } hdr = {
+               .pkttype = host_to_be16(tx ? 7 : 6),
+               .arphdr = host_to_be16(824),
+               .proto = host_to_be16(16),
+       };
+       const struct nlmsghdr *nlh = data;
+       const struct genlmsghdr *gnlh = data + NLMSG_HDRLEN;
+       struct udebug_buf *buf = &ud_nl[!!tx];
+
+       if (nlh->nlmsg_type == 0x10)
+               buf = &ud_nl[2];
+       else if (!tx && gnlh->cmd == NL80211_CMD_FRAME &&
+                !(udebug_buf_flags(buf) & UDEBUG_FLAG_RX_FRAME))
+               return;
+
+       if (!udebug_buf_valid(buf))
+               return;
+
+       udebug_entry_init(buf);
+       udebug_entry_append(buf, &hdr, sizeof(hdr));
+       udebug_entry_append(buf, data, len);
+       udebug_entry_add(buf);
+}
+
+static void
+wpa_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data,
+                 bool enabled)
+{
+       udebug_ubus_apply_config(&ud, udebug_rings, ARRAY_SIZE(udebug_rings),
+                                data, enabled);
+
+       if (udebug_buf_valid(&ud_log)) {
+               wpa_printf_hook = udebug_printf_hook;
+               wpa_hexdump_hook = udebug_hexdump_hook;
+       } else {
+               wpa_printf_hook = NULL;
+               wpa_hexdump_hook = NULL;
+       }
+
+       if (udebug_buf_valid(&ud_nl[0]) ||
+           udebug_buf_valid(&ud_nl[1]) ||
+           udebug_buf_valid(&ud_nl[2]))
+               wpa_netlink_hook = udebug_netlink_hook;
+       else
+               wpa_netlink_hook = NULL;
+}
+
+uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs)
+{
+       uc_value_t *name = uc_fn_arg(0);
+       uc_value_t *ubus = uc_fn_arg(1);
+       static bool enabled = false;
+       struct ubus_context *ctx;
+       bool cur_en;
+
+       cur_en = ucv_type(name) == UC_STRING;
+       ctx = ucv_resource_data(ubus, "ubus.connection");
+       if (!ctx)
+               cur_en = false;
+
+       if (enabled == cur_en)
+               return ucv_boolean_new(true);
+
+       enabled = cur_en;
+       if (enabled) {
+               udebug_service = strdup(ucv_string_get(name));
+               udebug_init(&ud);
+               udebug_auto_connect(&ud, NULL);
+               udebug_ubus_init(&ud_ubus, ctx, udebug_service, wpa_udebug_config);
+       } else {
+               udebug_ubus_free(&ud_ubus);
+               for (size_t i = 0; i < ARRAY_SIZE(udebug_rings); i++)
+                       if (udebug_buf_valid(udebug_rings[i].buf))
+                               udebug_buf_free(udebug_rings[i].buf);
+               udebug_free(&ud);
+               free(udebug_service);
+       }
+
+       return ucv_boolean_new(true);
+}
+
 uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
 {
        uc_value_t *global = uc_resource_new(global_type, NULL);
@@ -298,9 +465,15 @@ uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
 uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
 {
        uc_value_t *val = wpa_ucode_registry_get(reg, idx);
+       void **dataptr;
+
+       if (!val)
+               return NULL;
 
-       if (val)
-               ucv_array_set(reg, idx - 1, NULL);
+       ucv_array_set(reg, idx - 1, NULL);
+       dataptr = ucv_resource_dataptr(val, NULL);
+       if (dataptr)
+               *dataptr = NULL;
 
        return val;
 }