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.
27 #include <sys/types.h>
28 #ifdef HAVE_SYS_SOCKET_H
29 #include <sys/socket.h>
31 #ifdef HAVE_NETINET_IN_H
32 #include <netinet/in.h>
34 #ifdef HAVE_ARPA_INET_H
35 #include <arpa/inet.h>
40 #ifdef HAVE_NET_IF_ARP_H
41 #include <net/if_arp.h>
43 #ifdef HAVE_NETINET_TCP_H
44 #include <netinet/tcp.h>
46 #ifdef HAVE_NETINET_TCP_FSM_H
47 #include <netinet/tcp_fsm.h>
56 #ifndef TCPS_ESTABLISHED
57 # define TCPS_ESTABLISHED TCP_ESTABLISHED
60 # define TCPS_SYN_SENT TCP_SYN_SENT
62 #ifndef TCPS_SYN_RECEIVED
63 # define TCPS_SYN_RECEIVED TCP_SYN_RECV
65 #ifndef TCPS_FIN_WAIT_1
66 # define TCPS_FIN_WAIT_1 TCP_FIN_WAIT1
68 #ifndef TCPS_FIN_WAIT_2
69 # define TCPS_FIN_WAIT_2 TCP_FIN_WAIT2
71 #ifndef TCPS_TIME_WAIT
72 # define TCPS_TIME_WAIT TCP_TIME_WAIT
75 # define TCPS_CLOSED TCP_CLOSE
77 #ifndef TCPS_CLOSE_WAIT
78 # define TCPS_CLOSE_WAIT TCP_CLOSE_WAIT
81 # define TCPS_LAST_ACK TCP_LAST_ACK
84 # define TCPS_LISTEN TCP_LISTEN
87 # define TCPS_CLOSING TCP_CLOSING
90 DWORD
getInterfaceStatsByName(const char *name
, PMIB_IFROW entry
)
95 return ERROR_INVALID_PARAMETER
;
97 return ERROR_INVALID_PARAMETER
;
99 /* get interface stats from /proc/net/dev, no error if can't
100 no inUnknownProtos, outNUcastPkts, outQLen */
101 fp
= fopen("/proc/net/dev", "r");
103 char buf
[512] = { 0 }, *ptr
;
104 int nameLen
= strlen(name
), nameFound
= 0;
107 ptr
= fgets(buf
, sizeof(buf
), fp
);
108 while (ptr
&& !nameFound
) {
109 while (*ptr
&& isspace(*ptr
))
111 if (strncasecmp(ptr
, name
, nameLen
) == 0 && *(ptr
+ nameLen
) == ':')
114 ptr
= fgets(buf
, sizeof(buf
), fp
);
121 entry
->dwInOctets
= strtoul(ptr
, &endPtr
, 10);
125 entry
->dwInUcastPkts
= strtoul(ptr
, &endPtr
, 10);
129 entry
->dwInErrors
= strtoul(ptr
, &endPtr
, 10);
133 entry
->dwInDiscards
= strtoul(ptr
, &endPtr
, 10);
137 strtoul(ptr
, &endPtr
, 10); /* skip */
141 strtoul(ptr
, &endPtr
, 10); /* skip */
145 strtoul(ptr
, &endPtr
, 10); /* skip */
149 entry
->dwInNUcastPkts
= strtoul(ptr
, &endPtr
, 10);
153 entry
->dwOutOctets
= strtoul(ptr
, &endPtr
, 10);
157 entry
->dwOutUcastPkts
= strtoul(ptr
, &endPtr
, 10);
161 entry
->dwOutErrors
= strtoul(ptr
, &endPtr
, 10);
165 entry
->dwOutDiscards
= strtoul(ptr
, &endPtr
, 10);
174 DWORD
getICMPStats(MIB_ICMP
*stats
)
179 return ERROR_INVALID_PARAMETER
;
181 memset(stats
, 0, sizeof(MIB_ICMP
));
182 /* get most of these stats from /proc/net/snmp, no error if can't */
183 fp
= fopen("/proc/net/snmp", "r");
185 const char hdr
[] = "Icmp:";
186 char buf
[512] = { 0 }, *ptr
;
189 ptr
= fgets(buf
, sizeof(buf
), fp
);
190 } while (ptr
&& strncasecmp(buf
, hdr
, sizeof(hdr
) - 1));
192 /* last line was a header, get another */
193 ptr
= fgets(buf
, sizeof(buf
), fp
);
194 if (ptr
&& strncasecmp(buf
, hdr
, sizeof(hdr
) - 1) == 0) {
199 stats
->stats
.icmpInStats
.dwMsgs
= strtoul(ptr
, &endPtr
, 10);
203 stats
->stats
.icmpInStats
.dwErrors
= strtoul(ptr
, &endPtr
, 10);
207 stats
->stats
.icmpInStats
.dwDestUnreachs
= strtoul(ptr
, &endPtr
, 10);
211 stats
->stats
.icmpInStats
.dwTimeExcds
= strtoul(ptr
, &endPtr
, 10);
215 stats
->stats
.icmpInStats
.dwParmProbs
= strtoul(ptr
, &endPtr
, 10);
219 stats
->stats
.icmpInStats
.dwSrcQuenchs
= strtoul(ptr
, &endPtr
, 10);
223 stats
->stats
.icmpInStats
.dwRedirects
= strtoul(ptr
, &endPtr
, 10);
227 stats
->stats
.icmpInStats
.dwEchoReps
= strtoul(ptr
, &endPtr
, 10);
231 stats
->stats
.icmpInStats
.dwTimestamps
= strtoul(ptr
, &endPtr
, 10);
235 stats
->stats
.icmpInStats
.dwTimestampReps
= strtoul(ptr
, &endPtr
, 10);
239 stats
->stats
.icmpInStats
.dwAddrMasks
= strtoul(ptr
, &endPtr
, 10);
243 stats
->stats
.icmpInStats
.dwAddrMaskReps
= strtoul(ptr
, &endPtr
, 10);
247 stats
->stats
.icmpOutStats
.dwMsgs
= strtoul(ptr
, &endPtr
, 10);
251 stats
->stats
.icmpOutStats
.dwErrors
= strtoul(ptr
, &endPtr
, 10);
255 stats
->stats
.icmpOutStats
.dwDestUnreachs
= strtoul(ptr
, &endPtr
, 10);
259 stats
->stats
.icmpOutStats
.dwTimeExcds
= strtoul(ptr
, &endPtr
, 10);
263 stats
->stats
.icmpOutStats
.dwParmProbs
= strtoul(ptr
, &endPtr
, 10);
267 stats
->stats
.icmpOutStats
.dwSrcQuenchs
= strtoul(ptr
, &endPtr
, 10);
271 stats
->stats
.icmpOutStats
.dwRedirects
= strtoul(ptr
, &endPtr
, 10);
275 stats
->stats
.icmpOutStats
.dwEchoReps
= strtoul(ptr
, &endPtr
, 10);
279 stats
->stats
.icmpOutStats
.dwTimestamps
= strtoul(ptr
, &endPtr
, 10);
283 stats
->stats
.icmpOutStats
.dwTimestampReps
= strtoul(ptr
, &endPtr
, 10);
287 stats
->stats
.icmpOutStats
.dwAddrMasks
= strtoul(ptr
, &endPtr
, 10);
291 stats
->stats
.icmpOutStats
.dwAddrMaskReps
= strtoul(ptr
, &endPtr
, 10);
301 DWORD
getIPStats(PMIB_IPSTATS stats
)
306 return ERROR_INVALID_PARAMETER
;
308 memset(stats
, 0, sizeof(MIB_IPSTATS
));
309 stats
->dwNumIf
= stats
->dwNumAddr
= getNumInterfaces();
310 stats
->dwNumRoutes
= getNumRoutes();
312 /* get most of these stats from /proc/net/snmp, no error if can't */
313 fp
= fopen("/proc/net/snmp", "r");
315 const char hdr
[] = "Ip:";
316 char buf
[512] = { 0 }, *ptr
;
319 ptr
= fgets(buf
, sizeof(buf
), fp
);
320 } while (ptr
&& strncasecmp(buf
, hdr
, sizeof(hdr
) - 1));
322 /* last line was a header, get another */
323 ptr
= fgets(buf
, sizeof(buf
), fp
);
324 if (ptr
&& strncasecmp(buf
, hdr
, sizeof(hdr
) - 1) == 0) {
329 stats
->dwForwarding
= strtoul(ptr
, &endPtr
, 10);
333 stats
->dwDefaultTTL
= strtoul(ptr
, &endPtr
, 10);
337 stats
->dwInReceives
= strtoul(ptr
, &endPtr
, 10);
341 stats
->dwInHdrErrors
= strtoul(ptr
, &endPtr
, 10);
345 stats
->dwInAddrErrors
= strtoul(ptr
, &endPtr
, 10);
349 stats
->dwForwDatagrams
= strtoul(ptr
, &endPtr
, 10);
353 stats
->dwInUnknownProtos
= strtoul(ptr
, &endPtr
, 10);
357 stats
->dwInDiscards
= strtoul(ptr
, &endPtr
, 10);
361 stats
->dwInDelivers
= strtoul(ptr
, &endPtr
, 10);
365 stats
->dwOutRequests
= strtoul(ptr
, &endPtr
, 10);
369 stats
->dwOutDiscards
= strtoul(ptr
, &endPtr
, 10);
373 stats
->dwOutNoRoutes
= strtoul(ptr
, &endPtr
, 10);
377 stats
->dwReasmTimeout
= strtoul(ptr
, &endPtr
, 10);
381 stats
->dwReasmReqds
= strtoul(ptr
, &endPtr
, 10);
385 stats
->dwReasmOks
= strtoul(ptr
, &endPtr
, 10);
389 stats
->dwReasmFails
= strtoul(ptr
, &endPtr
, 10);
393 stats
->dwFragOks
= strtoul(ptr
, &endPtr
, 10);
397 stats
->dwFragFails
= strtoul(ptr
, &endPtr
, 10);
401 stats
->dwFragCreates
= strtoul(ptr
, &endPtr
, 10);
404 /* hmm, no routingDiscards */
412 DWORD
getTCPStats(MIB_TCPSTATS
*stats
)
417 return ERROR_INVALID_PARAMETER
;
419 memset(stats
, 0, sizeof(MIB_TCPSTATS
));
421 /* get from /proc/net/snmp, no error if can't */
422 fp
= fopen("/proc/net/snmp", "r");
424 const char hdr
[] = "Tcp:";
425 char buf
[512] = { 0 }, *ptr
;
429 ptr
= fgets(buf
, sizeof(buf
), fp
);
430 } while (ptr
&& strncasecmp(buf
, hdr
, sizeof(hdr
) - 1));
432 /* last line was a header, get another */
433 ptr
= fgets(buf
, sizeof(buf
), fp
);
434 if (ptr
&& strncasecmp(buf
, hdr
, sizeof(hdr
) - 1) == 0) {
439 stats
->dwRtoAlgorithm
= strtoul(ptr
, &endPtr
, 10);
443 stats
->dwRtoMin
= strtoul(ptr
, &endPtr
, 10);
447 stats
->dwRtoMin
= strtoul(ptr
, &endPtr
, 10);
451 stats
->dwMaxConn
= strtoul(ptr
, &endPtr
, 10);
455 stats
->dwActiveOpens
= strtoul(ptr
, &endPtr
, 10);
459 stats
->dwPassiveOpens
= strtoul(ptr
, &endPtr
, 10);
463 stats
->dwAttemptFails
= strtoul(ptr
, &endPtr
, 10);
467 stats
->dwEstabResets
= strtoul(ptr
, &endPtr
, 10);
471 stats
->dwCurrEstab
= strtoul(ptr
, &endPtr
, 10);
475 stats
->dwInSegs
= strtoul(ptr
, &endPtr
, 10);
479 stats
->dwOutSegs
= strtoul(ptr
, &endPtr
, 10);
483 stats
->dwRetransSegs
= strtoul(ptr
, &endPtr
, 10);
487 stats
->dwInErrs
= strtoul(ptr
, &endPtr
, 10);
491 stats
->dwOutRsts
= strtoul(ptr
, &endPtr
, 10);
494 stats
->dwNumConns
= getNumTcpEntries();
502 DWORD
getUDPStats(MIB_UDPSTATS
*stats
)
507 return ERROR_INVALID_PARAMETER
;
509 memset(stats
, 0, sizeof(MIB_UDPSTATS
));
511 /* get from /proc/net/snmp, no error if can't */
512 fp
= fopen("/proc/net/snmp", "r");
514 const char hdr
[] = "Udp:";
515 char buf
[512] = { 0 }, *ptr
;
519 ptr
= fgets(buf
, sizeof(buf
), fp
);
520 } while (ptr
&& strncasecmp(buf
, hdr
, sizeof(hdr
) - 1));
522 /* last line was a header, get another */
523 ptr
= fgets(buf
, sizeof(buf
), fp
);
524 if (ptr
&& strncasecmp(buf
, hdr
, sizeof(hdr
) - 1) == 0) {
529 stats
->dwInDatagrams
= strtoul(ptr
, &endPtr
, 10);
533 stats
->dwNoPorts
= strtoul(ptr
, &endPtr
, 10);
537 stats
->dwInErrors
= strtoul(ptr
, &endPtr
, 10);
541 stats
->dwOutDatagrams
= strtoul(ptr
, &endPtr
, 10);
545 stats
->dwNumAddrs
= strtoul(ptr
, &endPtr
, 10);
555 static DWORD
getNumWithOneHeader(const char *filename
)
560 fp
= fopen(filename
, "r");
562 char buf
[512] = { 0 }, *ptr
;
565 ptr
= fgets(buf
, sizeof(buf
), fp
);
568 ptr
= fgets(buf
, sizeof(buf
), fp
);
578 DWORD
getNumRoutes(void)
580 return getNumWithOneHeader("/proc/net/route");
583 RouteTable
*getRouteTable(void)
585 DWORD numRoutes
= getNumRoutes();
588 ret
= (RouteTable
*)calloc(1, sizeof(RouteTable
) +
589 (numRoutes
- 1) * sizeof(RouteEntry
));
593 /* get from /proc/net/route, no error if can't */
594 fp
= fopen("/proc/net/route", "r");
596 char buf
[512] = { 0 }, *ptr
;
598 /* skip header line */
599 ptr
= fgets(buf
, sizeof(buf
), fp
);
600 while (ptr
&& ret
->numRoutes
< numRoutes
) {
601 ptr
= fgets(buf
, sizeof(buf
), fp
);
605 while (!isspace(*ptr
))
609 if (getInterfaceIndexByName(buf
, &index
) == NO_ERROR
) {
612 ret
->routes
[ret
->numRoutes
].ifIndex
= index
;
614 ret
->routes
[ret
->numRoutes
].dest
= strtoul(ptr
, &endPtr
, 16);
618 ret
->routes
[ret
->numRoutes
].gateway
= strtoul(ptr
, &endPtr
, 16);
622 strtoul(ptr
, &endPtr
, 16); /* flags, skip */
626 strtoul(ptr
, &endPtr
, 16); /* refcount, skip */
630 strtoul(ptr
, &endPtr
, 16); /* use, skip */
634 ret
->routes
[ret
->numRoutes
].metric
= strtoul(ptr
, &endPtr
, 16);
638 ret
->routes
[ret
->numRoutes
].mask
= strtoul(ptr
, &endPtr
, 16);
651 DWORD
getNumArpEntries(void)
653 return getNumWithOneHeader("/proc/net/arp");
656 PMIB_IPNETTABLE
getArpTable(void)
658 DWORD numEntries
= getNumArpEntries();
661 ret
= (PMIB_IPNETTABLE
)calloc(1, sizeof(MIB_IPNETTABLE
) +
662 (numEntries
- 1) * sizeof(MIB_IPNETROW
));
666 /* get from /proc/net/arp, no error if can't */
667 fp
= fopen("/proc/net/arp", "r");
669 char buf
[512] = { 0 }, *ptr
;
671 /* skip header line */
672 ptr
= fgets(buf
, sizeof(buf
), fp
);
673 while (ptr
&& ret
->dwNumEntries
< numEntries
) {
674 ptr
= fgets(buf
, sizeof(buf
), fp
);
678 ret
->table
[ret
->dwNumEntries
].dwAddr
= inet_addr(ptr
);
679 while (ptr
&& *ptr
&& !isspace(*ptr
))
683 strtoul(ptr
, &endPtr
, 16); /* hw type (skip) */
687 DWORD flags
= strtoul(ptr
, &endPtr
, 16);
690 ret
->table
[ret
->dwNumEntries
].dwType
= MIB_IPNET_TYPE_DYNAMIC
;
691 else if (flags
& ATF_PERM
)
692 ret
->table
[ret
->dwNumEntries
].dwType
= MIB_IPNET_TYPE_STATIC
;
694 ret
->table
[ret
->dwNumEntries
].dwType
= MIB_IPNET_TYPE_OTHER
;
698 while (ptr
&& *ptr
&& isspace(*ptr
))
700 while (ptr
&& *ptr
&& !isspace(*ptr
)) {
701 DWORD byte
= strtoul(ptr
, &endPtr
, 16);
703 if (endPtr
&& *endPtr
) {
705 ret
->table
[ret
->dwNumEntries
].bPhysAddr
[
706 ret
->table
[ret
->dwNumEntries
].dwPhysAddrLen
++] = byte
& 0x0ff;
711 strtoul(ptr
, &endPtr
, 16); /* mask (skip) */
714 getInterfaceIndexByName(ptr
, &ret
->table
[ret
->dwNumEntries
].dwIndex
);
724 DWORD
getNumUdpEntries(void)
726 return getNumWithOneHeader("/proc/net/udp");
729 PMIB_UDPTABLE
getUdpTable(void)
731 DWORD numEntries
= getNumUdpEntries();
734 ret
= (PMIB_UDPTABLE
)calloc(1, sizeof(MIB_UDPTABLE
) +
735 (numEntries
- 1) * sizeof(MIB_UDPROW
));
739 /* get from /proc/net/udp, no error if can't */
740 fp
= fopen("/proc/net/udp", "r");
742 char buf
[512] = { 0 }, *ptr
;
744 /* skip header line */
745 ptr
= fgets(buf
, sizeof(buf
), fp
);
746 while (ptr
&& ret
->dwNumEntries
< numEntries
) {
747 ptr
= fgets(buf
, sizeof(buf
), fp
);
752 strtoul(ptr
, &endPtr
, 16); /* skip */
757 ret
->table
[ret
->dwNumEntries
].dwLocalAddr
= strtoul(ptr
, &endPtr
,
763 ret
->table
[ret
->dwNumEntries
].dwLocalPort
= strtoul(ptr
, &endPtr
,
776 DWORD
getNumTcpEntries(void)
778 return getNumWithOneHeader("/proc/net/tcp");
781 PMIB_TCPTABLE
getTcpTable(void)
783 DWORD numEntries
= getNumTcpEntries();
786 ret
= (PMIB_TCPTABLE
)calloc(1, sizeof(MIB_TCPTABLE
) +
787 (numEntries
- 1) * sizeof(MIB_TCPROW
));
791 /* get from /proc/net/tcp, no error if can't */
792 fp
= fopen("/proc/net/tcp", "r");
794 char buf
[512] = { 0 }, *ptr
;
796 /* skip header line */
797 ptr
= fgets(buf
, sizeof(buf
), fp
);
798 while (ptr
&& ret
->dwNumEntries
< numEntries
) {
799 ptr
= fgets(buf
, sizeof(buf
), fp
);
803 while (ptr
&& *ptr
&& *ptr
!= ':')
808 ret
->table
[ret
->dwNumEntries
].dwLocalAddr
= strtoul(ptr
, &endPtr
,
814 ret
->table
[ret
->dwNumEntries
].dwLocalPort
= strtoul(ptr
, &endPtr
,
819 ret
->table
[ret
->dwNumEntries
].dwRemoteAddr
= strtoul(ptr
, &endPtr
,
825 ret
->table
[ret
->dwNumEntries
].dwRemotePort
= strtoul(ptr
, &endPtr
,
830 DWORD state
= strtoul(ptr
, &endPtr
, 16);
834 case TCPS_ESTABLISHED
:
835 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_ESTAB
;
838 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_SYN_SENT
;
840 case TCPS_SYN_RECEIVED
:
841 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_SYN_RCVD
;
843 case TCPS_FIN_WAIT_1
:
844 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_FIN_WAIT1
;
846 case TCPS_FIN_WAIT_2
:
847 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_FIN_WAIT2
;
850 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_TIME_WAIT
;
853 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_CLOSED
;
855 case TCPS_CLOSE_WAIT
:
856 ret
->table
[ret
->dwNumEntries
].dwState
=
857 MIB_TCP_STATE_CLOSE_WAIT
;
860 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_LAST_ACK
;
863 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_LISTEN
;
866 ret
->table
[ret
->dwNumEntries
].dwState
= MIB_TCP_STATE_CLOSING
;