1379 lines
50 KiB
C
1379 lines
50 KiB
C
/*
|
|
* nsiproxy.sys ipv4 and ipv6 modules
|
|
*
|
|
* Copyright 2003, 2006, 2011 Juan Lang
|
|
* Copyright 2021 Huw Davies
|
|
*
|
|
* 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
|
|
*/
|
|
#include "config.h"
|
|
#include <stdarg.h>
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NET_ROUTE_H
|
|
#include <net/route.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_SYSCTL_H
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IP_H
|
|
#include <netinet/ip.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_IP_VAR_H
|
|
#include <netinet/ip_var.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_ICMP_VAR_H
|
|
#include <netinet/icmp_var.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IF_ETHER_H
|
|
#include <netinet/if_ether.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NET_IF_ARP_H
|
|
#include <net/if_arp.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NET_IF_DL_H
|
|
#include <net/if_dl.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_IFADDRS_H
|
|
#include <ifaddrs.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winternl.h"
|
|
#include "winioctl.h"
|
|
#define USE_WS_PREFIX
|
|
#include "winsock2.h"
|
|
#include "ws2ipdef.h"
|
|
#include "nldef.h"
|
|
#include "ifdef.h"
|
|
#include "ipmib.h"
|
|
#include "netiodef.h"
|
|
#include "wine/heap.h"
|
|
#include "wine/nsi.h"
|
|
#include "wine/debug.h"
|
|
|
|
#include "nsiproxy_private.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(nsi);
|
|
|
|
static inline DWORD nsi_popcount( DWORD m )
|
|
{
|
|
#ifdef HAVE___BUILTIN_POPCOUNT
|
|
return __builtin_popcount( m );
|
|
#else
|
|
m -= m >> 1 & 0x55555555;
|
|
m = (m & 0x33333333) + (m >> 2 & 0x33333333);
|
|
return ((m + (m >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
|
|
#endif
|
|
}
|
|
|
|
static DWORD mask_v4_to_prefix( struct in_addr *addr )
|
|
{
|
|
return nsi_popcount( addr->s_addr );
|
|
}
|
|
|
|
static DWORD mask_v6_to_prefix( struct in6_addr *addr )
|
|
{
|
|
DWORD ret;
|
|
|
|
ret = nsi_popcount( *(DWORD *)addr->s6_addr );
|
|
ret += nsi_popcount( *(DWORD *)(addr->s6_addr + 4) );
|
|
ret += nsi_popcount( *(DWORD *)(addr->s6_addr + 8) );
|
|
ret += nsi_popcount( *(DWORD *)(addr->s6_addr + 12) );
|
|
return ret;
|
|
}
|
|
|
|
static ULONG64 get_boot_time( void )
|
|
{
|
|
SYSTEM_TIMEOFDAY_INFORMATION ti;
|
|
|
|
NtQuerySystemInformation( SystemTimeOfDayInformation, &ti, sizeof(ti), NULL );
|
|
return ti.BootTime.QuadPart;
|
|
}
|
|
|
|
#if __linux__
|
|
static NTSTATUS read_sysctl_int( const char *file, int *val )
|
|
{
|
|
FILE *fp;
|
|
char buf[128], *endptr = buf;
|
|
|
|
fp = fopen( file, "r" );
|
|
if (!fp) return STATUS_NOT_SUPPORTED;
|
|
|
|
if (fgets( buf, sizeof(buf), fp ))
|
|
*val = strtol( buf, &endptr, 10 );
|
|
|
|
fclose( fp );
|
|
return (endptr == buf) ? STATUS_NOT_SUPPORTED : STATUS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
static NTSTATUS ip_cmpt_get_all_parameters( DWORD fam, const DWORD *key, DWORD key_size,
|
|
struct nsi_ip_cmpt_rw *rw_data, DWORD rw_size,
|
|
struct nsi_ip_cmpt_dynamic *dynamic_data, DWORD dynamic_size,
|
|
void *static_data, DWORD static_size )
|
|
{
|
|
const NPI_MODULEID *ip_mod = (fam == AF_INET) ? &NPI_MS_IPV4_MODULEID : &NPI_MS_IPV6_MODULEID;
|
|
struct nsi_ip_cmpt_rw rw;
|
|
struct nsi_ip_cmpt_dynamic dyn;
|
|
DWORD count;
|
|
|
|
memset( &rw, 0, sizeof(rw) );
|
|
memset( &dyn, 0, sizeof(dyn) );
|
|
|
|
if (*key != 1) return STATUS_NOT_SUPPORTED;
|
|
|
|
#if __linux__
|
|
{
|
|
const char *fwd = (fam == AF_INET) ? "/proc/sys/net/ipv4/conf/default/forwarding" :
|
|
"/proc/sys/net/ipv6/conf/default/forwarding";
|
|
const char *ttl = (fam == AF_INET) ? "/proc/sys/net/ipv4/ip_default_ttl" :
|
|
"/proc/sys/net/ipv6/conf/default/hop_limit";
|
|
int value;
|
|
|
|
if (!read_sysctl_int( fwd, &value )) rw.not_forwarding = !value;
|
|
if (!read_sysctl_int( ttl, &value )) rw.default_ttl = value;
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H)
|
|
{
|
|
int fwd_4[] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_FORWARDING };
|
|
int fwd_6[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_FORWARDING };
|
|
int ttl_4[] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_DEFTTL };
|
|
int ttl_6[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_DEFHLIM };
|
|
int value;
|
|
size_t needed;
|
|
|
|
needed = sizeof(value);
|
|
if (!sysctl( (fam == AF_INET) ? fwd_4 : fwd_6, ARRAY_SIZE(fwd_4), &value, &needed, NULL, 0 ))
|
|
rw.not_forwarding = value;
|
|
|
|
needed = sizeof(value);
|
|
if (!sysctl( (fam == AF_INET) ? ttl_4 : ttl_6, ARRAY_SIZE(ttl_4), &value, &needed, NULL, 0 ))
|
|
rw.default_ttl = value;
|
|
}
|
|
#else
|
|
FIXME( "forwarding and default ttl not implemented\n" );
|
|
#endif
|
|
|
|
count = 0;
|
|
if (!nsi_enumerate_all( 1, 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, NULL, 0, NULL, 0,
|
|
NULL, 0, NULL, 0, &count ))
|
|
dyn.num_ifs = count;
|
|
|
|
count = 0;
|
|
if (!nsi_enumerate_all( 1, 0, ip_mod, NSI_IP_FORWARD_TABLE, NULL, 0, NULL, 0,
|
|
NULL, 0, NULL, 0, &count ))
|
|
dyn.num_routes = count;
|
|
|
|
count = 0;
|
|
if (!nsi_enumerate_all( 1, 0, ip_mod, NSI_IP_UNICAST_TABLE, NULL, 0, NULL, 0,
|
|
NULL, 0, NULL, 0, &count ))
|
|
dyn.num_addrs = count;
|
|
|
|
if (rw_data) *rw_data = rw;
|
|
if (dynamic_data) *dynamic_data = dyn;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS ipv4_cmpt_get_all_parameters( const void *key, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size, void *static_data, DWORD static_size )
|
|
{
|
|
TRACE( "%p %d %p %d %p %d %p %d\n", key, key_size, rw_data, rw_size, dynamic_data, dynamic_size,
|
|
static_data, static_size );
|
|
return ip_cmpt_get_all_parameters( AF_INET, key, key_size, rw_data, rw_size,
|
|
dynamic_data, dynamic_size, static_data, static_size );
|
|
}
|
|
|
|
static NTSTATUS ipv6_cmpt_get_all_parameters( const void *key, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size, void *static_data, DWORD static_size )
|
|
{
|
|
TRACE( "%p %d %p %d %p %d %p %d\n", key, key_size, rw_data, rw_size, dynamic_data, dynamic_size,
|
|
static_data, static_size );
|
|
return ip_cmpt_get_all_parameters( AF_INET6, key, key_size, rw_data, rw_size,
|
|
dynamic_data, dynamic_size, static_data, static_size );
|
|
}
|
|
|
|
static NTSTATUS ipv4_icmpstats_get_all_parameters( const void *key, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size, void *static_data, DWORD static_size )
|
|
{
|
|
struct nsi_ip_icmpstats_dynamic dyn;
|
|
|
|
TRACE( "%p %d %p %d %p %d %p %d\n", key, key_size, rw_data, rw_size, dynamic_data, dynamic_size,
|
|
static_data, static_size );
|
|
|
|
memset( &dyn, 0, sizeof(dyn) );
|
|
|
|
#ifdef __linux__
|
|
{
|
|
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
|
static const char hdr[] = "Icmp:";
|
|
char buf[512], *ptr;
|
|
FILE *fp;
|
|
|
|
if (!(fp = fopen( "/proc/net/snmp", "r" ))) return STATUS_NOT_SUPPORTED;
|
|
|
|
while ((ptr = fgets( buf, sizeof(buf), fp )))
|
|
{
|
|
if (_strnicmp( buf, hdr, sizeof(hdr) - 1 )) continue;
|
|
/* last line was a header, get another */
|
|
if (!(ptr = fgets( buf, sizeof(buf), fp ))) break;
|
|
if (!_strnicmp( buf, hdr, sizeof(hdr) - 1 ))
|
|
{
|
|
ptr += sizeof(hdr);
|
|
sscanf( ptr, "%u %u %*u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
|
|
&dyn.in_msgs,
|
|
&dyn.in_errors,
|
|
&dyn.in_type_counts[ICMP4_DST_UNREACH],
|
|
&dyn.in_type_counts[ICMP4_TIME_EXCEEDED],
|
|
&dyn.in_type_counts[ICMP4_PARAM_PROB],
|
|
&dyn.in_type_counts[ICMP4_SOURCE_QUENCH],
|
|
&dyn.in_type_counts[ICMP4_REDIRECT],
|
|
&dyn.in_type_counts[ICMP4_ECHO_REQUEST],
|
|
&dyn.in_type_counts[ICMP4_ECHO_REPLY],
|
|
&dyn.in_type_counts[ICMP4_TIMESTAMP_REQUEST],
|
|
&dyn.in_type_counts[ICMP4_TIMESTAMP_REPLY],
|
|
&dyn.in_type_counts[ICMP4_MASK_REQUEST],
|
|
&dyn.in_type_counts[ICMP4_MASK_REPLY],
|
|
&dyn.out_msgs,
|
|
&dyn.out_errors,
|
|
&dyn.out_type_counts[ICMP4_DST_UNREACH],
|
|
&dyn.out_type_counts[ICMP4_TIME_EXCEEDED],
|
|
&dyn.out_type_counts[ICMP4_PARAM_PROB],
|
|
&dyn.out_type_counts[ICMP4_SOURCE_QUENCH],
|
|
&dyn.out_type_counts[ICMP4_REDIRECT],
|
|
&dyn.out_type_counts[ICMP4_ECHO_REQUEST],
|
|
&dyn.out_type_counts[ICMP4_ECHO_REPLY],
|
|
&dyn.out_type_counts[ICMP4_TIMESTAMP_REQUEST],
|
|
&dyn.out_type_counts[ICMP4_TIMESTAMP_REPLY],
|
|
&dyn.out_type_counts[ICMP4_MASK_REQUEST],
|
|
&dyn.out_type_counts[ICMP4_MASK_REPLY] );
|
|
status = STATUS_SUCCESS;
|
|
if (dynamic_data) *(struct nsi_ip_icmpstats_dynamic *)dynamic_data = dyn;
|
|
break;
|
|
}
|
|
}
|
|
fclose( fp );
|
|
return status;
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(ICMPCTL_STATS)
|
|
{
|
|
int mib[] = { CTL_NET, PF_INET, IPPROTO_ICMP, ICMPCTL_STATS };
|
|
struct icmpstat icmp_stat;
|
|
size_t needed = sizeof(icmp_stat);
|
|
int i;
|
|
|
|
if (sysctl( mib, ARRAY_SIZE(mib), &icmp_stat, &needed, NULL, 0 ) == -1) return STATUS_NOT_SUPPORTED;
|
|
|
|
dyn.in_msgs = icmp_stat.icps_badcode + icmp_stat.icps_checksum + icmp_stat.icps_tooshort + icmp_stat.icps_badlen;
|
|
for (i = 0; i <= ICMP_MAXTYPE; i++)
|
|
dyn.in_msgs += icmp_stat.icps_inhist[i];
|
|
|
|
dyn.in_errors = icmp_stat.icps_badcode + icmp_stat.icps_tooshort + icmp_stat.icps_checksum + icmp_stat.icps_badlen;
|
|
|
|
dyn.in_type_counts[ICMP4_DST_UNREACH] = icmp_stat.icps_inhist[ICMP_UNREACH];
|
|
dyn.in_type_counts[ICMP4_TIME_EXCEEDED] = icmp_stat.icps_inhist[ICMP_TIMXCEED];
|
|
dyn.in_type_counts[ICMP4_PARAM_PROB] = icmp_stat.icps_inhist[ICMP_PARAMPROB];
|
|
dyn.in_type_counts[ICMP4_SOURCE_QUENCH] = icmp_stat.icps_inhist[ICMP_SOURCEQUENCH];
|
|
dyn.in_type_counts[ICMP4_REDIRECT] = icmp_stat.icps_inhist[ICMP_REDIRECT];
|
|
dyn.in_type_counts[ICMP4_ECHO_REQUEST] = icmp_stat.icps_inhist[ICMP_ECHO];
|
|
dyn.in_type_counts[ICMP4_ECHO_REPLY] = icmp_stat.icps_inhist[ICMP_ECHOREPLY];
|
|
dyn.in_type_counts[ICMP4_TIMESTAMP_REQUEST] = icmp_stat.icps_inhist[ICMP_TSTAMP];
|
|
dyn.in_type_counts[ICMP4_TIMESTAMP_REPLY] = icmp_stat.icps_inhist[ICMP_TSTAMPREPLY];
|
|
dyn.in_type_counts[ICMP4_MASK_REQUEST] = icmp_stat.icps_inhist[ICMP_MASKREQ];
|
|
dyn.in_type_counts[ICMP4_MASK_REPLY] = icmp_stat.icps_inhist[ICMP_MASKREPLY];
|
|
|
|
dyn.out_msgs = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;
|
|
for (i = 0; i <= ICMP_MAXTYPE; i++)
|
|
dyn.out_msgs += icmp_stat.icps_outhist[i];
|
|
|
|
dyn.out_errors = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;
|
|
|
|
dyn.out_type_counts[ICMP4_DST_UNREACH] = icmp_stat.icps_outhist[ICMP_UNREACH];
|
|
dyn.out_type_counts[ICMP4_TIME_EXCEEDED] = icmp_stat.icps_outhist[ICMP_TIMXCEED];
|
|
dyn.out_type_counts[ICMP4_PARAM_PROB] = icmp_stat.icps_outhist[ICMP_PARAMPROB];
|
|
dyn.out_type_counts[ICMP4_SOURCE_QUENCH] = icmp_stat.icps_outhist[ICMP_SOURCEQUENCH];
|
|
dyn.out_type_counts[ICMP4_REDIRECT] = icmp_stat.icps_outhist[ICMP_REDIRECT];
|
|
dyn.out_type_counts[ICMP4_ECHO_REQUEST] = icmp_stat.icps_outhist[ICMP_ECHO];
|
|
dyn.out_type_counts[ICMP4_ECHO_REPLY] = icmp_stat.icps_outhist[ICMP_ECHOREPLY];
|
|
dyn.out_type_counts[ICMP4_TIMESTAMP_REQUEST] = icmp_stat.icps_outhist[ICMP_TSTAMP];
|
|
dyn.out_type_counts[ICMP4_TIMESTAMP_REPLY] = icmp_stat.icps_outhist[ICMP_TSTAMPREPLY];
|
|
dyn.out_type_counts[ICMP4_MASK_REQUEST] = icmp_stat.icps_outhist[ICMP_MASKREQ];
|
|
dyn.out_type_counts[ICMP4_MASK_REPLY] = icmp_stat.icps_outhist[ICMP_MASKREPLY];
|
|
if (dynamic_data) *(struct nsi_ip_icmpstats_dynamic *)dynamic_data = dyn;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
static NTSTATUS ipv6_icmpstats_get_all_parameters( const void *key, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size, void *static_data, DWORD static_size )
|
|
{
|
|
struct nsi_ip_icmpstats_dynamic dyn;
|
|
|
|
TRACE( "%p %d %p %d %p %d %p %d\n", key, key_size, rw_data, rw_size, dynamic_data, dynamic_size,
|
|
static_data, static_size );
|
|
|
|
memset( &dyn, 0, sizeof(dyn) );
|
|
|
|
#ifdef __linux__
|
|
{
|
|
struct data
|
|
{
|
|
const char *name;
|
|
DWORD pos;
|
|
};
|
|
static const struct data in_list[] =
|
|
{
|
|
{ "Icmp6InDestUnreachs", ICMP6_DST_UNREACH },
|
|
{ "Icmp6InPktTooBigs", ICMP6_PACKET_TOO_BIG },
|
|
{ "Icmp6InTimeExcds", ICMP6_TIME_EXCEEDED },
|
|
{ "Icmp6InParmProblems", ICMP6_PARAM_PROB },
|
|
{ "Icmp6InEchos", ICMP6_ECHO_REQUEST },
|
|
{ "Icmp6InEchoReplies", ICMP6_ECHO_REPLY },
|
|
{ "Icmp6InGroupMembQueries", ICMP6_MEMBERSHIP_QUERY },
|
|
{ "Icmp6InGroupMembResponses", ICMP6_MEMBERSHIP_REPORT },
|
|
{ "Icmp6InGroupMembReductions", ICMP6_MEMBERSHIP_REDUCTION },
|
|
{ "Icmp6InRouterSolicits", ND_ROUTER_SOLICIT },
|
|
{ "Icmp6InRouterAdvertisements", ND_ROUTER_ADVERT },
|
|
{ "Icmp6InNeighborSolicits", ND_NEIGHBOR_SOLICIT },
|
|
{ "Icmp6InNeighborAdvertisements", ND_NEIGHBOR_ADVERT },
|
|
{ "Icmp6InRedirects", ND_REDIRECT },
|
|
{ "Icmp6InMLDv2Reports", ICMP6_V2_MEMBERSHIP_REPORT },
|
|
};
|
|
static const struct data out_list[] =
|
|
{
|
|
{ "Icmp6OutDestUnreachs", ICMP6_DST_UNREACH },
|
|
{ "Icmp6OutPktTooBigs", ICMP6_PACKET_TOO_BIG },
|
|
{ "Icmp6OutTimeExcds", ICMP6_TIME_EXCEEDED },
|
|
{ "Icmp6OutParmProblems", ICMP6_PARAM_PROB },
|
|
{ "Icmp6OutEchos", ICMP6_ECHO_REQUEST },
|
|
{ "Icmp6OutEchoReplies", ICMP6_ECHO_REPLY },
|
|
{ "Icmp6OutGroupMembQueries", ICMP6_MEMBERSHIP_QUERY },
|
|
{ "Icmp6OutGroupMembResponses", ICMP6_MEMBERSHIP_REPORT },
|
|
{ "Icmp6OutGroupMembReductions", ICMP6_MEMBERSHIP_REDUCTION },
|
|
{ "Icmp6OutRouterSolicits", ND_ROUTER_SOLICIT },
|
|
{ "Icmp6OutRouterAdvertisements", ND_ROUTER_ADVERT },
|
|
{ "Icmp6OutNeighborSolicits", ND_NEIGHBOR_SOLICIT },
|
|
{ "Icmp6OutNeighborAdvertisements", ND_NEIGHBOR_ADVERT },
|
|
{ "Icmp6OutRedirects", ND_REDIRECT },
|
|
{ "Icmp6OutMLDv2Reports", ICMP6_V2_MEMBERSHIP_REPORT },
|
|
};
|
|
char buf[512], *ptr, *value;
|
|
DWORD res, i;
|
|
FILE *fp;
|
|
|
|
if (!(fp = fopen( "/proc/net/snmp6", "r" ))) return STATUS_NOT_SUPPORTED;
|
|
|
|
while ((ptr = fgets( buf, sizeof(buf), fp )))
|
|
{
|
|
if (!(value = strchr( buf, ' ' ))) continue;
|
|
|
|
/* terminate the valuename */
|
|
ptr = value - 1;
|
|
*(ptr + 1) = '\0';
|
|
|
|
/* and strip leading spaces from value */
|
|
value += 1;
|
|
while (*value == ' ') value++;
|
|
if ((ptr = strchr( value, '\n' ))) *ptr='\0';
|
|
|
|
if (!_strnicmp( buf, "Icmp6InMsgs", -1 ))
|
|
{
|
|
if (sscanf( value, "%d", &res )) dyn.in_msgs = res;
|
|
continue;
|
|
}
|
|
|
|
if (!_strnicmp( buf, "Icmp6InErrors", -1 ))
|
|
{
|
|
if (sscanf( value, "%d", &res )) dyn.in_errors = res;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(in_list); i++)
|
|
{
|
|
if (!_strnicmp( buf, in_list[i].name, -1 ))
|
|
{
|
|
if (sscanf( value, "%d", &res ))
|
|
dyn.in_type_counts[in_list[i].pos] = res;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!_strnicmp( buf, "Icmp6OutMsgs", -1 ))
|
|
{
|
|
if (sscanf( value, "%d", &res )) dyn.out_msgs = res;
|
|
continue;
|
|
}
|
|
|
|
if (!_strnicmp( buf, "Icmp6OutErrors", -1 ))
|
|
{
|
|
if (sscanf( value, "%d", &res )) dyn.out_errors = res;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(out_list); i++)
|
|
{
|
|
if (!_strnicmp( buf, out_list[i].name, -1 ))
|
|
{
|
|
if (sscanf( value, "%d", &res ))
|
|
dyn.out_type_counts[out_list[i].pos] = res;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fclose( fp );
|
|
if (dynamic_data) *(struct nsi_ip_icmpstats_dynamic *)dynamic_data = dyn;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
static NTSTATUS ipv4_ipstats_get_all_parameters( const void *key, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size, void *static_data, DWORD static_size )
|
|
{
|
|
struct nsi_ip_ipstats_dynamic dyn;
|
|
struct nsi_ip_ipstats_static stat;
|
|
|
|
TRACE( "%p %d %p %d %p %d %p %d\n", key, key_size, rw_data, rw_size, dynamic_data, dynamic_size,
|
|
static_data, static_size );
|
|
|
|
memset( &dyn, 0, sizeof(dyn) );
|
|
memset( &stat, 0, sizeof(stat) );
|
|
|
|
#ifdef __linux__
|
|
{
|
|
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
|
static const char hdr[] = "Ip:";
|
|
char buf[512], *ptr;
|
|
FILE *fp;
|
|
|
|
if (!(fp = fopen( "/proc/net/snmp", "r" ))) return STATUS_NOT_SUPPORTED;
|
|
|
|
while ((ptr = fgets( buf, sizeof(buf), fp )))
|
|
{
|
|
if (_strnicmp( buf, hdr, sizeof(hdr) - 1 )) continue;
|
|
/* last line was a header, get another */
|
|
if (!(ptr = fgets( buf, sizeof(buf), fp ))) break;
|
|
if (!_strnicmp( buf, hdr, sizeof(hdr) - 1 ))
|
|
{
|
|
DWORD in_recv, in_hdr_errs, fwd_dgrams, in_delivers, out_reqs;
|
|
ptr += sizeof(hdr);
|
|
sscanf( ptr, "%*u %*u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
|
|
&in_recv,
|
|
&in_hdr_errs,
|
|
&dyn.in_addr_errs,
|
|
&fwd_dgrams,
|
|
&dyn.in_unk_protos,
|
|
&dyn.in_discards,
|
|
&in_delivers,
|
|
&out_reqs,
|
|
&dyn.out_discards,
|
|
&dyn.out_no_routes,
|
|
&stat.reasm_timeout,
|
|
&dyn.reasm_reqds,
|
|
&dyn.reasm_oks,
|
|
&dyn.reasm_fails,
|
|
&dyn.frag_oks,
|
|
&dyn.frag_fails,
|
|
&dyn.frag_creates );
|
|
/* no routingDiscards */
|
|
dyn.in_recv = in_recv;
|
|
dyn.in_hdr_errs = in_hdr_errs;
|
|
dyn.fwd_dgrams = fwd_dgrams;
|
|
dyn.in_delivers = in_delivers;
|
|
dyn.out_reqs = out_reqs;
|
|
if (dynamic_data) *(struct nsi_ip_ipstats_dynamic *)dynamic_data = dyn;
|
|
if (static_data) *(struct nsi_ip_ipstats_static *)static_data = stat;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
fclose( fp );
|
|
return status;
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(IPCTL_STATS) && (defined(HAVE_STRUCT_IPSTAT_IPS_TOTAL) || defined(HAVE_STRUCT_IP_STATS_IPS_TOTAL))
|
|
{
|
|
int mib[] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS };
|
|
#if defined(HAVE_STRUCT_IPSTAT_IPS_TOTAL)
|
|
struct ipstat ip_stat;
|
|
#elif defined(HAVE_STRUCT_IP_STATS_IPS_TOTAL)
|
|
struct ip_stats ip_stat;
|
|
#endif
|
|
size_t needed;
|
|
|
|
needed = sizeof(ip_stat);
|
|
if (sysctl( mib, ARRAY_SIZE(mib), &ip_stat, &needed, NULL, 0 ) == -1) return STATUS_NOT_SUPPORTED;
|
|
|
|
dyn.in_recv = ip_stat.ips_total;
|
|
dyn.in_hdr_errs = ip_stat.ips_badhlen + ip_stat.ips_badsum + ip_stat.ips_tooshort + ip_stat.ips_badlen +
|
|
ip_stat.ips_badvers + ip_stat.ips_badoptions;
|
|
/* ips_badaddr also includes outgoing packets with a bad address, but we can't account for that right now */
|
|
dyn.in_addr_errs = ip_stat.ips_cantforward + ip_stat.ips_badaddr + ip_stat.ips_notmember;
|
|
dyn.fwd_dgrams = ip_stat.ips_forward;
|
|
dyn.in_unk_protos = ip_stat.ips_noproto;
|
|
dyn.in_discards = ip_stat.ips_fragdropped;
|
|
dyn.in_delivers = ip_stat.ips_delivered;
|
|
dyn.out_reqs = ip_stat.ips_localout;
|
|
dyn.out_discards = ip_stat.ips_odropped;
|
|
dyn.out_no_routes = ip_stat.ips_noroute;
|
|
stat.reasm_timeout = ip_stat.ips_fragtimeout;
|
|
dyn.reasm_reqds = ip_stat.ips_fragments;
|
|
dyn.reasm_oks = ip_stat.ips_reassembled;
|
|
dyn.reasm_fails = ip_stat.ips_fragments - ip_stat.ips_reassembled;
|
|
dyn.frag_oks = ip_stat.ips_fragmented;
|
|
dyn.frag_fails = ip_stat.ips_cantfrag;
|
|
dyn.frag_creates = ip_stat.ips_ofragments;
|
|
|
|
if (dynamic_data) *(struct nsi_ip_ipstats_dynamic *)dynamic_data = dyn;
|
|
if (static_data) *(struct nsi_ip_ipstats_static *)static_data = stat;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
static NTSTATUS ipv6_ipstats_get_all_parameters( const void *key, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size, void *static_data, DWORD static_size )
|
|
{
|
|
struct nsi_ip_ipstats_dynamic dyn;
|
|
struct nsi_ip_ipstats_static stat;
|
|
|
|
memset( &dyn, 0, sizeof(dyn) );
|
|
memset( &stat, 0, sizeof(stat) );
|
|
|
|
#ifdef __linux__
|
|
{
|
|
struct
|
|
{
|
|
const char *name;
|
|
void *elem;
|
|
int size;
|
|
} ipstatlist[] =
|
|
{
|
|
#define X(x) &x, sizeof(x)
|
|
{ "Ip6InReceives", X( dyn.in_recv ) },
|
|
{ "Ip6InHdrErrors", X( dyn.in_hdr_errs ) },
|
|
{ "Ip6InAddrErrors", X( dyn.in_addr_errs ) },
|
|
{ "Ip6OutForwDatagrams", X( dyn.fwd_dgrams ) },
|
|
{ "Ip6InUnknownProtos", X( dyn.in_unk_protos ) },
|
|
{ "Ip6InDiscards", X( dyn.in_discards ) },
|
|
{ "Ip6InDelivers", X( dyn.in_delivers ) },
|
|
{ "Ip6OutRequests", X( dyn.out_reqs ) },
|
|
{ "Ip6OutDiscards", X( dyn.out_discards ) },
|
|
{ "Ip6OutNoRoutes", X( dyn.out_no_routes ) },
|
|
{ "Ip6ReasmTimeout", X( stat.reasm_timeout ) },
|
|
{ "Ip6ReasmReqds", X( dyn.reasm_reqds ) },
|
|
{ "Ip6ReasmOKs", X( dyn.reasm_oks ) },
|
|
{ "Ip6ReasmFails", X( dyn.reasm_fails ) },
|
|
{ "Ip6FragOKs", X( dyn.frag_oks ) },
|
|
{ "Ip6FragFails", X( dyn.frag_fails ) },
|
|
{ "Ip6FragCreates", X( dyn.frag_creates ) },
|
|
/* no routingDiscards */
|
|
#undef X
|
|
};
|
|
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
|
char buf[512], *ptr, *value;
|
|
DWORD i;
|
|
FILE *fp;
|
|
|
|
if (!(fp = fopen( "/proc/net/snmp6", "r" ))) return STATUS_NOT_SUPPORTED;
|
|
|
|
while ((ptr = fgets( buf, sizeof(buf), fp )))
|
|
{
|
|
if (!(value = strchr( buf, ' ' ))) continue;
|
|
/* terminate the valuename */
|
|
*value++ = '\0';
|
|
/* and strip leading spaces from value */
|
|
while (*value == ' ') value++;
|
|
if ((ptr = strchr( value, '\n' ))) *ptr = '\0';
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ipstatlist); i++)
|
|
if (!_strnicmp( buf, ipstatlist[i].name, -1 ))
|
|
{
|
|
if (ipstatlist[i].size == sizeof(long))
|
|
*(long *)ipstatlist[i].elem = strtoul( value, NULL, 10 );
|
|
else
|
|
*(long long *)ipstatlist[i].elem = strtoull( value, NULL, 10 );
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
fclose( fp );
|
|
if (dynamic_data) *(struct nsi_ip_ipstats_dynamic *)dynamic_data = dyn;
|
|
if (static_data) *(struct nsi_ip_ipstats_static *)static_data = stat;
|
|
return status;
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
static void unicast_fill_entry( struct ifaddrs *entry, void *key, struct nsi_ip_unicast_rw *rw,
|
|
struct nsi_ip_unicast_dynamic *dyn, struct nsi_ip_unicast_static *stat )
|
|
{
|
|
struct nsi_ipv6_unicast_key placeholder, *key6 = key;
|
|
struct nsi_ipv4_unicast_key *key4 = key;
|
|
DWORD scope_id = 0;
|
|
|
|
if (!key)
|
|
{
|
|
key6 = &placeholder;
|
|
key4 = (struct nsi_ipv4_unicast_key *)&placeholder;
|
|
}
|
|
|
|
convert_unix_name_to_luid( entry->ifa_name, &key6->luid );
|
|
|
|
if (entry->ifa_addr->sa_family == AF_INET)
|
|
{
|
|
memcpy( &key4->addr, &((struct sockaddr_in *)entry->ifa_addr)->sin_addr, sizeof(key4->addr) );
|
|
key4->pad = 0;
|
|
}
|
|
else if (entry->ifa_addr->sa_family == AF_INET6)
|
|
{
|
|
memcpy( &key6->addr, &((struct sockaddr_in6 *)entry->ifa_addr)->sin6_addr, sizeof(key6->addr) );
|
|
scope_id = ((struct sockaddr_in6 *)entry->ifa_addr)->sin6_scope_id;
|
|
}
|
|
|
|
if (rw)
|
|
{
|
|
rw->preferred_lifetime = 60000;
|
|
rw->valid_lifetime = 60000;
|
|
|
|
if (key6->luid.Info.IfType != IF_TYPE_SOFTWARE_LOOPBACK)
|
|
{
|
|
rw->prefix_origin = IpPrefixOriginDhcp;
|
|
rw->suffix_origin = IpSuffixOriginDhcp;
|
|
}
|
|
else
|
|
{
|
|
rw->prefix_origin = IpPrefixOriginManual;
|
|
rw->suffix_origin = IpSuffixOriginManual;
|
|
}
|
|
if (entry->ifa_netmask && entry->ifa_netmask->sa_family == AF_INET)
|
|
rw->on_link_prefix = mask_v4_to_prefix( &((struct sockaddr_in *)entry->ifa_netmask)->sin_addr );
|
|
else if (entry->ifa_netmask && entry->ifa_netmask->sa_family == AF_INET6)
|
|
rw->on_link_prefix = mask_v6_to_prefix( &((struct sockaddr_in6 *)entry->ifa_netmask)->sin6_addr );
|
|
else rw->on_link_prefix = 0;
|
|
rw->unk[0] = 0;
|
|
rw->unk[1] = 0;
|
|
}
|
|
|
|
if (dyn)
|
|
{
|
|
dyn->scope_id = scope_id;
|
|
dyn->dad_state = IpDadStatePreferred;
|
|
}
|
|
|
|
if (stat) stat->creation_time = get_boot_time();
|
|
}
|
|
|
|
static NTSTATUS ip_unicast_enumerate_all( int family, void *key_data, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size,
|
|
void *static_data, DWORD static_size, DWORD_PTR *count )
|
|
{
|
|
DWORD num = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
BOOL want_data = key_size || rw_size || dynamic_size || static_size;
|
|
struct ifaddrs *addrs, *entry;
|
|
|
|
TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data, key_size, rw_data, rw_size,
|
|
dynamic_data, dynamic_size, static_data, static_size, count );
|
|
|
|
if (getifaddrs( &addrs )) return STATUS_NO_MORE_ENTRIES;
|
|
|
|
for (entry = addrs; entry; entry = entry->ifa_next)
|
|
{
|
|
if (!entry->ifa_addr || entry->ifa_addr->sa_family != family) continue;
|
|
|
|
if (num < *count)
|
|
{
|
|
unicast_fill_entry( entry, key_data, rw_data, dynamic_data, static_data );
|
|
key_data = (BYTE *)key_data + key_size;
|
|
rw_data = (BYTE *)rw_data + rw_size;
|
|
dynamic_data = (BYTE *)dynamic_data + dynamic_size;
|
|
static_data = (BYTE *)static_data + static_size;
|
|
}
|
|
num++;
|
|
}
|
|
|
|
freeifaddrs( addrs );
|
|
|
|
if (!want_data || num <= *count) *count = num;
|
|
else status = STATUS_BUFFER_OVERFLOW;
|
|
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS ipv4_unicast_enumerate_all( void *key_data, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size,
|
|
void *static_data, DWORD static_size, DWORD_PTR *count )
|
|
{
|
|
return ip_unicast_enumerate_all( AF_INET, key_data, key_size, rw_data, rw_size,
|
|
dynamic_data, dynamic_size, static_data, static_size, count );
|
|
}
|
|
|
|
static NTSTATUS ipv6_unicast_enumerate_all( void *key_data, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size,
|
|
void *static_data, DWORD static_size, DWORD_PTR *count )
|
|
{
|
|
return ip_unicast_enumerate_all( AF_INET6, key_data, key_size, rw_data, rw_size,
|
|
dynamic_data, dynamic_size, static_data, static_size, count );
|
|
}
|
|
|
|
static NTSTATUS ip_unicast_get_all_parameters( const void *key, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size,
|
|
void *static_data, DWORD static_size )
|
|
{
|
|
int family = (key_size == sizeof(struct nsi_ipv4_unicast_key)) ? AF_INET : AF_INET6;
|
|
NTSTATUS status = STATUS_NOT_FOUND;
|
|
const struct nsi_ipv6_unicast_key *key6 = key;
|
|
const struct nsi_ipv4_unicast_key *key4 = key;
|
|
struct ifaddrs *addrs, *entry;
|
|
const char *unix_name;
|
|
|
|
TRACE( "%p %d %p %d %p %d %p %d\n", key, key_size, rw_data, rw_size, dynamic_data, dynamic_size,
|
|
static_data, static_size );
|
|
|
|
if (!convert_luid_to_unix_name( &key6->luid, &unix_name )) return STATUS_NOT_FOUND;
|
|
|
|
if (getifaddrs( &addrs )) return STATUS_NO_MORE_ENTRIES;
|
|
|
|
for (entry = addrs; entry; entry = entry->ifa_next)
|
|
{
|
|
if (!entry->ifa_addr || entry->ifa_addr->sa_family != family) continue;
|
|
if (strcmp( entry->ifa_name, unix_name )) continue;
|
|
|
|
if (family == AF_INET &&
|
|
memcmp( &key4->addr, &((struct sockaddr_in *)entry->ifa_addr)->sin_addr, sizeof(key4->addr) )) continue;
|
|
if (family == AF_INET6 &&
|
|
memcmp( &key6->addr, &((struct sockaddr_in6 *)entry->ifa_addr)->sin6_addr, sizeof(key6->addr) )) continue;
|
|
|
|
unicast_fill_entry( entry, NULL, rw_data, dynamic_data, static_data );
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
freeifaddrs( addrs );
|
|
return status;
|
|
}
|
|
|
|
struct ipv4_neighbour_data
|
|
{
|
|
NET_LUID luid;
|
|
DWORD if_index;
|
|
struct in_addr addr;
|
|
BYTE phys_addr[IF_MAX_PHYS_ADDRESS_LENGTH];
|
|
DWORD state;
|
|
USHORT phys_addr_len;
|
|
BOOL is_router;
|
|
BOOL is_unreachable;
|
|
};
|
|
|
|
static void ipv4_neighbour_fill_entry( struct ipv4_neighbour_data *entry, struct nsi_ipv4_neighbour_key *key, struct nsi_ip_neighbour_rw *rw,
|
|
struct nsi_ip_neighbour_dynamic *dyn, void *stat )
|
|
{
|
|
USHORT phys_addr_len = entry->phys_addr_len > sizeof(rw->phys_addr) ? 0 : entry->phys_addr_len;
|
|
|
|
if (key)
|
|
{
|
|
key->luid = entry->luid;
|
|
key->luid2 = entry->luid;
|
|
key->addr.WS_s_addr = entry->addr.s_addr;
|
|
key->pad = 0;
|
|
}
|
|
|
|
if (rw)
|
|
{
|
|
memcpy( rw->phys_addr, entry->phys_addr, phys_addr_len );
|
|
memset( rw->phys_addr + entry->phys_addr_len, 0, sizeof(rw->phys_addr) - phys_addr_len );
|
|
}
|
|
|
|
if (dyn)
|
|
{
|
|
memset( dyn, 0, sizeof(*dyn) );
|
|
dyn->state = entry->state;
|
|
dyn->flags.is_router = entry->is_router;
|
|
dyn->flags.is_unreachable = entry->is_unreachable;
|
|
dyn->phys_addr_len = phys_addr_len;
|
|
}
|
|
}
|
|
|
|
static NTSTATUS ipv4_neighbour_enumerate_all( void *key_data, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size,
|
|
void *static_data, DWORD static_size, DWORD_PTR *count )
|
|
{
|
|
DWORD num = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
BOOL want_data = key_size || rw_size || dynamic_size || static_size;
|
|
struct ipv4_neighbour_data entry;
|
|
|
|
TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data, key_size, rw_data, rw_size,
|
|
dynamic_data, dynamic_size, static_data, static_size, count );
|
|
|
|
#ifdef __linux__
|
|
{
|
|
char buf[512], *ptr;
|
|
DWORD atf_flags;
|
|
FILE *fp;
|
|
|
|
if (!(fp = fopen( "/proc/net/arp", "r" ))) return STATUS_NOT_SUPPORTED;
|
|
|
|
/* skip header line */
|
|
ptr = fgets( buf, sizeof(buf), fp );
|
|
while ((ptr = fgets( buf, sizeof(buf), fp )))
|
|
{
|
|
entry.addr.s_addr = inet_addr( ptr );
|
|
while (*ptr && !isspace( *ptr )) ptr++;
|
|
strtoul( ptr + 1, &ptr, 16 ); /* hw type (skip) */
|
|
atf_flags = strtoul( ptr + 1, &ptr, 16 );
|
|
|
|
if (atf_flags & ATF_PERM) entry.state = NlnsPermanent;
|
|
else if (atf_flags & ATF_COM) entry.state = NlnsReachable;
|
|
else entry.state = NlnsStale;
|
|
|
|
entry.is_router = 0;
|
|
entry.is_unreachable = !(atf_flags & (ATF_PERM | ATF_COM));
|
|
|
|
while (*ptr && isspace( *ptr )) ptr++;
|
|
entry.phys_addr_len = 0;
|
|
while (*ptr && !isspace( *ptr ))
|
|
{
|
|
if (entry.phys_addr_len >= sizeof(entry.phys_addr))
|
|
{
|
|
entry.phys_addr_len = 0;
|
|
while (*ptr && !isspace( *ptr )) ptr++;
|
|
break;
|
|
}
|
|
entry.phys_addr[entry.phys_addr_len++] = strtoul( ptr, &ptr, 16 );
|
|
if (*ptr) ptr++;
|
|
}
|
|
while (*ptr && isspace( *ptr )) ptr++;
|
|
while (*ptr && !isspace( *ptr )) ptr++; /* mask (skip) */
|
|
while (*ptr && isspace( *ptr )) ptr++;
|
|
|
|
if (!convert_unix_name_to_luid( ptr, &entry.luid )) continue;
|
|
if (!convert_luid_to_index( &entry.luid, &entry.if_index )) continue;
|
|
|
|
if (num < *count)
|
|
{
|
|
ipv4_neighbour_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data );
|
|
|
|
if (key_data) key_data = (BYTE *)key_data + key_size;
|
|
if (rw_data) rw_data = (BYTE *)rw_data + rw_size;
|
|
if (dynamic_data) dynamic_data = (BYTE *)dynamic_data + dynamic_size;
|
|
if (static_data) static_data = (BYTE *)static_data + static_size;
|
|
}
|
|
num++;
|
|
}
|
|
fclose( fp );
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H)
|
|
{
|
|
int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO }, sinarp_len;
|
|
size_t needed;
|
|
char *buf = NULL, *lim, *next;
|
|
struct rt_msghdr *rtm;
|
|
struct sockaddr_inarp *sinarp;
|
|
struct sockaddr_dl *sdl;
|
|
|
|
if (sysctl( mib, ARRAY_SIZE(mib), NULL, &needed, NULL, 0 ) == -1) return STATUS_NOT_SUPPORTED;
|
|
|
|
buf = heap_alloc( needed );
|
|
if (!buf) return STATUS_NO_MEMORY;
|
|
|
|
if (sysctl( mib, ARRAY_SIZE(mib), buf, &needed, NULL, 0 ) == -1)
|
|
{
|
|
heap_free( buf );
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
lim = buf + needed;
|
|
next = buf;
|
|
while (next < lim)
|
|
{
|
|
rtm = (struct rt_msghdr *)next;
|
|
sinarp = (struct sockaddr_inarp *)(rtm + 1);
|
|
if (sinarp->sin_len) sinarp_len = (sinarp->sin_len + sizeof(int)-1) & ~(sizeof(int)-1);
|
|
else sinarp_len = sizeof(int);
|
|
sdl = (struct sockaddr_dl *)((char *)sinarp + sinarp_len);
|
|
|
|
if (sdl->sdl_alen) /* arp entry */
|
|
{
|
|
entry.addr = sinarp->sin_addr;
|
|
entry.if_index = sdl->sdl_index;
|
|
if (!convert_index_to_luid( entry.if_index, &entry.luid )) break;
|
|
entry.phys_addr_len = min( 8, sdl->sdl_alen );
|
|
if (entry.phys_addr_len > sizeof(entry.phys_addr)) entry.phys_addr_len = 0;
|
|
memcpy( entry.phys_addr, &sdl->sdl_data[sdl->sdl_nlen], entry.phys_addr_len );
|
|
if (rtm->rtm_rmx.rmx_expire == 0) entry.state = NlnsPermanent;
|
|
else entry.state = NlnsReachable;
|
|
#ifdef SIN_ROUTER
|
|
entry.is_router = sinarp->sin_other & SIN_ROUTER;
|
|
#else
|
|
entry.is_router = 0;
|
|
#endif
|
|
entry.is_unreachable = 0; /* FIXME */
|
|
|
|
if (num < *count)
|
|
{
|
|
ipv4_neighbour_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data );
|
|
|
|
if (key_data) key_data = (BYTE *)key_data + key_size;
|
|
if (rw_data) rw_data = (BYTE *)rw_data + rw_size;
|
|
if (dynamic_data) dynamic_data = (BYTE *)dynamic_data + dynamic_size;
|
|
if (static_data) static_data = (BYTE *)static_data + static_size;
|
|
}
|
|
num++;
|
|
}
|
|
next += rtm->rtm_msglen;
|
|
}
|
|
heap_free( buf );
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
|
|
if (!want_data || num <= *count) *count = num;
|
|
else status = STATUS_BUFFER_OVERFLOW;
|
|
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS ipv6_neighbour_enumerate_all( void *key_data, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size,
|
|
void *static_data, DWORD static_size, DWORD_PTR *count )
|
|
{
|
|
FIXME( "not implemented\n" );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
struct ipv4_route_data
|
|
{
|
|
NET_LUID luid;
|
|
DWORD if_index;
|
|
struct in_addr prefix;
|
|
DWORD prefix_len;
|
|
struct in_addr next_hop;
|
|
DWORD metric;
|
|
DWORD protocol;
|
|
BYTE loopback;
|
|
};
|
|
|
|
static void ipv4_forward_fill_entry( struct ipv4_route_data *entry, struct nsi_ipv4_forward_key *key,
|
|
struct nsi_ip_forward_rw *rw, struct nsi_ipv4_forward_dynamic *dyn,
|
|
struct nsi_ip_forward_static *stat )
|
|
{
|
|
if (key)
|
|
{
|
|
key->unk = 0;
|
|
key->prefix.WS_s_addr = entry->prefix.s_addr;
|
|
key->prefix_len = entry->prefix_len;
|
|
memset( key->unk2, 0, sizeof(key->unk2) );
|
|
memset( key->unk3, 0, sizeof(key->unk3) );
|
|
key->luid = entry->luid;
|
|
key->luid2 = entry->luid;
|
|
key->next_hop.WS_s_addr = entry->next_hop.s_addr;
|
|
key->pad = 0;
|
|
}
|
|
|
|
if (rw)
|
|
{
|
|
rw->site_prefix_len = 0;
|
|
rw->valid_lifetime = ~0u;
|
|
rw->preferred_lifetime = ~0u;
|
|
rw->metric = entry->metric;
|
|
rw->protocol = entry->protocol;
|
|
rw->loopback = entry->loopback;
|
|
rw->autoconf = 1;
|
|
rw->publish = 0;
|
|
rw->immortal = 1;
|
|
memset( rw->unk, 0, sizeof(rw->unk) );
|
|
rw->unk2 = 0;
|
|
}
|
|
|
|
if (dyn)
|
|
{
|
|
memset( dyn, 0, sizeof(*dyn) );
|
|
}
|
|
|
|
if (stat)
|
|
{
|
|
stat->origin = NlroManual;
|
|
stat->if_index = entry->if_index;
|
|
}
|
|
}
|
|
|
|
static NTSTATUS ipv4_forward_enumerate_all( void *key_data, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size,
|
|
void *static_data, DWORD static_size, DWORD_PTR *count )
|
|
{
|
|
DWORD num = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
BOOL want_data = key_size || rw_size || dynamic_size || static_size;
|
|
struct ipv4_route_data entry;
|
|
|
|
TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data, key_size, rw_data, rw_size,
|
|
dynamic_data, dynamic_size, static_data, static_size, count );
|
|
|
|
#ifdef __linux__
|
|
{
|
|
char buf[512], *ptr;
|
|
struct in_addr mask;
|
|
DWORD rtf_flags;
|
|
FILE *fp;
|
|
|
|
if (!(fp = fopen( "/proc/net/route", "r" ))) return STATUS_NOT_SUPPORTED;
|
|
|
|
/* skip header line */
|
|
fgets( buf, sizeof(buf), fp );
|
|
while ((ptr = fgets( buf, sizeof(buf), fp )))
|
|
{
|
|
while (!isspace( *ptr )) ptr++;
|
|
*ptr++ = '\0';
|
|
|
|
if (!convert_unix_name_to_luid( buf, &entry.luid )) continue;
|
|
if (!convert_luid_to_index( &entry.luid, &entry.if_index )) continue;
|
|
|
|
entry.prefix.s_addr = strtoul( ptr, &ptr, 16 );
|
|
entry.next_hop.s_addr = strtoul( ptr + 1, &ptr, 16 );
|
|
rtf_flags = strtoul( ptr + 1, &ptr, 16 );
|
|
strtoul( ptr + 1, &ptr, 16 ); /* refcount, skip */
|
|
strtoul( ptr + 1, &ptr, 16 ); /* use, skip */
|
|
entry.metric = strtoul( ptr + 1, &ptr, 16 );
|
|
mask.s_addr = strtoul( ptr + 1, &ptr, 16 );
|
|
entry.prefix_len = mask_v4_to_prefix( &mask );
|
|
entry.protocol = (rtf_flags & RTF_GATEWAY) ? MIB_IPPROTO_NETMGMT : MIB_IPPROTO_LOCAL;
|
|
entry.loopback = entry.protocol == MIB_IPPROTO_LOCAL && entry.prefix_len == 32;
|
|
|
|
if (num < *count)
|
|
{
|
|
ipv4_forward_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data );
|
|
key_data = (BYTE *)key_data + key_size;
|
|
rw_data = (BYTE *)rw_data + rw_size;
|
|
dynamic_data = (BYTE *)dynamic_data + dynamic_size;
|
|
static_data = (BYTE *)static_data + static_size;
|
|
}
|
|
num++;
|
|
}
|
|
fclose( fp );
|
|
}
|
|
#elif 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 = NULL, *lim, *next, *addr_ptr;
|
|
struct rt_msghdr *rtm;
|
|
|
|
if (sysctl( mib, ARRAY_SIZE(mib), NULL, &needed, NULL, 0 ) < 0) return STATUS_NOT_SUPPORTED;
|
|
|
|
buf = heap_alloc( needed );
|
|
if (!buf) return STATUS_NO_MEMORY;
|
|
|
|
if (sysctl( mib, 6, buf, &needed, NULL, 0 ) < 0)
|
|
{
|
|
heap_free( buf );
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
lim = buf + needed;
|
|
for (next = buf; next < lim; next += rtm->rtm_msglen)
|
|
{
|
|
int i;
|
|
sa_family_t dst_family = AF_UNSPEC;
|
|
|
|
rtm = (struct rt_msghdr *)next;
|
|
|
|
if (rtm->rtm_type != RTM_GET)
|
|
{
|
|
WARN( "Got unexpected message type 0x%x!\n", rtm->rtm_type );
|
|
continue;
|
|
}
|
|
|
|
/* Ignore gateway routes which are multicast */
|
|
if ((rtm->rtm_flags & RTF_GATEWAY) && (rtm->rtm_flags & RTF_MULTICAST)) continue;
|
|
|
|
entry.if_index = rtm->rtm_index;
|
|
if (!convert_index_to_luid( entry.if_index, &entry.luid )) continue;
|
|
entry.protocol = (rtm->rtm_flags & RTF_GATEWAY) ? MIB_IPPROTO_NETMGMT : MIB_IPPROTO_LOCAL;
|
|
entry.metric = rtm->rtm_rmx.rmx_hopcount;
|
|
|
|
addr_ptr = (char *)(rtm + 1);
|
|
|
|
for (i = 1; i; i <<= 1)
|
|
{
|
|
struct sockaddr *sa;
|
|
struct in_addr addr;
|
|
|
|
if (!(i & rtm->rtm_addrs)) continue;
|
|
|
|
sa = (struct sockaddr *)addr_ptr;
|
|
if (addr_ptr + sa->sa_len > next + rtm->rtm_msglen)
|
|
{
|
|
ERR( "struct sockaddr extends beyond the route message, %p > %p\n",
|
|
addr_ptr + sa->sa_len, next + rtm->rtm_msglen );
|
|
}
|
|
|
|
if (sa->sa_len) addr_ptr += (sa->sa_len + sizeof(int)-1) & ~(sizeof(int)-1);
|
|
else addr_ptr += sizeof(int);
|
|
/* Apple's netstat prints the netmask together with the destination
|
|
* and only looks at the destination's address family. The netmask's
|
|
* sa_family sometimes contains the non-existent value 0xff. */
|
|
switch (i == RTA_NETMASK ? dst_family : sa->sa_family)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
/* Netmasks (and possibly other addresses) have only enough size
|
|
* to represent the non-zero bits, e.g. a netmask of 255.0.0.0 has
|
|
* 5 bytes (1 sa_len, 1 sa_family, 2 sa_port and 1 for the first
|
|
* byte of sin_addr). */
|
|
struct sockaddr_in sin = {0};
|
|
memcpy( &sin, sa, sa->sa_len );
|
|
addr = sin.sin_addr;
|
|
break;
|
|
}
|
|
#ifdef AF_LINK
|
|
case AF_LINK:
|
|
if (i == RTA_GATEWAY && entry.protocol == MIB_IPPROTO_NETMGMT)
|
|
{
|
|
/* For direct route we may simply use dest addr as next hop */
|
|
C_ASSERT(RTA_DST < RTA_GATEWAY);
|
|
addr = entry.prefix;
|
|
break;
|
|
}
|
|
/* fallthrough */
|
|
#endif
|
|
default:
|
|
WARN( "Received unsupported sockaddr family 0x%x\n", sa->sa_family );
|
|
addr.s_addr = 0;
|
|
}
|
|
switch (i)
|
|
{
|
|
case RTA_DST:
|
|
entry.prefix = addr;
|
|
dst_family = sa->sa_family;
|
|
break;
|
|
case RTA_GATEWAY: entry.next_hop = addr; break;
|
|
case RTA_NETMASK: entry.prefix_len = mask_v4_to_prefix( &addr ); break;
|
|
default:
|
|
WARN( "Unexpected address type 0x%x\n", i );
|
|
}
|
|
}
|
|
|
|
if (num < *count)
|
|
{
|
|
ipv4_forward_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data );
|
|
key_data = (BYTE *)key_data + key_size;
|
|
rw_data = (BYTE *)rw_data + rw_size;
|
|
dynamic_data = (BYTE *)dynamic_data + dynamic_size;
|
|
static_data = (BYTE *)static_data + static_size;
|
|
}
|
|
num++;
|
|
}
|
|
HeapFree( GetProcessHeap (), 0, buf );
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
|
|
if (!want_data || num <= *count) *count = num;
|
|
else status = STATUS_BUFFER_OVERFLOW;
|
|
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS ipv6_forward_enumerate_all( void *key_data, DWORD key_size, void *rw_data, DWORD rw_size,
|
|
void *dynamic_data, DWORD dynamic_size,
|
|
void *static_data, DWORD static_size, DWORD_PTR *count )
|
|
{
|
|
FIXME( "not implemented\n" );
|
|
*count = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static struct module_table ipv4_tables[] =
|
|
{
|
|
{
|
|
NSI_IP_COMPARTMENT_TABLE,
|
|
{
|
|
sizeof(DWORD), sizeof(struct nsi_ip_cmpt_rw),
|
|
sizeof(struct nsi_ip_cmpt_dynamic), 0
|
|
},
|
|
NULL,
|
|
ipv4_cmpt_get_all_parameters,
|
|
},
|
|
{
|
|
NSI_IP_ICMPSTATS_TABLE,
|
|
{
|
|
0, 0,
|
|
sizeof(struct nsi_ip_icmpstats_dynamic), 0
|
|
},
|
|
NULL,
|
|
ipv4_icmpstats_get_all_parameters,
|
|
},
|
|
{
|
|
NSI_IP_IPSTATS_TABLE,
|
|
{
|
|
0, 0,
|
|
sizeof(struct nsi_ip_ipstats_dynamic), sizeof(struct nsi_ip_ipstats_static)
|
|
},
|
|
NULL,
|
|
ipv4_ipstats_get_all_parameters,
|
|
},
|
|
{
|
|
NSI_IP_UNICAST_TABLE,
|
|
{
|
|
sizeof(struct nsi_ipv4_unicast_key), sizeof(struct nsi_ip_unicast_rw),
|
|
sizeof(struct nsi_ip_unicast_dynamic), sizeof(struct nsi_ip_unicast_static)
|
|
},
|
|
ipv4_unicast_enumerate_all,
|
|
ip_unicast_get_all_parameters,
|
|
},
|
|
{
|
|
NSI_IP_NEIGHBOUR_TABLE,
|
|
{
|
|
sizeof(struct nsi_ipv4_neighbour_key), sizeof(struct nsi_ip_neighbour_rw),
|
|
sizeof(struct nsi_ip_neighbour_dynamic), 0
|
|
},
|
|
ipv4_neighbour_enumerate_all,
|
|
},
|
|
{
|
|
NSI_IP_FORWARD_TABLE,
|
|
{
|
|
sizeof(struct nsi_ipv4_forward_key), sizeof(struct nsi_ip_forward_rw),
|
|
sizeof(struct nsi_ipv4_forward_dynamic), sizeof(struct nsi_ip_forward_static)
|
|
},
|
|
ipv4_forward_enumerate_all,
|
|
},
|
|
{
|
|
~0u
|
|
}
|
|
};
|
|
|
|
const struct module ipv4_module =
|
|
{
|
|
&NPI_MS_IPV4_MODULEID,
|
|
ipv4_tables
|
|
};
|
|
|
|
static struct module_table ipv6_tables[] =
|
|
{
|
|
{
|
|
NSI_IP_COMPARTMENT_TABLE,
|
|
{
|
|
sizeof(DWORD), sizeof(struct nsi_ip_cmpt_rw),
|
|
sizeof(struct nsi_ip_cmpt_dynamic), 0
|
|
},
|
|
NULL,
|
|
ipv6_cmpt_get_all_parameters,
|
|
},
|
|
{
|
|
NSI_IP_ICMPSTATS_TABLE,
|
|
{
|
|
0, 0,
|
|
sizeof(struct nsi_ip_icmpstats_dynamic), 0
|
|
},
|
|
NULL,
|
|
ipv6_icmpstats_get_all_parameters,
|
|
},
|
|
{
|
|
NSI_IP_IPSTATS_TABLE,
|
|
{
|
|
0, 0,
|
|
sizeof(struct nsi_ip_ipstats_dynamic), sizeof(struct nsi_ip_ipstats_static)
|
|
},
|
|
NULL,
|
|
ipv6_ipstats_get_all_parameters,
|
|
},
|
|
{
|
|
NSI_IP_UNICAST_TABLE,
|
|
{
|
|
sizeof(struct nsi_ipv6_unicast_key), sizeof(struct nsi_ip_unicast_rw),
|
|
sizeof(struct nsi_ip_unicast_dynamic), sizeof(struct nsi_ip_unicast_static)
|
|
},
|
|
ipv6_unicast_enumerate_all,
|
|
ip_unicast_get_all_parameters,
|
|
},
|
|
{
|
|
NSI_IP_NEIGHBOUR_TABLE,
|
|
{
|
|
sizeof(struct nsi_ipv6_neighbour_key), sizeof(struct nsi_ip_neighbour_rw),
|
|
sizeof(struct nsi_ip_neighbour_dynamic), 0
|
|
},
|
|
ipv6_neighbour_enumerate_all,
|
|
},
|
|
{
|
|
NSI_IP_FORWARD_TABLE,
|
|
{
|
|
sizeof(struct nsi_ipv6_forward_key), sizeof(struct nsi_ip_forward_rw),
|
|
sizeof(struct nsi_ipv6_forward_dynamic), sizeof(struct nsi_ip_forward_static)
|
|
},
|
|
ipv6_forward_enumerate_all,
|
|
},
|
|
{
|
|
~0u
|
|
}
|
|
};
|
|
|
|
const struct module ipv6_module =
|
|
{
|
|
&NPI_MS_IPV6_MODULEID,
|
|
ipv6_tables
|
|
};
|