1 /* Copyright (C) 2003 Juan Lang
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * This file implements statistics getting using the /proc filesystem exported
18 * by Linux, and maybe other OSes.
26 #include <sys/types.h>
27 #ifdef HAVE_SYS_SOCKET_H
28 # include <sys/socket.h>
33 #if HAVE_NETINET_TCP_H
34 #include <netinet/tcp.h>
36 #if HAVE_NETINET_TCP_FSM_H
37 #include <netinet/tcp_fsm.h>
45 #ifndef TCPS_ESTABLISHED
46 # define TCPS_ESTABLISHED TCP_ESTABLISHED
49 # define TCPS_SYN_SENT TCP_SYN_SENT
51 #ifndef TCPS_SYN_RECEIVED
52 # define TCPS_SYN_RECEIVED TCP_SYN_RECV
54 #ifndef TCPS_FIN_WAIT_1
55 # define TCPS_FIN_WAIT_1 TCP_FIN_WAIT1
57 #ifndef TCPS_FIN_WAIT_2
58 # define TCPS_FIN_WAIT_2 TCP_FIN_WAIT2
60 #ifndef TCPS_TIME_WAIT
61 # define TCPS_TIME_WAIT TCP_TIME_WAIT
64 # define TCPS_CLOSED TCP_CLOSE
66 #ifndef TCPS_CLOSE_WAIT
67 # define TCPS_CLOSE_WAIT TCP_CLOSE_WAIT
70 # define TCPS_LAST_ACK TCP_LAST_ACK
73 # define TCPS_LISTEN TCP_LISTEN
76 # define TCPS_CLOSING TCP_CLOSING
79 DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry)
84 return ERROR_INVALID_PARAMETER;
86 return ERROR_INVALID_PARAMETER;
88 /* get interface stats from /proc/net/dev, no error if can't
89 no inUnknownProtos, outNUcastPkts, outQLen */
90 fp = fopen("/proc/net/dev", "r");
92 char buf[512] = { 0 }, *ptr;
93 int nameLen = strlen(name), nameFound = 0;
96 ptr = fgets(buf, sizeof(buf), fp);
97 while (ptr && !nameFound) {
98 while (*ptr && isspace(*ptr))
100 if (strncasecmp(ptr, name, nameLen) == 0 && *(ptr + nameLen) == ':')
103 ptr = fgets(buf, sizeof(buf), fp);
110 entry->dwInOctets = strtoul(ptr, &endPtr, 10);
114 entry->dwInUcastPkts = strtoul(ptr, &endPtr, 10);
118 entry->dwInErrors = strtoul(ptr, &endPtr, 10);
122 entry->dwInDiscards = strtoul(ptr, &endPtr, 10);
126 strtoul(ptr, &endPtr, 10); /* skip */
130 strtoul(ptr, &endPtr, 10); /* skip */
134 strtoul(ptr, &endPtr, 10); /* skip */
138 entry->dwInNUcastPkts = strtoul(ptr, &endPtr, 10);
142 entry->dwOutOctets = strtoul(ptr, &endPtr, 10);
146 entry->dwOutUcastPkts = strtoul(ptr, &endPtr, 10);
150 entry->dwOutErrors = strtoul(ptr, &endPtr, 10);
154 entry->dwOutDiscards = strtoul(ptr, &endPtr, 10);
163 DWORD getICMPStats(MIB_ICMP *stats)
168 return ERROR_INVALID_PARAMETER;
170 memset(stats, 0, sizeof(MIB_ICMP));
171 /* get most of these stats from /proc/net/snmp, no error if can't */
172 fp = fopen("/proc/net/snmp", "r");
174 const char hdr[] = "Icmp:";
175 char buf[512] = { 0 }, *ptr;
178 ptr = fgets(buf, sizeof(buf), fp);
179 } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
181 /* last line was a header, get another */
182 ptr = fgets(buf, sizeof(buf), fp);
183 if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
188 stats->stats.icmpInStats.dwMsgs = strtoul(ptr, &endPtr, 10);
192 stats->stats.icmpInStats.dwErrors = strtoul(ptr, &endPtr, 10);
196 stats->stats.icmpInStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
200 stats->stats.icmpInStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
204 stats->stats.icmpInStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
208 stats->stats.icmpInStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
212 stats->stats.icmpInStats.dwRedirects = strtoul(ptr, &endPtr, 10);
216 stats->stats.icmpInStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
220 stats->stats.icmpInStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
224 stats->stats.icmpInStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
228 stats->stats.icmpInStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
232 stats->stats.icmpInStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
236 stats->stats.icmpOutStats.dwMsgs = strtoul(ptr, &endPtr, 10);
240 stats->stats.icmpOutStats.dwErrors = strtoul(ptr, &endPtr, 10);
244 stats->stats.icmpOutStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
248 stats->stats.icmpOutStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
252 stats->stats.icmpOutStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
256 stats->stats.icmpOutStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
260 stats->stats.icmpOutStats.dwRedirects = strtoul(ptr, &endPtr, 10);
264 stats->stats.icmpOutStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
268 stats->stats.icmpOutStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
272 stats->stats.icmpOutStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
276 stats->stats.icmpOutStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
280 stats->stats.icmpOutStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
290 DWORD getIPStats(PMIB_IPSTATS stats)
295 return ERROR_INVALID_PARAMETER;
297 memset(stats, 0, sizeof(MIB_IPSTATS));
298 stats->dwNumIf = stats->dwNumAddr = getNumInterfaces();
299 stats->dwNumRoutes = getNumRoutes();
301 /* get most of these stats from /proc/net/snmp, no error if can't */
302 fp = fopen("/proc/net/snmp", "r");
304 const char hdr[] = "Ip:";
305 char buf[512] = { 0 }, *ptr;
308 ptr = fgets(buf, sizeof(buf), fp);
309 } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
311 /* last line was a header, get another */
312 ptr = fgets(buf, sizeof(buf), fp);
313 if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
318 stats->dwForwarding = strtoul(ptr, &endPtr, 10);
322 stats->dwDefaultTTL = strtoul(ptr, &endPtr, 10);
326 stats->dwInReceives = strtoul(ptr, &endPtr, 10);
330 stats->dwInHdrErrors = strtoul(ptr, &endPtr, 10);
334 stats->dwInAddrErrors = strtoul(ptr, &endPtr, 10);
338 stats->dwForwDatagrams = strtoul(ptr, &endPtr, 10);
342 stats->dwInUnknownProtos = strtoul(ptr, &endPtr, 10);
346 stats->dwInDiscards = strtoul(ptr, &endPtr, 10);
350 stats->dwInDelivers = strtoul(ptr, &endPtr, 10);
354 stats->dwOutRequests = strtoul(ptr, &endPtr, 10);
358 stats->dwOutDiscards = strtoul(ptr, &endPtr, 10);
362 stats->dwOutNoRoutes = strtoul(ptr, &endPtr, 10);
366 stats->dwReasmTimeout = strtoul(ptr, &endPtr, 10);
370 stats->dwReasmReqds = strtoul(ptr, &endPtr, 10);
374 stats->dwReasmOks = strtoul(ptr, &endPtr, 10);
378 stats->dwReasmFails = strtoul(ptr, &endPtr, 10);
382 stats->dwFragOks = strtoul(ptr, &endPtr, 10);
386 stats->dwFragFails = strtoul(ptr, &endPtr, 10);
390 stats->dwFragCreates = strtoul(ptr, &endPtr, 10);
393 /* hmm, no routingDiscards */
401 DWORD getTCPStats(MIB_TCPSTATS *stats)
406 return ERROR_INVALID_PARAMETER;
408 memset(stats, 0, sizeof(MIB_TCPSTATS));
410 /* get from /proc/net/snmp, no error if can't */
411 fp = fopen("/proc/net/snmp", "r");
413 const char hdr[] = "Tcp:";
414 char buf[512] = { 0 }, *ptr;
418 ptr = fgets(buf, sizeof(buf), fp);
419 } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
421 /* last line was a header, get another */
422 ptr = fgets(buf, sizeof(buf), fp);
423 if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
428 stats->dwRtoAlgorithm = strtoul(ptr, &endPtr, 10);
432 stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
436 stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
440 stats->dwMaxConn = strtoul(ptr, &endPtr, 10);
444 stats->dwActiveOpens = strtoul(ptr, &endPtr, 10);
448 stats->dwPassiveOpens = strtoul(ptr, &endPtr, 10);
452 stats->dwAttemptFails = strtoul(ptr, &endPtr, 10);
456 stats->dwEstabResets = strtoul(ptr, &endPtr, 10);
460 stats->dwCurrEstab = strtoul(ptr, &endPtr, 10);
464 stats->dwInSegs = strtoul(ptr, &endPtr, 10);
468 stats->dwOutSegs = strtoul(ptr, &endPtr, 10);
472 stats->dwRetransSegs = strtoul(ptr, &endPtr, 10);
476 stats->dwInErrs = strtoul(ptr, &endPtr, 10);
480 stats->dwOutRsts = strtoul(ptr, &endPtr, 10);
483 stats->dwNumConns = getNumTcpEntries();
491 DWORD getUDPStats(MIB_UDPSTATS *stats)
496 return ERROR_INVALID_PARAMETER;
498 memset(stats, 0, sizeof(MIB_UDPSTATS));
500 /* get from /proc/net/snmp, no error if can't */
501 fp = fopen("/proc/net/snmp", "r");
503 const char hdr[] = "Udp:";
504 char buf[512] = { 0 }, *ptr;
508 ptr = fgets(buf, sizeof(buf), fp);
509 } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
511 /* last line was a header, get another */
512 ptr = fgets(buf, sizeof(buf), fp);
513 if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
518 stats->dwInDatagrams = strtoul(ptr, &endPtr, 10);
522 stats->dwNoPorts = strtoul(ptr, &endPtr, 10);
526 stats->dwInErrors = strtoul(ptr, &endPtr, 10);
530 stats->dwOutDatagrams = strtoul(ptr, &endPtr, 10);
534 stats->dwNumAddrs = strtoul(ptr, &endPtr, 10);
544 static DWORD getNumWithOneHeader(const char *filename)
549 fp = fopen(filename, "r");
551 char buf[512] = { 0 }, *ptr;
554 ptr = fgets(buf, sizeof(buf), fp);
557 ptr = fgets(buf, sizeof(buf), fp);
567 DWORD getNumRoutes(void)
569 return getNumWithOneHeader("/proc/net/route");
572 RouteTable *getRouteTable(void)
574 DWORD numRoutes = getNumRoutes();
577 ret = (RouteTable *)calloc(1, sizeof(RouteTable) +
578 (numRoutes - 1) * sizeof(RouteEntry));
582 /* get from /proc/net/route, no error if can't */
583 fp = fopen("/proc/net/route", "r");
585 char buf[512] = { 0 }, *ptr;
587 /* skip header line */
588 ptr = fgets(buf, sizeof(buf), fp);
589 while (ptr && ret->numRoutes < numRoutes) {
590 ptr = fgets(buf, sizeof(buf), fp);
594 while (!isspace(*ptr))
598 if (getInterfaceIndexByName(buf, &index) == NO_ERROR) {
601 ret->routes[ret->numRoutes].ifIndex = index;
603 ret->routes[ret->numRoutes].dest = strtoul(ptr, &endPtr, 16);
607 ret->routes[ret->numRoutes].gateway = strtoul(ptr, &endPtr, 16);
611 strtoul(ptr, &endPtr, 16); /* flags, skip */
615 strtoul(ptr, &endPtr, 16); /* refcount, skip */
619 strtoul(ptr, &endPtr, 16); /* use, skip */
623 ret->routes[ret->numRoutes].metric = strtoul(ptr, &endPtr, 16);
627 ret->routes[ret->numRoutes].mask = strtoul(ptr, &endPtr, 16);
640 DWORD getNumArpEntries(void)
642 return getNumWithOneHeader("/proc/net/arp");
645 PMIB_IPNETTABLE getArpTable(void)
647 DWORD numEntries = getNumArpEntries();
650 ret = (PMIB_IPNETTABLE)calloc(1, sizeof(MIB_IPNETTABLE) +
651 (numEntries - 1) * sizeof(MIB_IPNETROW));
655 /* get from /proc/net/arp, no error if can't */
656 fp = fopen("/proc/net/arp", "r");
658 char buf[512] = { 0 }, *ptr;
660 /* skip header line */
661 ptr = fgets(buf, sizeof(buf), fp);
662 while (ptr && ret->dwNumEntries < numEntries) {
663 ptr = fgets(buf, sizeof(buf), fp);
668 ret->table[ret->dwNumEntries].dwAddr = strtoul(ptr, &endPtr, 16);
672 strtoul(ptr, &endPtr, 16); /* hw type (skip) */
676 strtoul(ptr, &endPtr, 16); /* flags (skip) */
679 /* FIXME: maybe this comes from flags? */
680 ret->table[ret->dwNumEntries].dwType = MIB_IPNET_TYPE_DYNAMIC;
681 while (ptr && *ptr && isspace(*ptr))
683 while (ptr && *ptr && !isspace(*ptr)) {
684 DWORD byte = strtoul(ptr, &endPtr, 16);
686 if (endPtr && *endPtr) {
688 ret->table[ret->dwNumEntries].bPhysAddr[
689 ret->table[ret->dwNumEntries].dwPhysAddrLen++] = byte & 0x0ff;
694 strtoul(ptr, &endPtr, 16); /* mask (skip) */
697 getInterfaceIndexByName(ptr, &ret->table[ret->dwNumEntries].dwIndex);
707 DWORD getNumUdpEntries(void)
709 return getNumWithOneHeader("/proc/net/udp");
712 PMIB_UDPTABLE getUdpTable(void)
714 DWORD numEntries = getNumUdpEntries();
717 ret = (PMIB_UDPTABLE)calloc(1, sizeof(MIB_UDPTABLE) +
718 (numEntries - 1) * sizeof(MIB_UDPROW));
722 /* get from /proc/net/udp, no error if can't */
723 fp = fopen("/proc/net/udp", "r");
725 char buf[512] = { 0 }, *ptr;
727 /* skip header line */
728 ptr = fgets(buf, sizeof(buf), fp);
729 while (ptr && ret->dwNumEntries < numEntries) {
730 ptr = fgets(buf, sizeof(buf), fp);
735 strtoul(ptr, &endPtr, 16); /* skip */
740 ret->table[ret->dwNumEntries].dwLocalAddr = strtoul(ptr, &endPtr,
746 ret->table[ret->dwNumEntries].dwLocalPort = strtoul(ptr, &endPtr,
759 DWORD getNumTcpEntries(void)
761 return getNumWithOneHeader("/proc/net/tcp");
764 PMIB_TCPTABLE getTcpTable(void)
766 DWORD numEntries = getNumTcpEntries();
769 ret = (PMIB_TCPTABLE)calloc(1, sizeof(MIB_TCPTABLE) +
770 (numEntries - 1) * sizeof(MIB_TCPROW));
774 /* get from /proc/net/tcp, no error if can't */
775 fp = fopen("/proc/net/tcp", "r");
777 char buf[512] = { 0 }, *ptr;
779 /* skip header line */
780 ptr = fgets(buf, sizeof(buf), fp);
781 while (ptr && ret->dwNumEntries < numEntries) {
782 ptr = fgets(buf, sizeof(buf), fp);
786 while (ptr && *ptr && *ptr != ':')
791 ret->table[ret->dwNumEntries].dwLocalAddr = strtoul(ptr, &endPtr,
797 ret->table[ret->dwNumEntries].dwLocalPort = strtoul(ptr, &endPtr,
802 ret->table[ret->dwNumEntries].dwRemoteAddr = strtoul(ptr, &endPtr,
808 ret->table[ret->dwNumEntries].dwRemotePort = strtoul(ptr, &endPtr,
813 DWORD state = strtoul(ptr, &endPtr, 16);
817 case TCPS_ESTABLISHED:
818 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_ESTAB;
821 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_SYN_SENT;
823 case TCPS_SYN_RECEIVED:
824 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_SYN_RCVD;
826 case TCPS_FIN_WAIT_1:
827 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_FIN_WAIT1;
829 case TCPS_FIN_WAIT_2:
830 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_FIN_WAIT2;
833 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_TIME_WAIT;
836 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_CLOSED;
838 case TCPS_CLOSE_WAIT:
839 ret->table[ret->dwNumEntries].dwState =
840 MIB_TCP_STATE_CLOSE_WAIT;
843 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_LAST_ACK;
846 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_LISTEN;
849 ret->table[ret->dwNumEntries].dwState = MIB_TCP_STATE_CLOSING;