| /* Copyright (C) 2003,2006 Juan Lang |
| * Copyright (C) 2007 TransGaming Technologies Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| * |
| * This file implements statistics getting using the /proc filesystem exported |
| * by Linux, and maybe other OSes. |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| #include "wine/debug.h" |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #ifdef HAVE_ALIAS_H |
| #include <alias.h> |
| #endif |
| #ifdef HAVE_SYS_SOCKET_H |
| #include <sys/socket.h> |
| #endif |
| #ifdef HAVE_SYS_SOCKETVAR_H |
| #include <sys/socketvar.h> |
| #endif |
| #ifdef HAVE_SYS_TIMEOUT_H |
| #include <sys/timeout.h> |
| #endif |
| #ifdef HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #ifdef HAVE_ARPA_INET_H |
| #include <arpa/inet.h> |
| #endif |
| #ifdef HAVE_NET_IF_H |
| #include <net/if.h> |
| #endif |
| #ifdef HAVE_NET_IF_DL_H |
| #include <net/if_dl.h> |
| #endif |
| #ifdef HAVE_NET_IF_TYPES_H |
| #include <net/if_types.h> |
| #endif |
| #ifdef HAVE_NET_ROUTE_H |
| #include <net/route.h> |
| #endif |
| #ifdef HAVE_NET_IF_ARP_H |
| #include <net/if_arp.h> |
| #endif |
| #ifdef HAVE_NETINET_IF_ETHER_H |
| #include <netinet/if_ether.h> |
| #endif |
| #ifdef HAVE_NETINET_IF_INARP_H |
| #include <netinet/if_inarp.h> |
| #endif |
| #ifdef HAVE_NETINET_IP_H |
| #include <netinet/ip.h> |
| #endif |
| #ifdef HAVE_NETINET_TCP_H |
| #include <netinet/tcp.h> |
| #endif |
| #ifdef HAVE_NETINET_IP_VAR_H |
| #include <netinet/ip_var.h> |
| #endif |
| #ifdef HAVE_NETINET_TCP_FSM_H |
| #include <netinet/tcp_fsm.h> |
| #endif |
| #ifdef HAVE_NETINET_IN_PCB_H |
| #include <netinet/in_pcb.h> |
| #endif |
| #ifdef HAVE_NETINET_TCP_TIMER_H |
| #include <netinet/tcp_timer.h> |
| #endif |
| #ifdef HAVE_NETINET_TCP_VAR_H |
| #include <netinet/tcp_var.h> |
| #endif |
| #ifdef HAVE_NETINET_IN_SYSTM_H |
| #include <netinet/in_systm.h> |
| #endif |
| #ifdef HAVE_NETINET_IP_ICMP_H |
| #include <netinet/ip_icmp.h> |
| #endif |
| #ifdef HAVE_NETINET_ICMP_VAR_H |
| #include <netinet/icmp_var.h> |
| #endif |
| #ifdef HAVE_NETINET_UDP_H |
| #include <netinet/udp.h> |
| #endif |
| #ifdef HAVE_NETINET_UDP_VAR_H |
| #include <netinet/udp_var.h> |
| #endif |
| #ifdef HAVE_SYS_PROTOSW_H |
| #include <sys/protosw.h> |
| #endif |
| #ifdef HAVE_SYS_SYSCTL_H |
| #include <sys/sysctl.h> |
| #endif |
| |
| #ifndef ROUNDUP |
| #define ROUNDUP(a) \ |
| ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) |
| #endif |
| #ifndef ADVANCE |
| #define ADVANCE(x, n) (x += ROUNDUP(((struct sockaddr *)n)->sa_len)) |
| #endif |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "iprtrmib.h" |
| #include "ifenum.h" |
| #include "ipstats.h" |
| |
| #ifndef HAVE_NETINET_TCP_FSM_H |
| #define TCPS_ESTABLISHED 1 |
| #define TCPS_SYN_SENT 2 |
| #define TCPS_SYN_RECEIVED 3 |
| #define TCPS_FIN_WAIT_1 4 |
| #define TCPS_FIN_WAIT_2 5 |
| #define TCPS_TIME_WAIT 6 |
| #define TCPS_CLOSED 7 |
| #define TCPS_CLOSE_WAIT 8 |
| #define TCPS_LAST_ACK 9 |
| #define TCPS_LISTEN 10 |
| #define TCPS_CLOSING 11 |
| #endif |
| |
| #ifndef RTF_MULTICAST |
| #define RTF_MULTICAST 0 /* Not available on NetBSD/OpenBSD */ |
| #endif |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi); |
| |
| DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST) |
| int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, if_nametoindex(name)}; |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) |
| |
| size_t needed; |
| char *buf, *end; |
| struct if_msghdr *ifm; |
| struct if_data ifdata; |
| if (!name || !entry) |
| return ERROR_INVALID_PARAMETER; |
| |
| if(sysctl(mib, MIB_LEN, NULL, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get size of iflist\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| buf = HeapAlloc (GetProcessHeap (), 0, needed); |
| if (!buf) return ERROR_NOT_SUPPORTED; |
| if(sysctl(mib, MIB_LEN, buf, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get iflist\n"); |
| HeapFree (GetProcessHeap (), 0, buf); |
| return ERROR_NOT_SUPPORTED; |
| } |
| else |
| for ( end = buf + needed; buf < end; buf += ifm->ifm_msglen) |
| { |
| ifm = (struct if_msghdr *) buf; |
| if(ifm->ifm_type == RTM_IFINFO && ifm->ifm_data.ifi_type == IFT_ETHER) |
| { |
| ifdata = ifm->ifm_data; |
| entry->dwMtu = ifdata.ifi_mtu; |
| entry->dwSpeed = ifdata.ifi_baudrate; |
| entry->dwInOctets = ifdata.ifi_ibytes; |
| entry->dwInErrors = ifdata.ifi_ierrors; |
| entry->dwInDiscards = ifdata.ifi_iqdrops; |
| entry->dwInUcastPkts = ifdata.ifi_ipackets; |
| entry->dwInNUcastPkts = ifdata.ifi_imcasts; |
| entry->dwOutOctets = ifdata.ifi_obytes; |
| entry->dwOutUcastPkts = ifdata.ifi_opackets; |
| entry->dwOutErrors = ifdata.ifi_oerrors; |
| HeapFree (GetProcessHeap (), 0, buf); |
| return NO_ERROR; |
| } |
| } |
| HeapFree (GetProcessHeap (), 0, buf); |
| return ERROR_NOT_SUPPORTED; |
| #else |
| /* get interface stats from /proc/net/dev, no error if can't |
| no inUnknownProtos, outNUcastPkts, outQLen */ |
| FILE *fp; |
| |
| if (!name || !entry) |
| return ERROR_INVALID_PARAMETER; |
| fp = fopen("/proc/net/dev", "r"); |
| if (fp) { |
| char buf[512] = { 0 }, *ptr; |
| int nameLen = strlen(name), nameFound = 0; |
| |
| |
| ptr = fgets(buf, sizeof(buf), fp); |
| while (ptr && !nameFound) { |
| while (*ptr && isspace(*ptr)) |
| ptr++; |
| if (strncasecmp(ptr, name, nameLen) == 0 && *(ptr + nameLen) == ':') |
| nameFound = 1; |
| else |
| ptr = fgets(buf, sizeof(buf), fp); |
| } |
| if (nameFound) { |
| char *endPtr; |
| |
| ptr += nameLen + 1; |
| if (ptr && *ptr) { |
| entry->dwInOctets = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| entry->dwInUcastPkts = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| entry->dwInErrors = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| entry->dwInDiscards = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| strtoul(ptr, &endPtr, 10); /* skip */ |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| strtoul(ptr, &endPtr, 10); /* skip */ |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| strtoul(ptr, &endPtr, 10); /* skip */ |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| entry->dwInNUcastPkts = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| entry->dwOutOctets = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| entry->dwOutUcastPkts = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| entry->dwOutErrors = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| entry->dwOutDiscards = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| } |
| fclose(fp); |
| } |
| else |
| { |
| ERR ("unimplemented!\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| return NO_ERROR; |
| #endif |
| } |
| |
| DWORD getICMPStats(MIB_ICMP *stats) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(ICMPCTL_STATS) |
| int mib[] = {CTL_NET, PF_INET, IPPROTO_ICMP, ICMPCTL_STATS}; |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) |
| size_t needed; |
| struct icmpstat icmp_stat; |
| int i; |
| |
| if (!stats) |
| return ERROR_INVALID_PARAMETER; |
| |
| needed = sizeof(icmp_stat); |
| if(sysctl(mib, MIB_LEN, &icmp_stat, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get icmpstat\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| |
| /*in stats */ |
| stats->stats.icmpInStats.dwMsgs = icmp_stat.icps_badcode + icmp_stat.icps_checksum + icmp_stat.icps_tooshort + icmp_stat.icps_badlen; |
| for(i = 0; i <= ICMP_MAXTYPE; i++) |
| stats->stats.icmpInStats.dwMsgs += icmp_stat.icps_inhist[i]; |
| |
| stats->stats.icmpInStats.dwErrors = icmp_stat.icps_badcode + icmp_stat.icps_tooshort + icmp_stat.icps_checksum + icmp_stat.icps_badlen; |
| |
| stats->stats.icmpInStats.dwDestUnreachs = icmp_stat.icps_inhist[ICMP_UNREACH]; |
| stats->stats.icmpInStats.dwTimeExcds = icmp_stat.icps_inhist[ICMP_TIMXCEED]; |
| stats->stats.icmpInStats.dwParmProbs = icmp_stat.icps_inhist[ICMP_PARAMPROB]; |
| stats->stats.icmpInStats.dwSrcQuenchs = icmp_stat.icps_inhist[ICMP_SOURCEQUENCH]; |
| stats->stats.icmpInStats.dwRedirects = icmp_stat.icps_inhist[ICMP_REDIRECT]; |
| stats->stats.icmpInStats.dwEchos = icmp_stat.icps_inhist[ICMP_ECHO]; |
| stats->stats.icmpInStats.dwEchoReps = icmp_stat.icps_inhist[ICMP_ECHOREPLY]; |
| stats->stats.icmpInStats.dwTimestamps = icmp_stat.icps_inhist[ICMP_TSTAMP]; |
| stats->stats.icmpInStats.dwTimestampReps = icmp_stat.icps_inhist[ICMP_TSTAMPREPLY]; |
| stats->stats.icmpInStats.dwAddrMasks = icmp_stat.icps_inhist[ICMP_MASKREQ]; |
| stats->stats.icmpInStats.dwAddrMaskReps = icmp_stat.icps_inhist[ICMP_MASKREPLY]; |
| |
| |
| /* out stats */ |
| stats->stats.icmpOutStats.dwMsgs = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp; |
| for(i = 0; i <= ICMP_MAXTYPE; i++) |
| stats->stats.icmpOutStats.dwMsgs += icmp_stat.icps_outhist[i]; |
| |
| stats->stats.icmpOutStats.dwErrors = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp; |
| |
| stats->stats.icmpOutStats.dwDestUnreachs = icmp_stat.icps_outhist[ICMP_UNREACH]; |
| stats->stats.icmpOutStats.dwTimeExcds = icmp_stat.icps_outhist[ICMP_TIMXCEED]; |
| stats->stats.icmpOutStats.dwParmProbs = icmp_stat.icps_outhist[ICMP_PARAMPROB]; |
| stats->stats.icmpOutStats.dwSrcQuenchs = icmp_stat.icps_outhist[ICMP_SOURCEQUENCH]; |
| stats->stats.icmpOutStats.dwRedirects = icmp_stat.icps_outhist[ICMP_REDIRECT]; |
| stats->stats.icmpOutStats.dwEchos = icmp_stat.icps_outhist[ICMP_ECHO]; |
| stats->stats.icmpOutStats.dwEchoReps = icmp_stat.icps_outhist[ICMP_ECHOREPLY]; |
| stats->stats.icmpOutStats.dwTimestamps = icmp_stat.icps_outhist[ICMP_TSTAMP]; |
| stats->stats.icmpOutStats.dwTimestampReps = icmp_stat.icps_outhist[ICMP_TSTAMPREPLY]; |
| stats->stats.icmpOutStats.dwAddrMasks = icmp_stat.icps_outhist[ICMP_MASKREQ]; |
| stats->stats.icmpOutStats.dwAddrMaskReps = icmp_stat.icps_outhist[ICMP_MASKREPLY]; |
| |
| return NO_ERROR; |
| #else |
| FILE *fp; |
| |
| if (!stats) |
| return ERROR_INVALID_PARAMETER; |
| |
| memset(stats, 0, sizeof(MIB_ICMP)); |
| /* get most of these stats from /proc/net/snmp, no error if can't */ |
| fp = fopen("/proc/net/snmp", "r"); |
| if (fp) { |
| static const char hdr[] = "Icmp:"; |
| char buf[512] = { 0 }, *ptr; |
| |
| do { |
| ptr = fgets(buf, sizeof(buf), fp); |
| } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1)); |
| if (ptr) { |
| /* last line was a header, get another */ |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) { |
| char *endPtr; |
| |
| ptr += sizeof(hdr); |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwMsgs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwErrors = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwTimeExcds = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwParmProbs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwRedirects = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwEchoReps = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwTimestamps = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwTimestampReps = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwAddrMasks = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpInStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwMsgs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwErrors = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwTimeExcds = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwParmProbs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwRedirects = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwEchoReps = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwTimestamps = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwTimestampReps = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwAddrMasks = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->stats.icmpOutStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| } |
| } |
| fclose(fp); |
| } |
| else |
| { |
| ERR ("unimplemented!\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| return NO_ERROR; |
| #endif |
| } |
| |
| DWORD getIPStats(PMIB_IPSTATS stats) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(IPCTL_STATS) |
| int mib[] = {CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS}; |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) |
| int ip_ttl, ip_forwarding; |
| struct ipstat ip_stat; |
| size_t needed; |
| |
| if (!stats) |
| return ERROR_INVALID_PARAMETER; |
| |
| needed = sizeof(ip_stat); |
| if(sysctl(mib, MIB_LEN, &ip_stat, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get ipstat\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| needed = sizeof(ip_ttl); |
| if (sysctlbyname ("net.inet.ip.ttl", &ip_ttl, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get ip Default TTL\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| needed = sizeof(ip_forwarding); |
| if (sysctlbyname ("net.inet.ip.forwarding", &ip_forwarding, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get ip forwarding\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| stats->dwForwarding = ip_forwarding; |
| stats->dwDefaultTTL = ip_ttl; |
| stats->dwInDelivers = ip_stat.ips_delivered; |
| stats->dwInHdrErrors = ip_stat.ips_badhlen + ip_stat.ips_badsum + ip_stat.ips_tooshort + ip_stat.ips_badlen; |
| stats->dwInAddrErrors = ip_stat.ips_cantforward; |
| stats->dwInReceives = ip_stat.ips_total; |
| stats->dwForwDatagrams = ip_stat.ips_forward; |
| stats->dwInUnknownProtos = ip_stat.ips_noproto; |
| stats->dwInDiscards = ip_stat.ips_fragdropped; |
| stats->dwOutDiscards = ip_stat.ips_odropped; |
| stats->dwReasmOks = ip_stat.ips_reassembled; |
| stats->dwFragOks = ip_stat.ips_fragmented; |
| stats->dwFragFails = ip_stat.ips_cantfrag; |
| stats->dwReasmTimeout = ip_stat.ips_fragtimeout; |
| stats->dwOutNoRoutes = ip_stat.ips_noroute; |
| stats->dwOutRequests = ip_stat.ips_localout; |
| stats->dwReasmReqds = ip_stat.ips_fragments; |
| |
| return NO_ERROR; |
| #else |
| FILE *fp; |
| |
| if (!stats) |
| return ERROR_INVALID_PARAMETER; |
| |
| memset(stats, 0, sizeof(MIB_IPSTATS)); |
| stats->dwNumIf = stats->dwNumAddr = getNumInterfaces(); |
| stats->dwNumRoutes = getNumRoutes(); |
| |
| /* get most of these stats from /proc/net/snmp, no error if can't */ |
| fp = fopen("/proc/net/snmp", "r"); |
| if (fp) { |
| static const char hdr[] = "Ip:"; |
| char buf[512] = { 0 }, *ptr; |
| |
| do { |
| ptr = fgets(buf, sizeof(buf), fp); |
| } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1)); |
| if (ptr) { |
| /* last line was a header, get another */ |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) { |
| char *endPtr; |
| |
| ptr += sizeof(hdr); |
| if (ptr && *ptr) { |
| stats->dwForwarding = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwDefaultTTL = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwInReceives = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwInHdrErrors = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwInAddrErrors = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwForwDatagrams = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwInUnknownProtos = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwInDiscards = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwInDelivers = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwOutRequests = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwOutDiscards = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwOutNoRoutes = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwReasmTimeout = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwReasmReqds = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwReasmOks = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwReasmFails = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwFragOks = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwFragFails = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwFragCreates = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| /* hmm, no routingDiscards */ |
| } |
| } |
| fclose(fp); |
| } |
| else |
| { |
| ERR ("unimplemented!\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| return NO_ERROR; |
| #endif |
| } |
| |
| DWORD getTCPStats(MIB_TCPSTATS *stats) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS) |
| #ifndef TCPTV_MIN /* got removed in Mac OS X for some reason */ |
| #define TCPTV_MIN 2 |
| #define TCPTV_REXMTMAX 128 |
| #endif |
| int mib[] = {CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS}; |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) |
| #define hz 1000 |
| struct tcpstat tcp_stat; |
| size_t needed; |
| |
| if (!stats) |
| return ERROR_INVALID_PARAMETER; |
| needed = sizeof(tcp_stat); |
| |
| if(sysctl(mib, MIB_LEN, &tcp_stat, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get tcpstat\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| stats->dwRtoAlgorithm = MIB_TCP_RTO_VANJ; |
| stats->dwRtoMin = TCPTV_MIN; |
| stats->dwRtoMax = TCPTV_REXMTMAX; |
| stats->dwMaxConn = -1; |
| stats->dwActiveOpens = tcp_stat.tcps_connattempt; |
| stats->dwPassiveOpens = tcp_stat.tcps_accepts; |
| stats->dwAttemptFails = tcp_stat.tcps_conndrops; |
| stats->dwEstabResets = tcp_stat.tcps_drops; |
| stats->dwCurrEstab = 0; |
| stats->dwInSegs = tcp_stat.tcps_rcvtotal; |
| stats->dwOutSegs = tcp_stat.tcps_sndtotal - tcp_stat.tcps_sndrexmitpack; |
| stats->dwRetransSegs = tcp_stat.tcps_sndrexmitpack; |
| stats->dwInErrs = tcp_stat.tcps_rcvbadsum + tcp_stat.tcps_rcvbadoff + tcp_stat.tcps_rcvmemdrop + tcp_stat.tcps_rcvshort; |
| stats->dwOutRsts = tcp_stat.tcps_sndctrl - tcp_stat.tcps_closed; |
| stats->dwNumConns = tcp_stat.tcps_connects; |
| |
| return NO_ERROR; |
| |
| #else |
| FILE *fp; |
| |
| if (!stats) |
| return ERROR_INVALID_PARAMETER; |
| |
| memset(stats, 0, sizeof(MIB_TCPSTATS)); |
| |
| /* get from /proc/net/snmp, no error if can't */ |
| fp = fopen("/proc/net/snmp", "r"); |
| if (fp) { |
| static const char hdr[] = "Tcp:"; |
| char buf[512] = { 0 }, *ptr; |
| |
| |
| do { |
| ptr = fgets(buf, sizeof(buf), fp); |
| } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1)); |
| if (ptr) { |
| /* last line was a header, get another */ |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) { |
| char *endPtr; |
| |
| ptr += sizeof(hdr); |
| if (ptr && *ptr) { |
| stats->dwRtoAlgorithm = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwRtoMin = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwRtoMax = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwMaxConn = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwActiveOpens = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwPassiveOpens = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwAttemptFails = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwEstabResets = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwCurrEstab = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwInSegs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwOutSegs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwRetransSegs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwInErrs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwOutRsts = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| stats->dwNumConns = getNumTcpEntries(); |
| } |
| } |
| fclose(fp); |
| } |
| else |
| { |
| ERR ("unimplemented!\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| return NO_ERROR; |
| #endif |
| } |
| |
| DWORD getUDPStats(MIB_UDPSTATS *stats) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS) |
| int mib[] = {CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_STATS}; |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) |
| struct udpstat udp_stat; |
| size_t needed; |
| if (!stats) |
| return ERROR_INVALID_PARAMETER; |
| |
| needed = sizeof(udp_stat); |
| |
| if(sysctl(mib, MIB_LEN, &udp_stat, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get udpstat\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| stats->dwInDatagrams = udp_stat.udps_ipackets; |
| stats->dwOutDatagrams = udp_stat.udps_opackets; |
| stats->dwNoPorts = udp_stat.udps_noport; |
| stats->dwInErrors = udp_stat.udps_hdrops + udp_stat.udps_badsum + udp_stat.udps_fullsock + udp_stat.udps_badlen; |
| stats->dwNumAddrs = getNumUdpEntries(); |
| |
| return NO_ERROR; |
| #else |
| FILE *fp; |
| |
| if (!stats) |
| return ERROR_INVALID_PARAMETER; |
| |
| memset(stats, 0, sizeof(MIB_UDPSTATS)); |
| |
| /* get from /proc/net/snmp, no error if can't */ |
| fp = fopen("/proc/net/snmp", "r"); |
| if (fp) { |
| static const char hdr[] = "Udp:"; |
| char buf[512] = { 0 }, *ptr; |
| |
| |
| do { |
| ptr = fgets(buf, sizeof(buf), fp); |
| } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1)); |
| if (ptr) { |
| /* last line was a header, get another */ |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) { |
| char *endPtr; |
| |
| ptr += sizeof(hdr); |
| if (ptr && *ptr) { |
| stats->dwInDatagrams = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwNoPorts = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwInErrors = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwOutDatagrams = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| stats->dwNumAddrs = strtoul(ptr, &endPtr, 10); |
| ptr = endPtr; |
| } |
| } |
| } |
| fclose(fp); |
| } |
| else |
| { |
| ERR ("unimplemented!\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| return NO_ERROR; |
| #endif |
| } |
| |
| static DWORD getNumWithOneHeader(const char *filename) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H) |
| size_t Len = 0; |
| char *Buf; |
| struct xinpgen *pXIG, *pOrigXIG; |
| int Protocol; |
| DWORD NumEntries = 0; |
| |
| if (!strcmp (filename, "net.inet.tcp.pcblist")) |
| Protocol = IPPROTO_TCP; |
| else if (!strcmp (filename, "net.inet.udp.pcblist")) |
| Protocol = IPPROTO_UDP; |
| else |
| { |
| ERR ("Unsupported mib '%s', needs protocol mapping\n", |
| filename); |
| return 0; |
| } |
| |
| if (sysctlbyname (filename, NULL, &Len, NULL, 0) < 0) |
| { |
| WARN ("Unable to read '%s' via sysctlbyname\n", filename); |
| return 0; |
| } |
| |
| Buf = HeapAlloc (GetProcessHeap (), 0, Len); |
| if (!Buf) |
| { |
| ERR ("Out of memory!\n"); |
| return 0; |
| } |
| |
| if (sysctlbyname (filename, Buf, &Len, NULL, 0) < 0) |
| { |
| ERR ("Failure to read '%s' via sysctlbyname!\n", filename); |
| HeapFree (GetProcessHeap (), 0, Buf); |
| return 0; |
| } |
| |
| /* Might be nothing here; first entry is just a header it seems */ |
| if (Len <= sizeof (struct xinpgen)) |
| { |
| HeapFree (GetProcessHeap (), 0, Buf); |
| return 0; |
| } |
| |
| pOrigXIG = (struct xinpgen *)Buf; |
| pXIG = pOrigXIG; |
| |
| for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len); |
| pXIG->xig_len > sizeof (struct xinpgen); |
| pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len)) |
| { |
| struct tcpcb *pTCPData = NULL; |
| struct inpcb *pINData; |
| struct xsocket *pSockData; |
| |
| if (Protocol == IPPROTO_TCP) |
| { |
| pTCPData = &((struct xtcpcb *)pXIG)->xt_tp; |
| pINData = &((struct xtcpcb *)pXIG)->xt_inp; |
| pSockData = &((struct xtcpcb *)pXIG)->xt_socket; |
| } |
| else |
| { |
| pINData = &((struct xinpcb *)pXIG)->xi_inp; |
| pSockData = &((struct xinpcb *)pXIG)->xi_socket; |
| } |
| |
| /* Ignore sockets for other protocols */ |
| if (pSockData->xso_protocol != Protocol) |
| continue; |
| |
| /* Ignore PCBs that were freed while generating the data */ |
| if (pINData->inp_gencnt > pOrigXIG->xig_gen) |
| continue; |
| |
| /* we're only interested in IPv4 addresses */ |
| if (!(pINData->inp_vflag & INP_IPV4) || |
| (pINData->inp_vflag & INP_IPV6)) |
| continue; |
| |
| /* If all 0's, skip it */ |
| if (!pINData->inp_laddr.s_addr && |
| !pINData->inp_lport && |
| !pINData->inp_faddr.s_addr && |
| !pINData->inp_fport) |
| continue; |
| |
| NumEntries++; |
| } |
| |
| HeapFree (GetProcessHeap (), 0, Buf); |
| return NumEntries; |
| #else |
| FILE *fp; |
| int ret = 0; |
| |
| fp = fopen(filename, "r"); |
| if (fp) { |
| char buf[512] = { 0 }, *ptr; |
| |
| |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr) { |
| do { |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr) |
| ret++; |
| } while (ptr); |
| } |
| fclose(fp); |
| } |
| else |
| ERR ("Unable to open '%s' to count entries!\n", filename); |
| |
| return ret; |
| #endif |
| } |
| |
| DWORD getNumRoutes(void) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP) |
| int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0}; |
| size_t needed; |
| char *buf, *lim, *next; |
| struct rt_msghdr *rtm; |
| DWORD RouteCount = 0; |
| |
| if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0) |
| { |
| ERR ("sysctl 1 failed!\n"); |
| return 0; |
| } |
| |
| buf = HeapAlloc (GetProcessHeap (), 0, needed); |
| if (!buf) return 0; |
| |
| if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) |
| { |
| ERR ("sysctl 2 failed!\n"); |
| HeapFree (GetProcessHeap (), 0, buf); |
| return 0; |
| } |
| |
| lim = buf + needed; |
| for (next = buf; next < lim; next += rtm->rtm_msglen) |
| { |
| rtm = (struct rt_msghdr *)next; |
| |
| if (rtm->rtm_type != RTM_GET) |
| { |
| WARN ("Got unexpected message type 0x%x!\n", |
| rtm->rtm_type); |
| continue; |
| } |
| |
| /* Ignore all entries except for gateway routes which aren't |
| multicast */ |
| if (!(rtm->rtm_flags & RTF_GATEWAY) || (rtm->rtm_flags & RTF_MULTICAST)) |
| continue; |
| |
| RouteCount++; |
| } |
| |
| HeapFree (GetProcessHeap (), 0, buf); |
| return RouteCount; |
| #else |
| return getNumWithOneHeader("/proc/net/route"); |
| #endif |
| } |
| |
| DWORD getRouteTable(PMIB_IPFORWARDTABLE *ppIpForwardTable, HANDLE heap, |
| DWORD flags) |
| { |
| DWORD ret; |
| |
| if (!ppIpForwardTable) |
| ret = ERROR_INVALID_PARAMETER; |
| else { |
| DWORD numRoutes = getNumRoutes(); |
| DWORD size = sizeof(MIB_IPFORWARDTABLE); |
| PMIB_IPFORWARDTABLE table; |
| |
| if (numRoutes > 1) |
| size += (numRoutes - 1) * sizeof(MIB_IPFORWARDROW); |
| table = HeapAlloc(heap, flags, size); |
| if (table) { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP) |
| int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0}; |
| size_t needed; |
| char *buf, *lim, *next, *addrPtr; |
| struct rt_msghdr *rtm; |
| |
| if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0) |
| { |
| ERR ("sysctl 1 failed!\n"); |
| HeapFree (GetProcessHeap (), 0, table); |
| return NO_ERROR; |
| } |
| |
| buf = HeapAlloc (GetProcessHeap (), 0, needed); |
| if (!buf) |
| { |
| HeapFree (GetProcessHeap (), 0, table); |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) |
| { |
| ERR ("sysctl 2 failed!\n"); |
| HeapFree (GetProcessHeap (), 0, table); |
| HeapFree (GetProcessHeap (), 0, buf); |
| return NO_ERROR; |
| } |
| |
| *ppIpForwardTable = table; |
| table->dwNumEntries = 0; |
| |
| lim = buf + needed; |
| for (next = buf; next < lim; next += rtm->rtm_msglen) |
| { |
| int i; |
| |
| rtm = (struct rt_msghdr *)next; |
| |
| if (rtm->rtm_type != RTM_GET) |
| { |
| WARN ("Got unexpected message type 0x%x!\n", |
| rtm->rtm_type); |
| continue; |
| } |
| |
| /* Ignore all entries except for gateway routes which aren't |
| multicast */ |
| if (!(rtm->rtm_flags & RTF_GATEWAY) || |
| (rtm->rtm_flags & RTF_MULTICAST)) |
| continue; |
| |
| memset (&table->table[table->dwNumEntries], 0, |
| sizeof (MIB_IPFORWARDROW)); |
| table->table[table->dwNumEntries].dwForwardIfIndex = rtm->rtm_index; |
| table->table[table->dwNumEntries].dwForwardType = |
| MIB_IPROUTE_TYPE_INDIRECT; |
| table->table[table->dwNumEntries].dwForwardMetric1 = |
| rtm->rtm_rmx.rmx_hopcount; |
| table->table[table->dwNumEntries].dwForwardProto = |
| MIB_IPPROTO_LOCAL; |
| |
| addrPtr = (char *)(rtm + 1); |
| |
| for (i = 1; i; i <<= 1) |
| { |
| struct sockaddr *sa; |
| DWORD addr; |
| |
| if (!(i & rtm->rtm_addrs)) |
| continue; |
| |
| sa = (struct sockaddr *)addrPtr; |
| ADVANCE (addrPtr, sa); |
| |
| /* default routes are encoded by length-zero sockaddr */ |
| if (sa->sa_len == 0) |
| addr = 0; |
| else if (sa->sa_family != AF_INET) |
| { |
| WARN ("Received unsupported sockaddr family 0x%x\n", |
| sa->sa_family); |
| addr = 0; |
| } |
| else |
| { |
| struct sockaddr_in *sin = (struct sockaddr_in *)sa; |
| |
| addr = sin->sin_addr.s_addr; |
| } |
| |
| switch (i) |
| { |
| case RTA_DST: |
| table->table[table->dwNumEntries].dwForwardDest = addr; |
| break; |
| |
| case RTA_GATEWAY: |
| table->table[table->dwNumEntries].dwForwardNextHop = addr; |
| break; |
| |
| case RTA_NETMASK: |
| table->table[table->dwNumEntries].dwForwardMask = addr; |
| break; |
| |
| default: |
| WARN ("Unexpected address type 0x%x\n", i); |
| } |
| } |
| |
| table->dwNumEntries++; |
| } |
| |
| HeapFree (GetProcessHeap (), 0, buf); |
| ret = NO_ERROR; |
| #else |
| FILE *fp; |
| |
| ret = NO_ERROR; |
| *ppIpForwardTable = table; |
| table->dwNumEntries = 0; |
| /* get from /proc/net/route, no error if can't */ |
| fp = fopen("/proc/net/route", "r"); |
| if (fp) { |
| char buf[512] = { 0 }, *ptr; |
| |
| /* skip header line */ |
| ptr = fgets(buf, sizeof(buf), fp); |
| while (ptr && table->dwNumEntries < numRoutes) { |
| memset(&table->table[table->dwNumEntries], 0, |
| sizeof(MIB_IPFORWARDROW)); |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr) { |
| DWORD index; |
| |
| while (!isspace(*ptr)) |
| ptr++; |
| *ptr = '\0'; |
| ptr++; |
| if (getInterfaceIndexByName(buf, &index) == NO_ERROR) { |
| char *endPtr; |
| |
| table->table[table->dwNumEntries].dwForwardIfIndex = index; |
| if (*ptr) { |
| table->table[table->dwNumEntries].dwForwardDest = |
| strtoul(ptr, &endPtr, 16); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| table->table[table->dwNumEntries].dwForwardNextHop = |
| strtoul(ptr, &endPtr, 16); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| DWORD flags = strtoul(ptr, &endPtr, 16); |
| |
| if (!(flags & RTF_UP)) |
| table->table[table->dwNumEntries].dwForwardType = |
| MIB_IPROUTE_TYPE_INVALID; |
| else if (flags & RTF_GATEWAY) |
| table->table[table->dwNumEntries].dwForwardType = |
| MIB_IPROUTE_TYPE_INDIRECT; |
| else |
| table->table[table->dwNumEntries].dwForwardType = |
| MIB_IPROUTE_TYPE_DIRECT; |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| strtoul(ptr, &endPtr, 16); /* refcount, skip */ |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| strtoul(ptr, &endPtr, 16); /* use, skip */ |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| table->table[table->dwNumEntries].dwForwardMetric1 = |
| strtoul(ptr, &endPtr, 16); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| table->table[table->dwNumEntries].dwForwardMask = |
| strtoul(ptr, &endPtr, 16); |
| ptr = endPtr; |
| } |
| /* FIXME: other protos might be appropriate, e.g. the default |
| * route is typically set with MIB_IPPROTO_NETMGMT instead */ |
| table->table[table->dwNumEntries].dwForwardProto = |
| MIB_IPPROTO_LOCAL; |
| table->dwNumEntries++; |
| } |
| } |
| } |
| fclose(fp); |
| } |
| else |
| { |
| ERR ("unimplemented!\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| #endif |
| } |
| else |
| ret = ERROR_OUTOFMEMORY; |
| } |
| return ret; |
| } |
| |
| DWORD getNumArpEntries(void) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP) |
| int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO}; |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) |
| DWORD arpEntries = 0; |
| size_t needed; |
| char *buf, *lim, *next; |
| struct rt_msghdr *rtm; |
| struct sockaddr_inarp *sinarp; |
| struct sockaddr_dl *sdl; |
| |
| if (sysctl (mib, MIB_LEN, NULL, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get size of arp table\n"); |
| return 0; |
| } |
| |
| buf = HeapAlloc (GetProcessHeap (), 0, needed); |
| if (!buf) return 0; |
| |
| if (sysctl (mib, MIB_LEN, buf, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get arp table\n"); |
| HeapFree (GetProcessHeap (), 0, buf); |
| return 0; |
| } |
| |
| lim = buf + needed; |
| next = buf; |
| while(next < lim) |
| { |
| rtm = (struct rt_msghdr *)next; |
| sinarp=(struct sockaddr_inarp *)(rtm + 1); |
| sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len)); |
| if(sdl->sdl_alen) /* arp entry */ |
| arpEntries++; |
| next += rtm->rtm_msglen; |
| } |
| HeapFree (GetProcessHeap (), 0, buf); |
| return arpEntries; |
| #endif |
| return getNumWithOneHeader("/proc/net/arp"); |
| } |
| |
| DWORD getArpTable(PMIB_IPNETTABLE *ppIpNetTable, HANDLE heap, DWORD flags) |
| { |
| DWORD ret = NO_ERROR; |
| if (!ppIpNetTable) |
| ret = ERROR_INVALID_PARAMETER; |
| else { |
| DWORD numEntries = getNumArpEntries(); |
| DWORD size = sizeof(MIB_IPNETTABLE); |
| PMIB_IPNETTABLE table; |
| |
| if (numEntries > 1) |
| size += (numEntries - 1) * sizeof(MIB_IPNETROW); |
| table = HeapAlloc(heap, flags, size); |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP) |
| if (table) |
| { |
| int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO}; |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) |
| size_t needed; |
| char *buf, *lim, *next; |
| struct rt_msghdr *rtm; |
| struct sockaddr_inarp *sinarp; |
| struct sockaddr_dl *sdl; |
| |
| *ppIpNetTable = table; |
| table->dwNumEntries = 0; |
| |
| if (sysctl (mib, MIB_LEN, NULL, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get size of arp table\n"); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| buf = HeapAlloc (GetProcessHeap (), 0, needed); |
| if (!buf) return ERROR_OUTOFMEMORY; |
| |
| if (sysctl (mib, MIB_LEN, buf, &needed, NULL, 0) == -1) |
| { |
| ERR ("failed to get arp table\n"); |
| HeapFree (GetProcessHeap (), 0, buf); |
| return ERROR_NOT_SUPPORTED; |
| } |
| |
| lim = buf + needed; |
| next = buf; |
| while(next < lim) |
| { |
| rtm = (struct rt_msghdr *)next; |
| sinarp=(struct sockaddr_inarp *)(rtm + 1); |
| sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len)); |
| if(sdl->sdl_alen) /* arp entry */ |
| { |
| DWORD byte = strtoul(&sdl->sdl_data[sdl->sdl_alen], NULL, 16); |
| memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_IPNETROW)); |
| table->table[table->dwNumEntries].dwAddr = sinarp->sin_addr.s_addr; |
| table->table[table->dwNumEntries].dwIndex = sdl->sdl_index; |
| table->table[table->dwNumEntries].dwPhysAddrLen = sdl->sdl_alen; |
| |
| table->table[table->dwNumEntries].bPhysAddr[ |
| table->table[table->dwNumEntries].dwPhysAddrLen++] = |
| byte & 0x0ff; |
| if(rtm->rtm_rmx.rmx_expire == 0) |
| table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_STATIC; |
| else if(sinarp->sin_other & SIN_PROXY) |
| table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_OTHER; |
| else if(rtm->rtm_rmx.rmx_expire != 0) |
| table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_DYNAMIC; |
| else |
| table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_INVALID; |
| |
| table->dwNumEntries++; |
| } |
| next += rtm->rtm_msglen; |
| } |
| HeapFree (GetProcessHeap (), 0, buf); |
| } |
| else |
| ret = ERROR_OUTOFMEMORY; |
| return ret; |
| #endif |
| |
| if (table) { |
| FILE *fp; |
| *ppIpNetTable = table; |
| table->dwNumEntries = 0; |
| /* get from /proc/net/arp, no error if can't */ |
| fp = fopen("/proc/net/arp", "r"); |
| if (fp) { |
| char buf[512] = { 0 }, *ptr; |
| |
| /* skip header line */ |
| ptr = fgets(buf, sizeof(buf), fp); |
| while (ptr && table->dwNumEntries < numEntries) { |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr) { |
| char *endPtr; |
| |
| memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_IPNETROW)); |
| table->table[table->dwNumEntries].dwAddr = inet_addr(ptr); |
| while (ptr && *ptr && !isspace(*ptr)) |
| ptr++; |
| |
| if (ptr && *ptr) { |
| strtoul(ptr, &endPtr, 16); /* hw type (skip) */ |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| DWORD flags = strtoul(ptr, &endPtr, 16); |
| |
| #ifdef ATF_COM |
| if (flags & ATF_COM) |
| table->table[table->dwNumEntries].dwType = |
| MIB_IPNET_TYPE_DYNAMIC; |
| else |
| #endif |
| #ifdef ATF_PERM |
| if (flags & ATF_PERM) |
| table->table[table->dwNumEntries].dwType = |
| MIB_IPNET_TYPE_STATIC; |
| else |
| #endif |
| table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_OTHER; |
| |
| ptr = endPtr; |
| } |
| while (ptr && *ptr && isspace(*ptr)) |
| ptr++; |
| while (ptr && *ptr && !isspace(*ptr)) { |
| DWORD byte = strtoul(ptr, &endPtr, 16); |
| |
| if (endPtr && *endPtr) { |
| endPtr++; |
| table->table[table->dwNumEntries].bPhysAddr[ |
| table->table[table->dwNumEntries].dwPhysAddrLen++] = |
| byte & 0x0ff; |
| } |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| strtoul(ptr, &endPtr, 16); /* mask (skip) */ |
| ptr = endPtr; |
| } |
| getInterfaceIndexByName(ptr, |
| &table->table[table->dwNumEntries].dwIndex); |
| table->dwNumEntries++; |
| } |
| } |
| fclose(fp); |
| } |
| else |
| ret = ERROR_NOT_SUPPORTED; |
| } |
| else |
| ret = ERROR_OUTOFMEMORY; |
| } |
| return ret; |
| } |
| |
| DWORD getNumUdpEntries(void) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H) |
| return getNumWithOneHeader ("net.inet.udp.pcblist"); |
| #else |
| return getNumWithOneHeader("/proc/net/udp"); |
| #endif |
| } |
| |
| DWORD getUdpTable(PMIB_UDPTABLE *ppUdpTable, HANDLE heap, DWORD flags) |
| { |
| DWORD ret; |
| |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP) |
| ERR ("unimplemented!\n"); |
| return ERROR_NOT_SUPPORTED; |
| #endif |
| |
| if (!ppUdpTable) |
| ret = ERROR_INVALID_PARAMETER; |
| else { |
| DWORD numEntries = getNumUdpEntries(); |
| DWORD size = sizeof(MIB_UDPTABLE); |
| PMIB_UDPTABLE table; |
| |
| if (numEntries > 1) |
| size += (numEntries - 1) * sizeof(MIB_UDPROW); |
| table = HeapAlloc(heap, flags, size); |
| if (table) { |
| FILE *fp; |
| |
| ret = NO_ERROR; |
| *ppUdpTable = table; |
| table->dwNumEntries = 0; |
| /* get from /proc/net/udp, no error if can't */ |
| fp = fopen("/proc/net/udp", "r"); |
| if (fp) { |
| char buf[512] = { 0 }, *ptr; |
| |
| /* skip header line */ |
| ptr = fgets(buf, sizeof(buf), fp); |
| while (ptr && table->dwNumEntries < numEntries) { |
| memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_UDPROW)); |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr) { |
| char *endPtr; |
| |
| if (ptr && *ptr) { |
| strtoul(ptr, &endPtr, 16); /* skip */ |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| ptr++; |
| table->table[table->dwNumEntries].dwLocalAddr = strtoul(ptr, |
| &endPtr, 16); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| ptr++; |
| table->table[table->dwNumEntries].dwLocalPort = strtoul(ptr, |
| &endPtr, 16); |
| ptr = endPtr; |
| } |
| table->dwNumEntries++; |
| } |
| } |
| fclose(fp); |
| } |
| else |
| ret = ERROR_NOT_SUPPORTED; |
| } |
| else |
| ret = ERROR_OUTOFMEMORY; |
| } |
| return ret; |
| } |
| |
| |
| DWORD getNumTcpEntries(void) |
| { |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H) |
| return getNumWithOneHeader ("net.inet.tcp.pcblist"); |
| #else |
| return getNumWithOneHeader ("/proc/net/tcp"); |
| #endif |
| } |
| |
| |
| /* Why not a lookup table? Because the TCPS_* constants are different |
| on different platforms */ |
| static DWORD TCPStateToMIBState (int state) |
| { |
| switch (state) |
| { |
| case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB; |
| case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT; |
| case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD; |
| case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1; |
| case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2; |
| case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT; |
| case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT; |
| case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK; |
| case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN; |
| case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING; |
| default: |
| case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED; |
| } |
| } |
| |
| |
| DWORD getTcpTable(PMIB_TCPTABLE *ppTcpTable, DWORD maxEntries, HANDLE heap, |
| DWORD flags) |
| { |
| DWORD numEntries; |
| PMIB_TCPTABLE table; |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H) |
| size_t Len = 0; |
| char *Buf; |
| struct xinpgen *pXIG, *pOrigXIG; |
| #else |
| FILE *fp; |
| char buf[512] = { 0 }, *ptr; |
| #endif |
| |
| if (!ppTcpTable) |
| return ERROR_INVALID_PARAMETER; |
| |
| numEntries = getNumTcpEntries (); |
| |
| if (!*ppTcpTable) |
| { |
| DWORD size = sizeof(MIB_TCPTABLE); |
| |
| if (numEntries > 1) |
| size += (numEntries - 1) * sizeof (MIB_TCPROW); |
| *ppTcpTable = HeapAlloc (heap, flags, size); |
| if (!*ppTcpTable) |
| { |
| ERR ("Out of memory!\n"); |
| return ERROR_OUTOFMEMORY; |
| } |
| maxEntries = numEntries; |
| } |
| |
| table = *ppTcpTable; |
| table->dwNumEntries = 0; |
| if (!numEntries) |
| return NO_ERROR; |
| |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H) |
| |
| if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0) |
| { |
| ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n"); |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| Buf = HeapAlloc (GetProcessHeap (), 0, Len); |
| if (!Buf) |
| { |
| ERR ("Out of memory!\n"); |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0) |
| { |
| ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n"); |
| HeapFree (GetProcessHeap (), 0, Buf); |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| /* Might be nothing here; first entry is just a header it seems */ |
| if (Len <= sizeof (struct xinpgen)) |
| { |
| HeapFree (GetProcessHeap (), 0, Buf); |
| return NO_ERROR; |
| } |
| |
| pOrigXIG = (struct xinpgen *)Buf; |
| pXIG = pOrigXIG; |
| |
| for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len); |
| (pXIG->xig_len > sizeof (struct xinpgen)) && |
| (table->dwNumEntries < maxEntries); |
| pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len)) |
| { |
| struct tcpcb *pTCPData = NULL; |
| struct inpcb *pINData; |
| struct xsocket *pSockData; |
| |
| pTCPData = &((struct xtcpcb *)pXIG)->xt_tp; |
| pINData = &((struct xtcpcb *)pXIG)->xt_inp; |
| pSockData = &((struct xtcpcb *)pXIG)->xt_socket; |
| |
| /* Ignore sockets for other protocols */ |
| if (pSockData->xso_protocol != IPPROTO_TCP) |
| continue; |
| |
| /* Ignore PCBs that were freed while generating the data */ |
| if (pINData->inp_gencnt > pOrigXIG->xig_gen) |
| continue; |
| |
| /* we're only interested in IPv4 addresses */ |
| if (!(pINData->inp_vflag & INP_IPV4) || |
| (pINData->inp_vflag & INP_IPV6)) |
| continue; |
| |
| /* If all 0's, skip it */ |
| if (!pINData->inp_laddr.s_addr && |
| !pINData->inp_lport && |
| !pINData->inp_faddr.s_addr && |
| !pINData->inp_fport) |
| continue; |
| |
| /* Fill in structure details */ |
| table->table[table->dwNumEntries].dwLocalAddr = |
| pINData->inp_laddr.s_addr; |
| table->table[table->dwNumEntries].dwLocalPort = |
| pINData->inp_lport; |
| table->table[table->dwNumEntries].dwRemoteAddr = |
| pINData->inp_faddr.s_addr; |
| table->table[table->dwNumEntries].dwRemotePort = |
| pINData->inp_fport; |
| table->table[table->dwNumEntries].dwState = |
| TCPStateToMIBState (pTCPData->t_state); |
| |
| table->dwNumEntries++; |
| } |
| |
| HeapFree (GetProcessHeap (), 0, Buf); |
| #else |
| /* get from /proc/net/tcp, no error if can't */ |
| fp = fopen("/proc/net/tcp", "r"); |
| if (!fp) |
| return ERROR_NOT_SUPPORTED; |
| |
| /* skip header line */ |
| ptr = fgets(buf, sizeof(buf), fp); |
| while (ptr && table->dwNumEntries < maxEntries) { |
| memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_TCPROW)); |
| ptr = fgets(buf, sizeof(buf), fp); |
| if (ptr) { |
| char *endPtr; |
| |
| while (ptr && *ptr && *ptr != ':') |
| ptr++; |
| if (ptr && *ptr) |
| ptr++; |
| if (ptr && *ptr) { |
| table->table[table->dwNumEntries].dwLocalAddr = |
| strtoul(ptr, &endPtr, 16); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| ptr++; |
| table->table[table->dwNumEntries].dwLocalPort = |
| htons ((unsigned short)strtoul(ptr, &endPtr, 16)); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| table->table[table->dwNumEntries].dwRemoteAddr = |
| strtoul(ptr, &endPtr, 16); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| ptr++; |
| table->table[table->dwNumEntries].dwRemotePort = |
| htons ((unsigned short)strtoul(ptr, &endPtr, 16)); |
| ptr = endPtr; |
| } |
| if (ptr && *ptr) { |
| DWORD state = strtoul(ptr, &endPtr, 16); |
| |
| table->table[table->dwNumEntries].dwState = |
| TCPStateToMIBState (state); |
| ptr = endPtr; |
| } |
| table->dwNumEntries++; |
| } |
| } |
| fclose(fp); |
| #endif |
| |
| return NO_ERROR; |
| } |