Skip to content

Commit 174b6cd

Browse files
KuzinAndreybvanassche
authored andcommitted
systemstats_linux: improve support of "Ip:" fields list from kernel /proc/net/snmp
This is inconvenient way every time check kernel data validness by compare magic number of header length. Any changes in kernel can broke all here. Introduce array of known fields for "Ip: " header in /proc/net/snmp. After first parsing we get prepared array of known fields and fill values in netsnmp_systemstats_entry.stats by fields offsets and their type `u_long` or `struct counter64`. Tested by: root@localhost# cat /proc/net/snmp | grep ^Ip: Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates OutTransmits Ip: 1 64 17458 0 0 0 0 0 17456 13388 14 451 0 0 0 0 0 0 0 13388 root@localhost# snmpwalk -v 2c -c public 127.0.0.1 IP-MIB::ipSystemStatsTable | grep ipv4 | grep -vE "(M|B)cast" | grep -vE "(In|Out)Octets" IP-MIB::ipSystemStatsInReceives.ipv4 = Counter32: 17272 IP-MIB::ipSystemStatsHCInReceives.ipv4 = Counter64: 17272 IP-MIB::ipSystemStatsInHdrErrors.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsInNoRoutes.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsInAddrErrors.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsInUnknownProtos.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsInTruncatedPkts.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsInForwDatagrams.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsHCInForwDatagrams.ipv4 = Counter64: 0 IP-MIB::ipSystemStatsReasmReqds.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsReasmOKs.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsReasmFails.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsInDiscards.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsInDelivers.ipv4 = Counter32: 17270 IP-MIB::ipSystemStatsHCInDelivers.ipv4 = Counter64: 17270 IP-MIB::ipSystemStatsOutRequests.ipv4 = Counter32: 13206 IP-MIB::ipSystemStatsHCOutRequests.ipv4 = Counter64: 13206 IP-MIB::ipSystemStatsOutNoRoutes.ipv4 = Counter32: 451 IP-MIB::ipSystemStatsOutForwDatagrams.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsHCOutForwDatagrams.ipv4 = Counter64: 0 IP-MIB::ipSystemStatsOutDiscards.ipv4 = Counter32: 14 IP-MIB::ipSystemStatsOutFragReqds.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsOutFragOKs.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsOutFragFails.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsOutFragCreates.ipv4 = Counter32: 0 IP-MIB::ipSystemStatsOutTransmits.ipv4 = Counter32: 12741 IP-MIB::ipSystemStatsHCOutTransmits.ipv4 = Counter64: 12741 IP-MIB::ipSystemStatsDiscontinuityTime.ipv4 = Timeticks: (0) 0:00:00.00 IP-MIB::ipSystemStatsRefreshRate.ipv4 = Gauge32: 60000 milli-seconds [ bvanassche: converted code to the Net-SNMP coding style ] Fixes: net-snmp#1026
1 parent 2b16948 commit 174b6cd

1 file changed

Lines changed: 123 additions & 78 deletions

File tree

agent/mibgroup/ip-mib/data_access/systemstats_linux.c

Lines changed: 123 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "systemstats_private.h"
1616

1717
#include <stdint.h>
18+
#include <stddef.h>
1819
#include <sys/types.h>
1920
#include <dirent.h>
2021
#include <ctype.h>
@@ -71,6 +72,41 @@ netsnmp_access_systemstats_arch_init(void)
7172
UdpLite: 0 0 0 0 0 0 0 0 0
7273
*/
7374

75+
static struct {
76+
const char *name;
77+
int avail_idx; /* -1 means no columnAvail[] index */
78+
int offset; /* -1 means no field in netsnmp_ipstats for that value */
79+
int type; /* 0 - u_long, 1 - struct counter64 */
80+
} systemstats_v4_ip_fields[] = {
81+
{"Forwarding", -1, -1, 0},
82+
{"DefaultTTL", -1, -1, 0},
83+
{"InReceives", IPSYSTEMSTATSTABLE_HCINRECEIVES, offsetof(netsnmp_ipstats, HCInReceives), 1},
84+
{"InHdrErrors", IPSYSTEMSTATSTABLE_INHDRERRORS, offsetof(netsnmp_ipstats, InHdrErrors), 0},
85+
{"InAddrErrors", IPSYSTEMSTATSTABLE_INADDRERRORS, offsetof(netsnmp_ipstats, InAddrErrors), 0},
86+
{"ForwDatagrams", IPSYSTEMSTATSTABLE_HCOUTFORWDATAGRAMS, offsetof(netsnmp_ipstats, HCOutForwDatagrams), 1},
87+
{"InUnknownProtos", IPSYSTEMSTATSTABLE_INUNKNOWNPROTOS, offsetof(netsnmp_ipstats, InUnknownProtos), 0},
88+
{"InDiscards", IPSYSTEMSTATSTABLE_INDISCARDS, offsetof(netsnmp_ipstats, InDiscards), 0},
89+
{"InDelivers", IPSYSTEMSTATSTABLE_HCINDELIVERS, offsetof(netsnmp_ipstats, HCInDelivers), 1},
90+
{"OutRequests", IPSYSTEMSTATSTABLE_HCOUTREQUESTS, offsetof(netsnmp_ipstats, HCOutRequests), 1},
91+
{"OutDiscards", IPSYSTEMSTATSTABLE_HCOUTDISCARDS, offsetof(netsnmp_ipstats, HCOutDiscards), 1},
92+
{"OutNoRoutes", IPSYSTEMSTATSTABLE_HCOUTNOROUTES, offsetof(netsnmp_ipstats, HCOutNoRoutes), 1},
93+
{"ReasmTimeout", -1, -1, 0},
94+
{"ReasmReqds", IPSYSTEMSTATSTABLE_REASMREQDS, offsetof(netsnmp_ipstats, ReasmReqds), 0},
95+
{"ReasmOKs", IPSYSTEMSTATSTABLE_REASMOKS, offsetof(netsnmp_ipstats, ReasmOKs), 0},
96+
{"ReasmFails", IPSYSTEMSTATSTABLE_REASMFAILS, offsetof(netsnmp_ipstats, ReasmFails), 0},
97+
{"FragOKs", IPSYSTEMSTATSTABLE_HCOUTFRAGOKS, offsetof(netsnmp_ipstats, HCOutFragOKs), 1},
98+
{"FragFails", IPSYSTEMSTATSTABLE_HCOUTFRAGFAILS, offsetof(netsnmp_ipstats, HCOutFragFails), 1},
99+
{"FragCreates", IPSYSTEMSTATSTABLE_HCOUTFRAGCREATES, offsetof(netsnmp_ipstats, HCOutFragCreates), 1},
100+
{"OutTransmits", -1, -1, 0},
101+
};
102+
103+
#define SYSTEMSTATS_V4_IP_FIELD_COUNT \
104+
(sizeof(systemstats_v4_ip_fields) / sizeof(systemstats_v4_ip_fields[0]))
105+
106+
static int systemstats_v4_ip_fields_prepared;
107+
108+
/* really found fields in header (index + 1); 0 means not found */
109+
static int systemstats_v4_ip_fields_idx[SYSTEMSTATS_V4_IP_FIELD_COUNT + 5 /*some extra space*/];
74110

75111
/*
76112
*
@@ -121,10 +157,12 @@ _systemstats_v4(netsnmp_container* container, u_int load_flags)
121157
FILE *devin;
122158
char line[1024];
123159
netsnmp_systemstats_entry *entry = NULL;
124-
int scan_count, expected_scan_count;
160+
int scan_count = 0;
125161
char *stats, *start = line;
126162
int len;
127-
unsigned long long scan_vals[20];
163+
unsigned long long scan_val;
164+
char *field, *ptr, *saveptr;
165+
int idx = 0;
128166

129167
DEBUGMSGTL(("access:systemstats:container:arch", "load v4 (flags %x)\n",
130168
load_flags));
@@ -142,41 +180,71 @@ _systemstats_v4(netsnmp_container* container, u_int load_flags)
142180
}
143181

144182
/*
145-
* skip header, but make sure it's the length we expect...
183+
* Parse header and compare column labels.
146184
*/
147-
NETSNMP_IGNORE_RESULT(fgets(line, sizeof(line), devin));
185+
start = fgets(line, sizeof(line), devin);
186+
if (!start) {
187+
fclose(devin);
188+
snmp_log_perror("systemstats_linux: cannot read /proc/net/snmp");
189+
return -3;
190+
}
191+
148192
len = strlen(line);
149-
switch (len) {
150-
case 224:
151-
expected_scan_count = 19;
152-
break;
153-
case 237:
154-
expected_scan_count = 20;
155-
break;
156-
default:
193+
if (len && line[len - 1] == '\n')
194+
line[len - 1] = '\0';
195+
196+
if (strncmp(start, "Ip: ", 4) || len < 224) {
157197
fclose(devin);
158-
snmp_log(LOG_ERR, "systemstats_linux: unexpected header length in /proc/net/snmp."
159-
" %d not in { 224, 237 } \n", len);
198+
snmp_log(LOG_ERR, "systemstats_linux: unexpected header in %s\n", "/proc/net/snmp");
160199
return -4;
200+
} else {
201+
start += sizeof("Ip: ") - 1;
202+
}
203+
204+
if (systemstats_v4_ip_fields_prepared == 0) {
205+
int found = 0;
206+
207+
for (ptr = start; ; ptr = NULL) {
208+
field = strtok_r(ptr, " ", &saveptr);
209+
if (!field)
210+
break;
211+
for (int i = 0; i < SYSTEMSTATS_V4_IP_FIELD_COUNT; ++i) {
212+
if (!strcmp(field, systemstats_v4_ip_fields[i].name)) {
213+
systemstats_v4_ip_fields_idx[idx] = i + 1;
214+
found++;
215+
break;
216+
}
217+
}
218+
idx++;
219+
if (idx >= sizeof(systemstats_v4_ip_fields_idx) /
220+
sizeof(systemstats_v4_ip_fields_idx[0])) {
221+
snmp_log(LOG_ERR, "systemstats_linux: known fields array overflow - report this as a net-snmp bug and include the kernel version\n");
222+
break;
223+
}
224+
}
225+
if (found != idx)
226+
snmp_log(LOG_ERR,
227+
"systemstats_linux: found only %d known fields of %d in %s\n",
228+
found, idx, "/proc/net/snmp");
229+
systemstats_v4_ip_fields_prepared = found;
161230
}
162231

163232
/*
164233
* This file provides the statistics for each systemstats.
165234
* Read in each line in turn, isolate the systemstats name
166-
* and retrieve (or create) the corresponding data structure.
235+
* and retrieve (or create) the corresponding data structure.
167236
*/
168237
start = fgets(line, sizeof(line), devin);
169238
fclose(devin);
170239
if (start) {
171-
172240
len = strlen(line);
173241
if (len && line[len - 1] == '\n')
174242
line[len - 1] = '\0';
175243

176244
while (*start && *start == ' ')
177245
start++;
178246

179-
if ((!*start) || ((stats = strrchr(start, ':')) == NULL)) {
247+
if (!*start || (stats = strrchr(start, ':')) == NULL) {
180248
snmp_log(LOG_ERR,
181249
"systemstats data format error 1, line ==|%s|\n", line);
182250
return -4;
@@ -190,9 +258,8 @@ _systemstats_v4(netsnmp_container* container, u_int load_flags)
190258

191259
entry = netsnmp_access_systemstats_entry_create(1, 0,
192260
"ipSystemStatsTable.ipv4");
193-
if(NULL == entry) {
261+
if (!entry)
194262
return -3;
195-
}
196263

197264
/*
198265
* OK - we've now got (or created) the data structure for
@@ -202,72 +269,50 @@ _systemstats_v4(netsnmp_container* container, u_int load_flags)
202269
* data structure accordingly.
203270
*/
204271

205-
memset(scan_vals, 0x0, sizeof(scan_vals));
206-
scan_count = sscanf(stats,
207-
"%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu"
208-
"%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
209-
&scan_vals[0],&scan_vals[1],&scan_vals[2],
210-
&scan_vals[3],&scan_vals[4],&scan_vals[5],
211-
&scan_vals[6],&scan_vals[7],&scan_vals[8],
212-
&scan_vals[9],&scan_vals[10],&scan_vals[11],
213-
&scan_vals[12],&scan_vals[13],&scan_vals[14],
214-
&scan_vals[15],&scan_vals[16],&scan_vals[17],
215-
&scan_vals[18],&scan_vals[19]);
272+
for (ptr = stats, len = 0; ; ptr = NULL, len++) {
273+
char *e;
274+
275+
if (!(field = strtok_r(ptr, " ", &saveptr)))
276+
break;
277+
scan_val = strtoull(field, &e, 10);
278+
if (field == e || *e != '\0')
279+
continue;
280+
if (len >= sizeof(systemstats_v4_ip_fields_idx) /
281+
sizeof(systemstats_v4_ip_fields_idx[0])) {
282+
snmp_log(LOG_ERR, "systemstats_linux: known fields array overflow - report this as a net-snmp bug and include the kernel version\n");
283+
break;
284+
}
285+
if (systemstats_v4_ip_fields_idx[len] == 0)
286+
continue;
287+
idx = systemstats_v4_ip_fields_idx[len] - 1;
288+
scan_count++;
289+
if (systemstats_v4_ip_fields[idx].offset != -1) {
290+
int offset = systemstats_v4_ip_fields[idx].offset;
291+
292+
if (systemstats_v4_ip_fields[idx].type == 0) {
293+
u_long *u = (u_long *)((void *)&entry->stats + offset);
294+
*u = scan_val;
295+
} else {
296+
struct counter64 *s = (struct counter64 *)((void*)&entry->stats + offset);
297+
s->low = scan_val & 0xffffffff;
298+
s->high = scan_val >> 32;
299+
}
300+
}
301+
if (systemstats_v4_ip_fields[idx].avail_idx != -1) {
302+
entry->stats.columnAvail[systemstats_v4_ip_fields[idx].avail_idx] = 1;
303+
}
304+
}
305+
216306
DEBUGMSGTL(("access:systemstats", " read %d values\n", scan_count));
217307

218-
if(scan_count != expected_scan_count) {
308+
if (scan_count != systemstats_v4_ip_fields_prepared) {
219309
snmp_log(LOG_ERR,
220310
"error scanning systemstats data (expected %d, got %d)\n",
221-
expected_scan_count, scan_count);
311+
systemstats_v4_ip_fields_prepared, scan_count);
222312
netsnmp_access_systemstats_entry_free(entry);
223313
return -4;
224314
}
225-
/* entry->stats. = scan_vals[0]; / * Forwarding */
226-
/* entry->stats. = scan_vals[1]; / * DefaultTTL */
227-
entry->stats.HCInReceives.low = scan_vals[2] & 0xffffffff;
228-
entry->stats.HCInReceives.high = scan_vals[2] >> 32;
229-
entry->stats.InHdrErrors = scan_vals[3];
230-
entry->stats.InAddrErrors = scan_vals[4];
231-
entry->stats.HCOutForwDatagrams.low = scan_vals[5] & 0xffffffff;
232-
entry->stats.HCOutForwDatagrams.high = scan_vals[5] >> 32;
233-
entry->stats.InUnknownProtos = scan_vals[6];
234-
entry->stats.InDiscards = scan_vals[7];
235-
entry->stats.HCInDelivers.low = scan_vals[8] & 0xffffffff;
236-
entry->stats.HCInDelivers.high = scan_vals[8] >> 32;
237-
entry->stats.HCOutRequests.low = scan_vals[9] & 0xffffffff;
238-
entry->stats.HCOutRequests.high = scan_vals[9] >> 32;
239-
entry->stats.HCOutDiscards.low = scan_vals[10] & 0xffffffff;
240-
entry->stats.HCOutDiscards.high = scan_vals[10] >> 32;
241-
entry->stats.HCOutNoRoutes.low = scan_vals[11] & 0xffffffff;
242-
entry->stats.HCOutNoRoutes.high = scan_vals[11] >> 32;
243-
/* entry->stats. = scan_vals[12]; / * ReasmTimeout */
244-
entry->stats.ReasmReqds = scan_vals[13];
245-
entry->stats.ReasmOKs = scan_vals[14];
246-
entry->stats.ReasmFails = scan_vals[15];
247-
entry->stats.HCOutFragOKs.low = scan_vals[16] & 0xffffffff;
248-
entry->stats.HCOutFragOKs.high = scan_vals[16] >> 32;
249-
entry->stats.HCOutFragFails.low = scan_vals[17] & 0xffffffff;
250-
entry->stats.HCOutFragFails.high = scan_vals[17] >> 32;
251-
entry->stats.HCOutFragCreates.low = scan_vals[18] & 0xffffffff;
252-
entry->stats.HCOutFragCreates.high = scan_vals[18] >> 32;
253-
/* entry->stats. = scan_vals[19]; / * OutTransmits */
254-
255-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCINRECEIVES] = 1;
256-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_INHDRERRORS] = 1;
257-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_INADDRERRORS] = 1;
258-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTFORWDATAGRAMS] = 1;
259-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_INUNKNOWNPROTOS] = 1;
260-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_INDISCARDS] = 1;
261-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCINDELIVERS] = 1;
262-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTREQUESTS] = 1;
263-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTDISCARDS] = 1;
264-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTNOROUTES] = 1;
265-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_REASMREQDS] = 1;
266-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_REASMOKS] = 1;
267-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_REASMFAILS] = 1;
268-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTFRAGOKS] = 1;
269-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTFRAGFAILS] = 1;
270-
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTFRAGCREATES] = 1;
315+
271316
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_DISCONTINUITYTIME] = 1;
272317
entry->stats.columnAvail[IPSYSTEMSTATSTABLE_REFRESHRATE] = 1;
273318

0 commit comments

Comments
 (0)