Add network statistics to ddsrt

Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
Erik Boasson 2019-08-02 08:57:14 +02:00 committed by eboasson
parent f9808c7656
commit ecb77d481c
6 changed files with 483 additions and 1 deletions

View file

@ -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

18
src/ddsrt/cmake/netstat.c Normal file
View file

@ -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

View file

@ -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 <stdint.h>
#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 */

View file

@ -0,0 +1,130 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/if.h>
#include <net/if_mib.h>
#include <errno.h>
#include <dds/ddsrt/heap.h>
#include <dds/ddsrt/string.h>
#include <dds/ddsrt/netstat.h>
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);
}

View file

@ -0,0 +1,146 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <dds/ddsrt/heap.h>
#include <dds/ddsrt/string.h>
#include <dds/ddsrt/netstat.h>
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;
}

View file

@ -0,0 +1,115 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <ws2def.h>
#include <ws2ipdef.h>
#include <iphlpapi.h>
#include <dds/ddsrt/heap.h>
#include <dds/ddsrt/string.h>
#include <dds/ddsrt/netstat.h>
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);
}