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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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
;