Files
usteer/event.c
2021-07-06 14:28:28 +02:00

283 lines
7.5 KiB
C

/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) 2020 embedd.ch
* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2020 John Crispin <john@phrozen.org>
*/
#include "usteer.h"
#include "event.h"
#define UEV_LOG_MAXLEN 256
static struct blob_buf b;
static const char * const uev_name[] = {
[UEV_PROBE_REQ_ACCEPT] = "probe_req_accept",
[UEV_PROBE_REQ_DENY] = "probe_req_deny",
[UEV_AUTH_REQ_ACCEPT] = "auth_req_accept",
[UEV_AUTH_REQ_DENY] = "auth_req_deny",
[UEV_ASSOC_REQ_ACCEPT] = "assoc_req_accept",
[UEV_ASSOC_REQ_DENY] = "assoc_req_deny",
[UEV_LOAD_KICK_TRIGGER] = "load_kick_trigger",
[UEV_LOAD_KICK_RESET] = "load_kick_reset",
[UEV_LOAD_KICK_MIN_CLIENTS] = "load_kick_min_clients",
[UEV_LOAD_KICK_NO_CLIENT] = "load_kick_no_client",
[UEV_LOAD_KICK_CLIENT] = "load_kick_client",
[UEV_SIGNAL_KICK] = "signal_kick",
};
static const char * const uev_reason[] = {
[UEV_REASON_NONE] = "none",
[UEV_REASON_RETRY_EXCEEDED] = "retry_exceeded",
[UEV_REASON_LOW_SIGNAL] = "low_signal",
[UEV_REASON_CONNECT_DELAY] = "connect_delay",
[UEV_REASON_BETTER_CANDIDATE] = "better_candidate",
};
static const char * const uev_select_reason[] = {
[UEV_SELECT_REASON_NUM_ASSOC] = "n_assoc",
[UEV_SELECT_REASON_SIGNAL] = "signal",
[UEV_SELECT_REASON_LOAD] = "load",
};
static void
usteer_event_add_node_status(struct usteer_node *node)
{
blobmsg_add_u32(&b, "load", node->load);
blobmsg_add_u32(&b, "assoc", node->n_assoc);
}
static void
usteer_event_send_ubus(struct uevent *ev)
{
void *c;
int i;
if (!usteer_obj.has_subscribers)
return;
blob_buf_init(&b, 0);
if (ev->node_local)
blobmsg_add_string(&b, "node", usteer_node_name(ev->node_local));
if (ev->sta)
blobmsg_printf(&b, "sta", MAC_ADDR_FMT, MAC_ADDR_DATA(ev->sta->addr));
if (ev->si_cur)
blobmsg_add_u32(&b, "signal", (int32_t)ev->si_cur->signal);
if (ev->reason)
blobmsg_add_string(&b, "reason", uev_reason[ev->reason]);
if (ev->threshold.ref) {
c = blobmsg_open_array(&b, "threshold");
blobmsg_add_u32(&b, NULL, ev->threshold.cur);
blobmsg_add_u32(&b, NULL, ev->threshold.ref);
blobmsg_close_array(&b, c);
}
if (ev->select_reasons) {
c = blobmsg_open_array(&b, "select_reason");
for (i = 0; i < ARRAY_SIZE(uev_select_reason); i++) {
if (!(ev->select_reasons & (1 << i)) ||
!uev_select_reason[i])
continue;
blobmsg_add_string(&b, NULL, uev_select_reason[i]);
}
blobmsg_close_array(&b, c);
}
if (ev->node_cur) {
c = blobmsg_open_table(&b, "local");
usteer_event_add_node_status(ev->node_cur);
blobmsg_close_table(&b, c);
}
if (ev->node_other) {
c = blobmsg_open_table(&b, "remote");
blobmsg_add_string(&b, "name", usteer_node_name(ev->node_other));
if (ev->si_other)
blobmsg_add_u32(&b, "signal", (int32_t)ev->si_other->signal);
usteer_event_add_node_status(ev->node_other);
blobmsg_close_table(&b, c);
}
if (ev->count)
blobmsg_add_u32(&b, "count", ev->count);
ubus_notify(ubus_ctx, &usteer_obj, uev_name[ev->type], b.head, -1);
}
static int
usteer_event_log_node(char *buf, int len, const char *prefix, struct usteer_node *node)
{
char *cur = buf;
char *end = buf + len;
cur += snprintf(cur, end - cur, " %sassoc=%d %sload=%d",
prefix, node->n_assoc,
prefix, node->load);
return cur - buf;
}
static void
usteer_event_log(struct uevent *ev)
{
char *str, *cur, *end;
int i;
if (!(config.event_log_mask & (1 << ev->type)))
return;
blob_buf_init(&b, 0);
cur = str = blobmsg_alloc_string_buffer(&b, NULL, UEV_LOG_MAXLEN);
end = str + UEV_LOG_MAXLEN;
cur += snprintf(cur, end - cur, "usteer event=%s", uev_name[ev->type]);
if (ev->node_local)
cur += snprintf(cur, end - cur, " node=%s", usteer_node_name(ev->node_local));
if (ev->sta)
cur += snprintf(cur, end - cur, " sta=" MAC_ADDR_FMT, MAC_ADDR_DATA(ev->sta->addr));
if (ev->reason)
cur += snprintf(cur, end - cur, " reason=%s", uev_reason[ev->reason]);
if (ev->si_cur)
cur += snprintf(cur, end - cur, " signal=%d", ev->si_cur->signal);
if (ev->threshold.ref)
cur += snprintf(cur, end - cur, " thr=%d/%d", ev->threshold.cur, ev->threshold.ref);
if (ev->count)
cur += snprintf(cur, end - cur, " count=%d", ev->count);
if (ev->node_cur)
cur += usteer_event_log_node(cur, end - cur, "", ev->node_cur);
if (ev->select_reasons) {
bool first = true;
cur += snprintf(cur, end - cur, " select_reason");
for (i = 0; i < ARRAY_SIZE(uev_select_reason); i++) {
if (!(ev->select_reasons & (1 << i)) ||
!uev_select_reason[i])
continue;
cur += snprintf(cur, end - cur, "%c%s", first ? '=' : ',',
uev_select_reason[i]);
first = false;
}
}
if (ev->node_other) {
cur += snprintf(cur, end - cur, " remote=%s", usteer_node_name(ev->node_other));
if (ev->si_other)
cur += snprintf(cur, end - cur, " remote_signal=%d",
ev->si_other->signal);
cur += usteer_event_log_node(cur, end - cur, "remote_", ev->node_other);
}
log_msg(str);
}
void usteer_event(struct uevent *ev)
{
if (ev->type >= ARRAY_SIZE(uev_name) || !uev_name[ev->type])
return;
if (ev->reason >= ARRAY_SIZE(uev_reason) || !uev_reason[ev->reason])
return;
if (ev->si_cur) {
if (!ev->node_local)
ev->node_local = ev->si_cur->node;
if (!ev->sta)
ev->sta = ev->si_cur->sta;
}
if (!ev->node_local && ev->node_cur)
ev->node_local = ev->node_cur;
if (ev->si_other && ev->node_cur && !ev->node_other)
ev->node_other = ev->si_other->node;
usteer_event_send_ubus(ev);
usteer_event_log(ev);
}
void config_set_event_log_types(struct blob_attr *attr)
{
struct blob_attr *cur;
int i, rem;
config.event_log_mask = 0;
if (!attr) {
static const uint32_t default_log[] = {
[MSG_INFO] =
(1 << UEV_LOAD_KICK_CLIENT) |
(1 << UEV_SIGNAL_KICK) |
(1 << UEV_AUTH_REQ_DENY) |
(1 << UEV_ASSOC_REQ_DENY),
[MSG_VERBOSE] =
(1 << UEV_PROBE_REQ_DENY),
[MSG_DEBUG] =
(1 << UEV_AUTH_REQ_ACCEPT) |
(1 << UEV_ASSOC_REQ_ACCEPT) |
(1 << UEV_LOAD_KICK_TRIGGER) |
(1 << UEV_LOAD_KICK_RESET) |
(1 << UEV_LOAD_KICK_MIN_CLIENTS) |
(1 << UEV_LOAD_KICK_NO_CLIENT),
};
if (config.debug_level >= MSG_DEBUG_ALL) {
config.event_log_mask = ~0;
return;
}
for (i = 0; i < ARRAY_SIZE(default_log) && i <= config.debug_level; i++)
config.event_log_mask |= default_log[i];
return;
}
if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
return;
blobmsg_for_each_attr(cur, attr, rem) {
const char *name = blobmsg_get_string(cur);
for (i = 0; i < ARRAY_SIZE(uev_name); i++) {
if (!uev_name[i] || strcmp(uev_name[i], name) != 0)
continue;
config.event_log_mask |= (1 << i);
break;
}
}
}
void config_get_event_log_types(struct blob_buf *buf)
{
uint32_t mask = config.event_log_mask;
void *c;
int i;
c = blobmsg_open_array(buf, "event_log_types");
for (i = 0; mask && i < ARRAY_SIZE(uev_name); i++) {
bool cur = mask & 1;
mask >>= 1;
if (!cur)
continue;
blobmsg_add_string(buf, NULL, uev_name[i]);
}
blobmsg_close_array(buf, c);
}