+struct netstat_columns {
+ char *name;
+ uint32_t hash;
+ unsigned long long value;
+ int multiplier; // not needed everywhere
+ char *label; // not needed everywhere
+};
+
+static struct netstat_columns ip_data[] = {
+// { "Forwarding", 0, 0, 1, NULL },
+// { "DefaultTTL", 0, 0, 1, NULL },
+ { "InReceives", 0, 0, 1, NULL },
+ { "InHdrErrors", 0, 0, 1, NULL },
+ { "InAddrErrors", 0, 0, 1, NULL },
+ { "ForwDatagrams", 0, 0, 1, NULL },
+ { "InUnknownProtos", 0, 0, 1, NULL },
+ { "InDiscards", 0, 0, 1, NULL },
+ { "InDelivers", 0, 0, 1, NULL },
+ { "OutRequests", 0, 0, 1, NULL },
+ { "OutDiscards", 0, 0, 1, NULL },
+ { "OutNoRoutes", 0, 0, 1, NULL },
+// { "ReasmTimeout", 0, 0, 1, NULL },
+ { "ReasmReqds", 0, 0, 1, NULL },
+ { "ReasmOKs", 0, 0, 1, NULL },
+ { "ReasmFails", 0, 0, 1, NULL },
+ { "FragOKs", 0, 0, 1, NULL },
+ { "FragFails", 0, 0, 1, NULL },
+ { "FragCreates", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns icmp_data[] = {
+ { "InMsgs", 0, 0, 1, NULL },
+ { "OutMsgs", 0, 0, -1, NULL },
+ { "InErrors", 0, 0, 1, NULL },
+ { "OutErrors", 0, 0, -1, NULL },
+ { "InCsumErrors", 0, 0, 1, NULL },
+
+ // all these are available in icmpmsg
+// { "InDestUnreachs", 0, 0, 1, NULL },
+// { "OutDestUnreachs", 0, 0, -1, NULL },
+// { "InTimeExcds", 0, 0, 1, NULL },
+// { "OutTimeExcds", 0, 0, -1, NULL },
+// { "InParmProbs", 0, 0, 1, NULL },
+// { "OutParmProbs", 0, 0, -1, NULL },
+// { "InSrcQuenchs", 0, 0, 1, NULL },
+// { "OutSrcQuenchs", 0, 0, -1, NULL },
+// { "InRedirects", 0, 0, 1, NULL },
+// { "OutRedirects", 0, 0, -1, NULL },
+// { "InEchos", 0, 0, 1, NULL },
+// { "OutEchos", 0, 0, -1, NULL },
+// { "InEchoReps", 0, 0, 1, NULL },
+// { "OutEchoReps", 0, 0, -1, NULL },
+// { "InTimestamps", 0, 0, 1, NULL },
+// { "OutTimestamps", 0, 0, -1, NULL },
+// { "InTimestampReps", 0, 0, 1, NULL },
+// { "OutTimestampReps", 0, 0, -1, NULL },
+// { "InAddrMasks", 0, 0, 1, NULL },
+// { "OutAddrMasks", 0, 0, -1, NULL },
+// { "InAddrMaskReps", 0, 0, 1, NULL },
+// { "OutAddrMaskReps", 0, 0, -1, NULL },
+
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns icmpmsg_data[] = {
+ { "InType0", 0, 0, 1, "InEchoReps" },
+ { "OutType0", 0, 0, -1, "OutEchoReps" },
+// { "InType1", 0, 0, 1, NULL }, // unassigned
+// { "OutType1", 0, 0, -1, NULL }, // unassigned
+// { "InType2", 0, 0, 1, NULL }, // unassigned
+// { "OutType2", 0, 0, -1, NULL }, // unassigned
+ { "InType3", 0, 0, 1, "InDestUnreachs" },
+ { "OutType3", 0, 0, -1, "OutDestUnreachs" },
+// { "InType4", 0, 0, 1, "InSrcQuenchs" }, // deprecated
+// { "OutType4", 0, 0, -1, "OutSrcQuenchs" }, // deprecated
+ { "InType5", 0, 0, 1, "InRedirects" },
+ { "OutType5", 0, 0, -1, "OutRedirects" },
+// { "InType6", 0, 0, 1, "InAlterHostAddr" }, // deprecated
+// { "OutType6", 0, 0, -1, "OutAlterHostAddr" }, // deprecated
+// { "InType7", 0, 0, 1, NULL }, // unassigned
+// { "OutType7", 0, 0, -1, NULL }, // unassigned
+ { "InType8", 0, 0, 1, "InEchos" },
+ { "OutType8", 0, 0, -1, "OutEchos" },
+ { "InType9", 0, 0, 1, "InRouterAdvert" },
+ { "OutType9", 0, 0, -1, "OutRouterAdvert" },
+ { "InType10", 0, 0, 1, "InRouterSelect" },
+ { "OutType10", 0, 0, -1, "OutRouterSelect" },
+ { "InType11", 0, 0, 1, "InTimeExcds" },
+ { "OutType11", 0, 0, -1, "OutTimeExcds" },
+ { "InType12", 0, 0, 1, "InParmProbs" },
+ { "OutType12", 0, 0, -1, "OutParmProbs" },
+ { "InType13", 0, 0, 1, "InTimestamps" },
+ { "OutType13", 0, 0, -1, "OutTimestamps" },
+ { "InType14", 0, 0, 1, "InTimestampReps" },
+ { "OutType14", 0, 0, -1, "OutTimestampReps" },
+// { "InType15", 0, 0, 1, "InInfos" }, // deprecated
+// { "OutType15", 0, 0, -1, "OutInfos" }, // deprecated
+// { "InType16", 0, 0, 1, "InInfoReps" }, // deprecated
+// { "OutType16", 0, 0, -1, "OutInfoReps" }, // deprecated
+// { "InType17", 0, 0, 1, "InAddrMasks" }, // deprecated
+// { "OutType17", 0, 0, -1, "OutAddrMasks" }, // deprecated
+// { "InType18", 0, 0, 1, "InAddrMaskReps" }, // deprecated
+// { "OutType18", 0, 0, -1, "OutAddrMaskReps" }, // deprecated
+// { "InType30", 0, 0, 1, "InTraceroute" }, // deprecated
+// { "OutType30", 0, 0, -1, "OutTraceroute" }, // deprecated
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns tcp_data[] = {
+// { "RtoAlgorithm", 0, 0, 1, NULL },
+// { "RtoMin", 0, 0, 1, NULL },
+// { "RtoMax", 0, 0, 1, NULL },
+// { "MaxConn", 0, 0, 1, NULL },
+ { "ActiveOpens", 0, 0, 1, NULL },
+ { "PassiveOpens", 0, 0, 1, NULL },
+ { "AttemptFails", 0, 0, 1, NULL },
+ { "EstabResets", 0, 0, 1, NULL },
+ { "CurrEstab", 0, 0, 1, NULL },
+ { "InSegs", 0, 0, 1, NULL },
+ { "OutSegs", 0, 0, 1, NULL },
+ { "RetransSegs", 0, 0, 1, NULL },
+ { "InErrs", 0, 0, 1, NULL },
+ { "OutRsts", 0, 0, 1, NULL },
+ { "InCsumErrors", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns udp_data[] = {
+ { "InDatagrams", 0, 0, 1, NULL },
+ { "NoPorts", 0, 0, 1, NULL },
+ { "InErrors", 0, 0, 1, NULL },
+ { "OutDatagrams", 0, 0, 1, NULL },
+ { "RcvbufErrors", 0, 0, 1, NULL },
+ { "SndbufErrors", 0, 0, 1, NULL },
+ { "InCsumErrors", 0, 0, 1, NULL },
+ { "IgnoredMulti", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns udplite_data[] = {
+ { "InDatagrams", 0, 0, 1, NULL },
+ { "NoPorts", 0, 0, 1, NULL },
+ { "InErrors", 0, 0, 1, NULL },
+ { "OutDatagrams", 0, 0, 1, NULL },
+ { "RcvbufErrors", 0, 0, 1, NULL },
+ { "SndbufErrors", 0, 0, 1, NULL },
+ { "InCsumErrors", 0, 0, 1, NULL },
+ { "IgnoredMulti", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static void hash_array(struct netstat_columns *nc) {
+ int i;
+
+ for(i = 0; nc[i].name ;i++)
+ nc[i].hash = simple_hash(nc[i].name);
+}
+
+static unsigned long long *netstat_columns_find(struct netstat_columns *nc, const char *name) {
+ uint32_t i, hash = simple_hash(name);
+
+ for(i = 0; nc[i].name ;i++)
+ if(unlikely(nc[i].hash == hash && !strcmp(nc[i].name, name)))
+ return &nc[i].value;
+
+ fatal("Cannot find key '%s' in /proc/net/snmp internal array.", name);
+}
+
+static void parse_line_pair(procfile *ff, struct netstat_columns *nc, size_t header_line, size_t values_line) {
+ size_t hwords = procfile_linewords(ff, header_line);
+ size_t vwords = procfile_linewords(ff, values_line);
+ size_t w, i;
+
+ if(unlikely(vwords > hwords)) {
+ error("File /proc/net/snmp on header line %zu has %zu words, but on value line %zu has %zu words.", header_line, hwords, values_line, vwords);
+ vwords = hwords;
+ }
+
+ for(w = 1; w < vwords ;w++) {
+ char *key = procfile_lineword(ff, header_line, w);
+ uint32_t hash = simple_hash(key);
+
+ for(i = 0 ; nc[i].name ;i++) {
+ if(unlikely(hash == nc[i].hash && !strcmp(key, nc[i].name))) {
+ nc[i].value = str2ull(procfile_lineword(ff, values_line, w));
+ break;
+ }
+ }
+ }
+}
+
+int do_proc_net_snmp(int update_every, usec_t dt) {