blob: d3c82885352791e209bbd92dc2d2e98a233bf439 [file] [log] [blame]
/*
*
* 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 <errno.h>
#include <ifaddrs.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <linux/wireless.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <lib/support/CHIPMemString.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/Tizen/ConnectivityUtils.h>
#include <platform/internal/CHIPDeviceLayerInternal.h>
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<uint16_t>(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<uint8_t>((frequency - 2407) / 5);
if (frequency == 2484)
return 14;
return static_cast<uint8_t>(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<char *>(&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<uint16_t>(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<int8_t>(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<int8_t>(dblevel);
}
}
else
{
if (!(qual->updated & IW_QUAL_LEVEL_INVALID))
{
VerifyOrExit(qual->level <= INT8_MAX, err = CHIP_ERROR_INVALID_INTEGER_VALUE);
rssi = static_cast<int8_t>(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<char *>(&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<char *>(&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