#include "wtp.h" #include "capwap_array.h" #include "capwap_list.h" #include "capwap_element.h" #include "capwap_element_80211_ie.h" #include #include #include #include "wtp_kmod.h" /* Local version of nl80211 with all feature to remove the problem of frag version of nl80211 */ #include "nl80211_v3_10.h" #include "wifi_drivers.h" #include "wifi_nl80211.h" /* Physical device info */ struct nl80211_phydevice_item { uint32_t index; char name[IFNAMSIZ]; }; /* Virtual device info */ struct nl80211_virtdevice_item { uint32_t phyindex; uint32_t virtindex; char virtname[IFNAMSIZ]; }; /* */ static const int g_stypes[] = { IEEE80211_FRAMECONTROL_MGMT_SUBTYPE_AUTHENTICATION, IEEE80211_FRAMECONTROL_MGMT_SUBTYPE_ASSOCIATION_REQUEST, IEEE80211_FRAMECONTROL_MGMT_SUBTYPE_REASSOCIATION_REQUEST, IEEE80211_FRAMECONTROL_MGMT_SUBTYPE_DISASSOCIATION, IEEE80211_FRAMECONTROL_MGMT_SUBTYPE_DEAUTHENTICATION, IEEE80211_FRAMECONTROL_MGMT_SUBTYPE_ACTION, IEEE80211_FRAMECONTROL_MGMT_SUBTYPE_PROBE_REQUEST }; /* */ struct family_data { int id; const char* group; }; /* Compatibility functions */ #ifdef HAVE_LIBNL_10 static uint32_t g_portbitmap[32] = { 0 }; static struct nl_sock* nl_socket_alloc_cb(void* cb) { int i; struct nl_sock* handle; uint32_t pid = getpid() & 0x3FFFFF; handle = nl_handle_alloc_cb(cb); for (i = 0; i < 1024; i++) { if (g_portbitmap[i / 32] & (1 << (i % 32))) { continue; } g_portbitmap[i / 32] |= 1 << (i % 32); pid += i << 22; break; } nl_socket_set_local_port(handle, pid); return handle; } static void nl_socket_free(struct nl_sock* handle) { uint32_t port = nl_socket_get_local_port(handle); port >>= 22; g_portbitmap[port / 32] &= ~(1 << (port % 32)); nl_handle_destroy(handle); } #endif static const char * nl80211_command_to_string(enum nl80211_commands cmd) { #define C2S(x) case x: return #x; switch (cmd) { C2S(NL80211_CMD_UNSPEC) C2S(NL80211_CMD_GET_WIPHY) C2S(NL80211_CMD_SET_WIPHY) C2S(NL80211_CMD_NEW_WIPHY) C2S(NL80211_CMD_DEL_WIPHY) C2S(NL80211_CMD_GET_INTERFACE) C2S(NL80211_CMD_SET_INTERFACE) C2S(NL80211_CMD_NEW_INTERFACE) C2S(NL80211_CMD_DEL_INTERFACE) C2S(NL80211_CMD_GET_KEY) C2S(NL80211_CMD_SET_KEY) C2S(NL80211_CMD_NEW_KEY) C2S(NL80211_CMD_DEL_KEY) C2S(NL80211_CMD_GET_BEACON) C2S(NL80211_CMD_SET_BEACON) C2S(NL80211_CMD_START_AP) C2S(NL80211_CMD_STOP_AP) C2S(NL80211_CMD_GET_STATION) C2S(NL80211_CMD_SET_STATION) C2S(NL80211_CMD_NEW_STATION) C2S(NL80211_CMD_DEL_STATION) C2S(NL80211_CMD_GET_MPATH) C2S(NL80211_CMD_SET_MPATH) C2S(NL80211_CMD_NEW_MPATH) C2S(NL80211_CMD_DEL_MPATH) C2S(NL80211_CMD_SET_BSS) C2S(NL80211_CMD_SET_REG) C2S(NL80211_CMD_REQ_SET_REG) C2S(NL80211_CMD_GET_MESH_CONFIG) C2S(NL80211_CMD_SET_MESH_CONFIG) C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) C2S(NL80211_CMD_GET_REG) C2S(NL80211_CMD_GET_SCAN) C2S(NL80211_CMD_TRIGGER_SCAN) C2S(NL80211_CMD_NEW_SCAN_RESULTS) C2S(NL80211_CMD_SCAN_ABORTED) C2S(NL80211_CMD_REG_CHANGE) C2S(NL80211_CMD_AUTHENTICATE) C2S(NL80211_CMD_ASSOCIATE) C2S(NL80211_CMD_DEAUTHENTICATE) C2S(NL80211_CMD_DISASSOCIATE) C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) C2S(NL80211_CMD_REG_BEACON_HINT) C2S(NL80211_CMD_JOIN_IBSS) C2S(NL80211_CMD_LEAVE_IBSS) C2S(NL80211_CMD_TESTMODE) C2S(NL80211_CMD_CONNECT) C2S(NL80211_CMD_ROAM) C2S(NL80211_CMD_DISCONNECT) C2S(NL80211_CMD_SET_WIPHY_NETNS) C2S(NL80211_CMD_GET_SURVEY) C2S(NL80211_CMD_NEW_SURVEY_RESULTS) C2S(NL80211_CMD_SET_PMKSA) C2S(NL80211_CMD_DEL_PMKSA) C2S(NL80211_CMD_FLUSH_PMKSA) C2S(NL80211_CMD_REMAIN_ON_CHANNEL) C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) C2S(NL80211_CMD_SET_TX_BITRATE_MASK) C2S(NL80211_CMD_REGISTER_FRAME) C2S(NL80211_CMD_FRAME) C2S(NL80211_CMD_FRAME_TX_STATUS) C2S(NL80211_CMD_SET_POWER_SAVE) C2S(NL80211_CMD_GET_POWER_SAVE) C2S(NL80211_CMD_SET_CQM) C2S(NL80211_CMD_NOTIFY_CQM) C2S(NL80211_CMD_SET_CHANNEL) C2S(NL80211_CMD_SET_WDS_PEER) C2S(NL80211_CMD_FRAME_WAIT_CANCEL) C2S(NL80211_CMD_JOIN_MESH) C2S(NL80211_CMD_LEAVE_MESH) C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) C2S(NL80211_CMD_UNPROT_DISASSOCIATE) C2S(NL80211_CMD_NEW_PEER_CANDIDATE) C2S(NL80211_CMD_GET_WOWLAN) C2S(NL80211_CMD_SET_WOWLAN) C2S(NL80211_CMD_START_SCHED_SCAN) C2S(NL80211_CMD_STOP_SCHED_SCAN) C2S(NL80211_CMD_SCHED_SCAN_RESULTS) C2S(NL80211_CMD_SCHED_SCAN_STOPPED) C2S(NL80211_CMD_SET_REKEY_OFFLOAD) C2S(NL80211_CMD_PMKSA_CANDIDATE) C2S(NL80211_CMD_TDLS_OPER) C2S(NL80211_CMD_TDLS_MGMT) C2S(NL80211_CMD_UNEXPECTED_FRAME) C2S(NL80211_CMD_PROBE_CLIENT) C2S(NL80211_CMD_REGISTER_BEACONS) C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) C2S(NL80211_CMD_SET_NOACK_MAP) C2S(NL80211_CMD_CH_SWITCH_NOTIFY) C2S(NL80211_CMD_START_P2P_DEVICE) C2S(NL80211_CMD_STOP_P2P_DEVICE) C2S(NL80211_CMD_CONN_FAILED) C2S(NL80211_CMD_SET_MCAST_RATE) C2S(NL80211_CMD_SET_MAC_ACL) C2S(NL80211_CMD_RADAR_DETECT) C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) C2S(NL80211_CMD_UPDATE_FT_IES) C2S(NL80211_CMD_FT_EVENT) C2S(NL80211_CMD_CRIT_PROTOCOL_START) C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) /* C2S(NL80211_CMD_GET_COALESCE) */ /* C2S(NL80211_CMD_SET_COALESCE) */ /* C2S(NL80211_CMD_CHANNEL_SWITCH) */ /* C2S(NL80211_CMD_VENDOR) */ /* C2S(NL80211_CMD_SET_QOS_MAP) */ /* C2S(NL80211_CMD_ADD_TX_TS) */ /* C2S(NL80211_CMD_DEL_TX_TS) */ default: return "NL80211_CMD_UNKNOWN"; } #undef C2S } /* */ static struct nl_sock* nl_create_handle(struct nl_cb* cb) { struct nl_sock* handle; handle = nl_socket_alloc_cb(cb); if (!handle) { return NULL; } if (genl_connect(handle)) { nl_socket_free(handle); return NULL; } return handle; } /* */ static int nl80211_no_seq_check(struct nl_msg* msg, void* arg) { return NL_OK; } /* */ static int nl80211_error_handler(struct sockaddr_nl* nla, struct nlmsgerr* err, void* arg) { *((int*)arg) = err->error; return NL_STOP; } /* */ static int nl80211_finish_handler(struct nl_msg* msg, void* arg) { *((int*)arg) = 0; return NL_SKIP; } /* */ static int nl80211_ack_handler(struct nl_msg* msg, void* arg) { *((int*)arg) = 0; return NL_STOP; } /* */ static int nl80211_send_and_recv(struct nl_sock *nl, struct nl_cb *nl_cb, struct nl_msg *msg, nl_valid_cb valid_cb, void *data) { struct nl_cb *cb; int result = -1; /* Clone netlink callback */ cb = nl_cb_clone(nl_cb); if (!cb) goto out; /* Complete send message */ result = nl_send_auto_complete(nl, msg); if (result < 0) goto out; result = 1; /* Customize message callback */ nl_cb_err(cb, NL_CB_CUSTOM, nl80211_error_handler, &result); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl80211_finish_handler, &result); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl80211_ack_handler, &result); if (valid_cb) nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_cb, data); while (result > 0) { int r = nl_recvmsgs(nl, cb); if (r < 0) log_printf(LOG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", __func__, r); } out: nl_cb_put(cb); nlmsg_free(msg); return result; } /* */ static int nl80211_send_and_recv_msg(struct nl80211_global_handle* globalhandle, struct nl_msg* msg, nl_valid_cb valid_cb, void* data) { return nl80211_send_and_recv(globalhandle->nl, globalhandle->nl_cb, msg, valid_cb, data); } static int nl80211_wlan_send_and_recv_msg(struct wifi_wlan *wlan, struct nl_msg *msg, nl_valid_cb valid_cb, void *data) { struct nl80211_wlan_handle *wlanhandle = (struct nl80211_wlan_handle *)wlan->handle; ASSERT(wlanhandle != NULL); return nl80211_send_and_recv_msg(wlanhandle->devicehandle->globalhandle, msg, valid_cb, data); } /* */ static int cb_family_handler(struct nl_msg* msg, void* data) { int i; struct nlattr* mcast_group; struct nlattr* tb_msg[CTRL_ATTR_MAX + 1]; struct nlattr* tb_msg_mcast_group[CTRL_ATTR_MCAST_GRP_MAX + 1]; struct genlmsghdr* gnlh = nlmsg_data(nlmsg_hdr(msg)); struct family_data* resource = (struct family_data*)data; nla_parse(tb_msg, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb_msg[CTRL_ATTR_MCAST_GROUPS]) { nla_for_each_nested(mcast_group, tb_msg[CTRL_ATTR_MCAST_GROUPS], i) { nla_parse(tb_msg_mcast_group, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcast_group), nla_len(mcast_group), NULL); if (tb_msg_mcast_group[CTRL_ATTR_MCAST_GRP_NAME] && tb_msg_mcast_group[CTRL_ATTR_MCAST_GRP_ID]) { if (!strncmp(nla_data(tb_msg_mcast_group[CTRL_ATTR_MCAST_GRP_NAME]), resource->group, nla_len(tb_msg_mcast_group[CTRL_ATTR_MCAST_GRP_NAME]))) { resource->id = nla_get_u32(tb_msg_mcast_group[CTRL_ATTR_MCAST_GRP_ID]); break; } } } } return NL_SKIP; } /* */ static int nl80211_get_multicast_id(struct nl80211_global_handle* globalhandle, const char* family, const char* group) { int result; struct nl_msg* msg; struct family_data resource = { -1, group }; ASSERT(globalhandle != NULL); ASSERT(family != NULL); ASSERT(group != NULL); /* */ msg = nlmsg_alloc(); if (!msg) return -1; /* */ genlmsg_put(msg, 0, 0, genl_ctrl_resolve(globalhandle->nl, "nlctrl"), 0, 0, CTRL_CMD_GETFAMILY, 0); nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family); /* */ result = nl80211_send_and_recv_msg(globalhandle, msg, cb_family_handler, &resource); if (!result) result = resource.id; else log_printf(LOG_ERR, "Unable get multicast id, error code: %d", result); return result; } static void *nl80211_command(struct nl80211_global_handle *globalhandle, struct nl_msg *msg, int flags, uint8_t cmd) { return genlmsg_put(msg, 0, 0, globalhandle->nl80211_id, 0, flags, cmd, 0); } static struct nl_msg *nl80211_ifindex_msg(struct nl80211_global_handle *globalhandle, int ifindex, int flags, uint8_t cmd) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return NULL; if (!nl80211_command(globalhandle, msg, flags, cmd) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) { nlmsg_free(msg); return NULL; } return msg; } static struct nl_msg *nl80211_wlan_msg(struct wifi_wlan *wlan, int flags, uint8_t cmd) { struct nl80211_wlan_handle *wlanhandle = (struct nl80211_wlan_handle*)wlan->handle;; return nl80211_ifindex_msg(wlanhandle->devicehandle->globalhandle, wlan->virtindex, flags, cmd); } /* */ static int nl80211_wlan_set_type(struct wifi_wlan* wlan, uint32_t type) { int result; struct nl_msg* msg; /* */ msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_SET_INTERFACE); if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, type)) { nlmsg_free(msg); return -1; } /* */ /* */ result = nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL); if (result) log_printf(LOG_ERR, "Unable set type, error code: %d", result); return result; } /* */ static int cb_get_type(struct nl_msg* msg, void* data) { struct nlattr* tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr* gnlh = nlmsg_data(nlmsg_hdr(msg)); uint32_t* type = (uint32_t*)data; nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb_msg[NL80211_ATTR_IFTYPE]) { *type = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]); } return NL_SKIP; } /* */ static uint32_t nl80211_wlan_get_type(struct wifi_wlan* wlan) { int result; struct nl_msg* msg; uint32_t type = NL80211_IFTYPE_UNSPECIFIED; /* */ msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_GET_INTERFACE); if (!msg) return NL80211_IFTYPE_UNSPECIFIED; /* */ result = nl80211_wlan_send_and_recv_msg(wlan, msg, cb_get_type, &type); if (result) { log_printf(LOG_ERR, "Unable get type, error code: %d", result); type = NL80211_IFTYPE_UNSPECIFIED; } return type; } /* */ static int nl80211_wlan_set_profile(struct wifi_wlan* wlan, uint32_t type) { int result; /* Change interface type */ result = nl80211_wlan_set_type(wlan, type); if (result && (type == nl80211_wlan_get_type(wlan))) { result = 0; /* No error */ } /* */ if (result) { if (result == -ENODEV) { return -1; } /* TODO */ } return result; } /* */ static int nl80211_device_changefrequency(struct wifi_device* device, struct wifi_frequency* freq) { int result; struct nl_msg* msg; struct capwap_list_item* wlansearch; struct nl80211_device_handle* devicehandle = (struct nl80211_device_handle*)device->handle; struct wifi_wlan* wlan = NULL; ASSERT(device != NULL); ASSERT(freq != NULL); /* Search a valid interface */ for (wlansearch = device->wlans->first; wlansearch; wlansearch = wlansearch->next) { struct wifi_wlan* element = (struct wifi_wlan*)wlansearch->item; if (element->flags & WIFI_WLAN_RUNNING) { wlan = element; break; } } /* */ if (!wlan) return -1; /* Set frequecy using device index of first BSS */ msg = nl80211_ifindex_msg(devicehandle->globalhandle, wlan->virtindex, 0, NL80211_CMD_SET_WIPHY); if (!msg || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->frequency)) { nlmsg_free(msg); return -1; } /* Set wifi frequency */ result = nl80211_send_and_recv_msg(devicehandle->globalhandle, msg, NULL, NULL); if (!result) log_printf(LOG_INFO, "Change %s frequency %d", wlan->virtname, (int)freq->frequency); else log_printf(LOG_ERR, "Unable set frequency %d, error code: %d", (int)freq->frequency, result); return result; } /* */ static void nl80211_wlan_client_probe_event(struct wifi_wlan* wlan, struct nlattr** tb_msg) { if (!tb_msg[NL80211_ATTR_MAC] || !tb_msg[NL80211_ATTR_ACK]) return; wifi_wlan_client_probe_event(wlan, nla_data(tb_msg[NL80211_ATTR_MAC])); } /* */ static int nl80211_wlan_event(struct wifi_wlan* wlan, struct genlmsghdr* gnlh, struct nlattr** tb_msg) { switch (gnlh->cmd) { case NL80211_CMD_FRAME: { uint32_t frequency = 0; int8_t rssi = 0; if (!tb_msg[NL80211_ATTR_FRAME]) break; if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) frequency = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]); if (tb_msg[NL80211_ATTR_RX_SIGNAL_DBM]) rssi = (uint8_t)nla_get_u32(tb_msg[NL80211_ATTR_RX_SIGNAL_DBM]); /* */ wifi_wlan_receive_station_frame( wlan, (struct ieee80211_header*)nla_data(tb_msg[NL80211_ATTR_FRAME]), nla_len(tb_msg[NL80211_ATTR_FRAME]), frequency, rssi, 0, 0); break; } case NL80211_CMD_FRAME_TX_STATUS: { struct nl80211_wlan_handle* wlanhandle = (struct nl80211_wlan_handle*)wlan->handle; uint64_t cookie; if (!tb_msg[NL80211_ATTR_FRAME] || !tb_msg[NL80211_ATTR_COOKIE]) break; cookie = nla_get_u64(tb_msg[NL80211_ATTR_COOKIE]); if (wlanhandle->last_cookie == cookie) { wlanhandle->last_cookie = 0; wifi_wlan_receive_station_ackframe( wlan, (struct ieee80211_header*)nla_data(tb_msg[NL80211_ATTR_FRAME]), nla_len(tb_msg[NL80211_ATTR_FRAME]), (tb_msg[NL80211_ATTR_ACK] ? 1 : 0)); } break; } case NL80211_CMD_TRIGGER_SCAN: case NL80211_CMD_NEW_SCAN_RESULTS: case NL80211_CMD_NEW_STATION: case NL80211_CMD_DEL_STATION: break; case NL80211_CMD_PROBE_CLIENT: nl80211_wlan_client_probe_event(wlan, tb_msg); break; default: log_printf(LOG_DEBUG, "*** nl80211_wlan_event: %s (%d)", nl80211_command_to_string((int)gnlh->cmd), (int)gnlh->cmd); break; } return NL_SKIP; } /* */ static int nl80211_wlan_valid_handler(struct nl_msg* msg, void* arg) { struct nlattr* tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr* gnlh = nlmsg_data(nlmsg_hdr(msg)); nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); return nl80211_wlan_event((struct wifi_wlan*)arg, gnlh, tb_msg); } /* */ static int nl80211_wlan_registerframe(struct wifi_wlan* wlan, uint16_t type, const uint8_t* match, int lengthmatch) { struct nl_msg* msg; struct nl80211_wlan_handle* wlanhandle = (struct nl80211_wlan_handle*)wlan->handle; /* */ msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_REGISTER_FRAME); if (!msg || nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, type) || nla_put(msg, NL80211_ATTR_FRAME_MATCH, lengthmatch, match)) { nlmsg_free(msg); return -1; } /* Destroy virtual device */ return nl80211_send_and_recv(wlanhandle->nl, wlanhandle->nl_cb, msg, NULL, NULL); } /* */ static int nl80211_global_destroy_virtdevice(struct nl80211_global_handle* globalhandle, uint32_t virtindex) { int result; struct nl_msg* msg; ASSERT(globalhandle != NULL); /* */ msg = nl80211_ifindex_msg(globalhandle, virtindex, 0, NL80211_CMD_DEL_INTERFACE); if (!msg) return -1; /* Destroy virtual device */ result = nl80211_send_and_recv_msg(globalhandle, msg, NULL, NULL); if (result) log_printf(LOG_ERR, "Unable destroy interface, error code: %d", result); return result; } /* */ static int cb_global_destroy_all_virtdevice(struct nl_msg* msg, void* data) { struct nlattr* tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr* gnlh = nlmsg_data(nlmsg_hdr(msg)); struct capwap_list* list = (struct capwap_list*)data; nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb_msg[NL80211_ATTR_WIPHY] && tb_msg[NL80211_ATTR_IFNAME] && tb_msg[NL80211_ATTR_IFINDEX]) { struct capwap_list_item* item = capwap_itemlist_create(sizeof(struct nl80211_virtdevice_item)); struct nl80211_virtdevice_item* virtitem = (struct nl80211_virtdevice_item*)item->item; /* Add virtual device info */ virtitem->phyindex = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]); virtitem->virtindex = nla_get_u32(tb_msg[NL80211_ATTR_IFINDEX]); strcpy(virtitem->virtname, nla_get_string(tb_msg[NL80211_ATTR_IFNAME])); capwap_itemlist_insert_after(list, NULL, item); } return NL_SKIP; } /* */ static void nl80211_global_destroy_all_virtdevice(struct nl80211_global_handle* globalhandle, uint32_t phyindex) { int result; struct nl_msg* msg; struct capwap_list* list; struct capwap_list_item* itemsearch; ASSERT(globalhandle != NULL); /* */ msg = nlmsg_alloc(); if (!msg || !nl80211_command(globalhandle, msg, NLM_F_DUMP, NL80211_CMD_GET_INTERFACE) || nla_put_u32(msg, NL80211_ATTR_WIPHY, phyindex)) { nlmsg_free(msg); return; } /* Retrieve all virtual interface */ list = capwap_list_create(); result = nl80211_send_and_recv_msg(globalhandle, msg, cb_global_destroy_all_virtdevice, list); if (!result) { for (itemsearch = list->first; itemsearch; itemsearch = itemsearch->next) { struct nl80211_virtdevice_item* virtitem = (struct nl80211_virtdevice_item*)itemsearch->item; /* Destroy virtual device */ if (virtitem->phyindex == phyindex) { wifi_iface_down(globalhandle->sock_util, virtitem->virtname); result = nl80211_global_destroy_virtdevice(globalhandle, virtitem->virtindex); if (result) { log_printf(LOG_ERR, "Unable to destroy virtual device, error code: %d", result); } } } } else { /* Error get virtual devices */ log_printf(LOG_ERR, "Unable retrieve virtual device info, error code: %d", result); } capwap_list_free(list); } /* */ static wifi_wlan_handle nl80211_wlan_create(struct wifi_device* device, struct wifi_wlan* wlan) { int result; struct nl_msg* msg; struct nl80211_wlan_handle* wlanhandle; struct nl80211_device_handle* devicehandle = (struct nl80211_device_handle*)device->handle; ASSERT(device != NULL); ASSERT(device->handle != NULL); ASSERT(wlan != NULL); if (!wlan->device->capability->supp_cmds.poll_command_supported) { log_printf(LOG_CRIT, "unable to run nl80211 WTP on device %s without " "probe_client command support", wlan->device->phyname); return NULL; } /* */ msg = nlmsg_alloc(); if (!msg || /* Create wlan interface */ !nl80211_command(devicehandle->globalhandle, msg, 0, NL80211_CMD_NEW_INTERFACE) || nla_put_u32(msg, NL80211_ATTR_WIPHY, device->phyindex) || #if defined(NL80211_ATTR_IFACE_SOCKET_OWNER) nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER) || #endif nla_put_u32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION) || nla_put_string(msg, NL80211_ATTR_IFNAME, wlan->virtname)) { nlmsg_free(msg); return NULL; } /* */ result = nl80211_send_and_recv_msg(devicehandle->globalhandle, msg, NULL, NULL); /* Check interface */ if (result || !wifi_iface_index(wlan->virtname)) { log_printf(LOG_ERR, "Unable create interface %s, error code: %d", wlan->virtname, result); return NULL; } /* Init wlan */ wlanhandle = (struct nl80211_wlan_handle*)capwap_alloc(sizeof(struct nl80211_wlan_handle)); memset(wlanhandle, 0, sizeof(struct nl80211_wlan_handle)); wlanhandle->devicehandle = devicehandle; return (wifi_wlan_handle)wlanhandle; } /* */ static void nl80211_global_event_receive_cb(EV_P_ ev_io *w, int revents) { struct nl80211_global_handle *globalhandle = (struct nl80211_global_handle *) (((char *)w) - offsetof(struct nl80211_global_handle, nl_event_ev)); int res; log_printf(LOG_WARNING, "nl80211_global_event_receive_cb on fd %d", w->fd); /* */ res = nl_recvmsgs(globalhandle->nl_event, globalhandle->nl_cb); if (res) { log_printf(LOG_WARNING, "Receive nl80211 message failed: %d", res); } } static void nl80211_wlan_event_receive_cb(EV_P_ ev_io *w, int revents) { struct nl80211_wlan_handle *wlanhandle = (struct nl80211_wlan_handle *) (((char *)w) - offsetof(struct nl80211_wlan_handle, nl_ev)); int res; log_printf(LOG_WARNING, "nl80211_wlan_event_receive_cb on fd %d", w->fd); /* */ res = nl_recvmsgs(wlanhandle->nl, wlanhandle->nl_cb); if (res) { log_printf(LOG_WARNING, "Receive nl80211 message failed: %d", res); } } /* */ static int nl80211_wlan_setbeacon(struct wifi_wlan* wlan) { int result; struct nl_msg* msg; uint8_t cmd = NL80211_CMD_START_AP; struct ieee80211_beacon_params params; uint8_t buffer[IEEE80211_MTU]; int beacon_set; /* Create beacon packet */ memset(¶ms, 0, sizeof(struct ieee80211_beacon_params)); memcpy(params.bssid, wlan->address, ETH_ALEN); params.beaconperiod = wlan->device->beaconperiod; params.capability = wifi_wlan_check_capability(wlan, wlan->capability); params.ssid = wlan->ssid; params.ssid_hidden = wlan->ssid_hidden; memcpy(params.supportedrates, wlan->device->supportedrates, wlan->device->supportedratescount); params.supportedratescount = wlan->device->supportedratescount; params.mode = wlan->device->currentfrequency.mode; params.erpinfo = ieee80211_get_erpinfo(wlan->device->currentfrequency.mode, wlan->device->olbc, wlan->device->stationsnonerpcount, wlan->device->stationsnoshortpreamblecount, wlan->device->shortpreamble); params.channel = wlan->device->currentfrequency.channel; params.beacon_ies = wlan->beacon_ies; params.beacon_ies_len = wlan->beacon_ies_len; params.response_ies = wlan->response_ies; params.response_ies_len = wlan->response_ies_len; /* Enable probe response offload only in CAPWAP Local Mac */ if ((wlan->macmode == CAPWAP_ADD_WLAN_MACMODE_LOCAL) && (wlan->device->capability->capability & WIFI_CAPABILITY_FLAGS_PROBE_RESPONSE_OFFLOAD)) { params.flags |= IEEE80221_CREATE_BEACON_FLAGS_PROBE_RESPONSE_OFFLOAD; } /* */ result = ieee80211_create_beacon(buffer, sizeof(buffer), ¶ms); if (result < 0) { return -1; } beacon_set = !!(wlan->flags & WIFI_WLAN_SET_BEACON); log_printf(LOG_DEBUG, "nl80211: Set beacon (beacon_set=%d)", beacon_set); if (beacon_set) cmd = NL80211_CMD_SET_BEACON; log_hexdump(LOG_DEBUG, "nl80211: Beacon head", params.headbeacon, params.headbeaconlength); log_hexdump(LOG_DEBUG, "nl80211: Beacon tail", params.tailbeacon, params.tailbeaconlength); log_printf(LOG_DEBUG, "nl80211: ifindex=%d", wlan->virtindex); log_printf(LOG_DEBUG, "nl80211: beacon_int=%d", wlan->device->beaconperiod); log_printf(LOG_DEBUG, "nl80211: dtim_period=%d", wlan->device->dtimperiod); log_hexdump(LOG_DEBUG, "nl80211: ssid", (uint8_t *)wlan->ssid, strlen(wlan->ssid)); msg = nl80211_wlan_msg(wlan, 0, cmd); if (!msg || nla_put(msg, NL80211_ATTR_BEACON_HEAD, params.headbeaconlength, params.headbeacon) || nla_put(msg, NL80211_ATTR_BEACON_TAIL, params.tailbeaconlength, params.tailbeacon) || nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL, wlan->device->beaconperiod) || nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, wlan->device->dtimperiod) || nla_put(msg, NL80211_ATTR_SSID, strlen(wlan->ssid), wlan->ssid)) goto out_err; if ((wlan->device->capability->capability & WIFI_CAPABILITY_FLAGS_PROBE_RESPONSE_OFFLOAD) && (params.proberesponseoffloadlength > 0)) { log_hexdump(LOG_DEBUG, "nl80211: proberesp (offload)", params.proberesponseoffload, params.proberesponseoffloadlength); if (nla_put(msg, NL80211_ATTR_PROBE_RESP, params.proberesponseoffloadlength, params.proberesponseoffload)) goto out_err; } if (!wlan->ssid_hidden) { log_printf(LOG_DEBUG, "nl80211: hidden SSID not in use"); if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID, NL80211_HIDDEN_SSID_NOT_IN_USE)) goto out_err; } else { log_printf(LOG_DEBUG, "nl80211: hidden SSID zero len"); if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID, NL80211_HIDDEN_SSID_ZERO_LEN)) goto out_err; } if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, ((wlan->authmode == CAPWAP_ADD_WLAN_AUTHTYPE_WEP) ? NL80211_AUTHTYPE_SHARED_KEY : NL80211_AUTHTYPE_OPEN_SYSTEM)) || nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)) goto out_err; /* privacy */ if (wlan->rsne) log_printf(LOG_DEBUG, "RSNE capability: %04x", wlan->capability); if (wlan->rsne && wlan->capability & IEEE80211_CAPABILITY_PRIVACY) { uint8_t *data = (uint8_t *)(wlan->rsne + 1); uint16_t suites_num; uint32_t *suites; int i; data += 2; nla_put_flag(msg, NL80211_ATTR_PRIVACY); nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, NL80211_WPA_VERSION_2); log_printf(LOG_ERR, "nl80211: Cipher Suite Group: %08x", ntohl(*(uint32_t *)data)); /* find a better place for the cipher suite assignment */ wlan->group_cipher_suite = ntohl(*(uint32_t *)data); nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, ntohl(*(uint32_t *)data)); data += sizeof(uint32_t); suites_num = *(uint16_t *)data; data += 2; suites = alloca(suites_num * sizeof(uint32_t)); for (i = 0; i < suites_num; i++) { suites[i] = ntohl(*(uint32_t *)data); log_printf(LOG_ERR, "nl80211: Cipher Suite Pairwise[%d]: %08x", i, suites[i]); data +=sizeof(uint32_t); } nla_put(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, suites_num * sizeof(uint32_t), suites); suites_num = *(uint16_t *)data; data += 2; suites = alloca(suites_num * sizeof(uint32_t)); for (i = 0; i < suites_num; i++) { suites[i] = ntohl(*(uint32_t *)data); log_printf(LOG_ERR, "nl80211: AKM Suite[%d]: %08x", i, suites[i]); data +=sizeof(uint32_t); } nla_put(msg, NL80211_ATTR_AKM_SUITES, suites_num * sizeof(uint32_t), suites); } /* Start AP */ result = nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL); if (result) log_printf(LOG_ERR, "Unable set beacon, error code: %d", result); /* Configure AP */ if (!result) { msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_SET_BSS); if (!msg || nla_put_u8(msg, NL80211_ATTR_BSS_CTS_PROT, ((params.erpinfo & IEEE80211_ERP_INFO_USE_PROTECTION) ? 1 : 0)) || nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, ((!wlan->device->stationsnoshortpreamblecount && wlan->device->shortpreamble) ? 1 : 0))) goto out_err; //nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ???); if (wlan->device->currentfrequency.mode & IEEE80211_RADIO_TYPE_80211G) if (nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, (!wlan->device->stationsnoshortslottimecount ? 1 : 0))) goto out_err; if (wlan->device->basicratescount > 0) if (nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, wlan->device->basicratescount, wlan->device->basicrates)) goto out_err; if (wlan->ht_opmode >= 0) { log_printf(LOG_DEBUG, "nl80211: h_opmode=%04x", wlan->ht_opmode); if (nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, wlan->ht_opmode)) goto out_err; } /* */ result = nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL); if (!result) wlan->flags |= WIFI_WLAN_SET_BEACON; else log_printf(LOG_ERR, "Unable set BSS, error code: %d", result); } return result; out_err: nlmsg_free(msg); return -1; } static inline int is_broadcast_ether_addr(const uint8_t *a) { return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; } #define broadcast_ether_addr (const uint8_t *) "\xff\xff\xff\xff\xff\xff" static int nl80211_set_key(struct wifi_wlan* wlan, uint32_t alg, const uint8_t *addr, int key_idx, int set_tx, const uint8_t *seq, size_t seq_len, const uint8_t *key, size_t key_len) { struct nl_msg *msg; int ret; log_printf(LOG_DEBUG, "%s: ifindex=%d alg=%08x addr=%p key_idx=%d " "set_tx=%d seq_len=%lu key_len=%lu", __func__, wlan->virtindex, alg, addr, key_idx, set_tx, (unsigned long) seq_len, (unsigned long) key_len); if (alg == 0) { msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_DEL_KEY); if (!msg) return -ENOBUFS; } else { msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_NEW_KEY); if (!msg || nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) || nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, alg)) goto fail; log_hexdump(LOG_DEBUG, "nl80211: KEY_DATA", key, key_len); } if (seq && seq_len) { if (nla_put(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq)) goto fail; log_hexdump(LOG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len); } if (addr && !is_broadcast_ether_addr(addr)) { log_printf(LOG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) goto fail; if (key_idx && !set_tx) { log_printf(LOG_DEBUG, " RSN IBSS RX GTK"); if (nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, NL80211_KEYTYPE_GROUP)) goto fail; } } else if (addr && is_broadcast_ether_addr(addr)) { struct nlattr *types; log_printf(LOG_DEBUG, " broadcast key"); types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); if (!types || nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST)) goto fail; nla_nest_end(msg, types); } if (nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx)) goto fail; ret = nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL); if ((ret == -ENOENT || ret == -ENOLINK) && alg == 0) ret = 0; if (ret) log_printf(LOG_DEBUG, "nl80211: set_key failed; err=%d %s)", ret, strerror(-ret)); /* * If we failed or don't need to set the default TX key (below), * we're done here. */ if (ret || !set_tx || alg == 0) return ret; if (addr && is_broadcast_ether_addr(addr)) { struct nlattr *types; msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_SET_KEY); if (!msg || nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) || nla_put_flag(msg, NL80211_ATTR_KEY_DEFAULT)) goto fail; types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); if (!types || nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST)) goto fail; nla_nest_end(msg, types); ret = nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL); if (ret == -ENOENT) ret = 0; if (ret) log_printf(LOG_DEBUG, "nl80211: set_key default failed; " "err=%d %s)", ret, strerror(-ret)); } return ret; fail: nlmsg_free(msg); return -ENOBUFS; } /* */ static int nl80211_wlan_startap(struct wifi_wlan* wlan) { int i; int result; struct nl80211_wlan_handle* wlanhandle; ASSERT(wlan != NULL); ASSERT(wlan->handle != NULL); /* Configure interface with AP profile */ if (nl80211_wlan_set_profile(wlan, NL80211_IFTYPE_AP)) { return -1; } /* Socket management */ wlanhandle = (struct nl80211_wlan_handle*)wlan->handle; wlanhandle->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (!wlanhandle->nl_cb) { return -1; } /* */ nl_cb_set(wlanhandle->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl80211_no_seq_check, NULL); nl_cb_set(wlanhandle->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, nl80211_wlan_valid_handler, (void*)wlan); wlanhandle->nl = nl_create_handle(wlanhandle->nl_cb); if (!wlanhandle->nl) return -1; /* Register frames */ for (i = 0; i < sizeof(g_stypes) / sizeof(g_stypes[0]); i++) { result = nl80211_wlan_registerframe(wlan, (IEEE80211_FRAMECONTROL_TYPE_MGMT << 2) | (g_stypes[i] << 4), NULL, 0); if (result) { log_printf(LOG_ERR, "Unable to register frame %d, error code: %d", g_stypes[i], result); return -1; } } /* */ if (wlan->tunnelmode != CAPWAP_ADD_WLAN_TUNNELMODE_LOCAL) { /* Join interface in kernel module */ uint32_t flags = ((wlan->tunnelmode == CAPWAP_ADD_WLAN_TUNNELMODE_80211) ? WTP_KMOD_FLAGS_TUNNEL_NATIVE : WTP_KMOD_FLAGS_TUNNEL_8023); if (!wtp_kmod_join_mac80211_device(wlan, flags)) { log_printf(LOG_INFO, "Joined the interface %d in kernel mode ", wlan->virtindex); } else { log_printf(LOG_ERR, "Unable to join the interface %d in kernel mode ", wlan->virtindex); return -1; } } /* Enable interface */ wlan->flags |= WIFI_WLAN_RUNNING; /* hook into I/O loop */ ev_io_init(&wlanhandle->nl_ev, nl80211_wlan_event_receive_cb, nl_socket_get_fd(wlanhandle->nl), EV_READ); ev_io_start(EV_DEFAULT_UC_ &wlanhandle->nl_ev); if (wifi_iface_up(wlanhandle->devicehandle->globalhandle->sock_util, wlan->virtname)) { return -1; } /* Configure device if first BSS device */ if (!wlan->device->wlanactive) { /* Set device frequency */ nl80211_device_changefrequency(wlan->device, &wlan->device->currentfrequency); /* TODO Get current frequency */ } /* Set beacon */ if (nl80211_wlan_setbeacon(wlan)) { return -1; } if (wlan->keylength && wlan->key) nl80211_set_key(wlan, wlan->group_cipher_suite, broadcast_ether_addr, wlan->keyindex, 1, NULL, 0, wlan->key, wlan->keylength); /* Enable operation status */ wlan->flags |= WIFI_WLAN_OPERSTATE_RUNNING; netlink_set_link_status(wlanhandle->devicehandle->globalhandle->netlinkhandle, wlan->virtindex, -1, IF_OPER_UP); return 0; } /* */ static void nl80211_wlan_stopap(struct wifi_wlan* wlan) { struct nl_msg* msg; struct nl80211_wlan_handle* wlanhandle; ASSERT(wlan != NULL); ASSERT(wlan->handle != NULL); /* */ wlanhandle = (struct nl80211_wlan_handle*)wlan->handle; /* */ if (wlan->tunnelmode != CAPWAP_ADD_WLAN_TUNNELMODE_LOCAL) { /* Leave interface from kernel module */ wtp_kmod_leave_mac80211_device(wlan); } /* */ if (wlan->flags & WIFI_WLAN_SET_BEACON) { msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_STOP_AP); if (msg) /* Stop AP */ nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL); } /* Disable interface */ wifi_iface_down(wlanhandle->devicehandle->globalhandle->sock_util, wlan->virtname); /* Configure interface with station profile */ nl80211_wlan_set_profile(wlan, NL80211_IFTYPE_STATION); /* */ if (ev_is_active(&wlanhandle->nl_ev)) ev_io_stop(EV_DEFAULT_UC_ &wlanhandle->nl_ev); if (wlanhandle->nl) { nl_socket_free(wlanhandle->nl); wlanhandle->nl = NULL; } if (wlanhandle->nl_cb) { nl_cb_put(wlanhandle->nl_cb); wlanhandle->nl_cb = NULL; } } /* */ static int cb_wlan_send_frame(struct nl_msg* msg, void* arg) { struct nlattr* tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr* gnlh = nlmsg_data(nlmsg_hdr(msg)); /* */ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb_msg[NL80211_ATTR_COOKIE]) { *(uint64_t*)arg = nla_get_u64(tb_msg[NL80211_ATTR_COOKIE]); } return NL_SKIP; } /* */ static int nl80211_wlan_sendframe(struct wifi_wlan* wlan, uint8_t* frame, int length, uint32_t frequency, uint32_t duration, int offchannel_tx_ok, int no_cck_rate, int no_wait_ack) { int result; uint64_t cookie; struct nl_msg* msg; struct nl80211_wlan_handle* wlanhandle = (struct nl80211_wlan_handle*)wlan->handle; ASSERT(wlan != NULL); ASSERT(wlan->handle != NULL); ASSERT(frame != NULL); ASSERT(length > 0); log_printf(LOG_DEBUG, "nl80211: CMD_FRAME frequency=%u duration=%u no_cck=%d " "no_ack=%d offchanok=%d", frequency, duration, no_cck_rate, no_wait_ack, offchannel_tx_ok); log_hexdump(LOG_DEBUG, "CMD_FRAME", frame, length); /* */ msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_FRAME); if (!msg) goto out_err; if (frequency) if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, frequency)) goto out_err; if (duration) if (nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) goto out_err; if (offchannel_tx_ok && (wlan->device->capability->capability & WIFI_CAPABILITY_FLAGS_OFFCHANNEL_TX_OK)) if (nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) goto out_err; if (no_cck_rate) if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) goto out_err; if (no_wait_ack) if (nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) goto out_err; if (nla_put(msg, NL80211_ATTR_FRAME, length, frame)) goto out_err; /* Send frame */ cookie = 0; result = nl80211_wlan_send_and_recv_msg(wlan, msg, cb_wlan_send_frame, &cookie); if (result) log_printf(LOG_DEBUG, "nl80211: Frame command failed: ret=%d " "(%s) (frequency=%u duration=%u)", result, strerror(-result), frequency, duration); wlanhandle->last_cookie = (result || no_wait_ack ? 0 : cookie); return result; out_err: nlmsg_free(msg); return -1; } #if 0 /* Sending non-Mgmt Frames via nl80211_wlan_sendframe does not work, disable this till the WTP kernel mode supports frame injection from userspace */ /* Send data frame to poll STA and check whether this frame is ACKed */ static void nl80211_wlan_send_null_frame(struct wifi_wlan* wlan, const uint8_t* address, int qos) { struct { struct ieee80211_header hdr; uint16_t qos_ctl; } STRUCT_PACKED nulldata; size_t size; memset(&nulldata, 0, sizeof(nulldata)); if (qos) { nulldata.hdr.framecontrol = IEEE80211_FRAME_CONTROL(IEEE80211_FRAMECONTROL_TYPE_DATA, IEEE80211_FRAMECONTROL_DATA_SUBTYPE_QOSNULL); size = sizeof(nulldata); } else { nulldata.hdr.framecontrol = IEEE80211_FRAME_CONTROL(IEEE80211_FRAMECONTROL_TYPE_DATA, IEEE80211_FRAMECONTROL_DATA_SUBTYPE_NULL); size = sizeof(struct ieee80211_header); } nulldata.hdr.framecontrol |= __cpu_to_le16(IEEE80211_FRAME_CONTROL_MASK_FROMDS); memcpy(nulldata.hdr.address1, address, ETH_ALEN); memcpy(nulldata.hdr.address2, wlan->address, ETH_ALEN); memcpy(nulldata.hdr.address3, wlan->address, ETH_ALEN); if (nl80211_wlan_sendframe(wlan, (uint8_t *)&nulldata, size, wlan->device->currentfrequency.frequency, 0, 0, 0, 0)) log_printf(LOG_DEBUG, "nl80211_send_null_frame: Failed to send poll frame"); } #endif /* */ static void nl80211_wlan_poll_station(struct wifi_wlan* wlan, const uint8_t* address, int qos) { int result; struct nl_msg* msg; ASSERT(wlan != NULL); ASSERT(wlan->handle != NULL); ASSERT(address != NULL); #if 0 /* see nl80211_wlan_send_null_frame for explanation */ if (!wlan->device->capability->supp_cmds.poll_command_supported) { nl80211_wlan_send_null_frame(wlan, address, qos); return; } #endif /* */ msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_PROBE_CLIENT); if (!msg || nla_put(msg, NL80211_ATTR_MAC, MACADDRESS_EUI48_LENGTH, address)) { nlmsg_free(msg); return; } /* */ result = nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL); if (result < 0) log_printf(LOG_DEBUG, "nl80211: Client probe request for " MACSTR " failed: ret=%d (%s)", MAC2STR(address), result, strerror(-result)); } /* */ static void nl80211_wlan_delete(struct wifi_wlan* wlan) { struct nl80211_wlan_handle* wlanhandle; ASSERT(wlan != NULL); /* */ wlanhandle = (struct nl80211_wlan_handle*)wlan->handle; if (wlanhandle) { if (wlan->virtindex) { nl80211_global_destroy_virtdevice(wlanhandle->devicehandle->globalhandle, wlan->virtindex); } capwap_free(wlanhandle); } } /* */ static uint32_t nl80211_station_get_flags(struct wifi_station* station) { uint32_t result = 0; ASSERT(station != NULL); if (station->flags & WIFI_STATION_FLAGS_AUTHORIZED) { result |= 1 << NL80211_STA_FLAG_AUTHORIZED; } if (!(station->flags & WIFI_STATION_FLAGS_NO_SHORT_PREAMBLE)) { result |= 1 << NL80211_STA_FLAG_SHORT_PREAMBLE; } if (station->flags & WIFI_STATION_FLAGS_WMM) { result |= 1 << NL80211_STA_FLAG_WME; } return result; } /* */ int nl80211_station_authorize(struct wifi_wlan* wlan, struct wifi_station* station) { int result; struct nl_msg* msg; struct nl80211_sta_flag_update flagstation; ASSERT(wlan != NULL); ASSERT(wlan->handle != NULL); ASSERT(station != NULL); ASSERT(wlan == station->wlan); log_printf(LOG_DEBUG, "nl80211: Add STA " MACSTR, MAC2STR(station->address)); log_hexdump(LOG_DEBUG, " * supported rates", station->supportedrates, station->supportedratescount); log_printf(LOG_DEBUG, " * aid=%u", station->aid); log_printf(LOG_DEBUG, " * listen_interval=%u", station->listeninterval); log_printf(LOG_DEBUG, " * capability=0x%x", station->capability); msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_NEW_STATION); if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, station->address) || nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES, station->supportedratescount, station->supportedrates) || nla_put_u16(msg, NL80211_ATTR_STA_AID, station->aid) || nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, station->listeninterval) || nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY, station->capability)) goto out_err; /* */ memset(&flagstation, 0, sizeof(struct nl80211_sta_flag_update)); flagstation.mask = nl80211_station_get_flags(station); flagstation.set = flagstation.mask; log_printf(LOG_DEBUG, " * flags set=0x%x mask=0x%x", flagstation.set, flagstation.mask); nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(struct nl80211_sta_flag_update), &flagstation); if (station->flags & WIFI_STATION_FLAGS_WMM) { struct nlattr *wme; log_printf(LOG_DEBUG, " * qosinfo=0x%x", station->qosinfo); wme = nla_nest_start(msg, NL80211_ATTR_STA_WME); if (!wme || nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES, station->qosinfo & WMM_QOSINFO_STA_AC_MASK) || nla_put_u8(msg, NL80211_STA_WME_MAX_SP, (station->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) & WMM_QOSINFO_STA_SP_MASK)) goto out_err; nla_nest_end(msg, wme); } if (station->flags & WIFI_STATION_FLAGS_HT_CAP) { log_hexdump(LOG_DEBUG, " * ht_capabilities", (uint8_t *)&station->ht_cap, sizeof(station->ht_cap)); if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(station->ht_cap), &station->ht_cap)) goto out_err; } /* */ result = nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL); switch (result) { case -EEXIST: result = 0; /* FALL THROUGH */ case 0: log_printf(LOG_INFO, "Authorized station: " MACSTR, MAC2STR(station->address)); break; default: log_printf(LOG_ERR, "Unable to authorized station, error code: %d", result); break; } return result; out_err: nlmsg_free(msg); return -1; } /* */ int nl80211_station_deauthorize(struct wifi_wlan* wlan, const uint8_t* address) { int result; struct nl_msg* msg; ASSERT(wlan != NULL); ASSERT(wlan->handle != NULL); ASSERT(address != NULL); /* */ msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_DEL_STATION); if (!msg || nla_put(msg, NL80211_ATTR_MAC, MACADDRESS_EUI48_LENGTH, address)) { nlmsg_free(msg); return -1; } /* */ result = nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL); switch (result) { case -ENOENT: result = 0; /* FALL THROUGH */ case 0: log_printf(LOG_INFO, "Deauthorize station: " MACSTR, MAC2STR(address)); break; default: log_printf(LOG_ERR, "Unable delete station, error code: %d", result); break; } return result; } static int cb_nl80211_station_data(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nl80211_station_data *data = arg; struct nlattr *stats[NL80211_STA_INFO_MAX + 1]; static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 }, [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_STA_INFO]) { log_printf(LOG_DEBUG, "sta stats missing!"); return NL_SKIP; } if (nla_parse_nested(stats, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO], stats_policy)) return NL_SKIP; if (stats[NL80211_STA_INFO_INACTIVE_TIME]) data->inactive_msec = nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); /* Fetch the 32-bit counters first. */ if (stats[NL80211_STA_INFO_RX_BYTES]) data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); if (stats[NL80211_STA_INFO_TX_BYTES]) data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); if (stats[NL80211_STA_INFO_RX_BYTES64] && stats[NL80211_STA_INFO_TX_BYTES64]) { /* * The driver supports 64-bit counters, so use them to override * the 32-bit values. */ data->rx_bytes = nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]); data->tx_bytes = nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]); data->bytes_64bit = 1; } if (stats[NL80211_STA_INFO_RX_PACKETS]) data->rx_packets = nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); if (stats[NL80211_STA_INFO_TX_PACKETS]) data->tx_packets = nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); if (stats[NL80211_STA_INFO_TX_FAILED]) data->tx_retry_failed = nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); return NL_SKIP; } /* */ static int nl80211_station_data(struct wifi_wlan *wlan, const uint8_t *address, struct nl80211_station_data *data) { struct nl_msg* msg; ASSERT(wlan != NULL); ASSERT(wlan->handle != NULL); ASSERT(address != NULL); ASSERT(data != NULL); /* */ msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_GET_STATION); if (!msg || nla_put(msg, NL80211_ATTR_MAC, MACADDRESS_EUI48_LENGTH, address)) { nlmsg_free(msg); return -1; } /* */ return nl80211_wlan_send_and_recv_msg(wlan, msg, cb_nl80211_station_data, data); } /* */ static int nl80211_station_get_inact_sec(struct wifi_wlan* wlan, const uint8_t* address) { int result; struct nl80211_station_data data; ASSERT(wlan != NULL); ASSERT(wlan->handle != NULL); ASSERT(address != NULL); result = nl80211_station_data(wlan, address, &data); if (!result) return data.inactive_msec / 1000; return -1; } /* */ static int cb_device_init(struct nl_msg* msg, void* data) { struct nlattr* tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr* gnlh = nlmsg_data(nlmsg_hdr(msg)); struct capwap_list* list = (struct capwap_list*)data; nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb_msg[NL80211_ATTR_WIPHY_NAME] && tb_msg[NL80211_ATTR_WIPHY]) { struct capwap_list_item* item = capwap_itemlist_create(sizeof(struct nl80211_phydevice_item)); struct nl80211_phydevice_item* phyitem = (struct nl80211_phydevice_item*)item->item; /* Add physical device info */ phyitem->index = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]); strcpy(phyitem->name, nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME])); capwap_itemlist_insert_after(list, NULL, item); } return NL_SKIP; } /* */ int nl80211_device_init(wifi_global_handle handle, struct wifi_device* device) { int result; struct nl_msg* msg; struct capwap_list* list; struct capwap_list_item* item; struct nl80211_device_handle* devicehandle = NULL; struct nl80211_global_handle* globalhandle = (struct nl80211_global_handle*)handle; ASSERT(handle != NULL); ASSERT(device != NULL); /* */ msg = nlmsg_alloc(); if (!msg || !nl80211_command(globalhandle, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY)) { nlmsg_free(msg); return -1; } /* Retrieve all physical interface */ list = capwap_list_create(); result = nl80211_send_and_recv_msg(globalhandle, msg, cb_device_init, list); if (!result) { for (item = list->first; item; item = item->next) { struct nl80211_phydevice_item* phyitem = (struct nl80211_phydevice_item*)item->item; if (!strcmp(phyitem->name, device->phyname)) { /* Create device */ devicehandle = (struct nl80211_device_handle*)capwap_alloc(sizeof(struct nl80211_device_handle)); memset(devicehandle, 0, sizeof(struct nl80211_device_handle)); /* */ devicehandle->globalhandle = globalhandle; /* */ device->handle = (wifi_device_handle)devicehandle; device->phyindex = phyitem->index; break; } } } else { /* Error get physical devices */ log_printf(LOG_ERR, "Unable retrieve physical device info, error code: %d", result); } /* */ capwap_list_free(list); if (!devicehandle) { return -1; } /* Remove all virtual adapter from wifi device */ nl80211_global_destroy_all_virtdevice(globalhandle, device->phyindex); return 0; } static void phydevice_capability_supported_iftypes(struct wifi_capability *capability, struct nlattr *tb) { struct nlattr *nl_mode; int i; if (tb == NULL) return; capability->flags |= WIFI_CAPABILITY_RADIOSUPPORTED; nla_for_each_nested(nl_mode, tb, i) { switch (nla_type(nl_mode)) { case NL80211_IFTYPE_AP: capability->radiosupported |= WIFI_CAPABILITY_AP_SUPPORTED; break; case NL80211_IFTYPE_AP_VLAN: capability->radiosupported |= WIFI_CAPABILITY_AP_VLAN_SUPPORTED; break; case NL80211_IFTYPE_ADHOC: capability->radiosupported |= WIFI_CAPABILITY_ADHOC_SUPPORTED; break; case NL80211_IFTYPE_WDS: capability->radiosupported |= WIFI_CAPABILITY_WDS_SUPPORTED; break; case NL80211_IFTYPE_MONITOR: capability->radiosupported |= WIFI_CAPABILITY_MONITOR_SUPPORTED; break; } } } static void phydevice_capability_feature_flags(struct wifi_capability *capability, struct nlattr *tb) { uint32_t flags; if (tb == NULL) return; flags = nla_get_u32(tb); log_printf(LOG_DEBUG, "nl80211: Feature Flag: %08x", flags); if (flags & NL80211_FEATURE_INACTIVITY_TIMER) { capability->flags |= WIFI_CAPABILITY_FLAGS_INACTIVITY_TIMER; log_printf(LOG_WARNING, "Driver supports NL80211 INACTIVITY_TIMER, but we don't"); } } static void phydevice_capability_supp_cmds(struct wifi_commands_capability *cmd_cap, struct nlattr *tb) { int i; struct nlattr *nl_cmd; if (tb == NULL) return; nla_for_each_nested(nl_cmd, tb, i) { switch (nla_get_u32(nl_cmd)) { case NL80211_CMD_PROBE_CLIENT: cmd_cap->poll_command_supported = 1; break; } } } static void phydevice_capability_cipher_suites(struct wifi_capability *capability, struct nlattr *tb) { size_t size; if (tb == NULL) return; size = nla_len(tb); if (size == 0 || (size % sizeof(uint32_t)) != 0) return; capability->ciphers = capwap_clone(nla_data(tb), size); if (!capability->ciphers) return; capability->ciphers_count = size / sizeof(uint32_t); capability->flags |= WIFI_CAPABILITY_CIPHERS; } static void phydevice_capability_freq(struct wifi_capability *capability, struct wifi_band_capability *bandcap, struct nlattr *tb_freq[]) { unsigned long frequency; unsigned long band; struct wifi_freq_capability *freq; frequency = (unsigned long)nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); band = (IS_IEEE80211_FREQ_BG(frequency) ? WIFI_BAND_2GHZ : (IS_IEEE80211_FREQ_A(frequency) ? WIFI_BAND_5GHZ : WIFI_BAND_UNKNOWN)); if (band == WIFI_BAND_UNKNOWN) return; freq = (struct wifi_freq_capability *) capwap_array_get_item_pointer(bandcap->freq, bandcap->freq->count); /* Set band */ if (bandcap->band == WIFI_BAND_UNKNOWN) { bandcap->band = band; } else if (bandcap->band != band) { log_printf(LOG_WARNING, "Multiple wireless band into logical band"); } /* Retrieve frequency and channel */ freq->frequency = frequency; freq->channel = ieee80211_frequency_to_channel(frequency); if (IS_IEEE80211_FREQ_BG(frequency)) { capability->flags |= WIFI_CAPABILITY_RADIOTYPE; capability->radiotype |= (CAPWAP_RADIO_TYPE_80211B | CAPWAP_RADIO_TYPE_80211G); } else if (IS_IEEE80211_FREQ_A(frequency)) { capability->flags |= WIFI_CAPABILITY_RADIOTYPE; capability->radiotype |= CAPWAP_RADIO_TYPE_80211A; } /* Get max tx power */ if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) freq->maxtxpower = (unsigned long)nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]); /* Get flags */ if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) { freq->flags |= FREQ_CAPABILITY_DISABLED; } else { if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) freq->flags |= FREQ_CAPABILITY_PASSIVE_SCAN; if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) freq->flags |= FREQ_CAPABILITY_NO_IBBS; if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) freq->flags |= FREQ_CAPABILITY_RADAR; if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { freq->flags |= FREQ_CAPABILITY_DFS_STATE; freq->dfsstate = (unsigned long)nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]) { freq->flags |= FREQ_CAPABILITY_DFS_TIME; freq->dfstime = (unsigned long)nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]); } } } } static void phydevice_capability_freqs(struct wifi_capability *capability, struct wifi_band_capability *bandcap, struct nlattr *tb_band) { int i; struct nlattr *nl_freq; struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, }; if (!tb_band) return; nla_for_each_nested(nl_freq, tb_band, i) { nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), freq_policy); if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) continue; phydevice_capability_freq(capability, bandcap, tb_freq); } } static void phydevice_capability_rates(struct wifi_band_capability *bandcap, struct nlattr *tb) { int i; struct nlattr *nl_rate; struct nlattr *tb_rate[NL80211_FREQUENCY_ATTR_MAX + 1]; struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG }, }; struct wifi_rate_capability *rate; if (!tb) return; nla_for_each_nested(nl_rate, tb, i) { nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), nla_len(nl_rate), rate_policy); if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) continue; rate = (struct wifi_rate_capability *) capwap_array_get_item_pointer(bandcap->rate, bandcap->rate->count); /* Set bitrate into multiple of 500Kbps */ rate->bitrate = (uint8_t)(nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]) / 5); if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) rate->flags |= RATE_CAPABILITY_SHORTPREAMBLE; } } static void phydevice_capability_band(struct wifi_capability *capability, struct nlattr *nl_band) { struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; struct wifi_band_capability *bandcap; nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), nla_len(nl_band), NULL); /* Init band */ bandcap = (struct wifi_band_capability *) capwap_array_get_item_pointer(capability->bands, capability->bands->count); bandcap->freq = capwap_array_create(sizeof(struct wifi_freq_capability), 0, 1); bandcap->rate = capwap_array_create(sizeof(struct wifi_rate_capability), 0, 1); /* Check High Throughput capability */ if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { bandcap->htcapability = (unsigned long)nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]); capability->flags |= WIFI_CAPABILITY_RADIOTYPE; capability->radiotype |= CAPWAP_RADIO_TYPE_80211N; } if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) bandcap->a_mpdu_params |= nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) & 0x03; if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) bandcap->a_mpdu_params |= nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) << 2; if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] && nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]) >= 16) memcpy(bandcap->mcs_set, nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]), 16); /* Frequency */ phydevice_capability_freqs(capability, bandcap, tb_band[NL80211_BAND_ATTR_FREQS]); /* Rate */ phydevice_capability_rates(bandcap, tb_band[NL80211_BAND_ATTR_RATES]); } static void phydevice_capability_bands(struct wifi_capability *capability, struct nlattr *tb) { int i; struct nlattr *nl_band; if (tb == NULL) return; capability->flags |= WIFI_CAPABILITY_BANDS; nla_for_each_nested(nl_band, tb, i) phydevice_capability_band(capability, nl_band); } /* */ static int cb_get_phydevice_capability(struct nl_msg* msg, void* data) { struct nlattr* tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr* gnlh = nlmsg_data(nlmsg_hdr(msg)); struct wifi_capability* capability = (struct wifi_capability*)data; nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb_msg[NL80211_ATTR_WIPHY] || nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]) != capability->device->phyindex) return NL_SKIP; /* Interface supported */ phydevice_capability_supported_iftypes(capability, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]); /* */ if (tb_msg[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) { capability->flags |= WIFI_CAPABILITY_MAX_SCAN_SSIDS; capability->maxscanssids = nla_get_u8(tb_msg[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); } if (tb_msg[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) { capability->flags |= WIFI_CAPABILITY_MAX_SCHED_SCAN_SSIDS; capability->maxschedscanssids = nla_get_u8(tb_msg[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); } if (tb_msg[NL80211_ATTR_MAX_MATCH_SETS]) { capability->flags |= WIFI_CAPABILITY_MAX_MATCH_SETS; capability->maxmatchsets = nla_get_u8(tb_msg[NL80211_ATTR_MAX_MATCH_SETS]); } if (tb_msg[NL80211_ATTR_MAC_ACL_MAX]) { capability->flags |= WIFI_CAPABILITY_MAX_ACL_MACADDRESS; capability->maxaclmacaddress = nla_get_u8(tb_msg[NL80211_ATTR_MAC_ACL_MAX]); } if (tb_msg[NL80211_ATTR_OFFCHANNEL_TX_OK]) capability->capability |= WIFI_CAPABILITY_FLAGS_OFFCHANNEL_TX_OK; if (tb_msg[NL80211_ATTR_ROAM_SUPPORT]) capability->capability |= WIFI_CAPABILITY_FLAGS_ROAM_SUPPORT; if (tb_msg[NL80211_ATTR_SUPPORT_AP_UAPSD]) capability->capability |= WIFI_CAPABILITY_FLAGS_SUPPORT_AP_UAPSD; if (tb_msg[NL80211_ATTR_DEVICE_AP_SME]) capability->capability |= WIFI_CAPABILITY_FLAGS_DEVICE_AP_SME; if (tb_msg[NL80211_ATTR_PROBE_RESP_OFFLOAD]) { log_printf(LOG_DEBUG, "nl80211: Supports Probe Response offload in AP mode"); capability->capability |= WIFI_CAPABILITY_FLAGS_PROBE_RESPONSE_OFFLOAD; /* TODO check offload protocol support */ } else log_printf(LOG_DEBUG, "nl80211: Does not support Probe Response offload in AP mode"); phydevice_capability_feature_flags(capability, tb_msg[NL80211_ATTR_FEATURE_FLAGS]); /* Commands supported */ phydevice_capability_supp_cmds(&capability->supp_cmds, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]); /* Cipher supported */ phydevice_capability_cipher_suites(capability, tb_msg[NL80211_ATTR_CIPHER_SUITES]); /* TX/RX Antenna count */ if (tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX] && tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]) { capability->flags |= WIFI_CAPABILITY_ANTENNA_MASK; capability->txantennamask = (unsigned long)nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX]); capability->rxantennamask = (unsigned long)nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]); } /* Band and datarate supported */ phydevice_capability_bands(capability, tb_msg[NL80211_ATTR_WIPHY_BANDS]); return NL_SKIP; } /* */ static int nl80211_device_getcapability(struct wifi_device* device, struct wifi_capability* capability) { int result; struct nl_msg* msg; struct nl80211_device_handle* devicehandle = (struct nl80211_device_handle*)device->handle; ASSERT(device != NULL); ASSERT(device->handle != NULL); ASSERT(capability != NULL); /* */ msg = nlmsg_alloc(); if (!msg || !nl80211_command(devicehandle->globalhandle, msg, 0, NL80211_CMD_GET_WIPHY) || nla_put_u32(msg, NL80211_ATTR_WIPHY, device->phyindex)) { nlmsg_free(msg); return -1; } /* Retrieve physical device capability */ capability->device = device; result = nl80211_send_and_recv_msg(devicehandle->globalhandle, msg, cb_get_phydevice_capability, capability); if (result) log_printf(LOG_ERR, "Unable retrieve physical device capability, error code: %d", result); return result; } /* */ static void nl80211_device_updatebeacons(struct wifi_device* device) { struct wifi_wlan* wlan; struct capwap_list_item* wlansearch; ASSERT(device != NULL); ASSERT(device->handle != NULL); /* Update all wlan beacon */ for (wlansearch = device->wlans->first; wlansearch; wlansearch = wlansearch->next) { wlan = (struct wifi_wlan*)wlansearch->item; if (wlan->flags & WIFI_WLAN_SET_BEACON) { if (nl80211_wlan_setbeacon(wlan)) { log_printf(LOG_WARNING, "Unable to update beacon on interface %d", wlan->virtindex); wifi_wlan_stopap(wlan); } } } } /* */ static int nl80211_device_settxqueue(struct wifi_device* device, int queue, int aifs, int cw_min, int cw_max, int txop) { int result; struct nl_msg* msg; struct capwap_list_item* wlansearch; struct nl80211_device_handle* devicehandle = (struct nl80211_device_handle*)device->handle; struct wifi_wlan* wlan = NULL; struct nlattr *txq, *params; ASSERT(device != NULL); ASSERT(device->handle != NULL); /* Search a valid interface */ for (wlansearch = device->wlans->first; wlansearch; wlansearch = wlansearch->next) { struct wifi_wlan* element = (struct wifi_wlan*)wlansearch->item; if (element->flags & WIFI_WLAN_RUNNING) { wlan = element; break; } } if (!wlan) return -1; /* Set TX Queue using device index of first BSS */ msg = nl80211_ifindex_msg(devicehandle->globalhandle, wlan->virtindex, 0, NL80211_CMD_SET_WIPHY); if (!msg) return -1; txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); if (!txq) goto out_err; /* We are only sending parameters for a single TXQ at a time */ params = nla_nest_start(msg, 1); if (!params) goto out_err; switch (queue) { case 0: if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO)) goto out_err; break; case 1: if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI)) goto out_err; break; case 2: if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE)) goto out_err; break; case 3: if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK)) goto out_err; break; default: goto out_err; } if (nla_put_u16(msg, NL80211_TXQ_ATTR_TXOP, txop) || nla_put_u16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min) || nla_put_u16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max) || nla_put_u8(msg, NL80211_TXQ_ATTR_AIFS, aifs)) goto out_err; nla_nest_end(msg, params); nla_nest_end(msg, txq); result = nl80211_send_and_recv_msg(devicehandle->globalhandle, msg, NULL, NULL); if (result) log_printf(LOG_ERR, "Unable set TX Queue, error code: %d", result); return result; out_err: nlmsg_free(msg); return -1; } /* */ static int nl80211_device_setfrequency(struct wifi_device* device) { ASSERT(device != NULL); ASSERT(device->handle != NULL); /* Delay request if not found BSS interface */ if (!device->wlanactive) { return 0; } return nl80211_device_changefrequency(device, &device->currentfrequency); } /* */ static void nl80211_device_deinit(struct wifi_device* device) { struct nl80211_device_handle* devicehandle; ASSERT(device != NULL); devicehandle = (struct nl80211_device_handle*)device->handle; if (devicehandle) { capwap_free(devicehandle); device->handle = NULL; } } /* */ static void nl80211_global_deinit(wifi_global_handle handle) { struct nl80211_global_handle* globalhandle = (struct nl80211_global_handle*)handle; if (!globalhandle) return; if (globalhandle->netlinkhandle) netlink_free(globalhandle->netlinkhandle); if (globalhandle->nl) nl_socket_free(globalhandle->nl); if (globalhandle->nl_event) nl_socket_free(globalhandle->nl_event); if (ev_is_active(&globalhandle->nl_event_ev)) ev_io_stop(EV_DEFAULT_UC_ &globalhandle->nl_event_ev); if (globalhandle->nl_cb) nl_cb_put(globalhandle->nl_cb); if (globalhandle->sock_util >= 0) close(globalhandle->sock_util); capwap_free(globalhandle); } /* */ static void nl80211_global_newlink_event(wifi_global_handle handle, struct ifinfomsg* infomsg, uint8_t* data, int length) { struct wifi_wlan* wlan; struct nl80211_global_handle* globalhandle = (struct nl80211_global_handle*)handle; ASSERT(handle != NULL); ASSERT(infomsg != NULL); /* Search device */ wlan = wifi_get_wlan(infomsg->ifi_index); if (!wlan) return; if (!(wlan->flags & WIFI_WLAN_RUNNING)) { if ((infomsg->ifi_flags & IFF_UP) && (wifi_iface_getstatus(globalhandle->sock_util, wlan->virtname) > 0)) { wifi_iface_down(globalhandle->sock_util, wlan->virtname); } } else if (wlan->flags & WIFI_WLAN_SET_BEACON) { if ((wlan->flags & WIFI_WLAN_OPERSTATE_RUNNING) && (infomsg->ifi_flags & IFF_LOWER_UP) && !(infomsg->ifi_flags & (IFF_RUNNING | IFF_DORMANT))) { struct nl80211_wlan_handle* wlanhandle = (struct nl80211_wlan_handle*)wlan->handle; netlink_set_link_status(wlanhandle->devicehandle->globalhandle->netlinkhandle, wlan->virtindex, -1, IF_OPER_UP); } } } /* */ static void nl80211_global_dellink_event(wifi_global_handle handle, struct ifinfomsg* infomsg, uint8_t* data, int length) { } /* */ static int nl80211_global_valid_handler(struct nl_msg* msg, void* data) { uint32_t ifindex; struct wifi_wlan* wlan; struct nlattr* tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr* gnlh = nlmsg_data(nlmsg_hdr(msg)); nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb_msg[NL80211_ATTR_IFINDEX]) { ifindex = (int)nla_get_u32(tb_msg[NL80211_ATTR_IFINDEX]); /* Search interface */ wlan = wifi_get_wlan(ifindex); if (wlan) { return nl80211_wlan_event(wlan, gnlh, tb_msg); } } return NL_SKIP; } /* */ static wifi_global_handle nl80211_global_init() { int result; struct nl80211_global_handle* globalhandle; /* */ globalhandle = (struct nl80211_global_handle*)capwap_alloc(sizeof(struct nl80211_global_handle)); memset(globalhandle, 0, sizeof(struct nl80211_global_handle)); globalhandle->sock_util = -1; /* Configure global netlink callback */ globalhandle->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (!globalhandle->nl_cb) { nl80211_global_deinit((wifi_global_handle)globalhandle); return NULL; } /* Create netlink socket */ globalhandle->nl = nl_create_handle(globalhandle->nl_cb); if (!globalhandle->nl) { nl80211_global_deinit((wifi_global_handle)globalhandle); return NULL; } /* Create netlink socket for event */ globalhandle->nl_event = nl_create_handle(globalhandle->nl_cb); if (!globalhandle->nl_event) { nl80211_global_deinit((wifi_global_handle)globalhandle); return NULL; } /* hook into I/O loop */ ev_io_init(&globalhandle->nl_event_ev, nl80211_global_event_receive_cb, nl_socket_get_fd(globalhandle->nl_event), EV_READ); ev_io_start(EV_DEFAULT_UC_ &globalhandle->nl_event_ev); /* Add membership scan events */ result = nl80211_get_multicast_id(globalhandle, "nl80211", "scan"); if (result >= 0) { result = nl_socket_add_membership(globalhandle->nl_event, result); } if (result < 0) { nl80211_global_deinit((wifi_global_handle)globalhandle); return NULL; } /* Add membership mlme events */ result = nl80211_get_multicast_id(globalhandle, "nl80211", "mlme"); if (result >= 0) { result = nl_socket_add_membership(globalhandle->nl_event, result); } if (result < 0) { nl80211_global_deinit((wifi_global_handle)globalhandle); return NULL; } /* Add membership regulatory events */ result = nl80211_get_multicast_id(globalhandle, "nl80211", "regulatory"); if (result >= 0) { nl_socket_add_membership(globalhandle->nl_event, result); } /* Get nl80211 netlink family */ globalhandle->nl80211_id = genl_ctrl_resolve(globalhandle->nl, "nl80211"); if (globalhandle->nl80211_id < 0) { log_printf(LOG_WARNING, "Unable to found mac80211 kernel module"); nl80211_global_deinit((wifi_global_handle)globalhandle); return NULL; } /* Configure global callback function */ nl_cb_set(globalhandle->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl80211_no_seq_check, NULL); nl_cb_set(globalhandle->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, nl80211_global_valid_handler, NULL); /* Netlink lisk status */ globalhandle->netlinkhandle = netlink_init((wifi_global_handle)globalhandle); if (!globalhandle->netlinkhandle) { nl80211_global_deinit((wifi_global_handle)globalhandle); return NULL; } globalhandle->netlinkhandle->newlink_event = nl80211_global_newlink_event; globalhandle->netlinkhandle->dellink_event = nl80211_global_dellink_event; /* Socket utils */ globalhandle->sock_util = socket(AF_PACKET, SOCK_RAW, 0); if (globalhandle->sock_util < 0) { nl80211_global_deinit((wifi_global_handle)globalhandle); return NULL; } return (wifi_global_handle)globalhandle; } /* Driver function */ const struct wifi_driver_ops wifi_driver_nl80211_ops = { .name = "nl80211", .description = "Linux nl80211/cfg80211", .global_init = nl80211_global_init, .global_deinit = nl80211_global_deinit, .device_init = nl80211_device_init, .device_getcapability = nl80211_device_getcapability, .device_updatebeacons = nl80211_device_updatebeacons, .device_settxqueue = nl80211_device_settxqueue, .device_setfrequency = nl80211_device_setfrequency, .device_deinit = nl80211_device_deinit, .wlan_create = nl80211_wlan_create, .wlan_startap = nl80211_wlan_startap, .wlan_stopap = nl80211_wlan_stopap, .wlan_sendframe = nl80211_wlan_sendframe, .wlan_poll_station = nl80211_wlan_poll_station, .wlan_delete = nl80211_wlan_delete, .wlan_set_key = nl80211_set_key, .station_authorize = nl80211_station_authorize, .station_deauthorize = nl80211_station_deauthorize, .station_get_inact_sec = nl80211_station_get_inact_sec };