brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · 8b984fa Raw
274 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <inttypes.h>3#include <pthread.h>4#include <stdio.h>5#include "../../../../../include/linux/compiler.h"6#include "../../../../../include/linux/kernel.h"7#include "aolib.h"8 9struct netstat_counter {10	uint64_t val;11	char *name;12};13 14struct netstat {15	char *header_name;16	struct netstat *next;17	size_t counters_nr;18	struct netstat_counter *counters;19};20 21static struct netstat *lookup_type(struct netstat *ns,22		const char *type, size_t len)23{24	while (ns != NULL) {25		size_t cmp = max(len, strlen(ns->header_name));26 27		if (!strncmp(ns->header_name, type, cmp))28			return ns;29		ns = ns->next;30	}31	return NULL;32}33 34static struct netstat *lookup_get(struct netstat *ns,35				  const char *type, const size_t len)36{37	struct netstat *ret;38 39	ret = lookup_type(ns, type, len);40	if (ret != NULL)41		return ret;42 43	ret = malloc(sizeof(struct netstat));44	if (!ret)45		test_error("malloc()");46 47	ret->header_name = strndup(type, len);48	if (ret->header_name == NULL)49		test_error("strndup()");50	ret->next = ns;51	ret->counters_nr = 0;52	ret->counters = NULL;53 54	return ret;55}56 57static struct netstat *lookup_get_column(struct netstat *ns, const char *line)58{59	char *column;60 61	column = strchr(line, ':');62	if (!column)63		test_error("can't parse netstat file");64 65	return lookup_get(ns, line, column - line);66}67 68static void netstat_read_type(FILE *fnetstat, struct netstat **dest, char *line)69{70	struct netstat *type = lookup_get_column(*dest, line);71	const char *pos = line;72	size_t i, nr_elems = 0;73	char tmp;74 75	while ((pos = strchr(pos, ' '))) {76		nr_elems++;77		pos++;78	}79 80	*dest = type;81	type->counters = reallocarray(type->counters,82				type->counters_nr + nr_elems,83				sizeof(struct netstat_counter));84	if (!type->counters)85		test_error("reallocarray()");86 87	pos = strchr(line, ' ') + 1;88 89	if (fscanf(fnetstat, "%[^ :]", type->header_name) == EOF)90		test_error("fscanf(%s)", type->header_name);91	if (fread(&tmp, 1, 1, fnetstat) != 1 || tmp != ':')92		test_error("Unexpected netstat format (%c)", tmp);93 94	for (i = type->counters_nr; i < type->counters_nr + nr_elems; i++) {95		struct netstat_counter *nc = &type->counters[i];96		const char *new_pos = strchr(pos, ' ');97		const char *fmt = " %" PRIu64;98 99		if (new_pos == NULL)100			new_pos = strchr(pos, '\n');101 102		nc->name = strndup(pos, new_pos - pos);103		if (nc->name == NULL)104			test_error("strndup()");105 106		if (unlikely(!strcmp(nc->name, "MaxConn")))107			fmt = " %" PRId64; /* MaxConn is signed, RFC 2012 */108		if (fscanf(fnetstat, fmt, &nc->val) != 1)109			test_error("fscanf(%s)", nc->name);110		pos = new_pos + 1;111	}112	type->counters_nr += nr_elems;113 114	if (fread(&tmp, 1, 1, fnetstat) != 1 || tmp != '\n')115		test_error("Unexpected netstat format");116}117 118static const char *snmp6_name = "Snmp6";119static void snmp6_read(FILE *fnetstat, struct netstat **dest)120{121	struct netstat *type = lookup_get(*dest, snmp6_name, strlen(snmp6_name));122	char *counter_name;123	size_t i;124 125	for (i = type->counters_nr;; i++) {126		struct netstat_counter *nc;127		uint64_t counter;128 129		if (fscanf(fnetstat, "%ms", &counter_name) == EOF)130			break;131		if (fscanf(fnetstat, "%" PRIu64, &counter) == EOF)132			test_error("Unexpected snmp6 format");133		type->counters = reallocarray(type->counters, i + 1,134					sizeof(struct netstat_counter));135		if (!type->counters)136			test_error("reallocarray()");137		nc = &type->counters[i];138		nc->name = counter_name;139		nc->val = counter;140	}141	type->counters_nr = i;142	*dest = type;143}144 145struct netstat *netstat_read(void)146{147	struct netstat *ret = 0;148	size_t line_sz = 0;149	char *line = NULL;150	FILE *fnetstat;151 152	/*153	 * Opening thread-self instead of /proc/net/... as the latter154	 * points to /proc/self/net/ which instantiates thread-leader's155	 * net-ns, see:156	 * commit 155134fef2b6 ("Revert "proc: Point /proc/{mounts,net} at..")157	 */158	errno = 0;159	fnetstat = fopen("/proc/thread-self/net/netstat", "r");160	if (fnetstat == NULL)161		test_error("failed to open /proc/net/netstat");162 163	while (getline(&line, &line_sz, fnetstat) != -1)164		netstat_read_type(fnetstat, &ret, line);165	fclose(fnetstat);166 167	errno = 0;168	fnetstat = fopen("/proc/thread-self/net/snmp", "r");169	if (fnetstat == NULL)170		test_error("failed to open /proc/net/snmp");171 172	while (getline(&line, &line_sz, fnetstat) != -1)173		netstat_read_type(fnetstat, &ret, line);174	fclose(fnetstat);175 176	errno = 0;177	fnetstat = fopen("/proc/thread-self/net/snmp6", "r");178	if (fnetstat == NULL)179		test_error("failed to open /proc/net/snmp6");180 181	snmp6_read(fnetstat, &ret);182	fclose(fnetstat);183 184	free(line);185	return ret;186}187 188void netstat_free(struct netstat *ns)189{190	while (ns != NULL) {191		struct netstat *prev = ns;192		size_t i;193 194		free(ns->header_name);195		for (i = 0; i < ns->counters_nr; i++)196			free(ns->counters[i].name);197		free(ns->counters);198		ns = ns->next;199		free(prev);200	}201}202 203static inline void204__netstat_print_diff(uint64_t a, struct netstat *nsb, size_t i)205{206	if (unlikely(!strcmp(nsb->header_name, "MaxConn"))) {207		test_print("%8s %25s: %" PRId64 " => %" PRId64,208				nsb->header_name, nsb->counters[i].name,209				a, nsb->counters[i].val);210		return;211	}212 213	test_print("%8s %25s: %" PRIu64 " => %" PRIu64, nsb->header_name,214			nsb->counters[i].name, a, nsb->counters[i].val);215}216 217void netstat_print_diff(struct netstat *nsa, struct netstat *nsb)218{219	size_t i, j;220 221	while (nsb != NULL) {222		if (unlikely(strcmp(nsb->header_name, nsa->header_name))) {223			for (i = 0; i < nsb->counters_nr; i++)224				__netstat_print_diff(0, nsb, i);225			nsb = nsb->next;226			continue;227		}228 229		if (nsb->counters_nr < nsa->counters_nr)230			test_error("Unexpected: some counters disappeared!");231 232		for (j = 0, i = 0; i < nsb->counters_nr; i++) {233			if (strcmp(nsb->counters[i].name, nsa->counters[j].name)) {234				__netstat_print_diff(0, nsb, i);235				continue;236			}237 238			if (nsa->counters[j].val == nsb->counters[i].val) {239				j++;240				continue;241			}242 243			__netstat_print_diff(nsa->counters[j].val, nsb, i);244			j++;245		}246		if (j != nsa->counters_nr)247			test_error("Unexpected: some counters disappeared!");248 249		nsb = nsb->next;250		nsa = nsa->next;251	}252}253 254uint64_t netstat_get(struct netstat *ns, const char *name, bool *not_found)255{256	if (not_found)257		*not_found = false;258 259	while (ns != NULL) {260		size_t i;261 262		for (i = 0; i < ns->counters_nr; i++) {263			if (!strcmp(name, ns->counters[i].name))264				return ns->counters[i].val;265		}266 267		ns = ns->next;268	}269 270	if (not_found)271		*not_found = true;272	return 0;273}274