diff --git a/src/ckernelwifi.cc b/src/ckernelwifi.cc index 97e438b..b4f6c6a 100644 --- a/src/ckernelwifi.cc +++ b/src/ckernelwifi.cc @@ -36,6 +36,31 @@ CDynBuffer Buffer; /* allow calling non static function from static function */ ckernelwifi::CallFromStaticFunc * CKernelWifi::forward = nullptr ; +/* Frequency routing: track each radio's channel from its TX frames */ +void CKernelWifi::update_radio_freq(const struct ether_addr* mac, TFrequency freq) { + if (freq == 0) return; + for (int i = 0; i < _radio_freq_cache_size; i++) { + if (memcmp(&_radio_freq_cache[i].mac, mac, sizeof(struct ether_addr)) == 0) { + _radio_freq_cache[i].freq = freq; + return; + } + } + if (_radio_freq_cache_size < MAX_RADIO_FREQ_CACHE) { + _radio_freq_cache[_radio_freq_cache_size].mac = *mac; + _radio_freq_cache[_radio_freq_cache_size].freq = freq; + _radio_freq_cache_size++; + } +} + +TFrequency CKernelWifi::get_radio_freq(const struct ether_addr* mac) const { + for (int i = 0; i < _radio_freq_cache_size; i++) { + if (memcmp(&_radio_freq_cache[i].mac, mac, sizeof(struct ether_addr)) == 0) { + return _radio_freq_cache[i].freq; + } + } + return 0; /* unknown — deliver frame (conservative fallback) */ +} + void CKernelWifi::cout_mac_address(struct ether_addr *src) { char addr[18]; @@ -210,14 +235,47 @@ int CKernelWifi::process_messages(struct nl_msg *msg) else freq = 0; + /* Update frequency cache for this transmitting radio */ + update_radio_freq(&macsrchwsim, freq); + { + static int _tx_dbg = 0; + if (_tx_dbg < 10) { + char _txm[18]; + sprintf(_txm, "%02X:%02X:%02X:%02X:%02X:%02X", macsrchwsim.ether_addr_octet[0], macsrchwsim.ether_addr_octet[1], macsrchwsim.ether_addr_octet[2], macsrchwsim.ether_addr_octet[3], macsrchwsim.ether_addr_octet[4], macsrchwsim.ether_addr_octet[5]); + std::cerr << "TX process_messages #" << _tx_dbg << " src_mac=" << _txm << " freq=" << freq << std::endl; + _tx_dbg++; + } + } + int rate_idx = 7; // number of attempts const auto& inets = _list_winterfaces.list_devices(); + /* Deduplicate by permanent hwsim MAC (see recv_from_server comment) */ + struct ether_addr sent_macs_local[16]; + int n_sent_local = 0; for (const auto& inet : inets) { struct ether_addr macdsthwsim = inet.getMachwsim(); - if( memcmp(&macsrchwsim,&macdsthwsim,sizeof(struct ether_addr)) ) // if( macsrchwsim != macdsthwsim ) - send_cloned_frame_msg(&macdsthwsim, data, data_len, rate_idx, power, freq); + if( memcmp(&macsrchwsim,&macdsthwsim,sizeof(struct ether_addr)) ) { + bool dup = false; + for (int i = 0; i < n_sent_local; i++) { + if (memcmp(&sent_macs_local[i], &macdsthwsim, sizeof(struct ether_addr)) == 0) { + dup = true; + break; + } + } + if (!dup) { + if (n_sent_local < 16) + sent_macs_local[n_sent_local++] = macdsthwsim; + /* Frequency routing: skip radios not on this channel. + * freq=0 in frame: deliver to all (no freq info). + * target_freq=0: radio not tuned yet, skip it. */ + TFrequency target_freq = get_radio_freq(&macdsthwsim); + if (freq != 0 && target_freq != 0 && target_freq != freq) + continue; + send_cloned_frame_msg(&macdsthwsim, data, data_len, rate_idx, power, freq); + } + } } delete &inets; // <------------------------ @@ -317,6 +375,10 @@ int CKernelWifi::init_netlink_first(void) perror("setsockopt"); } + /* Increase netlink receive buffer to 4MB to prevent frame drops under load. + * Must be after genl_connect() which assigns the socket fd. */ + nl_socket_set_buffer_size(_netlink_socket, 16 * 1024 * 1024, 0); + return 1; } @@ -398,6 +460,10 @@ int CKernelWifi::init_netlink(void) perror("setsockopt"); } + /* Increase netlink receive buffer to 4MB to prevent frame drops under load. + * Must be after genl_connect() which assigns the socket fd. */ + nl_socket_set_buffer_size(_netlink_socket, 16 * 1024 * 1024, 0); + return 1; } @@ -442,11 +508,18 @@ int CKernelWifi::send_cloned_frame_msg(struct ether_addr *dst, char *data, int d // nl_send_auto_complete(_netlink_socket, msg); - if (nl_send_auto(_netlink_socket, msg) < 0) + int _nl_rc = nl_send_auto(_netlink_socket, msg); + static int _send_count = 0; + _send_count++; + if (_nl_rc < 0) { + std::cerr << "nl_send_auto FAILED: rc=" << _nl_rc << " count=" << _send_count << std::endl; nlmsg_free(msg); return 0 ; } + if (_send_count <= 10 || _send_count % 100 == 0) { + std::cerr << "nl_send_auto OK: rc=" << _nl_rc << " count=" << _send_count << " freq=" << freq << std::endl; + } nlmsg_free(msg); @@ -455,6 +528,21 @@ int CKernelWifi::send_cloned_frame_msg(struct ether_addr *dst, char *data, int d void CKernelWifi::recv_from_server(){ + static int _dbg_count = 0; + if (_dbg_count < 5 || _dbg_count % 1000 == 0) { + const auto& _dbg_inets = _list_winterfaces.list_devices(); + std::cerr << "recv_from_server #" << _dbg_count << " list_size=" << _dbg_inets.size(); + for (const auto& _di : _dbg_inets) { + char _dm[18]; + const struct ether_addr _dpm = _di.getMachwsim(); + sprintf(_dm, "%02X:%02X:%02X:%02X:%02X:%02X", _dpm.ether_addr_octet[0], _dpm.ether_addr_octet[1], _dpm.ether_addr_octet[2], _dpm.ether_addr_octet[3], _dpm.ether_addr_octet[4], _dpm.ether_addr_octet[5]); + std::cerr << " [" << _di.getName() << "=" << _dm << "]"; + } + std::cerr << std::endl; + delete &_dbg_inets; + } + _dbg_count++; + if ( ! is_connected_to_server()) return ; @@ -535,11 +623,52 @@ void CKernelWifi::recv_from_server(){ const auto& inets = _list_winterfaces.list_devices(); + /* Deduplicate by permanent hwsim MAC: multiple BSS interfaces + * (wlan0, wlan0-1, wlan0-2) share the same PHY permanent MAC. + * Without dedup, each frame is delivered N times to the same radio, + * causing duplicate auth/assoc and breaking WPA handshakes. */ + struct ether_addr sent_macs[16]; + int n_sent = 0; + for (const auto& inet : inets) { struct ether_addr macdsthwsim = inet.getMachwsim(); - send_cloned_frame_msg(&macdsthwsim, data, data_len, rate_idx, signal, freq); + bool dup = false; + for (int i = 0; i < n_sent; i++) { + if (memcmp(&sent_macs[i], &macdsthwsim, sizeof(struct ether_addr)) == 0) { + dup = true; + break; + } + } + if (dup) + continue; + if (n_sent < 16) + sent_macs[n_sent++] = macdsthwsim; + + /* Frequency routing: skip radios not on this channel. + * freq=0 in frame: deliver to all (no freq info). + * target_freq=0: radio freq unknown, deliver anyway. */ + TFrequency target_freq = get_radio_freq(&macdsthwsim); + if (_dbg_count < 20) { + char _fm[18]; + sprintf(_fm, "%02X:%02X:%02X:%02X:%02X:%02X", macdsthwsim.ether_addr_octet[0], macdsthwsim.ether_addr_octet[1], macdsthwsim.ether_addr_octet[2], macdsthwsim.ether_addr_octet[3], macdsthwsim.ether_addr_octet[4], macdsthwsim.ether_addr_octet[5]); + std::cerr << " freq_route: mac=" << _fm << " frame_freq=" << freq << " target_freq=" << target_freq << (((freq != 0 && target_freq != 0 && target_freq != freq) ? " SKIP" : " DELIVER")) << std::endl; + } + /* Frequency routing disabled for recv_from_server: kernel + * mac80211_hwsim handles RX frequency filtering internally. + * The userspace check races with scan dwell timing over TCP, + * causing 5GHz beacons to be dropped during client scans. */ + // if (freq != 0 && target_freq != 0 && target_freq != freq) + // continue; + + /* Pass freq=0 so the kernel skips its own HWSIM_ATTR_FREQ + * check in hwsim_cloned_frame_received_nl. Without this, + * the kernel drops frames whose freq doesn't match the + * radio's current channel — which races with scan dwell + * timing over TCP. The kernel's mac80211 RX path handles + * frequency validation at a higher level. */ + send_cloned_frame_msg(&macdsthwsim, data, data_len, rate_idx, signal, 0); } delete &inets; } @@ -550,6 +679,9 @@ void CKernelWifi::monitor_hwsim_loop() sock = nl_socket_alloc(); genl_connect(sock); + /* Increase netlink receive buffer to 4MB to prevent frame drops under load. + * Must be after genl_connect() which assigns the socket fd. */ + nl_socket_set_buffer_size(sock, 16 * 1024 * 1024, 0); /* loop for waiting incoming msg from hwsim driver*/ while (true) { @@ -1008,8 +1140,13 @@ void CKernelWifi::handle_new_winet_notification(WirelessDevice wirelessdevice){ //paddr.ether_addr_octet[0] |= 0x40 ; wirelessdevice.setMachwsim(paddr); - _list_winterfaces.add_device(wirelessdevice); + } else { + /* Fallback: ethtool GPERMADDR fails on OpenWrt hwsim VIFs. + Use the nl80211 MAC (addresses[0]) — setMachwsim applies |=0x40 + to derive addresses[1] for the hwsim rhashtable key. */ + wirelessdevice.setMachwsim(wirelessdevice.getMacaddr()); } + _list_winterfaces.add_device(wirelessdevice); //std::cout << __func__ << _list_winterfaces << std::endl ; @@ -1032,8 +1169,13 @@ void CKernelWifi::handle_init_winet_notification(WirelessDevice wirelessdevice){ //paddr.ether_addr_octet[0] |= 0x40 ; wirelessdevice.setMachwsim(paddr); - _list_winterfaces.add_device(wirelessdevice); + } else { + /* Fallback: ethtool GPERMADDR fails on OpenWrt hwsim VIFs. + Use the nl80211 MAC (addresses[0]) — setMachwsim applies |=0x40 + to derive addresses[1] for the hwsim rhashtable key. */ + wirelessdevice.setMachwsim(wirelessdevice.getMacaddr()); } + _list_winterfaces.add_device(wirelessdevice); //std::cout << __func__ << _list_winterfaces << std::endl ; } diff --git a/src/ckernelwifi.h b/src/ckernelwifi.h index c10ca12..fd9ef05 100644 --- a/src/ckernelwifi.h +++ b/src/ckernelwifi.h @@ -16,6 +16,7 @@ #include #include "cdynbuffer.h" +#include "types.h" namespace ckernelwifi{ @@ -39,6 +40,22 @@ class CKernelWifi : public intthread::AsyncTask { WirelessDeviceList _list_winterfaces ; + /* Frequency routing: cache each radio's last-seen frequency. + * Updated from TX frames (HWSIM_ATTR_FREQ). Used to skip + * radios not tuned to the frame's frequency, eliminating + * the N-radio broadcast overhead. freq=0 means unknown + * (radio hasn't transmitted yet) — deliver as fallback. */ + static constexpr int MAX_RADIO_FREQ_CACHE = 16; + struct RadioFreqEntry { + struct ether_addr mac; + TFrequency freq; + }; + RadioFreqEntry _radio_freq_cache[MAX_RADIO_FREQ_CACHE] = {}; + int _radio_freq_cache_size = 0; + + void update_radio_freq(const struct ether_addr* mac, TFrequency freq); + TFrequency get_radio_freq(const struct ether_addr* mac) const; + /** pointer for netlink socket */ struct nl_sock * _netlink_socket { nullptr }; diff --git a/src/csocketclientitcp.cc b/src/csocketclientitcp.cc index c825324..9b95688 100644 --- a/src/csocketclientitcp.cc +++ b/src/csocketclientitcp.cc @@ -5,6 +5,7 @@ #include // open #include // INADDR_ANY +#include #include // close #include // assert @@ -34,6 +35,9 @@ bool CSocketClientITCP::_Configure() return false; } + int nodelay = 1; + setsockopt(Master, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)); + return true; } diff --git a/src/csocketserverfunctionitcp.cc b/src/csocketserverfunctionitcp.cc index 1b8fe99..ad1cdc2 100644 --- a/src/csocketserverfunctionitcp.cc +++ b/src/csocketserverfunctionitcp.cc @@ -5,6 +5,7 @@ #include // INADDR_ANY #include +#include #include "csocket.h" // SOCKET_ERROR #include "csocketserverfunctionitcp.h" @@ -91,5 +92,8 @@ TDescriptor CSocketServerFunctionITCP::_Accept(TDescriptor master, TCID& cid) cid=hash_ipaddr(&address); + int nodelay = 1; + setsockopt(new_socket, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)); + return new_socket; } diff --git a/src/cwifi.cc b/src/cwifi.cc index ff693c0..c3a55e0 100644 --- a/src/cwifi.cc +++ b/src/cwifi.cc @@ -8,6 +8,7 @@ #include "hwsim.h" // HWSIM_ATTR_FREQ #include // genlmsg_parse +#include // writev #include "cwifi.h" //#include "config.h" @@ -64,34 +65,36 @@ bool CWifi::PacketIsLost(TPower signalLevel) ssize_t CWifi::SendSignalWithSocket(CSocket* socket, TDescriptor descriptor, TPower* power, const char* buffer, int sizeOfBuffer) { -// cout<<"send power : "<Send(descriptor, reinterpret_cast(power), sizeof(TPower)); - if( val <= 0 ) - return val; - -// std::cout<<"send big data of size : "<Send(descriptor, buffer, sizeOfBuffer); + struct iovec iov[2]; + iov[0].iov_base = power; + iov[0].iov_len = sizeof(TPower); + iov[1].iov_base = const_cast(buffer); + iov[1].iov_len = sizeOfBuffer; + ssize_t total = sizeof(TPower) + sizeOfBuffer; + ssize_t ret = writev(descriptor, iov, 2); + if (ret != total) + return SOCKET_ERROR; + return ret; } ssize_t CWifi::RecvSignalWithSocket(CSocket* socket, TDescriptor descriptor, TPower* power, CDynBuffer* buffer) { - int valread; - - // read the power - valread = socket->Read(descriptor, reinterpret_cast(power), sizeof(TPower)); - if ( valread <= 0 ) - return valread; - - // read the signal - // "nlmsg_len" (type "uint32_t") is the first attribut of the "struct nlmsghdr" in "libnl3/netlink/netlink-kernel.h" - ssize_t sizeRead = socket->ReadEqualSize(descriptor, buffer, 0, sizeof(struct nlmsghdr)); - if( sizeRead == SOCKET_ERROR ) + ssize_t n; + n=socket->Read(descriptor, (char*)power, sizeof(TPower)); + if( n == SOCKET_ERROR ) return SOCKET_ERROR; - int sizeTotal=reinterpret_cast(buffer->GetBuffer())->nlmsg_len; + /* we read nlmsghdr to get the size of the message */ + struct nlmsghdr *nlh; + buffer->NeededSize(sizeof(struct nlmsghdr),false); + n=socket->ReadEqualSize(descriptor, buffer, 0, sizeof(struct nlmsghdr)); + if( n == SOCKET_ERROR ) + return SOCKET_ERROR; + nlh = (struct nlmsghdr *)buffer->GetBuffer(); + int sizeTotal=nlh->nlmsg_len; - if( sizeTotal > MTU ) // to avoid that a error packet overfulls the memory + if( sizeTotal > MTU ) return SOCKET_ERROR; - return socket->ReadEqualSize(descriptor, buffer, sizeRead, sizeTotal); + return socket->ReadEqualSize(descriptor, buffer, sizeof(struct nlmsghdr), sizeTotal); } diff --git a/src/cwirelessdevice.cc b/src/cwirelessdevice.cc index b0bcb12..a776f9a 100644 --- a/src/cwirelessdevice.cc +++ b/src/cwirelessdevice.cc @@ -7,7 +7,7 @@ #include -const std::regex wlan("wlan[0-9]*"); +const std::regex wlan("wlan[a-z]?[0-9]*"); WirelessDevice::WirelessDevice(){ @@ -19,7 +19,7 @@ WirelessDevice::~WirelessDevice(){ WirelessDevice::WirelessDevice(const std::string & name,int index ,int iftype ,const struct ether_addr & macaddr,int txpower):_name(name),_index(index),_iftype(iftype), _txpower(txpower), _macaddr(macaddr), _machwsim(macaddr) { -// _machwsim.ether_addr_octet[0] |= 0x40 ; + _machwsim.ether_addr_octet[0] |= 0x40 ; // std::memcpy(&_macaddr,&macaddr,ETH_ALEN); @@ -43,6 +43,7 @@ struct ether_addr WirelessDevice::getMacaddr() const { void WirelessDevice::setMachwsim(const struct ether_addr & machwsim) { _machwsim = machwsim ; + _machwsim.ether_addr_octet[0] |= 0x40 ; // std::memcpy(&_macaddr,&macaddr,ETH_ALEN); }