/* * FeitCSI is the tool for extracting CSI information from supported intel NICs. * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "nl80211.h" //fix old libs #include "WiFIController.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "Logger.h" #include "Arguments.h" const char *IF_MODES[NL80211_IFTYPE_MAX + 1] = { "unspecified", "IBSS", "managed", "AP", "AP/VLAN", "WDS", "monitor", "mesh point", "P2P-client", "P2P-GO", "P2P-device", "outside context of a BSS", "NAN", }; void WiFIController::killNetworkProcesses() { std::string procesNames[] = { "wpa_action", "wpa_supplicant", "wpa_cli", "dhclient", "ifplugd", "dhcdbd", "dhcpcd", "udhcpc", "NetworkManager", "knetworkmanager", "avahi-autoipd", "avahi-daemon", "wlassistant", "wifibox", "net_applet", "wicd-daemon", "wicd-client", "iwd", "hostapd"}; for (std::string name : procesNames) { char buf[512]; std::string cmnd = "pidof " + name; FILE *pipe = popen(cmnd.c_str(), "r"); fgets(buf, 512, pipe); std::stringstream ss(buf); std::string word; while (ss >> word) { // Extract word from the stream. pid_t pid = std::atoi(word.c_str()); if (pid) { kill(pid, SIGKILL); } } pclose(pipe); } } void WiFIController::getInterfaces() { Cmd cmd{ .id = NL80211_CMD_GET_INTERFACE, .idby = CIB_NONE, .nlFlags = NLM_F_DUMP, .handler = NULL, .valid_handler = this->processGetInterfacesHandler, }; this->nlExecCommand(cmd); } void WiFIController::setTxPower() { Cmd cmd{ .id = NL80211_CMD_SET_WIPHY, .idby = CIB_NETDEV, .nlFlags = 0, .device = if_nametoindex(MONITOR_INTERFACE_NAME), .handler = this->setTxPowerHandler, }; this->nlExecCommand(cmd); } void WiFIController::addMonitorDevice(const char *name, const nl80211_iftype type) { if (this->phys.empty()) { throw std::ios_base::failure("Error no phy devices\n"); return; } const void *settings[] = {name, &type}; Cmd cmd{ .id = NL80211_CMD_NEW_INTERFACE, .idby = CIB_PHY, .nlFlags = 0, .device = this->phys[0], .handler = this->processAddMonitorDeviceHandler, .valid_handler = NULL, .args = settings, }; this->nlExecCommand(cmd); } int WiFIController::setInterfaceUpDown(const char *ifName, bool up) { // Create a socket file descriptor int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("Failed to create socket"); return 1; } // Get the interface index struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifName, IFNAMSIZ - 1); if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) { perror("Failed to get interface index"); close(sockfd); return 1; } if (up) { ifr.ifr_flags |= IFF_UP; } else { ifr.ifr_flags &= ~IFF_UP; } if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { perror("Failed to bring down interface"); close(sockfd); return 1; } // Close the socket close(sockfd); if (Arguments::arguments.verbose) { Logger::log(info) << "Interface " << ifName << " has been brought " << (up ? "up" : "down") << "\n"; } return 0; } void WiFIController::setFreq(uint16_t freq, const char *bw) { const void *settings[] = {&freq, bw}; Cmd cmd{ .id = NL80211_CMD_SET_WIPHY, .idby = CIB_NETDEV, .nlFlags = 0, .device = if_nametoindex(MONITOR_INTERFACE_NAME), .handler = processSetFreq, .valid_handler = NULL, .args = settings, }; this->nlExecCommand(cmd); } void WiFIController::removeInterface(const char *name, int ifIndex) { Cmd cmd{ .id = NL80211_CMD_DEL_INTERFACE, .idby = CIB_NETDEV, .nlFlags = 0, .device = ifIndex ? ifIndex : if_nametoindex(name), .handler = NULL, .valid_handler = NULL, }; this->nlExecCommand(cmd); } void WiFIController::getPhys() { Cmd cmd{ .id = NL80211_CMD_GET_WIPHY, .idby = CIB_NONE, .nlFlags = NLM_F_DUMP, .handler = NULL, .valid_handler = this->processGetPhysHandler, }; this->nlExecCommand(cmd); } int WiFIController::processGetPhysHandler(struct nl_msg *msg, void *arg) { WiFIController *instance = (WiFIController *)arg; struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = (genlmsghdr *)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_WIPHY]) { int64_t phyId = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]); instance->phys.push_back(phyId); } return NL_OK; } uint16_t WiFIController::chanModeToWidth(struct ChanMode &chanMode) { switch (chanMode.width) { case NL80211_CHAN_WIDTH_20: return 20; case NL80211_CHAN_WIDTH_40: return 40; case NL80211_CHAN_WIDTH_80: return 80; case NL80211_CHAN_WIDTH_160: return 160; case NL80211_CHAN_WIDTH_320: return 320; default: break; } return 0; } ChanMode WiFIController::getChanMode(const char *width) { static const struct ChanMode chanMode[] = { {.name = "20", .width = NL80211_CHAN_WIDTH_20, .freq1_diff = 0, .chantype = NL80211_CHAN_HT20}, {.name = "40", .width = NL80211_CHAN_WIDTH_40, .freq1_diff = 10, .chantype = NL80211_CHAN_HT40PLUS}, {.name = "HT40-", .width = NL80211_CHAN_WIDTH_40, .freq1_diff = -10, .chantype = NL80211_CHAN_HT40MINUS}, {.name = "NOHT", .width = NL80211_CHAN_WIDTH_20_NOHT, .freq1_diff = 0, .chantype = NL80211_CHAN_NO_HT}, {.name = "5MHz", .width = NL80211_CHAN_WIDTH_5, .freq1_diff = 0, .chantype = -1}, {.name = "10MHz", .width = NL80211_CHAN_WIDTH_10, .freq1_diff = 0, .chantype = -1}, {.name = "80", .width = NL80211_CHAN_WIDTH_80, .freq1_diff = 0, .chantype = -1}, {.name = "160", .width = NL80211_CHAN_WIDTH_160, .freq1_diff = 0, .chantype = -1}, {.name = "320MHz", .width = NL80211_CHAN_WIDTH_320, .freq1_diff = 0, .chantype = -1}, {.name = "1MHz", .width = NL80211_CHAN_WIDTH_1, .freq1_diff = 0, .chantype = -1}, {.name = "2MHz", .width = NL80211_CHAN_WIDTH_2, .freq1_diff = 0, .chantype = -1}, {.name = "4MHz", .width = NL80211_CHAN_WIDTH_4, .freq1_diff = 0, .chantype = -1}, {.name = "8MHz", .width = NL80211_CHAN_WIDTH_8, .freq1_diff = 0, .chantype = -1}, {.name = "16MHz", .width = NL80211_CHAN_WIDTH_16, .freq1_diff = 0, .chantype = -1}, }; struct ChanMode selectedChanMode = {}; for (unsigned int i = 0; i < ARRAY_SIZE(chanMode); i++) { if (strcasecmp(chanMode[i].name, width) == 0) { selectedChanMode = chanMode[i]; break; } } return selectedChanMode; } int WiFIController::processSetFreq(struct nl80211_state *state, struct nl_msg *msg, void *arg) { void **settings = (void **)arg; uint16_t settingsFreq = *(uint16_t *)(settings[0]); char *settingsWidth = (char *)(settings[1]); if (Arguments::arguments.verbose) { Logger::log(info) << "Setting frequency " << settingsFreq << " channel width " << settingsWidth << "\n"; } uint32_t freq = settingsFreq; uint32_t freq_offset = 0; unsigned int control_freq = freq; unsigned int control_freq_offset = freq_offset; unsigned int center_freq1 = freq; unsigned int center_freq1_offset = freq_offset; enum nl80211_chan_width width = control_freq < 1000 ? NL80211_CHAN_WIDTH_16 : NL80211_CHAN_WIDTH_20_NOHT; struct ChanMode selectedChanMode = getChanMode(settingsWidth); center_freq1 = getCf1(&selectedChanMode, freq); /* For non-S1G frequency */ if (center_freq1 > 1000) center_freq1_offset = 0; width = (nl80211_chan_width)selectedChanMode.width; NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, control_freq); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ_OFFSET, control_freq_offset); NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, width); switch (width) { case NL80211_CHAN_WIDTH_20_NOHT: NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_NO_HT); break; case NL80211_CHAN_WIDTH_20: NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20); break; case NL80211_CHAN_WIDTH_40: if (control_freq > center_freq1) { NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40MINUS); } else { NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40PLUS); } break; default: break; } if (center_freq1) { NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq1); } if (center_freq1_offset) { NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1_OFFSET, center_freq1_offset); } return 0; nla_put_failure: return -ENOBUFS; } int WiFIController::setTxPowerHandler(nl80211_state * state, nl_msg * msg, void * arg) { enum nl80211_tx_power_setting type = NL80211_TX_POWER_FIXED; int mbm = Arguments::arguments.txPower * 100; //dBm to mbm *100 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm); return 0; nla_put_failure: return -ENOBUFS; } int WiFIController::processAddMonitorDeviceHandler(struct nl80211_state *state, struct nl_msg *msg, void *arg) { void **settings = (void **)arg; const char *name = (const char *)settings[0]; nl80211_iftype *type = (nl80211_iftype *)(settings[1]); NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, *type); return 0; nla_put_failure: return -ENOBUFS; } int WiFIController::getCf1(const struct ChanMode *chanmode, unsigned long freq) { unsigned int cf1 = freq, j; unsigned int bw80[] = {5180, 5260, 5500, 5580, 5660, 5745, 5955, 6035, 6115, 6195, 6275, 6355, 6435, 6515, 6595, 6675, 6755, 6835, 6195, 6995}; unsigned int bw160[] = {5180, 5500, 5955, 6115, 6275, 6435, 6595, 6755, 6915}; /* based on 11be D2 E.1 Country information and operating classes */ unsigned int bw320[] = {5955, 6115, 6275, 6435, 6595, 6755}; switch (chanmode->width) { case NL80211_CHAN_WIDTH_80: /* setup center_freq1 */ for (j = 0; j < ARRAY_SIZE(bw80); j++) { if (freq >= bw80[j] && freq < bw80[j] + 80) break; } if (j == ARRAY_SIZE(bw80)) break; cf1 = bw80[j] + 30; break; case NL80211_CHAN_WIDTH_160: /* setup center_freq1 */ for (j = 0; j < ARRAY_SIZE(bw160); j++) { if (freq >= bw160[j] && freq < bw160[j] + 160) break; } if (j == ARRAY_SIZE(bw160)) break; cf1 = bw160[j] + 70; break; case NL80211_CHAN_WIDTH_320: /* setup center_freq1 */ for (j = 0; j < ARRAY_SIZE(bw320); j++) { if (freq >= bw320[j] && freq < bw320[j] + 160) break; } if (j == ARRAY_SIZE(bw320)) break; cf1 = bw320[j] + 150; break; default: cf1 = freq + chanmode->freq1_diff; break; } return cf1; } int WiFIController::processGetInterfacesHandler(struct nl_msg *msg, void *arg) { WiFIController *instance = (WiFIController *)arg; struct genlmsghdr *gnlh = (genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; struct InterfaceInfo ifInfo; nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb_msg[NL80211_ATTR_IFNAME]) { ifInfo.ifName = nla_get_string(tb_msg[NL80211_ATTR_IFNAME]); } else { ifInfo.ifName = "Unnamed/non-netdev interface"; } if (tb_msg[NL80211_ATTR_IFINDEX]) { ifInfo.ifIndex = nla_get_u32(tb_msg[NL80211_ATTR_IFINDEX]); } if (tb_msg[NL80211_ATTR_WDEV]) { ifInfo.wdev = (uint64_t)nla_get_u64(tb_msg[NL80211_ATTR_WDEV]); } if (tb_msg[NL80211_ATTR_MAC]) { ifInfo.mac = instance->macN2a((const unsigned char *)nla_data(tb_msg[NL80211_ATTR_MAC])); } if (tb_msg[NL80211_ATTR_SSID]) { ifInfo.ssid = (const char *)nla_data(tb_msg[NL80211_ATTR_SSID]); } if (tb_msg[NL80211_ATTR_IFTYPE]) { ifInfo.ifType = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]); /* if (iftype <= NL80211_IFTYPE_MAX && IF_MODES[iftype]) { ifInfo.type = IF_MODES[iftype]; } else { ifInfo.type = "unknown"; } */ } if (tb_msg[NL80211_ATTR_WIPHY]) { ifInfo.wiphy = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]); } if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) { ifInfo.freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]); ifInfo.channel = instance->freqToCHannel(ifInfo.freq); if (tb_msg[NL80211_ATTR_CHANNEL_WIDTH]) { ifInfo.channelWidth = nla_get_u32(tb_msg[NL80211_ATTR_CHANNEL_WIDTH]); if (tb_msg[NL80211_ATTR_CENTER_FREQ1]) { ifInfo.centerFreq1 = nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ1]); } if (tb_msg[NL80211_ATTR_CENTER_FREQ2]) { ifInfo.centerFreq2 = nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ2]); } } else if (tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { ifInfo.channelType = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); } } if (tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]) { int32_t txp = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]); ifInfo.txdBm = txp % 100; ifInfo.txdBm += txp / 100; } if (tb_msg[NL80211_ATTR_IFINDEX]) { instance->interfaces.push_back(ifInfo); } return NL_OK; } std::string WiFIController::macN2a(const unsigned char *arg) { int i, l; char mac_addr[20]; l = 0; for (i = 0; i < ETH_ALEN; i++) { if (i == 0) { sprintf(mac_addr + l, "%02x", arg[i]); l += 2; } else { sprintf(mac_addr + l, ":%02x", arg[i]); l += 3; } } std::string ret = mac_addr; return ret; } int WiFIController::freqToCHannel(int freq) { if (freq < 1000) return 0; /* see 802.11-2007 17.3.8.3.2 and Annex J */ if (freq == 2484) return 14; /* see 802.11ax D6.1 27.3.23.2 and Annex E */ else if (freq == 5935) return 2; else if (freq < 2484) return (freq - 2407) / 5; else if (freq >= 4910 && freq <= 4980) return (freq - 4000) / 5; else if (freq < 5950) return (freq - 5000) / 5; else if (freq <= 45000) /* DMG band lower limit */ /* see 802.11ax D6.1 27.3.23.2 */ return (freq - 5950) / 5; else if (freq >= 58320 && freq <= 70200) return (freq - 56160) / 2160; else return 0; } int WiFIController::phyLookup(char *name) { char buf[200]; int fd, pos; snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name); fd = open(buf, O_RDONLY); if (fd < 0) return -1; pos = read(fd, buf, sizeof(buf) - 1); if (pos < 0) { close(fd); return -1; } buf[pos] = '\0'; close(fd); return atoi(buf); }