/* * Copyright (c) 2014 Red Hat. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include "pmapi.h" #include "impl.h" #include "pmda.h" #include "indom.h" #include "proc_net_netstat.h" extern proc_net_netstat_t _pm_proc_net_netstat; typedef struct { const char *field; __uint64_t *offset; } netstat_fields_t; netstat_fields_t netstat_ip_fields[] = { { .field = "InNoRoutes", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INNOROUTES] }, { .field = "InTruncatedPkts", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INTRUNCATEDPKTS] }, { .field = "InMcastPkts", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INMCASTPKTS] }, { .field = "OutMcastPkts ", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTMCASTPKTS] }, { .field = "InBcastPkts", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INBCASTPKTS] }, { .field = "OutBcastPkts", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTBCASTPKTS] }, { .field = "InOctets", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INOCTETS] }, { .field = "OutOctets", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTOCTETS] }, { .field = "InMcastOctets", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INMCASTOCTETS] }, { .field = "OutMcastOctets", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTMCASTOCTETS] }, { .field = "InBcastOctets", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INBCASTOCTETS] }, { .field = "OutBcastOctets", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTBCASTOCTETS] }, { .field = "InCsumErrors", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_CSUMERRORS] }, { .field = "InNoECTPkts", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_NOECTPKTS] }, { .field = "InECT1Pkts", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_ECT1PKTS] }, { .field = "InECT0Pkts", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_ECT0PKTS] }, { .field = "InCEPkts", .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_CEPKTS] } }; netstat_fields_t netstat_tcp_fields[] = { { .field = "SyncookiesSent", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SYNCOOKIESSENT] }, { .field = "SyncookiesRecv", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SYNCOOKIESRECV] }, { .field = "SyncookiesFailed", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SYNCOOKIESFAILED] }, { .field = "EmbryonicRsts", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_EMBRYONICRSTS] }, { .field = "PruneCalled", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PRUNECALLED] }, { .field = "RcvPruned", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_RCVPRUNED] }, { .field = "OfoPruned", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_OFOPRUNED] }, { .field = "OutOfWindowIcmps", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_OUTOFWINDOWICMPS] }, { .field = "LockDroppedIcmps", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_LOCKDROPPEDICMPS] }, { .field = "ArpFilter", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_ARPFILTER] }, { .field = "TW", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TIMEWAITED] }, { .field = "TWRecycled", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TIMEWAITRECYCLED] }, { .field = "TWKilled", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TIMEWAITKILLED] }, { .field = "PAWSPassive", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PAWSPASSIVEREJECTED] }, { .field = "PAWSActive", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PAWSACTIVEREJECTED] }, { .field = "PAWSEstab", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PAWSESTABREJECTED] }, { .field = "DelayedACKs", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_DELAYEDACKS] }, { .field = "DelayedACKLocked", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_DELAYEDACKLOCKED] }, { .field = "DelayedACKLost", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_DELAYEDACKLOST] }, { .field = "ListenOverflows", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_LISTENOVERFLOWS] }, { .field = "ListenDrops", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_LISTENDROPS] }, { .field = "TCPPrequeued", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPREQUEUED] }, { .field = "TCPDirectCopyFromBacklog", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDIRECTCOPYFROMBACKLOG] }, { .field = "TCPDirectCopyFromPrequeue", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDIRECTCOPYFROMPREQUEUE] }, { .field = "TCPPrequeueDropped", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPREQUEUEDROPPED] }, { .field = "TCPHPHits", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPHPHITS] }, { .field = "TCPHPHitsToUser", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPHPHITSTOUSER] }, { .field = "TCPPureAcks", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPUREACKS] }, { .field = "TCPHPAcks", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPHPACKS] }, { .field = "TCPRenoRecovery", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENORECOVERY] }, { .field = "TCPSackRecovery", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKRECOVERY] }, { .field = "TCPSACKReneging", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKRENEGING] }, { .field = "TCPFACKReorder", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFACKREORDER] }, { .field = "TCPSACKReorder", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKREORDER] }, { .field = "TCPRenoReorder", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENOREORDER] }, { .field = "TCPTSReorder", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTSREORDER] }, { .field = "TCPFullUndo", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFULLUNDO] }, { .field = "TCPPartialUndo", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPARTIALUNDO] }, { .field = "TCPDSACKUndo", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKUNDO] }, { .field = "TCPLossUndo", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSUNDO] }, { .field = "TCPLostRetransmit", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSTRETRANSMIT] }, { .field = "TCPRenoFailures", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENOFAILURES] }, { .field = "TCPSackFailures", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKFAILURES] }, { .field = "TCPLossFailures", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSFAILURES] }, { .field = "TCPFastRetrans", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTRETRANS] }, { .field = "TCPForwardRetrans", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFORWARDRETRANS] }, { .field = "TCPSlowStartRetrans", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSLOWSTARTRETRANS] }, { .field = "TCPTimeouts", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTIMEOUTS] }, { .field = "TCPLossProbes", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSPROBES] }, { .field = "TCPLossProbeRecovery", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSPROBERECOVERY] }, { .field = "TCPRenoRecoveryFail", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENORECOVERYFAIL] }, { .field = "TCPSackRecoveryFail", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKRECOVERYFAIL] }, { .field = "TCPSchedulerFailed", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSCHEDULERFAILED] }, { .field = "TCPRcvCollapsed", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRCVCOLLAPSED] }, { .field = "TCPDSACKOldSent", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKOLDSENT] }, { .field = "TCPDSACKOfoSent", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKOFOSENT] }, { .field = "TCPDSACKRecv", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKRECV] }, { .field = "TCPDSACKOfoRecv", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKOFORECV] }, { .field = "TCPAbortOnData", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONDATA] }, { .field = "TCPAbortOnClose", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONCLOSE] }, { .field = "TCPAbortOnMemory", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONMEMORY] }, { .field = "TCPAbortOnTimeout", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONTIMEOUT] }, { .field = "TCPAbortOnLinger", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONLINGER] }, { .field = "TCPAbortFailed", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTFAILED] }, { .field = "TCPMemoryPressures", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMEMORYPRESSURES] }, { .field = "TCPSACKDiscard", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKDISCARD] }, { .field = "TCPDSACKIgnoredOld", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKIGNOREDOLD] }, { .field = "TCPDSACKIgnoredNoUndo", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKIGNOREDNOUNDO] }, { .field = "TCPSpuriousRTOs", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSPURIOUSRTOS] }, { .field = "TCPMD5NotFound", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMD5NOTFOUND] }, { .field = "TCPMD5Unexpected", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMD5UNEXPECTED] }, { .field = "TCPSackShifted", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SACKSHIFTED] }, { .field = "TCPSackMerged", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SACKMERGED] }, { .field = "TCPSackShiftFallback", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SACKSHIFTFALLBACK] }, { .field = "TCPBacklogDrop", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPBACKLOGDROP] }, { .field = "TCPMinTTLDrop", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMINTTLDROP] }, { .field = "TCPDeferAcceptDrop", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDEFERACCEPTDROP] }, { .field = "IPReversePathFilter", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_IPRPFILTER] }, { .field = "TCPTimeWaitOverflow", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTIMEWAITOVERFLOW] }, { .field = "TCPReqQFullDoCookies", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPREQQFULLDOCOOKIES] }, { .field = "TCPReqQFullDrop", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPREQQFULLDROP] }, { .field = "TCPRetransFail", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRETRANSFAIL] }, { .field = "TCPRcvCoalesce", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRCVCOALESCE] }, { .field = "TCPOFOQueue", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPOFOQUEUE] }, { .field = "TCPOFODrop", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPOFODROP] }, { .field = "TCPOFOMerge", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPOFOMERGE] }, { .field = "TCPChallengeACK", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPCHALLENGEACK] }, { .field = "TCPSYNChallenge", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSYNCHALLENGE] }, { .field = "TCPFastOpenActive", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENACTIVE] }, { .field = "TCPFastOpenPassive", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENACTIVEFAIL] }, { .field = "TCPFastOpenPassiveFail", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENPASSIVEFAIL] }, { .field = "TCPFastOpenListenOverflow", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENLISTENOVERFLOW] }, { .field = "TCPFastOpenCookieReqd", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENCOOKIEREQD] }, { .field = "TCPSpuriousRtxHostQueues", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSPURIOUS_RTX_HOSTQUEUES] }, { .field = "BusyPollRxPackets", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_BUSYPOLLRXPACKETS] }, { .field = "TCPAutoCorking", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPAUTOCORKING] }, { .field = "TCPFromZeroWindowAdv", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFROMZEROWINDOWADV] }, { .field = "TCPToZeroWindowAdv", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTOZEROWINDOWADV] }, { .field = "TCPWantZeroWindowAdv", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPWANTZEROWINDOWADV] }, { .field = "TCPSynRetrans", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSYNRETRANS] }, { .field = "TCPOrigDataSent", .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPORIGDATASENT] } }; static void get_fields(netstat_fields_t *fields, char *header, char *buffer) { int i, j, count; char *p, *indices[NETSTAT_MAX_COLUMNS]; /* first get pointers to each of the column headings */ strtok(header, " "); for (i = 0; i < NETSTAT_MAX_COLUMNS; i++) { if ((p = strtok(NULL, " \n")) == NULL) break; indices[i] = p; } count = i; /* * Extract values via back-referencing column headings. * "i" is the last found index, which we use for a bit * of optimisation for the (common) in-order maps case * (where "in order" means in the order defined by the * passed in "fields" table which typically matches the * kernel - but may be out-of-order for older kernels). */ strtok(buffer, " "); for (i = j = 0; j < count && fields[i].field; j++, i++) { if ((p = strtok(NULL, " \n")) == NULL) break; if (strcmp(fields[i].field, indices[j]) == 0) *fields[i].offset = strtoull(p, NULL, 10); else { for (i = 0; fields[i].field; i++) { if (strcmp(fields[i].field, indices[j]) != 0) continue; *fields[i].offset = strtoull(p, NULL, 10); break; } if (fields[i].field == NULL) /* not found, ignore */ i = 0; } } } #define NETSTAT_IP_OFFSET(ii, pp) (int64_t *)((char *)pp + \ (__psint_t)netstat_ip_fields[ii].offset - (__psint_t)&_pm_proc_net_netstat.ip) #define NETSTAT_TCP_OFFSET(ii, pp) (int64_t *)((char *)pp + \ (__psint_t)netstat_tcp_fields[ii].offset - (__psint_t)&_pm_proc_net_netstat.tcp) static void init_refresh_proc_net_netstat(proc_net_netstat_t *netstat) { int i; /* initially, all marked as "no value available" */ for (i = 0; netstat_ip_fields[i].field != NULL; i++) *(NETSTAT_IP_OFFSET(i, netstat->ip)) = -1; for (i = 0; netstat_tcp_fields[i].field != NULL; i++) *(NETSTAT_TCP_OFFSET(i, netstat->tcp)) = -1; } int refresh_proc_net_netstat(proc_net_netstat_t *netstat) { /* Need a sufficiently large value to hold a full line */ char buf[MAXPATHLEN]; char header[2048]; FILE *fp; init_refresh_proc_net_netstat(netstat); if ((fp = linux_statsfile("/proc/net/netstat", buf, sizeof(buf))) == NULL) return -oserror(); while (fgets(header, sizeof(header), fp) != NULL) { if (fgets(buf, sizeof(buf), fp) != NULL) { if (strncmp(buf, "IpExt:", 6) == 0) get_fields(netstat_ip_fields, header, buf); else if (strncmp(buf, "TcpExt:", 7) == 0) get_fields(netstat_tcp_fields, header, buf); else __pmNotifyErr(LOG_ERR, "Unrecognised netstat row: %s\n", buf); } } fclose(fp); return 0; }