/* * * Copyright (c) 2021-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ConnectivityUtils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::chip::app::Clusters::GeneralDiagnostics; using namespace ::chip::app::Clusters::EthernetNetworkDiagnostics; namespace { constexpr uint16_t Map2400MHz(const uint8_t inChannel) { if (inChannel >= 1 && inChannel <= 13) return static_cast(2412 + ((inChannel - 1) * 5)); if (inChannel == 14) return 2484; return 0; } constexpr uint8_t MapFrequencyToChannel(const uint16_t frequency) { if (frequency < 2412) return 0; if (frequency < 2484) return static_cast((frequency - 2407) / 5); if (frequency == 2484) return 14; return static_cast(frequency / 5 - 1000); } constexpr uint16_t Map5000MHz(const uint8_t inChannel) { switch (inChannel) { case 183: return 4915; case 184: return 4920; case 185: return 4925; case 187: return 4935; case 188: return 4940; case 189: return 4945; case 192: return 4960; case 196: return 4980; case 7: return 5035; case 8: return 5040; case 9: return 5045; case 11: return 5055; case 12: return 5060; case 16: return 5080; case 34: return 5170; case 36: return 5180; case 38: return 5190; case 40: return 5200; case 42: return 5210; case 44: return 5220; case 46: return 5230; case 48: return 5240; case 52: return 5260; case 56: return 5280; case 60: return 5300; case 64: return 5320; case 100: return 5500; case 104: return 5520; case 108: return 5540; case 112: return 5560; case 116: return 5580; case 120: return 5600; case 124: return 5620; case 128: return 5640; case 132: return 5660; case 136: return 5680; case 140: return 5700; case 149: return 5745; case 153: return 5765; case 157: return 5785; case 161: return 5805; case 165: return 5825; default: return 0; } } constexpr double ConvertFrequencyToFloat(const iw_freq * in) { double result = (double) in->m; for (int i = 0; i < in->e; i++) result *= 10; return result; } CHIP_ERROR GetWiFiParameter(int sock, /* Socket to the kernel */ const char * ifname, /* Device name */ int request, /* WE ID */ struct iwreq * pwrq) /* Fixed part of the request */ { chip::Platform::CopyString(pwrq->ifr_name, ifname); if (ioctl(sock, request, pwrq) == -1) return CHIP_ERROR_POSIX(errno); return CHIP_NO_ERROR; } CHIP_ERROR GetWiFiStats(int sock, const char * ifname, struct iw_statistics * stats) { struct iwreq wrq = {}; wrq.u.data.pointer = (caddr_t) stats; wrq.u.data.length = sizeof(*stats); wrq.u.data.flags = 1; /* Clear updated flag */ return GetWiFiParameter(sock, ifname, SIOCGIWSTATS, &wrq); } } // namespace namespace chip { namespace DeviceLayer { namespace Internal { namespace ConnectivityUtils { InterfaceTypeEnum GetInterfaceConnectionType(const char * ifname) { InterfaceTypeEnum ret = InterfaceTypeEnum::kUnspecified; int sock = -1; if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { ChipLogError(DeviceLayer, "Failed to create INET socket: %s", strerror(errno)); return ret; } // Test wireless extensions for CONNECTION_WIFI struct iwreq pwrq = {}; Platform::CopyString(pwrq.ifr_name, ifname); if (ioctl(sock, SIOCGIWNAME, &pwrq) != -1) { ret = InterfaceTypeEnum::kWiFi; } else if ((strncmp(ifname, "en", 2) == 0) || (strncmp(ifname, "eth", 3) == 0)) { struct ethtool_cmd ecmd = {}; ecmd.cmd = ETHTOOL_GSET; struct ifreq ifr = {}; ifr.ifr_data = reinterpret_cast(&ecmd); Platform::CopyString(ifr.ifr_name, ifname); if (ioctl(sock, SIOCETHTOOL, &ifr) != -1) ret = InterfaceTypeEnum::kEthernet; } close(sock); return ret; } CHIP_ERROR GetInterfaceHardwareAddrs(const char * ifname, uint8_t * buf, size_t bufSize) { CHIP_ERROR err = CHIP_NO_ERROR; int sock = socket(AF_INET, SOCK_DGRAM, 0); VerifyOrReturnError(sock != -1, CHIP_ERROR_POSIX(errno), ChipLogError(DeviceLayer, "Failed to create INET socket: %s", strerror(errno))); struct ifreq req; Platform::CopyString(req.ifr_name, ifname); VerifyOrExit(ioctl(sock, SIOCGIFHWADDR, &req) != -1, err = CHIP_ERROR_POSIX(errno); ChipLogError(DeviceLayer, "Failed to get hardware address: %s", strerror(errno))); // Copy 48-bit IEEE MAC Address VerifyOrExit(bufSize >= 6, err = CHIP_ERROR_BUFFER_TOO_SMALL); memset(buf, 0, bufSize); memcpy(buf, req.ifr_ifru.ifru_hwaddr.sa_data, 6); exit: close(sock); return err; } CHIP_ERROR GetInterfaceIPv4Addrs(const char * ifname, uint8_t & size, NetworkInterface * ifp) { CHIP_ERROR err = CHIP_ERROR_READ_FAILED; struct ifaddrs * ifaddr = nullptr; if (getifaddrs(&ifaddr) == -1) { ChipLogError(DeviceLayer, "Failed to get network interfaces: %s", strerror(errno)); return CHIP_ERROR_POSIX(errno); } uint8_t index = 0; for (struct ifaddrs * ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { if (strcmp(ifname, ifa->ifa_name) == 0) { void * addPtr = &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr; memcpy(ifp->Ipv4AddressesBuffer[index], addPtr, kMaxIPv4AddrSize); ifp->Ipv4AddressSpans[index] = ByteSpan(ifp->Ipv4AddressesBuffer[index], kMaxIPv4AddrSize); index++; if (index >= kMaxIPv4AddrCount) { break; } } } } if (index > 0) { err = CHIP_NO_ERROR; size = index; } freeifaddrs(ifaddr); return err; } CHIP_ERROR GetInterfaceIPv6Addrs(const char * ifname, uint8_t & size, NetworkInterface * ifp) { CHIP_ERROR err = CHIP_ERROR_READ_FAILED; struct ifaddrs * ifaddr = nullptr; if (getifaddrs(&ifaddr) == -1) { ChipLogError(DeviceLayer, "Failed to get network interfaces: %s", strerror(errno)); return CHIP_ERROR_POSIX(errno); } uint8_t index = 0; for (struct ifaddrs * ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) { if (strcmp(ifname, ifa->ifa_name) == 0) { void * addPtr = &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr; memcpy(ifp->Ipv6AddressesBuffer[index], addPtr, kMaxIPv6AddrSize); ifp->Ipv6AddressSpans[index] = ByteSpan(ifp->Ipv6AddressesBuffer[index], kMaxIPv6AddrSize); index++; if (index >= kMaxIPv6AddrCount) { break; } } } } if (index > 0) { err = CHIP_NO_ERROR; size = index; } freeifaddrs(ifaddr); return err; } CHIP_ERROR GetWiFiInterfaceName(char * ifname, size_t bufSize) { CHIP_ERROR err = CHIP_ERROR_READ_FAILED; struct ifaddrs * ifaddr = nullptr; if (getifaddrs(&ifaddr) == -1) { ChipLogError(DeviceLayer, "Failed to get network interfaces: %s", strerror(errno)); return CHIP_ERROR_POSIX(errno); } struct ifaddrs * ifa = nullptr; for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { if (GetInterfaceConnectionType(ifa->ifa_name) == InterfaceTypeEnum::kWiFi) { Platform::CopyString(ifname, bufSize, ifa->ifa_name); err = CHIP_NO_ERROR; break; } } freeifaddrs(ifaddr); return err; } CHIP_ERROR GetWiFiChannelNumber(const char * ifname, uint16_t & channelNumber) { CHIP_ERROR err; struct iwreq wrq; double freq; int sock = socket(AF_INET, SOCK_DGRAM, 0); VerifyOrReturnError(sock != -1, CHIP_ERROR_POSIX(errno), ChipLogError(DeviceLayer, "Failed to create INET socket: %s", strerror(errno))); err = GetWiFiParameter(sock, ifname, SIOCGIWFREQ, &wrq); VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "Failed to get channel/frequency: %" CHIP_ERROR_FORMAT, err.Format())); freq = ConvertFrequencyToFloat(&(wrq.u.freq)); VerifyOrExit((freq / 1000000) <= UINT16_MAX, err = CHIP_ERROR_INVALID_INTEGER_VALUE); channelNumber = MapFrequencyToChannel(static_cast(freq / 1000000)); exit: close(sock); return err; } CHIP_ERROR GetWiFiRssi(const char * ifname, int8_t & rssi) { CHIP_ERROR err; struct iw_statistics stats; int sock = socket(AF_INET, SOCK_DGRAM, 0); VerifyOrReturnError(sock != -1, CHIP_ERROR_POSIX(errno), ChipLogError(DeviceLayer, "Failed to create INET socket: %s", strerror(errno))); err = GetWiFiStats(sock, ifname, &stats); VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "Failed to get Wi-Fi stats: %" CHIP_ERROR_FORMAT, err.Format())); { struct iw_quality * qual = &stats.qual; if (qual->updated & IW_QUAL_RCPI) { /* RCPI = int{(Power in dBm +110)*2} for 0dbm > Power > -110dBm */ if (!(qual->updated & IW_QUAL_LEVEL_INVALID)) { double rcpilevel = (qual->level / 2.0) - 110.0; VerifyOrExit(rcpilevel <= INT8_MAX, err = CHIP_ERROR_INVALID_INTEGER_VALUE); rssi = static_cast(rcpilevel); } } else { if (qual->updated & IW_QUAL_DBM) { if (!(qual->updated & IW_QUAL_LEVEL_INVALID)) { int dblevel = qual->level; /* dBm[-192; 63] */ if (qual->level >= 64) dblevel -= 0x100; VerifyOrExit(dblevel <= INT8_MAX, err = CHIP_ERROR_INVALID_INTEGER_VALUE); rssi = static_cast(dblevel); } } else { if (!(qual->updated & IW_QUAL_LEVEL_INVALID)) { VerifyOrExit(qual->level <= INT8_MAX, err = CHIP_ERROR_INVALID_INTEGER_VALUE); rssi = static_cast(qual->level); } } } } exit: close(sock); return err; } CHIP_ERROR GetWiFiBeaconLostCount(const char * ifname, uint32_t & beaconLostCount) { CHIP_ERROR err; struct iw_statistics stats; int sock = socket(AF_INET, SOCK_DGRAM, 0); VerifyOrReturnError(sock != -1, CHIP_ERROR_POSIX(errno), ChipLogError(DeviceLayer, "Failed to create INET socket: %s", strerror(errno))); err = GetWiFiStats(sock, ifname, &stats); VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "Failed to get Wi-Fi stats: %" CHIP_ERROR_FORMAT, err.Format())); beaconLostCount = stats.miss.beacon; exit: close(sock); return err; } CHIP_ERROR GetWiFiCurrentMaxRate(const char * ifname, uint64_t & currentMaxRate) { CHIP_ERROR err; struct iwreq wrq; int sock = socket(AF_INET, SOCK_DGRAM, 0); VerifyOrReturnError(sock != -1, CHIP_ERROR_POSIX(errno), ChipLogError(DeviceLayer, "Failed to create INET socket: %s", strerror(errno))); err = GetWiFiParameter(sock, ifname, SIOCGIWRATE, &wrq); VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "Failed to get channel/frequency: %" CHIP_ERROR_FORMAT, err.Format())); currentMaxRate = wrq.u.bitrate.value; exit: close(sock); return err; } CHIP_ERROR GetEthInterfaceName(char * ifname, size_t bufSize) { CHIP_ERROR err = CHIP_ERROR_READ_FAILED; struct ifaddrs * ifaddr = nullptr; if (getifaddrs(&ifaddr) == -1) { ChipLogError(DeviceLayer, "Failed to get network interfaces: %s", strerror(errno)); return CHIP_ERROR_POSIX(errno); } struct ifaddrs * ifa = nullptr; for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { if (GetInterfaceConnectionType(ifa->ifa_name) == InterfaceTypeEnum::kEthernet) { Platform::CopyString(ifname, bufSize, ifa->ifa_name); err = CHIP_NO_ERROR; break; } } freeifaddrs(ifaddr); return err; } CHIP_ERROR GetEthPHYRate(const char * ifname, PHYRateEnum & pHYRate) { CHIP_ERROR err = CHIP_NO_ERROR; uint32_t speed = 0; struct ethtool_cmd ecmd = {}; ecmd.cmd = ETHTOOL_GSET; struct ifreq ifr = {}; ifr.ifr_data = reinterpret_cast(&ecmd); Platform::CopyString(ifr.ifr_name, ifname); int sock = socket(AF_INET, SOCK_DGRAM, 0); VerifyOrReturnError(sock != -1, CHIP_ERROR_POSIX(errno), ChipLogError(DeviceLayer, "Failed to create INET socket: %s", strerror(errno))); VerifyOrExit(ioctl(sock, SIOCETHTOOL, &ifr) != -1, err = CHIP_ERROR_POSIX(errno); ChipLogError(DeviceLayer, "Cannot get device settings: %s", strerror(errno))); speed = (ecmd.speed_hi << 16) | ecmd.speed; switch (speed) { case 10: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate10M; break; case 100: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate100M; break; case 1000: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate1G; break; case 25000: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate25g; break; case 5000: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate5G; break; case 10000: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate10G; break; case 40000: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate40G; break; case 100000: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate100G; break; case 200000: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate200G; break; case 400000: pHYRate = app::Clusters::EthernetNetworkDiagnostics::PHYRateEnum::kRate400G; break; default: ChipLogError(DeviceLayer, "Undefined speed! (%d)\n", speed); err = CHIP_ERROR_READ_FAILED; break; }; exit: close(sock); return err; } CHIP_ERROR GetEthFullDuplex(const char * ifname, bool & fullDuplex) { CHIP_ERROR err = CHIP_NO_ERROR; struct ethtool_cmd ecmd = {}; ecmd.cmd = ETHTOOL_GSET; struct ifreq ifr = {}; ifr.ifr_data = reinterpret_cast(&ecmd); Platform::CopyString(ifr.ifr_name, ifname); int sock = socket(AF_INET, SOCK_DGRAM, 0); VerifyOrReturnError(sock != -1, CHIP_ERROR_POSIX(errno), ChipLogError(DeviceLayer, "Failed to create INET socket: %s", strerror(errno))); VerifyOrExit(ioctl(sock, SIOCETHTOOL, &ifr) != -1, err = CHIP_ERROR_POSIX(errno); ChipLogError(DeviceLayer, "Cannot get device settings: %s", strerror(errno))); fullDuplex = ecmd.duplex == DUPLEX_FULL; exit: close(sock); return err; } } // namespace ConnectivityUtils } // namespace Internal } // namespace DeviceLayer } // namespace chip