FeitCSI/src/WiFIController.cpp
2024-02-15 15:55:24 +01:00

662 lines
17 KiB
C++

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "nl80211.h" //fix old libs
#include "WiFIController.h"
#include <errno.h>
#include <iwlib.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/attr.h>
#include <chrono>
#include <thread>
#include <sstream>
#include <iostream>
#include <signal.h>
#include <thread>
#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);
}