diff options
Diffstat (limited to 'src/detection/netio')
| -rw-r--r-- | src/detection/netio/netio.c | 81 | ||||
| -rw-r--r-- | src/detection/netio/netio.h | 20 | ||||
| -rw-r--r-- | src/detection/netio/netio_apple.c | 55 | ||||
| -rw-r--r-- | src/detection/netio/netio_bsd.c | 62 | ||||
| -rw-r--r-- | src/detection/netio/netio_haiku.cpp | 52 | ||||
| -rw-r--r-- | src/detection/netio/netio_linux.c | 91 | ||||
| -rw-r--r-- | src/detection/netio/netio_nosupport.c | 5 | ||||
| -rw-r--r-- | src/detection/netio/netio_sunos.c | 68 | ||||
| -rw-r--r-- | src/detection/netio/netio_windows.c | 71 |
9 files changed, 505 insertions, 0 deletions
diff --git a/src/detection/netio/netio.c b/src/detection/netio/netio.c new file mode 100644 index 0000000..c6b9da6 --- /dev/null +++ b/src/detection/netio/netio.c @@ -0,0 +1,81 @@ +#include "netio.h" + +#include "common/time.h" + +static FFlist ioCounters1; +static uint64_t time1; + +void ffPrepareNetIO(FFNetIOOptions* options) { + if (options->detectTotal) { + return; + } + + if (time1 != 0) { + return; // Already prepared + } + + ffListInit(&ioCounters1); + ffNetIOGetIoCounters(&ioCounters1, options); + time1 = ffTimeGetNow(); +} + +const char* ffDetectNetIO(FFlist* result, FFNetIOOptions* options) { + const char* error = NULL; + + if (options->detectTotal) { + error = ffNetIOGetIoCounters(result, options); + if (error) { + return error; + } + return NULL; + } + + if (time1 == 0) { + ffListInit(&ioCounters1); + error = ffNetIOGetIoCounters(&ioCounters1, options); + if (error) { + return error; + } + time1 = ffTimeGetNow(); + } + + if (ioCounters1.length == 0) { + return "No network interfaces found"; + } + + uint64_t time2 = ffTimeGetNow(); + while (time2 - time1 < options->waitTime) { + ffTimeSleep((uint32_t) (options->waitTime - (time2 - time1))); + time2 = ffTimeGetNow(); + } + + error = ffNetIOGetIoCounters(result, options); + if (error) { + return error; + } + + if (result->length != ioCounters1.length) { + return "Different number of network interfaces. Network change?"; + } + + for (uint32_t i = 0; i < result->length; ++i) { + FFNetIOResult* icPrev = FF_LIST_GET(FFNetIOResult, ioCounters1, i); + FFNetIOResult* icCurr = FF_LIST_GET(FFNetIOResult, *result, i); + if (!ffStrbufEqual(&icPrev->name, &icCurr->name)) { + return "Network interface name changed"; + } + + static_assert(sizeof(FFNetIOResult) - offsetof(FFNetIOResult, txBytes) == sizeof(uint64_t) * 8, "Unexpected struct FFNetIOResult layout"); + for (size_t off = offsetof(FFNetIOResult, txBytes); off < sizeof(FFNetIOResult); off += sizeof(uint64_t)) { + uint64_t* prevValue = (uint64_t*) ((uint8_t*) icPrev + off); + uint64_t* currValue = (uint64_t*) ((uint8_t*) icCurr + off); + uint64_t temp = *currValue; + *currValue -= *prevValue; + *currValue /= (time2 - time1) / 1000 /* seconds */; + *prevValue = temp; + } + } + time1 = time2; + + return NULL; +} diff --git a/src/detection/netio/netio.h b/src/detection/netio/netio.h new file mode 100644 index 0000000..1e48b53 --- /dev/null +++ b/src/detection/netio/netio.h @@ -0,0 +1,20 @@ +#pragma once + +#include "fastfetch.h" +#include "modules/netio/option.h" + +typedef struct FFNetIOResult { + FFstrbuf name; + bool defaultRoute; + uint64_t txBytes; + uint64_t rxBytes; + uint64_t txPackets; + uint64_t rxPackets; + uint64_t rxErrors; + uint64_t txErrors; + uint64_t rxDrops; + uint64_t txDrops; +} FFNetIOResult; + +const char* ffDetectNetIO(FFlist* result, FFNetIOOptions* options); +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options); diff --git a/src/detection/netio/netio_apple.c b/src/detection/netio/netio_apple.c new file mode 100644 index 0000000..125cfd9 --- /dev/null +++ b/src/detection/netio/netio_apple.c @@ -0,0 +1,55 @@ +#include "netio.h" + +#include "common/netif.h" +#include "common/mallocHelper.h" + +#include <net/if.h> +#include <net/if_mib.h> +#include <sys/sysctl.h> + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) { + int mib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, options->defaultRouteOnly ? IFMIB_IFDATA : IFMIB_IFALLDATA, options->defaultRouteOnly ? (int) ffNetifGetDefaultRouteV4()->ifIndex : 0, IFDATA_GENERAL }; + + size_t bufSize = 0; + if (sysctl(mib, ARRAY_SIZE(mib), NULL, &bufSize, 0, 0) < 0) { + return "sysctl(mib, ARRAY_SIZE(mib), NULL, &bufSize, 0, 0) failed"; + } + + assert(bufSize % sizeof(struct ifmibdata) == 0); + + FF_AUTO_FREE struct ifmibdata* buf = (struct ifmibdata*) malloc(bufSize); + if (sysctl(mib, ARRAY_SIZE(mib), buf, &bufSize, 0, 0) < 0) { + return "sysctl(mib, ARRAY_SIZE(mib), buf, &bufSize, 0, 0) failed"; + } + + size_t ifCount = bufSize / sizeof(struct ifmibdata); + + const char* defaultRouteIfName = ffNetifGetDefaultRouteV4()->ifName; + + for (size_t i = 0; i < ifCount; i++) { + struct ifmibdata* mibdata = &buf[i]; + if (!(mibdata->ifmd_flags & IFF_RUNNING) || (mibdata->ifmd_flags & IFF_NOARP)) { + continue; + } + + if (options->namePrefix.length && strncmp(mibdata->ifmd_name, options->namePrefix.chars, options->namePrefix.length) != 0) { + continue; + } + + FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result); + *counters = (FFNetIOResult) { + .name = ffStrbufCreateS(mibdata->ifmd_name), + .txBytes = mibdata->ifmd_data.ifi_obytes, + .rxBytes = mibdata->ifmd_data.ifi_ibytes, + .txPackets = mibdata->ifmd_data.ifi_opackets, + .rxPackets = mibdata->ifmd_data.ifi_ipackets, + .txErrors = mibdata->ifmd_data.ifi_oerrors, + .rxErrors = mibdata->ifmd_data.ifi_ierrors, + .txDrops = mibdata->ifmd_snd_drops, + .rxDrops = mibdata->ifmd_data.ifi_iqdrops, + .defaultRoute = strncmp(mibdata->ifmd_name, defaultRouteIfName, IFNAMSIZ) == 0, + }; + } + + return NULL; +} diff --git a/src/detection/netio/netio_bsd.c b/src/detection/netio/netio_bsd.c new file mode 100644 index 0000000..83c1b6b --- /dev/null +++ b/src/detection/netio/netio_bsd.c @@ -0,0 +1,62 @@ +#include "netio.h" + +#include "common/netif.h" +#include "common/mallocHelper.h" + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> +#include <sys/sysctl.h> +#include <sys/socket.h> + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) { + uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteV4()->ifIndex; + + size_t bufSize = 0; + if (sysctl((int[]) { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, (options->defaultRouteOnly ? (int) defaultRouteIfIndex : 0) }, 6, NULL, &bufSize, 0, 0) < 0) { + return "sysctl({ CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, ifIndex }, 6, NULL, &bufSize, 0, 0) failed"; + } + + FF_AUTO_FREE struct if_msghdr* buf = (struct if_msghdr*) malloc(bufSize); + if (sysctl((int[]) { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, (options->defaultRouteOnly ? (int) defaultRouteIfIndex : 0) }, 6, buf, &bufSize, 0, 0) < 0) { + return "sysctl({ CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, ifIndex }, 6, buf, &bufSize, 0, 0) failed"; + } + + for (struct if_msghdr* ifm = buf; + ifm < (struct if_msghdr*) ((uint8_t*) buf + bufSize); + ifm = (struct if_msghdr*) ((uint8_t*) ifm + ifm->ifm_msglen)) { + if (ifm->ifm_type != RTM_IFINFO || !(ifm->ifm_flags & IFF_RUNNING) || (ifm->ifm_flags & IFF_NOARP)) { + continue; + } + + struct sockaddr_dl* sdl = (struct sockaddr_dl*) (ifm + 1); + assert(sdl->sdl_family == AF_LINK); + + sdl->sdl_data[sdl->sdl_nlen] = 0; + + if (options->namePrefix.length && strncmp(sdl->sdl_data, options->namePrefix.chars, options->namePrefix.length) != 0) { + continue; + } + + FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result); + *counters = (FFNetIOResult) { + .name = ffStrbufCreateNS(sdl->sdl_nlen, sdl->sdl_data), + .txBytes = ifm->ifm_data.ifi_obytes, + .rxBytes = ifm->ifm_data.ifi_ibytes, + .txPackets = ifm->ifm_data.ifi_opackets, + .rxPackets = ifm->ifm_data.ifi_ipackets, + .txErrors = ifm->ifm_data.ifi_oerrors, + .rxErrors = ifm->ifm_data.ifi_ierrors, +#ifdef FF_HAVE_IFI_OQDROPS + .txDrops = ifm->ifm_data.ifi_oqdrops, +#else + .txDrops = 0, // unsupported +#endif + .rxDrops = ifm->ifm_data.ifi_iqdrops, + .defaultRoute = sdl->sdl_index == defaultRouteIfIndex, + }; + } + + return NULL; +} diff --git a/src/detection/netio/netio_haiku.cpp b/src/detection/netio/netio_haiku.cpp new file mode 100644 index 0000000..88a123d --- /dev/null +++ b/src/detection/netio/netio_haiku.cpp @@ -0,0 +1,52 @@ +extern "C" { +#include "netio.h" +#include "common/netif.h" +} + +#include <NetworkInterface.h> +#include <NetworkRoster.h> + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) { + BNetworkRoster& roster = BNetworkRoster::Default(); + + BNetworkInterface interface; + uint32 cookie = 0; + + uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteV4()->ifIndex; + + while (roster.GetNextInterface(&cookie, interface) == B_OK) { + if (!interface.Exists()) { + continue; + } + + bool defaultRoute = interface.Index() == defaultRouteIfIndex; + if (options->defaultRouteOnly && !defaultRoute) { + continue; + } + + if (options->namePrefix.length && strncmp(interface.Name(), options->namePrefix.chars, options->namePrefix.length) != 0) { + continue; + } + + ifreq_stats stats = {}; + if (interface.GetStats(stats) != B_OK) { + continue; + } + + FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result); + *counters = (FFNetIOResult) { + .name = ffStrbufCreateS(interface.Name()), + .defaultRoute = defaultRoute, + .txBytes = stats.send.bytes, + .rxBytes = stats.receive.bytes, + .txPackets = stats.send.packets, + .rxPackets = stats.receive.packets, + .rxErrors = stats.receive.errors, + .txErrors = stats.send.errors, + .rxDrops = stats.receive.dropped, + .txDrops = stats.send.dropped + }; + } + + return NULL; +} diff --git a/src/detection/netio/netio_linux.c b/src/detection/netio/netio_linux.c new file mode 100644 index 0000000..5870d3f --- /dev/null +++ b/src/detection/netio/netio_linux.c @@ -0,0 +1,91 @@ +#include "netio.h" + +#include "common/io.h" +#include "common/netif.h" +#include "common/strutil.h" + +#include <fcntl.h> +#include <net/if.h> + +static void getData(FFstrbuf* buffer, const char* ifName, bool isDefaultRoute, int basefd, FFlist* result) { + FF_AUTO_CLOSE_FD int dfd = openat(basefd, ifName, O_RDONLY | O_DIRECTORY); + if (dfd < 0) { + return; + } + + char operstate; + if (!ffReadFileDataRelative(dfd, "operstate", 1, &operstate) || operstate != 'u' /* up or unknown */) { + return; + } + + FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result); + ffStrbufInitS(&counters->name, ifName); + counters->defaultRoute = isDefaultRoute; + + if (ffReadFileBufferRelative(dfd, "statistics/rx_bytes", buffer)) { + counters->rxBytes = ffStrbufToUInt(buffer, 0); + } + + if (ffReadFileBufferRelative(dfd, "statistics/tx_bytes", buffer)) { + counters->txBytes = ffStrbufToUInt(buffer, 0); + } + + if (ffReadFileBufferRelative(dfd, "statistics/rx_packets", buffer)) { + counters->rxPackets = ffStrbufToUInt(buffer, 0); + } + + if (ffReadFileBufferRelative(dfd, "statistics/tx_packets", buffer)) { + counters->txPackets = ffStrbufToUInt(buffer, 0); + } + + if (ffReadFileBufferRelative(dfd, "statistics/rx_errors", buffer)) { + counters->rxErrors = ffStrbufToUInt(buffer, 0); + } + + if (ffReadFileBufferRelative(dfd, "statistics/tx_errors", buffer)) { + counters->txErrors = ffStrbufToUInt(buffer, 0); + } + + if (ffReadFileBufferRelative(dfd, "statistics/rx_dropped", buffer)) { + counters->rxDrops = ffStrbufToUInt(buffer, 0); + } + + if (ffReadFileBufferRelative(dfd, "statistics/tx_dropped", buffer)) { + counters->txDrops = ffStrbufToUInt(buffer, 0); + } +} + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) { + FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/net"); + if (!dirp) { + return "opendir(\"/sys/class/net\") == NULL"; + } + + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + + const char* defaultRouteIfName = ffNetifGetDefaultRouteV4()->ifName; + + if (options->defaultRouteOnly) { + if (options->namePrefix.length && strncmp(defaultRouteIfName, options->namePrefix.chars, options->namePrefix.length) != 0) { + return NULL; + } + + getData(&buffer, defaultRouteIfName, true, dirfd(dirp), result); + } else { + struct dirent* entry; + while ((entry = readdir(dirp)) != NULL) { + const char* ifName = entry->d_name; + if (ifName[0] == '.') { + continue; + } + + if (options->namePrefix.length && strncmp(ifName, options->namePrefix.chars, options->namePrefix.length) != 0) { + continue; + } + + getData(&buffer, ifName, ffStrEquals(ifName, defaultRouteIfName), dirfd(dirp), result); + } + } + + return NULL; +} diff --git a/src/detection/netio/netio_nosupport.c b/src/detection/netio/netio_nosupport.c new file mode 100644 index 0000000..30f73a7 --- /dev/null +++ b/src/detection/netio/netio_nosupport.c @@ -0,0 +1,5 @@ +#include "netio.h" + +const char* ffNetIOGetIoCounters(FF_A_UNUSED FFlist* result, FF_A_UNUSED FFNetIOOptions* options) { + return "Not supported on this platform"; +} diff --git a/src/detection/netio/netio_sunos.c b/src/detection/netio/netio_sunos.c new file mode 100644 index 0000000..3789e45 --- /dev/null +++ b/src/detection/netio/netio_sunos.c @@ -0,0 +1,68 @@ +#include "netio.h" +#include "common/netif.h" +#include "common/strutil.h" + +#include <kstat.h> + +static inline void kstatFreeWrap(kstat_ctl_t** pkc) { + assert(pkc); + if (*pkc) { + kstat_close(*pkc); + } +} + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) { + FF_A_CLEANUP(kstatFreeWrap) kstat_ctl_t* kc = kstat_open(); + if (!kc) { + return "kstat_open() failed"; + } + + const char* defaultRouteIfName = ffNetifGetDefaultRouteV4()->ifName; + + for (kstat_t* ks = kc->kc_chain; ks; ks = ks->ks_next) { + if (!ffStrEquals(ks->ks_class, "net") || !ffStrEquals(ks->ks_module, "link")) { + continue; + } + + if (options->namePrefix.length && strncmp(ks->ks_name, options->namePrefix.chars, options->namePrefix.length) != 0) { + continue; + } + + bool isDefaultRoute = ffStrEquals(ks->ks_name, defaultRouteIfName); + if (options->defaultRouteOnly && !isDefaultRoute) { + continue; + } + + if (kstat_read(kc, ks, NULL) < 0) { + continue; + } + + FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result); + + kstat_named_t* wbytes = (kstat_named_t*) kstat_data_lookup(ks, "obytes64"); + kstat_named_t* rbytes = (kstat_named_t*) kstat_data_lookup(ks, "rbytes64"); + kstat_named_t* wpkts = (kstat_named_t*) kstat_data_lookup(ks, "opackets64"); + kstat_named_t* rpkts = (kstat_named_t*) kstat_data_lookup(ks, "ipackets64"); + kstat_named_t* werrs = (kstat_named_t*) kstat_data_lookup(ks, "oerrors"); + kstat_named_t* rerrs = (kstat_named_t*) kstat_data_lookup(ks, "ierrors"); + + *counters = (FFNetIOResult) { + .name = ffStrbufCreateS(ks->ks_name), + .txBytes = wbytes->value.ui64, + .rxBytes = rbytes->value.ui64, + .txPackets = wpkts->value.ui64, + .rxPackets = rpkts->value.ui64, + .txErrors = werrs->value.ui64, + .rxErrors = rerrs->value.ui64, + .txDrops = 0, // unsupported + .rxDrops = 0, + .defaultRoute = isDefaultRoute, + }; + + if (options->defaultRouteOnly) { + break; + } + } + + return NULL; +} diff --git a/src/detection/netio/netio_windows.c b/src/detection/netio/netio_windows.c new file mode 100644 index 0000000..3c64334 --- /dev/null +++ b/src/detection/netio/netio_windows.c @@ -0,0 +1,71 @@ +#include "netio.h" + +#include "common/netif.h" +#include "common/mallocHelper.h" +#include "common/windows/unicode.h" + +#include <ws2tcpip.h> +#include <iphlpapi.h> + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) { + IP_ADAPTER_ADDRESSES* FF_AUTO_FREE adapter_addresses = NULL; + + // Multiple attempts in case interfaces change while + // we are in the middle of querying them. + DWORD adapter_addresses_buffer_size = 0; + for (int attempts = 0;; ++attempts) { + if (adapter_addresses_buffer_size) { + adapter_addresses = (IP_ADAPTER_ADDRESSES*) realloc(adapter_addresses, adapter_addresses_buffer_size); + assert(adapter_addresses); + } + + DWORD error = GetAdaptersAddresses( + AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, + NULL, + adapter_addresses, + &adapter_addresses_buffer_size); + + if (error == ERROR_SUCCESS) { + break; + } else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) { + continue; + } else { + return "GetAdaptersAddresses() failed"; + } + } + + uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteV4()->ifIndex; + + // Iterate through all of the adapters + for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) { + bool isDefaultRoute = adapter->IfIndex == defaultRouteIfIndex; + if (options->defaultRouteOnly && !isDefaultRoute) { + continue; + } + + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(adapter->FriendlyName); + if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) { + continue; + } + + MIB_IF_ROW2 ifRow = { .InterfaceIndex = adapter->IfIndex }; + if (GetIfEntry2(&ifRow) == NO_ERROR) { + FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result); + *counters = (FFNetIOResult) { + .name = ffStrbufCreateMove(&name), + .txBytes = ifRow.OutOctets, + .rxBytes = ifRow.InOctets, + .txPackets = (ifRow.OutUcastPkts + ifRow.OutNUcastPkts), + .rxPackets = (ifRow.InUcastPkts + ifRow.InNUcastPkts), + .rxErrors = ifRow.InErrors, + .txErrors = ifRow.OutErrors, + .rxDrops = ifRow.InDiscards, + .txDrops = ifRow.OutDiscards, + .defaultRoute = isDefaultRoute, + }; + } + } + + return NULL; +} |