From ecb77d481c6514eae94fcc167fdbc900a42b1f5d Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Fri, 2 Aug 2019 08:57:14 +0200 Subject: [PATCH] Add network statistics to ddsrt Signed-off-by: Erik Boasson --- src/ddsrt/CMakeLists.txt | 2 +- src/ddsrt/cmake/netstat.c | 18 +++ src/ddsrt/include/dds/ddsrt/netstat.h | 73 ++++++++++++ src/ddsrt/src/netstat/darwin/netstat.c | 130 +++++++++++++++++++++ src/ddsrt/src/netstat/linux/netstat.c | 146 ++++++++++++++++++++++++ src/ddsrt/src/netstat/windows/netstat.c | 115 +++++++++++++++++++ 6 files changed, 483 insertions(+), 1 deletion(-) create mode 100644 src/ddsrt/cmake/netstat.c create mode 100644 src/ddsrt/include/dds/ddsrt/netstat.h create mode 100644 src/ddsrt/src/netstat/darwin/netstat.c create mode 100644 src/ddsrt/src/netstat/linux/netstat.c create mode 100644 src/ddsrt/src/netstat/windows/netstat.c diff --git a/src/ddsrt/CMakeLists.txt b/src/ddsrt/CMakeLists.txt index c78faf1..32ab777 100644 --- a/src/ddsrt/CMakeLists.txt +++ b/src/ddsrt/CMakeLists.txt @@ -147,7 +147,7 @@ list(APPEND sources # network stack. In order to mix-and-match various compilers, architectures, # operating systems, etc input from the build system is required. foreach(feature atomics cdtors environ heap ifaddrs random rusage - sockets string sync threads time md5 process) + sockets string sync threads time md5 process netstat) if(EXISTS "${include_path}/dds/ddsrt/${feature}.h") list(APPEND headers "${include_path}/dds/ddsrt/${feature}.h") file(GLOB_RECURSE diff --git a/src/ddsrt/cmake/netstat.c b/src/ddsrt/cmake/netstat.c new file mode 100644 index 0000000..a64b512 --- /dev/null +++ b/src/ddsrt/cmake/netstat.c @@ -0,0 +1,18 @@ +/* + * Copyright(c) 2006 to 2018 ADLINK Technology Limited and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include "dds/ddsrt/netstat.h" + +#if DDSRT_HAVE_NETSTAT +# error "cmake_HAVE_NETSTAT=true" +#else +# error "cmake_HAVE_NETSTAT=false" +#endif diff --git a/src/ddsrt/include/dds/ddsrt/netstat.h b/src/ddsrt/include/dds/ddsrt/netstat.h new file mode 100644 index 0000000..49151d2 --- /dev/null +++ b/src/ddsrt/include/dds/ddsrt/netstat.h @@ -0,0 +1,73 @@ +/* + * Copyright(c) 2019 ADLINK Technology Limited and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSRT_NETSTAT_H +#define DDSRT_NETSTAT_H + +#include + +#include "dds/export.h" +#include "dds/ddsrt/retcode.h" + +#if defined (__linux) || defined (__APPLE__) || defined (_WIN32) +#define DDSRT_HAVE_NETSTAT (1) +#else +#define DDSRT_HAVE_NETSTAT (0) +#endif + +#if DDSRT_HAVE_NETSTAT + +#if defined(__cplusplus) +extern "C" { +#endif + +struct ddsrt_netstat { + uint64_t ipkt; + uint64_t opkt; + uint64_t ibytes; + uint64_t obytes; +}; + +/** + * @brief Platform dependent control structure for network statistics + */ +struct ddsrt_netstat_control; + +/** + * @brief Prepare for gathering network statistics for specified interface. + */ +DDS_EXPORT dds_return_t +ddsrt_netstat_new ( + struct ddsrt_netstat_control **control, + const char *device); + +/** + * @brief Release resources for gathering network statistics. + */ +DDS_EXPORT dds_return_t +ddsrt_netstat_free ( + struct ddsrt_netstat_control *control); + +/** + * @brief Get network statistics. + */ +DDS_EXPORT dds_return_t +ddsrt_netstat_get ( + struct ddsrt_netstat_control *control, + struct ddsrt_netstat *stats); + +#if defined(__cplusplus) +} +#endif + +#endif /* DDSRT_HAVE_NETSTAT */ + +#endif /* DDSRT_NETSTAT_H */ diff --git a/src/ddsrt/src/netstat/darwin/netstat.c b/src/ddsrt/src/netstat/darwin/netstat.c new file mode 100644 index 0000000..7ffd7c8 --- /dev/null +++ b/src/ddsrt/src/netstat/darwin/netstat.c @@ -0,0 +1,130 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct ddsrt_netstat_control { + char *name; + int cached_row; +}; + +static dds_return_t ddsrt_netstat_get_int (struct ddsrt_netstat_control *control, struct ddsrt_netstat *stats) +{ + int name[6]; + size_t len; + int count; + struct ifmibdata ifmd; + + if (control->cached_row > 0) + { + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_IFDATA; + name[4] = control->cached_row; + name[5] = IFDATA_GENERAL; + len = sizeof (ifmd); + if (sysctl (name, 6, &ifmd, &len, NULL, 0) != 0) + control->cached_row = 0; + else if (strcmp (ifmd.ifmd_name, control->name) != 0) + control->cached_row = 0; + } + + if (control->cached_row == 0) + { + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_SYSTEM; + name[4] = IFMIB_IFCOUNT; + len = sizeof (count); + if (sysctl (name, 5, &count, &len, NULL, 0) != 0) + goto error; + for (int row = 1; row <= count; row++) + { + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_IFDATA; + name[4] = row; + name[5] = IFDATA_GENERAL; + len = sizeof (ifmd); + if (sysctl (name, 6, &ifmd, &len, NULL, 0) != 0) + { + if (errno != ENOENT) + goto error; + } + else if (strcmp (control->name, ifmd.ifmd_name) == 0) + { + control->cached_row = row; + break; + } + } + } + + if (control->cached_row == 0) + return DDS_RETCODE_NOT_FOUND; + else + { + stats->ipkt = ifmd.ifmd_data.ifi_ipackets; + stats->opkt = ifmd.ifmd_data.ifi_opackets; + stats->ibytes = ifmd.ifmd_data.ifi_ibytes; + stats->obytes = ifmd.ifmd_data.ifi_obytes; + return DDS_RETCODE_OK; + } + + error: + control->cached_row = -1; + return DDS_RETCODE_ERROR; +} + +dds_return_t ddsrt_netstat_new (struct ddsrt_netstat_control **control, const char *device) +{ + struct ddsrt_netstat_control *c = ddsrt_malloc (sizeof (*c)); + struct ddsrt_netstat dummy; + c->name = ddsrt_strdup (device); + c->cached_row = 0; + if (ddsrt_netstat_get_int (c, &dummy) != DDS_RETCODE_OK) + { + ddsrt_free (c->name); + ddsrt_free (c); + *control = NULL; + return DDS_RETCODE_ERROR; + } + else + { + *control = c; + return DDS_RETCODE_OK; + } +} + +dds_return_t ddsrt_netstat_free (struct ddsrt_netstat_control *control) +{ + ddsrt_free (control->name); + ddsrt_free (control); + return DDS_RETCODE_OK; +} + +dds_return_t ddsrt_netstat_get (struct ddsrt_netstat_control *control, struct ddsrt_netstat *stats) +{ + if (control->cached_row < 0) + return DDS_RETCODE_ERROR; + else + return ddsrt_netstat_get_int (control, stats); +} diff --git a/src/ddsrt/src/netstat/linux/netstat.c b/src/ddsrt/src/netstat/linux/netstat.c new file mode 100644 index 0000000..b0c2c81 --- /dev/null +++ b/src/ddsrt/src/netstat/linux/netstat.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include + +#include +#include +#include + +struct ddsrt_netstat_control { + char *name; +}; + +dds_return_t ddsrt_netstat_get (struct ddsrt_netstat_control *control, struct ddsrt_netstat *stats) +{ + FILE *fp; + char save[256]; + int c; + size_t np; + int field = 0; + if ((fp = fopen ("/proc/net/dev", "r")) == NULL) + return DDS_RETCODE_ERROR; + /* expected format: 2 header lines, then on each line, white space/interface + name/colon and then numbers. Received bytes is 1st data field, transmitted + bytes is 9th. + + SKIP_HEADER_1 skips up to and including the first newline; then SKIP_TO_EOL + skips up to and including the second newline, so the first line that gets + interpreted is the third. + */ + dds_return_t res = DDS_RETCODE_NOT_FOUND; + enum { SKIP_HEADER_1, SKIP_WHITE, READ_NAME, SKIP_TO_EOL, READ_SEP, READ_INT } state = SKIP_HEADER_1; + np = 0; + while (res == DDS_RETCODE_NOT_FOUND && (c = fgetc (fp)) != EOF) { + switch (state) { + case SKIP_HEADER_1: + if (c == '\n') { + state = SKIP_TO_EOL; + } + break; + case SKIP_WHITE: + if (c != ' ' && c != '\t') { + save[np++] = (char) c; + state = READ_NAME; + } + break; + case READ_NAME: + if (c == ':') { + save[np] = 0; + np = 0; + if (strcmp (save, control->name) != 0) + state = SKIP_TO_EOL; + else + state = READ_SEP; + } else if (np < sizeof (save) - 1) { + save[np++] = (char) c; + } + break; + case SKIP_TO_EOL: + if (c == '\n') { + state = SKIP_WHITE; + } + break; + case READ_SEP: + if (c == '\n') + { + /* unexpected end of line */ + res = DDS_RETCODE_ERROR; + } + else if (c >= '0' && c <= '9') + { + field++; + save[np++] = (char) c; + state = READ_INT; + } + break; + case READ_INT: + if (c >= '0' && c <= '9') + { + if (np == sizeof (save) - 1) + { + res = DDS_RETCODE_ERROR; + break; + } + save[np++] = (char) c; + } + else + { + save[np] = 0; + np = 0; + if (field == 1 || field == 2 || field == 9 || field == 10) + { + int pos; + uint64_t val; + if (sscanf (save, "%"SCNu64"%n", &val, &pos) != 1 || save[pos] != 0) + res = DDS_RETCODE_ERROR; + else + { + switch (field) + { + case 1: stats->ibytes = val; break; + case 2: stats->ipkt = val; break; + case 9: stats->obytes = val; break; + case 10: stats->opkt = val; res = DDS_RETCODE_OK; break; + } + } + } + if (c == '\n' && res != DDS_RETCODE_OK) + { + /* newline before all expected fields have been read */ + res = DDS_RETCODE_ERROR; + } + state = READ_SEP; + } + break; + } + } + fclose (fp); + return res; +} + +dds_return_t ddsrt_netstat_new (struct ddsrt_netstat_control **control, const char *device) +{ + struct ddsrt_netstat_control *c = ddsrt_malloc (sizeof (*c)); + struct ddsrt_netstat dummy; + c->name = ddsrt_strdup (device); + if (ddsrt_netstat_get (c, &dummy) != DDS_RETCODE_OK) + { + ddsrt_free (c->name); + ddsrt_free (c); + *control = NULL; + return DDS_RETCODE_ERROR; + } + else + { + *control = c; + return DDS_RETCODE_OK; + } +} + +dds_return_t ddsrt_netstat_free (struct ddsrt_netstat_control *control) +{ + ddsrt_free (control->name); + ddsrt_free (control); + return DDS_RETCODE_OK; +} diff --git a/src/ddsrt/src/netstat/windows/netstat.c b/src/ddsrt/src/netstat/windows/netstat.c new file mode 100644 index 0000000..678662c --- /dev/null +++ b/src/ddsrt/src/netstat/windows/netstat.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include + +#include +#include +#include + +struct ddsrt_netstat_control { + wchar_t *name; + bool errored; + bool have_index; + NET_IFINDEX index; +}; + +static void copy_data (struct ddsrt_netstat *dst, const MIB_IF_ROW2 *src) +{ + dst->ipkt = src->InUcastPkts + src->InNUcastPkts; + dst->opkt = src->OutUcastPkts + src->OutNUcastPkts; + dst->ibytes = src->InOctets; + dst->obytes = src->OutOctets; +} + +static bool is_desired_interface (const struct ddsrt_netstat_control *control, const MIB_IF_ROW2 *info) +{ + return wcscmp (control->name, info->Description) == 0 || wcscmp (control->name, info->Alias) == 0; +} + +static dds_return_t ddsrt_netstat_get_int (struct ddsrt_netstat_control *control, struct ddsrt_netstat *stats) +{ + if (control->errored) + return DDS_RETCODE_ERROR; + + if (control->have_index) + { + MIB_IF_ROW2 info; + memset (&info, 0, sizeof (info)); + info.InterfaceIndex = control->index; + if (GetIfEntry2 (&info) != NO_ERROR || !is_desired_interface (control, &info)) + control->have_index = false; + else + { + copy_data (stats, &info); + return DDS_RETCODE_OK; + } + } + + MIB_IF_TABLE2 *table; + if (GetIfTable2 (&table) != NO_ERROR) + goto error; + control->have_index = false; + for (ULONG row = 0; row < table->NumEntries; row++) + { + if (is_desired_interface (control, &table->Table[row])) + { + control->index = table->Table[row].InterfaceIndex; + control->have_index = true; + copy_data (stats, &table->Table[row]); + break; + } + } + FreeMibTable (table); + return control->have_index ? DDS_RETCODE_OK : DDS_RETCODE_NOT_FOUND; + + error: + control->errored = true; + return DDS_RETCODE_ERROR; +} + +dds_return_t ddsrt_netstat_new (struct ddsrt_netstat_control **control, const char *device) +{ + struct ddsrt_netstat_control *c = ddsrt_malloc (sizeof (*c)); + struct ddsrt_netstat dummy; + size_t name_size = strlen (device) + 1; + c->name = ddsrt_malloc (name_size * sizeof (*c->name)); + size_t cnt = 0; + mbstowcs_s (&cnt, c->name, name_size, device, _TRUNCATE); + c->have_index = false; + c->errored = false; + c->index = 0; + if (ddsrt_netstat_get_int (c, &dummy) != DDS_RETCODE_OK) + { + ddsrt_free (c->name); + ddsrt_free (c); + *control = NULL; + return DDS_RETCODE_ERROR; + } + else + { + *control = c; + return DDS_RETCODE_OK; + } +} + +dds_return_t ddsrt_netstat_free (struct ddsrt_netstat_control *control) +{ + ddsrt_free (control->name); + ddsrt_free (control); + return DDS_RETCODE_OK; +} + +dds_return_t ddsrt_netstat_get (struct ddsrt_netstat_control *control, struct ddsrt_netstat *stats) +{ + return ddsrt_netstat_get_int (control, stats); +}