diff --git a/feeds/wifi-ax/hostapd/patches/900-hapd-netlink-ubus-bridge.patch b/feeds/wifi-ax/hostapd/patches/900-hapd-netlink-ubus-bridge.patch new file mode 100644 index 000000000..54904ff42 --- /dev/null +++ b/feeds/wifi-ax/hostapd/patches/900-hapd-netlink-ubus-bridge.patch @@ -0,0 +1,881 @@ +--- a/hostapd/main.c ++++ b/hostapd/main.c +@@ -41,7 +41,7 @@ struct hapd_global { + static struct hapd_global global; + static int daemonize = 0; + static char *pid_file = NULL; +- ++uint32_t cached_events_nr = 0; + + #ifndef CONFIG_NO_HOSTAPD_LOGGER + static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, +--- a/src/ap/hostapd.h ++++ b/src/ap/hostapd.h +@@ -97,6 +97,10 @@ struct hostapd_probereq_cb { + }; + + #define HOSTAPD_RATE_BASIC 0x00000001 ++/* max number of cached events */ ++#define HOSTAPD_MAX_CACHED_EVENTS 500 ++/* event counter */ ++uint32_t cached_events_nr; + + struct hostapd_rate_data { + int rate; /* rate in 100 kbps */ +--- a/src/ap/ieee802_11.c ++++ b/src/ap/ieee802_11.c +@@ -4908,6 +4908,7 @@ int ieee802_11_mgmt(struct hostapd_data + int ret = 0; + unsigned int freq; + int ssi_signal = fi ? fi->ssi_signal : 0; ++ int ubus_resp; + + if (len < 24) + return 0; +@@ -4977,25 +4978,52 @@ int ieee802_11_mgmt(struct hostapd_data + if (hapd->iconf->track_sta_max_num) + sta_track_add(hapd->iface, mgmt->sa, ssi_signal); + ++ /* ubus request */ ++ struct hostapd_ubus_request req = { ++ .type = HOSTAPD_UBUS_TYPE_MAX, ++ .mgmt_frame = mgmt, ++ .ssi_signal = ssi_signal ++ }; ++ + switch (stype) { + case WLAN_FC_STYPE_AUTH: + wpa_printf(MSG_DEBUG, "mgmt::auth"); + handle_auth(hapd, mgmt, len, ssi_signal, 0); ++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, "mgmt: [%d] [%s]", ++ stype, ++ "AUTH"); ++ req.type = HOSTAPD_UBUS_AUTH_REQ; + ret = 1; + break; + case WLAN_FC_STYPE_ASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); + handle_assoc(hapd, mgmt, len, 0, ssi_signal); ++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, "mgmt: [%d] [%s]", ++ stype, ++ "ASSOC"); ++ req.type = HOSTAPD_UBUS_ASSOC_REQ; + ret = 1; + break; + case WLAN_FC_STYPE_REASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); + handle_assoc(hapd, mgmt, len, 1, ssi_signal); ++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, "mgmt: [%d] [%s]", ++ stype, ++ "RE-ASSOC"); ++ req.type = HOSTAPD_UBUS_REASSOC_REQ; + ret = 1; + break; + case WLAN_FC_STYPE_DISASSOC: + wpa_printf(MSG_DEBUG, "mgmt::disassoc"); + handle_disassoc(hapd, mgmt, len); ++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, "mgmt: [%d] [%s]", ++ stype, ++ "DISASSOC"); ++ req.type = HOSTAPD_UBUS_DISASSOC_REQ; + ret = 1; + break; + case WLAN_FC_STYPE_DEAUTH: +@@ -5014,6 +5042,15 @@ int ieee802_11_mgmt(struct hostapd_data + break; + } + ++ /* ubus send */ ++ if (req.type != HOSTAPD_UBUS_TYPE_MAX) { ++ ubus_resp = hostapd_ubus_handle_rt_event(hapd, &req); ++ if (ubus_resp) { ++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, ++ "hostapd_ubus_handle_rt_event: ERROR"); ++ } ++ } + return ret; + } + +--- a/src/ap/sta_info.c ++++ b/src/ap/sta_info.c +@@ -423,8 +423,14 @@ void ap_handle_timer(void *eloop_ctx, vo + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "local deauth request"); +- ap_free_sta(hapd, sta); + hostapd_ubus_notify(hapd, "local-deauth", sta->addr); ++ struct hostapd_ubus_request req = { ++ .type = HOSTAPD_UBUS_DISASSOC_REQ, ++ .sta = sta, ++ .reason = sta->deauth_reason ++ }; ++ hostapd_ubus_handle_rt_event(hapd, &req); ++ ap_free_sta(hapd, sta); + return; + } + +@@ -530,6 +536,14 @@ skip_poll: + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY : + WLAN_REASON_PREV_AUTH_NOT_VALID; + ++ /* sessions */ ++ struct hostapd_ubus_request req = { ++ .type = HOSTAPD_UBUS_DISASSOC_REQ, ++ .sta = sta, ++ .reason = reason ++ }; ++ hostapd_ubus_handle_rt_event(hapd, &req); ++ + hostapd_drv_sta_disassoc(hapd, sta->addr, reason); + } + } +@@ -566,6 +580,15 @@ skip_poll: + __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY); + eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, + hapd, sta); ++ ++ /* sessions */ ++ struct hostapd_ubus_request req = { ++ .type = HOSTAPD_UBUS_DISASSOC_REQ, ++ .sta = sta, ++ .reason = reason ++ }; ++ hostapd_ubus_handle_rt_event(hapd, &req); ++ + mlme_disassociate_indication(hapd, sta, reason); + break; + case STA_DEAUTH: +--- a/src/ap/sta_info.h ++++ b/src/ap/sta_info.h +@@ -77,6 +77,9 @@ struct sta_info { + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + int supported_rates_len; + u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ ++ u64 cl_session_id; /* client fnv1a 64bit session id */ ++ u8 fdata; /* client first data flag */ ++ u8 first_ip; /* client first ip flag */ + + #ifdef CONFIG_MESH + enum mesh_plink_state plink_state; +--- a/src/ap/ubus.c ++++ b/src/ap/ubus.c +@@ -24,7 +24,10 @@ + + static struct ubus_context *ctx; + static struct blob_buf b; ++static struct blob_buf b_ev; + static int ctx_ref; ++static char** bss_lst = NULL; ++static size_t bss_nr = 0; + + static inline struct hapd_interfaces *get_hapd_interfaces_from_object(struct ubus_object *obj) + { +@@ -63,6 +66,21 @@ static void hostapd_ubus_connection_lost + eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL); + } + ++static int avl_compare_sess_id(const void *k1, const void *k2, void *ptr) ++{ ++ const uint32_t *id1 = k1, *id2 = k2; ++ ++ if (*id1 < *id2) ++ return -1; ++ else ++ return *id1 > *id2; ++} ++ ++uint64_t get_time_in_ms(struct timespec *ts) ++{ ++ return (uint64_t) ts->tv_sec * 1000 + ts->tv_nsec / 1000000; ++} ++ + static bool hostapd_ubus_init(void) + { + if (ctx) +@@ -525,6 +543,177 @@ static const struct blobmsg_policy csa_p + }; + + #ifdef NEED_AP_MLME ++enum { CSESS_REQ_SESS_ID, ++ __CSESS_REQ_MAX, ++}; ++ ++static const struct blobmsg_policy client_session_del_policy[__CSESS_REQ_MAX] = { ++ [CSESS_REQ_SESS_ID] = { .name = "session_id", .type = BLOBMSG_TYPE_INT64 }, ++}; ++ ++static int hostapd_clear_session(struct ubus_context *ctx, ++ struct ubus_object *obj, ++ struct ubus_request_data *req, ++ const char *method, ++ struct blob_attr *msg) ++{ ++ struct blob_attr *tb[__CSESS_REQ_MAX]; ++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); ++ struct hostapd_event_avl_rec *rec = NULL; ++ struct hostapd_event_avl_rec *rec_next = NULL; ++ uint64_t session_id = 0; ++ ++ blobmsg_parse(client_session_del_policy, __CSESS_REQ_MAX, tb, ++ blob_data(msg), blob_len(msg)); ++ ++ if (!tb[CSESS_REQ_SESS_ID]) ++ return UBUS_STATUS_INVALID_ARGUMENT; ++ ++ session_id = blobmsg_get_u64(tb[CSESS_REQ_SESS_ID]); ++ /* remove from AVL and ubus session object) */ ++ avl_for_each_element_safe(&hapd->ubus.rt_events, rec, avl, rec_next) ++ { ++ if (session_id == rec->session_id) { ++ /* dec counter and delete */ ++ cached_events_nr -= rec->rec_nr; ++ avl_delete(&hapd->ubus.rt_events, &rec->avl); ++ os_free(rec->records); ++ os_free(rec); ++ } ++ } ++ return 0; ++} ++ ++static int hostapd_clear_sessions(struct ubus_context *ctx, ++ struct ubus_object *obj, ++ struct ubus_request_data *req, ++ const char *method, ++ struct blob_attr *msg) ++{ ++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); ++ struct hostapd_event_avl_rec *rec = NULL; ++ struct hostapd_event_avl_rec *rec_n = NULL; ++ ++ avl_remove_all_elements(&hapd->ubus.rt_events, rec, avl, rec_n) ++ { ++ /* free events array */ ++ os_free(rec->records); ++ os_free(rec); ++ } ++ /* reset counter */ ++ cached_events_nr = 0; ++ return 0; ++} ++ ++static int hostapd_sessions(struct ubus_context *ctx, ++ struct ubus_object *obj, ++ struct ubus_request_data *req, ++ const char *method, ++ struct blob_attr *msg) ++{ ++ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); ++ void *a = NULL; ++ void *t = NULL; ++ void *t_msg = NULL; ++ struct hostapd_event_avl_rec *rec = NULL; ++ struct client_session_record *c_rec = NULL; ++ ++ blob_buf_init(&b_ev, 0); ++ a = blobmsg_open_table(&b_ev, "sessions"); ++ avl_for_each_element(&hapd->ubus.rt_events, rec, avl) ++ { ++ /* open session */ ++ t = blobmsg_open_table(&b_ev, "ClientSession"); ++ blobmsg_add_u64(&b_ev, "session_id", rec->session_id); ++ /* messages for current session */ ++ for (size_t i = 0; i < rec->rec_nr; i++) { ++ c_rec = &rec->records[i]; ++ /* check message type */ ++ switch (c_rec->type) { ++ /* ClientAuthEvent */ ++ case CST_AUTH: { ++ struct client_auth_event *p = &c_rec->u.auth; ++ t_msg = blobmsg_open_table(&b_ev, "ClientAuthEvent"); ++ blobmsg_add_u64(&b_ev, "session_id", rec->session_id); ++ blobmsg_add_u64(&b_ev, "timestamp", c_rec->timestamp); ++ blobmsg_add_string(&b_ev, "sta_mac", p->sta_mac); ++ blobmsg_add_u32(&b_ev, "band", p->band); ++ blobmsg_add_u32(&b_ev, "auth_status", p->auth_status); ++ blobmsg_add_string(&b_ev, "ssid", p->ssid); ++ blobmsg_close_table(&b_ev, t_msg); ++ break; ++ } ++ ++ /* ClientAssocEvent */ ++ case CST_ASSOC: { ++ struct client_assoc_event *p = &c_rec->u.assoc; ++ t_msg = blobmsg_open_table(&b_ev, "ClientAssocEvent"); ++ blobmsg_add_u64(&b_ev, "session_id", rec->session_id); ++ blobmsg_add_u64(&b_ev, "timestamp", c_rec->timestamp); ++ blobmsg_add_string(&b_ev, "sta_mac", p->sta_mac); ++ blobmsg_add_u32(&b_ev, "band", p->band); ++ blobmsg_add_u32(&b_ev, "assoc_type", 0); ++ blobmsg_add_u32(&b_ev, "rssi", p->rssi); ++ blobmsg_add_u32(&b_ev, "internal_sc", 0); ++ blobmsg_add_u8(&b_ev, "using11k", p->using11k); ++ blobmsg_add_u8(&b_ev, "using11r", p->using11r); ++ blobmsg_add_u8(&b_ev, "using11v", p->using11v); ++ blobmsg_add_string(&b_ev, "ssid", p->ssid); ++ blobmsg_close_table(&b_ev, t_msg); ++ break; ++ } ++ ++ /* ClientDisconnectEvent */ ++ case CST_DISASSOC: { ++ struct client_disassoc_event *p = &c_rec->u.disassoc; ++ t_msg = blobmsg_open_table(&b_ev, "ClientDisconnectEvent"); ++ blobmsg_add_u64(&b_ev, "session_id", rec->session_id); ++ blobmsg_add_u64(&b_ev, "timestamp", c_rec->timestamp); ++ blobmsg_add_string(&b_ev, "sta_mac", p->sta_mac); ++ blobmsg_add_u32(&b_ev, "band", p->band); ++ blobmsg_add_u32(&b_ev, "rssi", p->rssi); ++ blobmsg_add_u32(&b_ev, "internal_rc", p->internal_rc); ++ blobmsg_add_string(&b_ev, "ssid", p->ssid); ++ blobmsg_close_table(&b_ev, t_msg); ++ break; ++ } ++ ++ /* ClientFirstDataEvent */ ++ case CST_FDATA: { ++ struct client_fdata_event *p = &c_rec->u.fdata; ++ t_msg = blobmsg_open_table(&b_ev, "ClientFirstDataEvent"); ++ blobmsg_add_u64(&b_ev, "session_id", rec->session_id); ++ blobmsg_add_u64(&b_ev, "timestamp", c_rec->timestamp); ++ blobmsg_add_string(&b_ev, "sta_mac", p->sta_mac); ++ blobmsg_add_u64(&b_ev, "fdata_tx_up_ts_in_us", p->tx_ts.tv_sec * (uint64_t)1000000); ++ blobmsg_add_u64(&b_ev, "fdata_rx_up_ts_in_us", p->rx_ts.tv_sec * (uint64_t)1000000); ++ blobmsg_close_table(&b_ev, t_msg); ++ break; ++ } ++ ++ /* ClientIpEvent */ ++ case CST_IP: { ++ struct client_ip_event *p = &c_rec->u.ip; ++ t_msg = blobmsg_open_table(&b_ev, "ClientIpEvent"); ++ blobmsg_add_u64(&b_ev, "session_id", rec->session_id); ++ blobmsg_add_u64(&b_ev, "timestamp", c_rec->timestamp); ++ blobmsg_add_string(&b_ev, "sta_mac", p->sta_mac); ++ blobmsg_add_string(&b_ev, "ip_address", p->ip_addr); ++ blobmsg_close_table(&b_ev, t_msg); ++ break; ++ } ++ ++ default: ++ break; ++ } ++ } ++ blobmsg_close_table(&b_ev, t); ++ } ++ blobmsg_close_table(&b_ev, a); ++ ubus_send_reply(ctx, req, b_ev.head); ++ return 0; ++} ++ + static int + hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, +@@ -1148,6 +1337,9 @@ static const struct ubus_method bss_meth + UBUS_METHOD_NOARG("get_features", hostapd_bss_get_features), + #ifdef NEED_AP_MLME + UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy), ++ UBUS_METHOD_NOARG("get_sessions", hostapd_sessions), ++ UBUS_METHOD_NOARG("clear_sessions", hostapd_clear_sessions), ++ UBUS_METHOD("clear_session", hostapd_clear_session, client_session_del_policy), + #endif + UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy), + UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy), +@@ -1187,6 +1379,7 @@ void hostapd_ubus_add_bss(struct hostapd + if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0) + return; + ++ avl_init(&hapd->ubus.rt_events, avl_compare_sess_id, false, NULL); + avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL); + obj->name = name; + obj->type = &bss_object_type; +@@ -1194,6 +1387,9 @@ void hostapd_ubus_add_bss(struct hostapd + obj->n_methods = bss_object_type.n_methods; + ret = ubus_add_object(ctx, obj); + hostapd_ubus_ref_inc(); ++ bss_nr++; ++ bss_lst = os_realloc(bss_lst, sizeof(char *) * bss_nr); ++ bss_lst[bss_nr - 1] = strdup(name); + if (hapd->conf->signal_stay_min > -128) + eloop_register_timeout(3, 0, hostapd_bss_signal_check, NULL, hapd); /* Start up the poll timer. */ + } +@@ -1212,11 +1408,42 @@ void hostapd_ubus_free_bss(struct hostap + } + + free(name); ++ for (size_t i = 0; i < bss_nr; i++) ++ os_free(bss_lst[i]); ++ free(bss_lst); ++ bss_lst = NULL; ++} ++ ++static int hostapd_get_bss_list(struct ubus_context *ctx, ++ struct ubus_object *obj, ++ struct ubus_request_data *req, ++ const char *method, ++ struct blob_attr *msg) ++{ ++ if (!bss_lst) ++ return 0; ++ ++ void *a = NULL; ++ void *b = NULL; ++ ++ /* create reply */ ++ blob_buf_init(&b_ev, 0); ++ a = blobmsg_open_array(&b_ev, "bss_list"); ++ /* check bss list from hapd */ ++ for (size_t i = 0; i < bss_nr; i++) { ++ b = blobmsg_open_table(&b_ev, NULL); ++ blobmsg_add_string(&b_ev, "name", bss_lst[i]); ++ blobmsg_close_table(&b_ev, b); ++ } ++ blobmsg_close_array(&b_ev, a); ++ ubus_send_reply(ctx, req, b_ev.head); ++ return 0; + } + + static const struct ubus_method daemon_methods[] = { + UBUS_METHOD("config_add", hostapd_config_add, config_add_policy), + UBUS_METHOD("config_remove", hostapd_config_remove, config_remove_policy), ++ UBUS_METHOD_NOARG("get_bss_list", hostapd_get_bss_list), + }; + + static struct ubus_object_type daemon_object_type = +@@ -1260,6 +1487,18 @@ struct ubus_event_req { + int resp; + }; + ++static uint64_t hash_fnv1a_64bit(const void *key, int len) ++{ ++ if (key == NULL) ++ return 0; ++ unsigned char *p = (unsigned char *)key; ++ uint64_t h = 14695981039346656037UL; ++ int i; ++ for (i = 0; i < len; i++) ++ h = (h ^ p[i]) * 1099511628211UL; ++ return h; ++} ++ + static void + ubus_event_cb(struct ubus_notify_request *req, int idx, int ret) + { +@@ -1268,6 +1507,224 @@ ubus_event_cb(struct ubus_notify_request + ureq->resp = ret; + } + ++int hostapd_ubus_handle_rt_event(struct hostapd_data *hapd, ++ struct hostapd_ubus_request *req) ++{ ++ /* check event counter */ ++ if (cached_events_nr > HOSTAPD_MAX_CACHED_EVENTS) { ++ hostapd_logger( ++ hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_WARNING, ++ "hostapd_ubus_handle_rt_event: HOSTAPD_MAX_CACHED_EVENTS [%d] exceeded", ++ HOSTAPD_MAX_CACHED_EVENTS); ++ return WLAN_STATUS_SUCCESS; ++ } ++ struct hostapd_event_avl_rec *rec = NULL; ++ struct timespec ts; ++ uint64_t timestamp = 0; ++ clock_gettime(CLOCK_REALTIME, &ts); ++ timestamp = get_time_in_ms(&ts); ++ uint64_t session_id = 0; ++ uint8_t new_rec = 0; ++ const struct ieee80211_mgmt *mgmt = req->mgmt_frame; ++ struct sta_info *sta = req->sta ? (void *)req->sta : ap_get_sta(hapd, mgmt->sa); ++ /* null pointer check */ ++ if (!sta) return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; ++ ++ struct hostapd_bss_config *bss_conf = hapd->conf; ++ session_id = sta->cl_session_id; ++ ++ /* find by session id */ ++ rec = avl_find_element(&hapd->ubus.rt_events, &session_id, rec, avl); ++ ++ /* prepare rec if not found */ ++ if (!rec) { ++ new_rec = 1; ++ rec = os_zalloc(sizeof(struct hostapd_event_avl_rec)); ++ session_id = hash_fnv1a_64bit(&ts, sizeof(struct timespec)); ++ } ++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "hostapd_ubus_handle_rt_event: REQ TYPE [%d]", ++ req->type); ++ ++ switch (req->type) { ++ case HOSTAPD_UBUS_AUTH_REQ: { ++ /* session id */ ++ rec->session_id = session_id; ++ rec->rec_nr++; ++ rec->records = os_realloc(rec->records, ++ sizeof(struct client_session_record) * rec->rec_nr); ++ struct client_session_record *rp = &rec->records[rec->rec_nr - 1]; ++ rp->type = CST_AUTH; ++ rp->u.auth.session_id = rec->session_id; ++ ++ /* timestamp */ ++ rp->timestamp = timestamp; ++ /* frequency */ ++ rp->u.auth.band = hapd->iface->freq; ++ /* STA MAC */ ++ sprintf(rp->u.auth.sta_mac, MACSTR, MAC2STR(sta->addr)); ++ /* ssid */ ++ rp->u.auth.ssid[0] = 0; ++ printf_encode(rp->u.auth.ssid, sizeof(rp->u.auth.ssid), ++ bss_conf->ssid.ssid, bss_conf->ssid.ssid_len); ++ /* auth status */ ++ rp->u.auth.auth_status = le_to_host16(mgmt->u.auth.status_code); ++ hostapd_logger( ++ hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "hostapd_ubus_handle_rt_event:HOSTAPD_UBUS_AUTH_REQ"); ++ /* inc counter */ ++ cached_events_nr++; ++ break; ++ } ++ case HOSTAPD_UBUS_ASSOC_REQ: { ++ /* session id */ ++ rec->session_id = session_id; ++ rec->rec_nr++; ++ rec->records = os_realloc(rec->records, ++ sizeof(struct client_session_record) * rec->rec_nr); ++ struct client_session_record *rp = &rec->records[rec->rec_nr - 1]; ++ rp->type = CST_ASSOC; ++ rp->u.assoc.session_id = rec->session_id; ++ /* timestamp */ ++ rp->timestamp = timestamp; ++ /* frequency */ ++ rp->u.assoc.band = hapd->iface->freq; ++ /* STA MAC */ ++ sprintf(rp->u.assoc.sta_mac, MACSTR, MAC2STR(sta->addr)); ++ /* ssid */ ++ rp->u.assoc.ssid[0] = 0; ++ printf_encode(rp->u.assoc.ssid, sizeof(rp->u.assoc.ssid), ++ bss_conf->ssid.ssid, bss_conf->ssid.ssid_len); ++ /* rssi */ ++ rp->u.assoc.rssi = req->ssi_signal; ++ /* using 11r */ ++ rp->u.assoc.using11r = (sta->auth_alg & WPA_AUTH_ALG_FT); ++ /* using 11k */ ++ if (sta->rrm_enabled_capa[0] || sta->rrm_enabled_capa[1] || ++ sta->rrm_enabled_capa[2] || sta->rrm_enabled_capa[3] || ++ sta->rrm_enabled_capa[4]) ++ rp->u.assoc.using11k = 1; ++ else ++ rp->u.assoc.using11k = 0; ++ /* using 11v */ ++ if (bss_conf->time_advertisement || bss_conf->wnm_sleep_mode || ++ bss_conf->bss_transition) ++ rp->u.assoc.using11v = 1; ++ else ++ rp->u.assoc.using11v = 0; ++ hostapd_logger( ++ hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "hostapd_ubus_handle_rt_event:HOSTAPD_UBUS_ASSOC_REQ"); ++ /* inc counter */ ++ cached_events_nr++; ++ break; ++ } ++ case HOSTAPD_UBUS_DISASSOC_REQ: { ++ /* session id */ ++ rec->session_id = session_id; ++ rec->rec_nr++; ++ rec->records = os_realloc(rec->records, ++ sizeof(struct client_session_record) * rec->rec_nr); ++ struct client_session_record *rp = &rec->records[rec->rec_nr - 1]; ++ rp->type = CST_DISASSOC; ++ rp->u.disassoc.session_id = rec->session_id; ++ /* timestamp */ ++ rp->timestamp = timestamp; ++ /* frequency */ ++ rp->u.disassoc.band = hapd->iface->freq; ++ /* STA MAC */ ++ sprintf(rp->u.disassoc.sta_mac, MACSTR, MAC2STR(sta->addr)); ++ /* ssid */ ++ rp->u.disassoc.ssid[0] = 0; ++ printf_encode(rp->u.disassoc.ssid, sizeof(rp->u.disassoc.ssid), ++ bss_conf->ssid.ssid, bss_conf->ssid.ssid_len); ++ /* rssi */ ++ rp->u.disassoc.rssi = req->ssi_signal; ++ /* internal_rc */ ++ rp->u.disassoc.internal_rc = req->reason; ++ hostapd_logger( ++ hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "hostapd_ubus_handle_rt_event:HOSTAPD_UBUS_DISASSOC_REQ"); ++ /* inc counter */ ++ cached_events_nr++; ++ break; ++ } ++ case HOSTAPD_UBUS_FDATA_REQ: { ++ /* session id */ ++ rec->session_id = session_id; ++ rec->rec_nr++; ++ rec->records = os_realloc(rec->records, ++ sizeof(struct client_session_record) * rec->rec_nr); ++ struct client_session_record *rp = &rec->records[rec->rec_nr - 1]; ++ rp->type = CST_FDATA; ++ rp->u.fdata.session_id = rec->session_id; ++ /* timestamp */ ++ rp->timestamp = timestamp; ++ /* STA MAC */ ++ sprintf(rp->u.fdata.sta_mac, MACSTR, MAC2STR(sta->addr)); ++ /* rx ts */ ++ rp->u.fdata.rx_ts = ts; ++ /* tx ts */ ++ rp->u.fdata.tx_ts = ts; ++ /* single event only */ ++ sta->fdata = 1; ++ hostapd_logger( ++ hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "hostapd_ubus_handle_rt_event:HOSTAPD_UBUS_FDATA_REQ"); ++ /* inc counter */ ++ cached_events_nr++; ++ break; ++ } ++ case HOSTAPD_UBUS_IP_REQ: { ++ if(sta->first_ip) break; ++ /* session id */ ++ rec->session_id = session_id; ++ rec->rec_nr++; ++ rec->records = os_realloc(rec->records, ++ sizeof(struct client_session_record) * rec->rec_nr); ++ struct client_session_record *rp = &rec->records[rec->rec_nr - 1]; ++ rp->type = CST_IP; ++ rp->u.ip.session_id = rec->session_id; ++ /* timestamp */ ++ rp->timestamp = timestamp; ++ /* STA MAC */ ++ sprintf(rp->u.ip.sta_mac, MACSTR, MAC2STR(sta->addr)); ++ /* ip address */ ++ snprintf(rp->u.ip.ip_addr, 20, "%u.%u.%u.%u", ++ (req->ipaddr >> 24) & 0xFF, ++ (req->ipaddr >> 16) & 0xFF, ++ (req->ipaddr >> 8) & 0xFF, ++ req->ipaddr & 0xFF); ++ /* single event only */ ++ sta->first_ip = 1; ++ /* inc counter */ ++ cached_events_nr++; ++ break; ++ } ++ default: ++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "hostapd_ubus_handle_rt_event:UNKNOWN"); ++ new_rec = 0; ++ break; ++ } ++ if (new_rec) { ++ /* insert new client session */ ++ rec->avl.key = &rec->session_id; ++ sta->cl_session_id = session_id; ++ if (avl_insert(&hapd->ubus.rt_events, &rec->avl)) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ return WLAN_STATUS_SUCCESS; ++} ++ + int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req) + { + struct ubus_banned_client *ban; +--- a/src/ap/ubus.h ++++ b/src/ap/ubus.h +@@ -12,6 +12,11 @@ enum hostapd_ubus_event_type { + HOSTAPD_UBUS_PROBE_REQ, + HOSTAPD_UBUS_AUTH_REQ, + HOSTAPD_UBUS_ASSOC_REQ, ++ HOSTAPD_UBUS_REASSOC_REQ, ++ HOSTAPD_UBUS_DISASSOC_REQ, ++ HOSTAPD_UBUS_DEAUTH_REQ, ++ HOSTAPD_UBUS_FDATA_REQ, ++ HOSTAPD_UBUS_IP_REQ, + HOSTAPD_UBUS_TYPE_MAX + }; + +@@ -19,8 +24,11 @@ struct hostapd_ubus_request { + enum hostapd_ubus_event_type type; + const struct ieee80211_mgmt *mgmt_frame; + const struct ieee802_11_elems *elems; ++ const struct sta_info *sta; + int ssi_signal; /* dBm */ ++ int reason; + const u8 *addr; ++ be32 ipaddr; + }; + + struct hostapd_iface; +@@ -37,6 +45,80 @@ struct hostapd_ubus_bss { + struct ubus_object obj; + struct avl_tree banned; + int notify_response; ++ struct avl_tree rt_events; ++}; ++ ++enum client_sess_type { ++ CST_ASSOC, ++ CST_AUTH, ++ CST_DISASSOC, ++ CST_FDATA, ++ CST_IP ++}; ++ ++struct client_assoc_event { ++ unsigned char sta_mac[20]; ++ uint64_t session_id; ++ char ssid[SSID_MAX_LEN]; ++ int band; ++ int assoc_type; ++ int status; ++ int rssi; ++ int internal_sc; ++ uint8_t using11k; ++ uint8_t using11r; ++ uint8_t using11v; ++}; ++ ++struct client_disassoc_event { ++ unsigned char sta_mac[20]; ++ uint64_t session_id; ++ char ssid[SSID_MAX_LEN]; ++ int band; ++ int assoc_type; ++ int status; ++ int rssi; ++ int internal_rc; ++}; ++ ++struct client_auth_event { ++ unsigned char sta_mac[20]; ++ uint64_t session_id; ++ char ssid[SSID_MAX_LEN]; ++ int band; ++ uint32_t auth_status; ++}; ++ ++struct client_fdata_event { ++ unsigned char sta_mac[20]; ++ uint64_t session_id; ++ struct timespec rx_ts; ++ struct timespec tx_ts; ++}; ++ ++struct client_ip_event { ++ unsigned char sta_mac[20]; ++ uint64_t session_id; ++ unsigned char ip_addr[16]; ++}; ++ ++struct client_session_record { ++ int type; ++ uint64_t timestamp; ++ union { ++ struct client_assoc_event assoc; ++ struct client_disassoc_event disassoc; ++ struct client_auth_event auth; ++ struct client_fdata_event fdata; ++ struct client_ip_event ip; ++ } u; ++}; ++ ++struct hostapd_event_avl_rec { ++ uint64_t session_id; ++ struct client_session_record *records; ++ size_t rec_nr; ++ struct avl_node avl; + }; + + void hostapd_ubus_add_iface(struct hostapd_iface *iface); +@@ -45,6 +127,7 @@ void hostapd_ubus_add_bss(struct hostapd + void hostapd_ubus_free_bss(struct hostapd_data *hapd); + + int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req); ++int hostapd_ubus_handle_rt_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req); + void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac); + void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd, + const u8 *addr, u8 token, u8 rep_mode, +@@ -78,6 +161,11 @@ static inline int hostapd_ubus_handle_ev + { + return 0; + } ++ ++static inline int hostapd_ubus_handle_rt_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req) ++{ ++ return 0; ++} + + static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac) + { +--- a/src/ap/hostapd.c ++++ b/src/ap/hostapd.c +@@ -1406,19 +1406,22 @@ static int hostapd_setup_bss(struct host + "Generic snooping infrastructure initialization failed"); + return -1; + } +- +- if (dhcp_snoop_init(hapd)) { +- wpa_printf(MSG_ERROR, +- "DHCP snooping initialization failed"); +- return -1; +- } +- + if (ndisc_snoop_init(hapd)) { + wpa_printf(MSG_ERROR, + "Neighbor Discovery snooping initialization failed"); + return -1; + } + } ++ if (dhcp_snoop_init(hapd)) { ++ wpa_printf(MSG_ERROR, ++ "DHCP snooping initialization failed"); ++ hostapd_logger( ++ hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "dfranusic:DHCP snooping initialization failed"); ++ ++ return -1; ++ } + + if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { + wpa_printf(MSG_ERROR, "VLAN initialization failed."); +--- a/src/ap/dhcp_snoop.c ++++ b/src/ap/dhcp_snoop.c +@@ -40,6 +40,7 @@ static void handle_dhcp(void *ctx, const + int res, msgtype = 0, prefixlen = 32; + u32 subnet_mask = 0; + u16 ip_len; ++ int ubus_resp; + + exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten)); + if (exten_len < 4) +@@ -112,6 +113,19 @@ static void handle_dhcp(void *ctx, const + ipaddr_str(be_to_host32(b->your_ip)), + prefixlen); + ++ struct hostapd_ubus_request req = { ++ .type = HOSTAPD_UBUS_IP_REQ, ++ .sta = sta, ++ .ipaddr = be_to_host32(b->your_ip) ++ }; ++ ubus_resp = hostapd_ubus_handle_rt_event(hapd, &req); ++ if (ubus_resp) { ++ hostapd_logger( ++ hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "hostapd_ubus_handle_rt_event: ERROR"); ++ } ++ + if (sta->ipaddr == b->your_ip) + return; +