1 /* Copyright (C) 2003,2006 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.
22 #include "wine/port.h"
28 #include <sys/types.h>
29 #ifdef HAVE_SYS_SOCKET_H
30 #include <sys/socket.h>
32 #ifdef HAVE_NETINET_IN_H
33 #include <netinet/in.h>
35 #ifdef HAVE_ARPA_INET_H
36 #include <arpa/inet.h>
41 #ifdef HAVE_NET_ROUTE_H
42 #include <net/route.h>
44 #ifdef HAVE_NET_IF_ARP_H
45 #include <net/if_arp.h>
47 #ifdef HAVE_NETINET_TCP_H
48 #include <netinet/tcp.h>
50 #ifdef HAVE_NETINET_TCP_FSM_H
51 #include <netinet/tcp_fsm.h>
61 #define TCPS_ESTABLISHED 1
62 #define TCPS_SYN_SENT 2
63 #define TCPS_SYN_RECEIVED 3
64 #define TCPS_FIN_WAIT_1 4
65 #define TCPS_FIN_WAIT_2 5
66 #define TCPS_TIME_WAIT 6
68 #define TCPS_CLOSE_WAIT 8
69 #define TCPS_LAST_ACK 9
70 #define TCPS_LISTEN 10
71 #define TCPS_CLOSING 11
74 DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry)
79 return ERROR_INVALID_PARAMETER;
81 return ERROR_INVALID_PARAMETER;
83 /* get interface stats from /proc/net/dev, no error if can't
84 no inUnknownProtos, outNUcastPkts, outQLen */
85 fp = fopen("/proc/net/dev", "r");
87 char buf[512] = { 0 }, *ptr;
88 int nameLen = strlen(name), nameFound = 0;
91 ptr = fgets(buf, sizeof(buf), fp);
92 while (ptr && !nameFound) {
93 while (*ptr && isspace(*ptr))
95 if (strncasecmp(ptr, name, nameLen) == 0 && *(ptr + nameLen) == ':')
98 ptr = fgets(buf, sizeof(buf), fp);
105 entry->dwInOctets = strtoul(ptr, &endPtr, 10);
109 entry->dwInUcastPkts = strtoul(ptr, &endPtr, 10);
113 entry->dwInErrors = strtoul(ptr, &endPtr, 10);
117 entry->dwInDiscards = strtoul(ptr, &endPtr, 10);
121 strtoul(ptr, &endPtr, 10); /* skip */
125 strtoul(ptr, &endPtr, 10); /* skip */
129 strtoul(ptr, &endPtr, 10); /* skip */
133 entry->dwInNUcastPkts = strtoul(ptr, &endPtr, 10);
137 entry->dwOutOctets = strtoul(ptr, &endPtr, 10);
141 entry->dwOutUcastPkts = strtoul(ptr, &endPtr, 10);
145 entry->dwOutErrors = strtoul(ptr, &endPtr, 10);
149 entry->dwOutDiscards = strtoul(ptr, &endPtr, 10);
158 DWORD getICMPStats(MIB_ICMP *stats)
163 return ERROR_INVALID_PARAMETER;
165 memset(stats, 0, sizeof(MIB_ICMP));
166 /* get most of these stats from /proc/net/snmp, no error if can't */
167 fp = fopen("/proc/net/snmp", "r");
169 static const char hdr[] = "Icmp:";
170 char buf[512] = { 0 }, *ptr;
173 ptr = fgets(buf, sizeof(buf), fp);
174 } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
176 /* last line was a header, get another */
177 ptr = fgets(buf, sizeof(buf), fp);
178 if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
183 stats->stats.icmpInStats.dwMsgs = strtoul(ptr, &endPtr, 10);
187 stats->stats.icmpInStats.dwErrors = strtoul(ptr, &endPtr, 10);
191 stats->stats.icmpInStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
195 stats->stats.icmpInStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
199 stats->stats.icmpInStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
203 stats->stats.icmpInStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
207 stats->stats.icmpInStats.dwRedirects = strtoul(ptr, &endPtr, 10);
211 stats->stats.icmpInStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
215 stats->stats.icmpInStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
219 stats->stats.icmpInStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
223 stats->stats.icmpInStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
227 stats->stats.icmpInStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
231 stats->stats.icmpOutStats.dwMsgs = strtoul(ptr, &endPtr, 10);
235 stats->stats.icmpOutStats.dwErrors = strtoul(ptr, &endPtr, 10);
239 stats->stats.icmpOutStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
243 stats->stats.icmpOutStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
247 stats->stats.icmpOutStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
251 stats->stats.icmpOutStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
255 stats->stats.icmpOutStats.dwRedirects = strtoul(ptr, &endPtr, 10);
259 stats->stats.icmpOutStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
263 stats->stats.icmpOutStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
267 stats->stats.icmpOutStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
271 stats->stats.icmpOutStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
275 stats->stats.icmpOutStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
285 DWORD getIPStats(PMIB_IPSTATS stats)
290 return ERROR_INVALID_PARAMETER;
292 memset(stats, 0, sizeof(MIB_IPSTATS));
293 stats->dwNumIf = stats->dwNumAddr = getNumInterfaces();
294 stats->dwNumRoutes = getNumRoutes();
296 /* get most of these stats from /proc/net/snmp, no error if can't */
297 fp = fopen("/proc/net/snmp", "r");
299 static const char hdr[] = "Ip:";
300 char buf[512] = { 0 }, *ptr;
303 ptr = fgets(buf, sizeof(buf), fp);
304 } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
306 /* last line was a header, get another */
307 ptr = fgets(buf, sizeof(buf), fp);
308 if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
313 stats->dwForwarding = strtoul(ptr, &endPtr, 10);
317 stats->dwDefaultTTL = strtoul(ptr, &endPtr, 10);
321 stats->dwInReceives = strtoul(ptr, &endPtr, 10);
325 stats->dwInHdrErrors = strtoul(ptr, &endPtr, 10);
329 stats->dwInAddrErrors = strtoul(ptr, &endPtr, 10);
333 stats->dwForwDatagrams = strtoul(ptr, &endPtr, 10);
337 stats->dwInUnknownProtos = strtoul(ptr, &endPtr, 10);
341 stats->dwInDiscards = strtoul(ptr, &endPtr, 10);
345 stats->dwInDelivers = strtoul(ptr, &endPtr, 10);
349 stats->dwOutRequests = strtoul(ptr, &endPtr, 10);
353 stats->dwOutDiscards = strtoul(ptr, &endPtr, 10);
357 stats->dwOutNoRoutes = strtoul(ptr, &endPtr, 10);
361 stats->dwReasmTimeout = strtoul(ptr, &endPtr, 10);
365 stats->dwReasmReqds = strtoul(ptr, &endPtr, 10);
369 stats->dwReasmOks = strtoul(ptr, &endPtr, 10);
373 stats->dwReasmFails = strtoul(ptr, &endPtr, 10);
377 stats->dwFragOks = strtoul(ptr, &endPtr, 10);
381 stats->dwFragFails = strtoul(ptr, &endPtr, 10);
385 stats->dwFragCreates = strtoul(ptr, &endPtr, 10);
388 /* hmm, no routingDiscards */
396 DWORD getTCPStats(MIB_TCPSTATS *stats)
401 return ERROR_INVALID_PARAMETER;
403 memset(stats, 0, sizeof(MIB_TCPSTATS));
405 /* get from /proc/net/snmp, no error if can't */
406 fp = fopen("/proc/net/snmp", "r");
408 static const char hdr[] = "Tcp:";
409 char buf[512] = { 0 }, *ptr;
413 ptr = fgets(buf, sizeof(buf), fp);
414 } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
416 /* last line was a header, get another */
417 ptr = fgets(buf, sizeof(buf), fp);
418 if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
423 stats->dwRtoAlgorithm = strtoul(ptr, &endPtr, 10);
427 stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
431 stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
435 stats->dwMaxConn = strtoul(ptr, &endPtr, 10);
439 stats->dwActiveOpens = strtoul(ptr, &endPtr, 10);
443 stats->dwPassiveOpens = strtoul(ptr, &endPtr, 10);
447 stats->dwAttemptFails = strtoul(ptr, &endPtr, 10);
451 stats->dwEstabResets = strtoul(ptr, &endPtr, 10);
455 stats->dwCurrEstab = strtoul(ptr, &endPtr, 10);
459 stats->dwInSegs = strtoul(ptr, &endPtr, 10);
463 stats->dwOutSegs = strtoul(ptr, &endPtr, 10);
467 stats->dwRetransSegs = strtoul(ptr, &endPtr, 10);
471 stats->dwInErrs = strtoul(ptr, &endPtr, 10);
475 stats->dwOutRsts = strtoul(ptr, &endPtr, 10);
478 stats->dwNumConns = getNumTcpEntries();
486 DWORD getUDPStats(MIB_UDPSTATS *stats)
491 return ERROR_INVALID_PARAMETER;
493 memset(stats, 0, sizeof(MIB_UDPSTATS));
495 /* get from /proc/net/snmp, no error if can't */
496 fp = fopen("/proc/net/snmp", "r");
498 static const char hdr[] = "Udp:";
499 char buf[512] = { 0 }, *ptr;
503 ptr = fgets(buf, sizeof(buf), fp);
504 } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
506 /* last line was a header, get another */
507 ptr = fgets(buf, sizeof(buf), fp);
508 if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
513 stats->dwInDatagrams = strtoul(ptr, &endPtr, 10);
517 stats->dwNoPorts = strtoul(ptr, &endPtr, 10);
521 stats->dwInErrors = strtoul(ptr, &endPtr, 10);
525 stats->dwOutDatagrams = strtoul(ptr, &endPtr, 10);
529 stats->dwNumAddrs = strtoul(ptr, &endPtr, 10);
539 static DWORD getNumWithOneHeader(const char *filename)
544 fp = fopen(filename, "r");
546 char buf[512] = { 0 }, *ptr;
549 ptr = fgets(buf, sizeof(buf), fp);
552 ptr = fgets(buf, sizeof(buf), fp);
562 DWORD getNumRoutes(void)
564 return getNumWithOneHeader("/proc/net/route");
567 DWORD getRouteTable(PMIB_IPFORWARDTABLE *ppIpForwardTable, HANDLE heap,
572 if (!ppIpForwardTable)
573 ret = ERROR_INVALID_PARAMETER;
575 DWORD numRoutes = getNumRoutes();
576 PMIB_IPFORWARDTABLE table = HeapAlloc(heap, flags,
577 sizeof(MIB_IPFORWARDTABLE) + (numRoutes - 1) * sizeof(MIB_IPFORWARDROW));
583 *ppIpForwardTable = table;
584 table->dwNumEntries = 0;
585 /* get from /proc/net/route, no error if can't */
586 fp = fopen("/proc/net/route", "r");
588 char buf[512] = { 0 }, *ptr;
590 /* skip header line */
591 ptr = fgets(buf, sizeof(buf), fp);
592 while (ptr && table->dwNumEntries < numRoutes) {
593 memset(&table->table[table->dwNumEntries], 0,
594 sizeof(MIB_IPFORWARDROW));
595 ptr = fgets(buf, sizeof(buf), fp);
599 while (!isspace(*ptr))
603 if (getInterfaceIndexByName(buf, &index) == NO_ERROR) {
606 table->table[table->dwNumEntries].dwForwardIfIndex = index;
608 table->table[table->dwNumEntries].dwForwardDest =
609 strtoul(ptr, &endPtr, 16);
613 table->table[table->dwNumEntries].dwForwardNextHop =
614 strtoul(ptr, &endPtr, 16);
618 DWORD flags = strtoul(ptr, &endPtr, 16);
620 if (!(flags & RTF_UP))
621 table->table[table->dwNumEntries].dwForwardType =
622 MIB_IPROUTE_TYPE_INVALID;
623 else if (flags & RTF_GATEWAY)
624 table->table[table->dwNumEntries].dwForwardType =
625 MIB_IPROUTE_TYPE_INDIRECT;
627 table->table[table->dwNumEntries].dwForwardType =
628 MIB_IPROUTE_TYPE_DIRECT;
632 strtoul(ptr, &endPtr, 16); /* refcount, skip */
636 strtoul(ptr, &endPtr, 16); /* use, skip */
640 table->table[table->dwNumEntries].dwForwardMetric1 =
641 strtoul(ptr, &endPtr, 16);
645 table->table[table->dwNumEntries].dwForwardMask =
646 strtoul(ptr, &endPtr, 16);
649 /* FIXME: other protos might be appropriate, e.g. the default
650 * route is typically set with MIB_IPPROTO_NETMGMT instead */
651 table->table[table->dwNumEntries].dwForwardProto =
653 table->dwNumEntries++;
661 ret = ERROR_OUTOFMEMORY;
666 DWORD getNumArpEntries(void)
668 return getNumWithOneHeader("/proc/net/arp");
671 DWORD getArpTable(PMIB_IPNETTABLE *ppIpNetTable, HANDLE heap, DWORD flags)
676 ret = ERROR_INVALID_PARAMETER;
678 DWORD numEntries = getNumArpEntries();
679 PMIB_IPNETTABLE table = HeapAlloc(heap, flags,
680 sizeof(MIB_IPNETTABLE) + (numEntries - 1) * sizeof(MIB_IPNETROW));
686 *ppIpNetTable = table;
687 table->dwNumEntries = 0;
688 /* get from /proc/net/arp, no error if can't */
689 fp = fopen("/proc/net/arp", "r");
691 char buf[512] = { 0 }, *ptr;
693 /* skip header line */
694 ptr = fgets(buf, sizeof(buf), fp);
695 while (ptr && table->dwNumEntries < numEntries) {
696 ptr = fgets(buf, sizeof(buf), fp);
700 memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_IPNETROW));
701 table->table[table->dwNumEntries].dwAddr = inet_addr(ptr);
702 while (ptr && *ptr && !isspace(*ptr))
706 strtoul(ptr, &endPtr, 16); /* hw type (skip) */
710 DWORD flags = strtoul(ptr, &endPtr, 16);
714 table->table[table->dwNumEntries].dwType =
715 MIB_IPNET_TYPE_DYNAMIC;
719 if (flags & ATF_PERM)
720 table->table[table->dwNumEntries].dwType =
721 MIB_IPNET_TYPE_STATIC;
724 table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_OTHER;
728 while (ptr && *ptr && isspace(*ptr))
730 while (ptr && *ptr && !isspace(*ptr)) {
731 DWORD byte = strtoul(ptr, &endPtr, 16);
733 if (endPtr && *endPtr) {
735 table->table[table->dwNumEntries].bPhysAddr[
736 table->table[table->dwNumEntries].dwPhysAddrLen++] =
742 strtoul(ptr, &endPtr, 16); /* mask (skip) */
745 getInterfaceIndexByName(ptr,
746 &table->table[table->dwNumEntries].dwIndex);
747 table->dwNumEntries++;
754 ret = ERROR_OUTOFMEMORY;
759 DWORD getNumUdpEntries(void)
761 return getNumWithOneHeader("/proc/net/udp");
764 DWORD getUdpTable(PMIB_UDPTABLE *ppUdpTable, HANDLE heap, DWORD flags)
769 ret = ERROR_INVALID_PARAMETER;
771 DWORD numEntries = getNumUdpEntries();
772 PMIB_UDPTABLE table = HeapAlloc(heap, flags,
773 sizeof(MIB_UDPTABLE) + (numEntries - 1) * sizeof(MIB_UDPROW));
780 table->dwNumEntries = 0;
781 /* get from /proc/net/udp, no error if can't */
782 fp = fopen("/proc/net/udp", "r");
784 char buf[512] = { 0 }, *ptr;
786 /* skip header line */
787 ptr = fgets(buf, sizeof(buf), fp);
788 while (ptr && table->dwNumEntries < numEntries) {
789 memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_UDPROW));
790 ptr = fgets(buf, sizeof(buf), fp);
795 strtoul(ptr, &endPtr, 16); /* skip */
800 table->table[table->dwNumEntries].dwLocalAddr = strtoul(ptr,
806 table->table[table->dwNumEntries].dwLocalPort = strtoul(ptr,
810 table->dwNumEntries++;
817 ret = ERROR_OUTOFMEMORY;
822 DWORD getNumTcpEntries(void)
824 return getNumWithOneHeader("/proc/net/tcp");
827 DWORD getTcpTable(PMIB_TCPTABLE *ppTcpTable, HANDLE heap, DWORD flags)
832 ret = ERROR_INVALID_PARAMETER;
834 DWORD numEntries = getNumTcpEntries();
835 PMIB_TCPTABLE table = HeapAlloc(heap, flags,
836 sizeof(MIB_TCPTABLE) + (numEntries - 1) * sizeof(MIB_TCPROW));
843 table->dwNumEntries = 0;
844 /* get from /proc/net/tcp, no error if can't */
845 fp = fopen("/proc/net/tcp", "r");
847 char buf[512] = { 0 }, *ptr;
849 /* skip header line */
850 ptr = fgets(buf, sizeof(buf), fp);
851 while (ptr && table->dwNumEntries < numEntries) {
852 memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_TCPROW));
853 ptr = fgets(buf, sizeof(buf), fp);
857 while (ptr && *ptr && *ptr != ':')
862 table->table[table->dwNumEntries].dwLocalAddr = strtoul(ptr,
868 table->table[table->dwNumEntries].dwLocalPort = strtoul(ptr,
873 table->table[table->dwNumEntries].dwRemoteAddr = strtoul(ptr,
879 table->table[table->dwNumEntries].dwRemotePort = strtoul(ptr,
884 DWORD state = strtoul(ptr, &endPtr, 16);
888 case TCPS_ESTABLISHED:
889 table->table[table->dwNumEntries].dwState =
893 table->table[table->dwNumEntries].dwState =
894 MIB_TCP_STATE_SYN_SENT;
896 case TCPS_SYN_RECEIVED:
897 table->table[table->dwNumEntries].dwState =
898 MIB_TCP_STATE_SYN_RCVD;
900 case TCPS_FIN_WAIT_1:
901 table->table[table->dwNumEntries].dwState =
902 MIB_TCP_STATE_FIN_WAIT1;
904 case TCPS_FIN_WAIT_2:
905 table->table[table->dwNumEntries].dwState =
906 MIB_TCP_STATE_FIN_WAIT2;
909 table->table[table->dwNumEntries].dwState =
910 MIB_TCP_STATE_TIME_WAIT;
913 table->table[table->dwNumEntries].dwState =
914 MIB_TCP_STATE_CLOSED;
916 case TCPS_CLOSE_WAIT:
917 table->table[table->dwNumEntries].dwState =
918 MIB_TCP_STATE_CLOSE_WAIT;
921 table->table[table->dwNumEntries].dwState =
922 MIB_TCP_STATE_LAST_ACK;
925 table->table[table->dwNumEntries].dwState =
926 MIB_TCP_STATE_LISTEN;
929 table->table[table->dwNumEntries].dwState =
930 MIB_TCP_STATE_CLOSING;
935 table->dwNumEntries++;
942 ret = ERROR_OUTOFMEMORY;