2 * Copyright (C) 2003,2006 Juan Lang
3 * Copyright (C) 2007 TransGaming Technologies Inc.
4 * Copyright (C) 2009 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
29 #include <sys/types.h>
33 #ifdef HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
36 #ifdef HAVE_SYS_SOCKETVAR_H
37 #include <sys/socketvar.h>
39 #ifdef HAVE_SYS_TIMEOUT_H
40 #include <sys/timeout.h>
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
45 #ifdef HAVE_NETINET_IN_SYSTM_H
46 #include <netinet/in_systm.h>
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
54 #ifdef HAVE_NET_IF_DL_H
55 #include <net/if_dl.h>
57 #ifdef HAVE_NET_IF_TYPES_H
58 #include <net/if_types.h>
60 #ifdef HAVE_NET_ROUTE_H
61 #include <net/route.h>
63 #ifdef HAVE_NET_IF_ARP_H
64 #include <net/if_arp.h>
66 #ifdef HAVE_NETINET_IF_ETHER_H
67 #include <netinet/if_ether.h>
69 #ifdef HAVE_NETINET_IF_INARP_H
70 #include <netinet/if_inarp.h>
72 #ifdef HAVE_NETINET_IP_H
73 #include <netinet/ip.h>
75 #ifdef HAVE_NETINET_TCP_H
76 #include <netinet/tcp.h>
78 #ifdef HAVE_NETINET_IP_VAR_H
79 #include <netinet/ip_var.h>
81 #ifdef HAVE_NETINET_TCP_FSM_H
82 #include <netinet/tcp_fsm.h>
84 #ifdef HAVE_NETINET_IN_PCB_H
85 #include <netinet/in_pcb.h>
87 #ifdef HAVE_NETINET_TCP_TIMER_H
88 #include <netinet/tcp_timer.h>
90 #ifdef HAVE_NETINET_TCP_VAR_H
91 #include <netinet/tcp_var.h>
93 #ifdef HAVE_NETINET_IP_ICMP_H
94 #include <netinet/ip_icmp.h>
96 #ifdef HAVE_NETINET_ICMP_VAR_H
97 #include <netinet/icmp_var.h>
99 #ifdef HAVE_NETINET_UDP_H
100 #include <netinet/udp.h>
102 #ifdef HAVE_NETINET_UDP_VAR_H
103 #include <netinet/udp_var.h>
105 #ifdef HAVE_SYS_PROTOSW_H
106 #include <sys/protosw.h>
108 #ifdef HAVE_SYS_SYSCTL_H
109 #include <sys/sysctl.h>
114 #ifdef HAVE_INET_MIB2_H
115 #include <inet/mib2.h>
117 #ifdef HAVE_STROPTS_H
120 #ifdef HAVE_SYS_TIHDR_H
121 #include <sys/tihdr.h>
126 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
129 #define ADVANCE(x, n) (x += ROUNDUP(((struct sockaddr *)n)->sa_len))
134 #include "iprtrmib.h"
138 #include "wine/debug.h"
140 #ifndef HAVE_NETINET_TCP_FSM_H
141 #define TCPS_ESTABLISHED 1
142 #define TCPS_SYN_SENT 2
143 #define TCPS_SYN_RECEIVED 3
144 #define TCPS_FIN_WAIT_1 4
145 #define TCPS_FIN_WAIT_2 5
146 #define TCPS_TIME_WAIT 6
147 #define TCPS_CLOSED 7
148 #define TCPS_CLOSE_WAIT 8
149 #define TCPS_LAST_ACK 9
150 #define TCPS_LISTEN 10
151 #define TCPS_CLOSING 11
154 #ifndef RTF_MULTICAST
155 #define RTF_MULTICAST 0 /* Not available on NetBSD/OpenBSD */
159 #define RTF_LLINFO 0 /* Not available on FreeBSD 8 and above */
162 WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi
);
165 static DWORD
kstat_get_ui32( kstat_t
*ksp
, const char *name
)
168 kstat_named_t
*data
= ksp
->ks_data
;
170 for (i
= 0; i
< ksp
->ks_ndata
; i
++)
171 if (!strcmp( data
[i
].name
, name
)) return data
[i
].value
.ui32
;
175 static ULONGLONG
kstat_get_ui64( kstat_t
*ksp
, const char *name
)
178 kstat_named_t
*data
= ksp
->ks_data
;
180 for (i
= 0; i
< ksp
->ks_ndata
; i
++)
181 if (!strcmp( data
[i
].name
, name
)) return data
[i
].value
.ui64
;
186 #if defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
187 static int open_streams_mib( const char *proto
)
193 struct T_optmgmt_req req_header
;
194 struct opthdr opt_header
;
197 if ((fd
= open( "/dev/arp", O_RDWR
)) == -1)
199 WARN( "could not open /dev/arp: %s\n", strerror(errno
) );
202 if (proto
) ioctl( fd
, I_PUSH
, proto
);
204 request
.req_header
.PRIM_type
= T_SVR4_OPTMGMT_REQ
;
205 request
.req_header
.OPT_length
= sizeof(request
.opt_header
);
206 request
.req_header
.OPT_offset
= FIELD_OFFSET( struct request
, opt_header
);
207 request
.req_header
.MGMT_flags
= T_CURRENT
;
208 request
.opt_header
.level
= MIB2_IP
;
209 request
.opt_header
.name
= 0;
210 request
.opt_header
.len
= 0;
212 buf
.len
= sizeof(request
);
213 buf
.buf
= (caddr_t
)&request
;
214 if (putmsg( fd
, &buf
, NULL
, 0 ) == -1)
216 WARN( "putmsg: %s\n", strerror(errno
) );
223 static void *read_mib_entry( int fd
, int level
, int name
, int *len
)
231 struct T_optmgmt_ack ack_header
;
232 struct opthdr opt_header
;
237 buf
.maxlen
= sizeof(reply
);
238 buf
.buf
= (caddr_t
)&reply
;
239 if ((ret
= getmsg( fd
, &buf
, NULL
, &flags
)) < 0) return NULL
;
240 if (!(ret
& MOREDATA
)) return NULL
;
241 if (reply
.ack_header
.PRIM_type
!= T_OPTMGMT_ACK
) return NULL
;
242 if (buf
.len
< sizeof(reply
.ack_header
)) return NULL
;
243 if (reply
.ack_header
.OPT_length
< sizeof(reply
.opt_header
)) return NULL
;
245 if (!(data
= HeapAlloc( GetProcessHeap(), 0, reply
.opt_header
.len
))) return NULL
;
246 buf
.maxlen
= reply
.opt_header
.len
;
247 buf
.buf
= (caddr_t
)data
;
249 if (getmsg( fd
, NULL
, &buf
, &flags
) >= 0 &&
250 reply
.opt_header
.level
== level
&&
251 reply
.opt_header
.name
== name
)
256 HeapFree( GetProcessHeap(), 0, data
);
259 #endif /* HAVE_SYS_TIHDR_H && T_OPTMGMT_ACK */
261 DWORD
getInterfaceStatsByName(const char *name
, PMIB_IFROW entry
)
263 DWORD ret
= ERROR_NOT_SUPPORTED
;
265 if (!name
|| !entry
) return ERROR_INVALID_PARAMETER
;
271 if ((fp
= fopen("/proc/net/dev", "r")))
275 int nameLen
= strlen(name
);
277 while ((ptr
= fgets(buf
, sizeof(buf
), fp
)))
279 while (*ptr
&& isspace(*ptr
)) ptr
++;
280 if (strncasecmp(ptr
, name
, nameLen
) == 0 && *(ptr
+ nameLen
) == ':')
283 sscanf( ptr
, "%u %u %u %u %u %u %u %u %u %u %u %u",
284 &entry
->dwInOctets
, &entry
->dwInUcastPkts
,
285 &entry
->dwInErrors
, &entry
->dwInDiscards
,
287 &entry
->dwInNUcastPkts
, &entry
->dwOutOctets
,
288 &entry
->dwOutUcastPkts
, &entry
->dwOutErrors
,
289 &entry
->dwOutDiscards
);
297 #elif defined(HAVE_LIBKSTAT)
302 if ((kc
= kstat_open()) &&
303 (ksp
= kstat_lookup( kc
, NULL
, -1, (char *)name
)) &&
304 kstat_read( kc
, ksp
, NULL
) != -1 &&
305 ksp
->ks_type
== KSTAT_TYPE_NAMED
)
307 entry
->dwMtu
= 1500; /* FIXME */
308 entry
->dwSpeed
= min( kstat_get_ui64( ksp
, "ifspeed" ), ~0u );
309 entry
->dwInOctets
= kstat_get_ui32( ksp
, "rbytes" );
310 entry
->dwInNUcastPkts
= kstat_get_ui32( ksp
, "multircv" );
311 entry
->dwInNUcastPkts
+= kstat_get_ui32( ksp
, "brdcstrcv" );
312 entry
->dwInUcastPkts
= kstat_get_ui32( ksp
, "ipackets" ) - entry
->dwInNUcastPkts
;
313 entry
->dwInDiscards
= kstat_get_ui32( ksp
, "norcvbuf" );
314 entry
->dwInErrors
= kstat_get_ui32( ksp
, "ierrors" );
315 entry
->dwInUnknownProtos
= kstat_get_ui32( ksp
, "unknowns" );
316 entry
->dwOutOctets
= kstat_get_ui32( ksp
, "obytes" );
317 entry
->dwOutNUcastPkts
= kstat_get_ui32( ksp
, "multixmt" );
318 entry
->dwOutNUcastPkts
+= kstat_get_ui32( ksp
, "brdcstxmt" );
319 entry
->dwOutUcastPkts
= kstat_get_ui32( ksp
, "opackets" ) - entry
->dwOutNUcastPkts
;
320 entry
->dwOutDiscards
= 0; /* FIXME */
321 entry
->dwOutErrors
= kstat_get_ui32( ksp
, "oerrors" );
322 entry
->dwOutQLen
= kstat_get_ui32( ksp
, "noxmtbuf" );
325 if (kc
) kstat_close( kc
);
327 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST)
329 int mib
[] = {CTL_NET
, PF_ROUTE
, 0, AF_INET
, NET_RT_IFLIST
, if_nametoindex(name
)};
330 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
333 char *buf
= NULL
, *end
;
334 struct if_msghdr
*ifm
;
335 struct if_data ifdata
;
337 if(sysctl(mib
, MIB_LEN
, NULL
, &needed
, NULL
, 0) == -1)
339 ERR ("failed to get size of iflist\n");
342 buf
= HeapAlloc (GetProcessHeap (), 0, needed
);
345 ret
= ERROR_OUTOFMEMORY
;
348 if(sysctl(mib
, MIB_LEN
, buf
, &needed
, NULL
, 0) == -1)
350 ERR ("failed to get iflist\n");
353 for ( end
= buf
+ needed
; buf
< end
; buf
+= ifm
->ifm_msglen
)
355 ifm
= (struct if_msghdr
*) buf
;
356 if(ifm
->ifm_type
== RTM_IFINFO
)
358 ifdata
= ifm
->ifm_data
;
359 entry
->dwMtu
= ifdata
.ifi_mtu
;
360 entry
->dwSpeed
= ifdata
.ifi_baudrate
;
361 entry
->dwInOctets
= ifdata
.ifi_ibytes
;
362 entry
->dwInErrors
= ifdata
.ifi_ierrors
;
363 entry
->dwInDiscards
= ifdata
.ifi_iqdrops
;
364 entry
->dwInUcastPkts
= ifdata
.ifi_ipackets
;
365 entry
->dwInNUcastPkts
= ifdata
.ifi_imcasts
;
366 entry
->dwOutOctets
= ifdata
.ifi_obytes
;
367 entry
->dwOutUcastPkts
= ifdata
.ifi_opackets
;
368 entry
->dwOutErrors
= ifdata
.ifi_oerrors
;
374 HeapFree (GetProcessHeap (), 0, buf
);
377 FIXME( "unimplemented\n" );
383 /******************************************************************
384 * GetIcmpStatistics (IPHLPAPI.@)
386 * Get the ICMP statistics for the local computer.
389 * stats [Out] buffer for ICMP statistics
393 * Failure: error code from winerror.h
395 DWORD WINAPI
GetIcmpStatistics(PMIB_ICMP stats
)
397 DWORD ret
= ERROR_NOT_SUPPORTED
;
399 if (!stats
) return ERROR_INVALID_PARAMETER
;
400 memset( stats
, 0, sizeof(MIB_ICMP
) );
406 if ((fp
= fopen("/proc/net/snmp", "r")))
408 static const char hdr
[] = "Icmp:";
411 while ((ptr
= fgets(buf
, sizeof(buf
), fp
)))
413 if (strncasecmp(buf
, hdr
, sizeof(hdr
) - 1)) continue;
414 /* last line was a header, get another */
415 if (!(ptr
= fgets(buf
, sizeof(buf
), fp
))) break;
416 if (!strncasecmp(buf
, hdr
, sizeof(hdr
) - 1))
419 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",
420 &stats
->stats
.icmpInStats
.dwMsgs
,
421 &stats
->stats
.icmpInStats
.dwErrors
,
422 &stats
->stats
.icmpInStats
.dwDestUnreachs
,
423 &stats
->stats
.icmpInStats
.dwTimeExcds
,
424 &stats
->stats
.icmpInStats
.dwParmProbs
,
425 &stats
->stats
.icmpInStats
.dwSrcQuenchs
,
426 &stats
->stats
.icmpInStats
.dwRedirects
,
427 &stats
->stats
.icmpInStats
.dwEchoReps
,
428 &stats
->stats
.icmpInStats
.dwTimestamps
,
429 &stats
->stats
.icmpInStats
.dwTimestampReps
,
430 &stats
->stats
.icmpInStats
.dwAddrMasks
,
431 &stats
->stats
.icmpInStats
.dwAddrMaskReps
,
432 &stats
->stats
.icmpOutStats
.dwMsgs
,
433 &stats
->stats
.icmpOutStats
.dwErrors
,
434 &stats
->stats
.icmpOutStats
.dwDestUnreachs
,
435 &stats
->stats
.icmpOutStats
.dwTimeExcds
,
436 &stats
->stats
.icmpOutStats
.dwParmProbs
,
437 &stats
->stats
.icmpOutStats
.dwSrcQuenchs
,
438 &stats
->stats
.icmpOutStats
.dwRedirects
,
439 &stats
->stats
.icmpOutStats
.dwEchoReps
,
440 &stats
->stats
.icmpOutStats
.dwTimestamps
,
441 &stats
->stats
.icmpOutStats
.dwTimestampReps
,
442 &stats
->stats
.icmpOutStats
.dwAddrMasks
,
443 &stats
->stats
.icmpOutStats
.dwAddrMaskReps
);
451 #elif defined(HAVE_LIBKSTAT)
453 static char ip
[] = "ip", icmp
[] = "icmp";
457 if ((kc
= kstat_open()) &&
458 (ksp
= kstat_lookup( kc
, ip
, 0, icmp
)) &&
459 kstat_read( kc
, ksp
, NULL
) != -1 &&
460 ksp
->ks_type
== KSTAT_TYPE_NAMED
)
462 stats
->stats
.icmpInStats
.dwMsgs
= kstat_get_ui32( ksp
, "inMsgs" );
463 stats
->stats
.icmpInStats
.dwErrors
= kstat_get_ui32( ksp
, "inErrors" );
464 stats
->stats
.icmpInStats
.dwDestUnreachs
= kstat_get_ui32( ksp
, "inDestUnreachs" );
465 stats
->stats
.icmpInStats
.dwTimeExcds
= kstat_get_ui32( ksp
, "inTimeExcds" );
466 stats
->stats
.icmpInStats
.dwParmProbs
= kstat_get_ui32( ksp
, "inParmProbs" );
467 stats
->stats
.icmpInStats
.dwSrcQuenchs
= kstat_get_ui32( ksp
, "inSrcQuenchs" );
468 stats
->stats
.icmpInStats
.dwRedirects
= kstat_get_ui32( ksp
, "inRedirects" );
469 stats
->stats
.icmpInStats
.dwEchos
= kstat_get_ui32( ksp
, "inEchos" );
470 stats
->stats
.icmpInStats
.dwEchoReps
= kstat_get_ui32( ksp
, "inEchoReps" );
471 stats
->stats
.icmpInStats
.dwTimestamps
= kstat_get_ui32( ksp
, "inTimestamps" );
472 stats
->stats
.icmpInStats
.dwTimestampReps
= kstat_get_ui32( ksp
, "inTimestampReps" );
473 stats
->stats
.icmpInStats
.dwAddrMasks
= kstat_get_ui32( ksp
, "inAddrMasks" );
474 stats
->stats
.icmpInStats
.dwAddrMaskReps
= kstat_get_ui32( ksp
, "inAddrMaskReps" );
475 stats
->stats
.icmpOutStats
.dwMsgs
= kstat_get_ui32( ksp
, "outMsgs" );
476 stats
->stats
.icmpOutStats
.dwErrors
= kstat_get_ui32( ksp
, "outErrors" );
477 stats
->stats
.icmpOutStats
.dwDestUnreachs
= kstat_get_ui32( ksp
, "outDestUnreachs" );
478 stats
->stats
.icmpOutStats
.dwTimeExcds
= kstat_get_ui32( ksp
, "outTimeExcds" );
479 stats
->stats
.icmpOutStats
.dwParmProbs
= kstat_get_ui32( ksp
, "outParmProbs" );
480 stats
->stats
.icmpOutStats
.dwSrcQuenchs
= kstat_get_ui32( ksp
, "outSrcQuenchs" );
481 stats
->stats
.icmpOutStats
.dwRedirects
= kstat_get_ui32( ksp
, "outRedirects" );
482 stats
->stats
.icmpOutStats
.dwEchos
= kstat_get_ui32( ksp
, "outEchos" );
483 stats
->stats
.icmpOutStats
.dwEchoReps
= kstat_get_ui32( ksp
, "outEchoReps" );
484 stats
->stats
.icmpOutStats
.dwTimestamps
= kstat_get_ui32( ksp
, "outTimestamps" );
485 stats
->stats
.icmpOutStats
.dwTimestampReps
= kstat_get_ui32( ksp
, "outTimestampReps" );
486 stats
->stats
.icmpOutStats
.dwAddrMasks
= kstat_get_ui32( ksp
, "outAddrMasks" );
487 stats
->stats
.icmpOutStats
.dwAddrMaskReps
= kstat_get_ui32( ksp
, "outAddrMaskReps" );
490 if (kc
) kstat_close( kc
);
492 #elif defined(HAVE_SYS_SYSCTL_H) && defined(ICMPCTL_STATS)
494 int mib
[] = {CTL_NET
, PF_INET
, IPPROTO_ICMP
, ICMPCTL_STATS
};
495 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
496 struct icmpstat icmp_stat
;
497 size_t needed
= sizeof(icmp_stat
);
500 if(sysctl(mib
, MIB_LEN
, &icmp_stat
, &needed
, NULL
, 0) != -1)
503 stats
->stats
.icmpInStats
.dwMsgs
= icmp_stat
.icps_badcode
+ icmp_stat
.icps_checksum
+ icmp_stat
.icps_tooshort
+ icmp_stat
.icps_badlen
;
504 for(i
= 0; i
<= ICMP_MAXTYPE
; i
++)
505 stats
->stats
.icmpInStats
.dwMsgs
+= icmp_stat
.icps_inhist
[i
];
507 stats
->stats
.icmpInStats
.dwErrors
= icmp_stat
.icps_badcode
+ icmp_stat
.icps_tooshort
+ icmp_stat
.icps_checksum
+ icmp_stat
.icps_badlen
;
509 stats
->stats
.icmpInStats
.dwDestUnreachs
= icmp_stat
.icps_inhist
[ICMP_UNREACH
];
510 stats
->stats
.icmpInStats
.dwTimeExcds
= icmp_stat
.icps_inhist
[ICMP_TIMXCEED
];
511 stats
->stats
.icmpInStats
.dwParmProbs
= icmp_stat
.icps_inhist
[ICMP_PARAMPROB
];
512 stats
->stats
.icmpInStats
.dwSrcQuenchs
= icmp_stat
.icps_inhist
[ICMP_SOURCEQUENCH
];
513 stats
->stats
.icmpInStats
.dwRedirects
= icmp_stat
.icps_inhist
[ICMP_REDIRECT
];
514 stats
->stats
.icmpInStats
.dwEchos
= icmp_stat
.icps_inhist
[ICMP_ECHO
];
515 stats
->stats
.icmpInStats
.dwEchoReps
= icmp_stat
.icps_inhist
[ICMP_ECHOREPLY
];
516 stats
->stats
.icmpInStats
.dwTimestamps
= icmp_stat
.icps_inhist
[ICMP_TSTAMP
];
517 stats
->stats
.icmpInStats
.dwTimestampReps
= icmp_stat
.icps_inhist
[ICMP_TSTAMPREPLY
];
518 stats
->stats
.icmpInStats
.dwAddrMasks
= icmp_stat
.icps_inhist
[ICMP_MASKREQ
];
519 stats
->stats
.icmpInStats
.dwAddrMaskReps
= icmp_stat
.icps_inhist
[ICMP_MASKREPLY
];
521 #ifdef HAVE_ICPS_OUTHIST
523 stats
->stats
.icmpOutStats
.dwMsgs
= icmp_stat
.icps_oldshort
+ icmp_stat
.icps_oldicmp
;
524 for(i
= 0; i
<= ICMP_MAXTYPE
; i
++)
525 stats
->stats
.icmpOutStats
.dwMsgs
+= icmp_stat
.icps_outhist
[i
];
527 stats
->stats
.icmpOutStats
.dwErrors
= icmp_stat
.icps_oldshort
+ icmp_stat
.icps_oldicmp
;
529 stats
->stats
.icmpOutStats
.dwDestUnreachs
= icmp_stat
.icps_outhist
[ICMP_UNREACH
];
530 stats
->stats
.icmpOutStats
.dwTimeExcds
= icmp_stat
.icps_outhist
[ICMP_TIMXCEED
];
531 stats
->stats
.icmpOutStats
.dwParmProbs
= icmp_stat
.icps_outhist
[ICMP_PARAMPROB
];
532 stats
->stats
.icmpOutStats
.dwSrcQuenchs
= icmp_stat
.icps_outhist
[ICMP_SOURCEQUENCH
];
533 stats
->stats
.icmpOutStats
.dwRedirects
= icmp_stat
.icps_outhist
[ICMP_REDIRECT
];
534 stats
->stats
.icmpOutStats
.dwEchos
= icmp_stat
.icps_outhist
[ICMP_ECHO
];
535 stats
->stats
.icmpOutStats
.dwEchoReps
= icmp_stat
.icps_outhist
[ICMP_ECHOREPLY
];
536 stats
->stats
.icmpOutStats
.dwTimestamps
= icmp_stat
.icps_outhist
[ICMP_TSTAMP
];
537 stats
->stats
.icmpOutStats
.dwTimestampReps
= icmp_stat
.icps_outhist
[ICMP_TSTAMPREPLY
];
538 stats
->stats
.icmpOutStats
.dwAddrMasks
= icmp_stat
.icps_outhist
[ICMP_MASKREQ
];
539 stats
->stats
.icmpOutStats
.dwAddrMaskReps
= icmp_stat
.icps_outhist
[ICMP_MASKREPLY
];
540 #endif /* ICPS_OUTHIST */
544 #else /* ICMPCTL_STATS */
545 FIXME( "unimplemented\n" );
551 /******************************************************************
552 * GetIpStatistics (IPHLPAPI.@)
554 * Get the IP statistics for the local computer.
557 * stats [Out] buffer for IP statistics
561 * Failure: error code from winerror.h
563 DWORD WINAPI
GetIpStatistics(PMIB_IPSTATS stats
)
565 DWORD ret
= ERROR_NOT_SUPPORTED
;
566 MIB_IPFORWARDTABLE
*fwd_table
;
568 if (!stats
) return ERROR_INVALID_PARAMETER
;
569 memset( stats
, 0, sizeof(*stats
) );
571 stats
->dwNumIf
= stats
->dwNumAddr
= getNumInterfaces();
572 if (!AllocateAndGetIpForwardTableFromStack( &fwd_table
, FALSE
, GetProcessHeap(), 0 ))
574 stats
->dwNumRoutes
= fwd_table
->dwNumEntries
;
575 HeapFree( GetProcessHeap(), 0, fwd_table
);
582 if ((fp
= fopen("/proc/net/snmp", "r")))
584 static const char hdr
[] = "Ip:";
587 while ((ptr
= fgets(buf
, sizeof(buf
), fp
)))
589 if (strncasecmp(buf
, hdr
, sizeof(hdr
) - 1)) continue;
590 /* last line was a header, get another */
591 if (!(ptr
= fgets(buf
, sizeof(buf
), fp
))) break;
592 if (!strncasecmp(buf
, hdr
, sizeof(hdr
) - 1))
595 sscanf( ptr
, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
596 &stats
->dwForwarding
,
597 &stats
->dwDefaultTTL
,
598 &stats
->dwInReceives
,
599 &stats
->dwInHdrErrors
,
600 &stats
->dwInAddrErrors
,
601 &stats
->dwForwDatagrams
,
602 &stats
->dwInUnknownProtos
,
603 &stats
->dwInDiscards
,
604 &stats
->dwInDelivers
,
605 &stats
->dwOutRequests
,
606 &stats
->dwOutDiscards
,
607 &stats
->dwOutNoRoutes
,
608 &stats
->dwReasmTimeout
,
609 &stats
->dwReasmReqds
,
611 &stats
->dwReasmFails
,
614 &stats
->dwFragCreates
);
615 /* hmm, no routingDiscards */
623 #elif defined(HAVE_LIBKSTAT)
625 static char ip
[] = "ip";
629 if ((kc
= kstat_open()) &&
630 (ksp
= kstat_lookup( kc
, ip
, 0, ip
)) &&
631 kstat_read( kc
, ksp
, NULL
) != -1 &&
632 ksp
->ks_type
== KSTAT_TYPE_NAMED
)
634 stats
->dwForwarding
= kstat_get_ui32( ksp
, "forwarding" );
635 stats
->dwDefaultTTL
= kstat_get_ui32( ksp
, "defaultTTL" );
636 stats
->dwInReceives
= kstat_get_ui32( ksp
, "inReceives" );
637 stats
->dwInHdrErrors
= kstat_get_ui32( ksp
, "inHdrErrors" );
638 stats
->dwInAddrErrors
= kstat_get_ui32( ksp
, "inAddrErrors" );
639 stats
->dwForwDatagrams
= kstat_get_ui32( ksp
, "forwDatagrams" );
640 stats
->dwInUnknownProtos
= kstat_get_ui32( ksp
, "inUnknownProtos" );
641 stats
->dwInDiscards
= kstat_get_ui32( ksp
, "inDiscards" );
642 stats
->dwInDelivers
= kstat_get_ui32( ksp
, "inDelivers" );
643 stats
->dwOutRequests
= kstat_get_ui32( ksp
, "outRequests" );
644 stats
->dwRoutingDiscards
= kstat_get_ui32( ksp
, "routingDiscards" );
645 stats
->dwOutDiscards
= kstat_get_ui32( ksp
, "outDiscards" );
646 stats
->dwOutNoRoutes
= kstat_get_ui32( ksp
, "outNoRoutes" );
647 stats
->dwReasmTimeout
= kstat_get_ui32( ksp
, "reasmTimeout" );
648 stats
->dwReasmReqds
= kstat_get_ui32( ksp
, "reasmReqds" );
649 stats
->dwReasmOks
= kstat_get_ui32( ksp
, "reasmOKs" );
650 stats
->dwReasmFails
= kstat_get_ui32( ksp
, "reasmFails" );
651 stats
->dwFragOks
= kstat_get_ui32( ksp
, "fragOKs" );
652 stats
->dwFragFails
= kstat_get_ui32( ksp
, "fragFails" );
653 stats
->dwFragCreates
= kstat_get_ui32( ksp
, "fragCreates" );
656 if (kc
) kstat_close( kc
);
658 #elif defined(HAVE_SYS_SYSCTL_H) && defined(IPCTL_STATS)
660 int mib
[] = {CTL_NET
, PF_INET
, IPPROTO_IP
, IPCTL_STATS
};
661 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
662 int ip_ttl
, ip_forwarding
;
663 struct ipstat ip_stat
;
666 needed
= sizeof(ip_stat
);
667 if(sysctl(mib
, MIB_LEN
, &ip_stat
, &needed
, NULL
, 0) == -1)
669 ERR ("failed to get ipstat\n");
670 return ERROR_NOT_SUPPORTED
;
673 needed
= sizeof(ip_ttl
);
674 if (sysctlbyname ("net.inet.ip.ttl", &ip_ttl
, &needed
, NULL
, 0) == -1)
676 ERR ("failed to get ip Default TTL\n");
677 return ERROR_NOT_SUPPORTED
;
680 needed
= sizeof(ip_forwarding
);
681 if (sysctlbyname ("net.inet.ip.forwarding", &ip_forwarding
, &needed
, NULL
, 0) == -1)
683 ERR ("failed to get ip forwarding\n");
684 return ERROR_NOT_SUPPORTED
;
687 stats
->dwForwarding
= ip_forwarding
;
688 stats
->dwDefaultTTL
= ip_ttl
;
689 stats
->dwInDelivers
= ip_stat
.ips_delivered
;
690 stats
->dwInHdrErrors
= ip_stat
.ips_badhlen
+ ip_stat
.ips_badsum
+ ip_stat
.ips_tooshort
+ ip_stat
.ips_badlen
;
691 stats
->dwInAddrErrors
= ip_stat
.ips_cantforward
;
692 stats
->dwInReceives
= ip_stat
.ips_total
;
693 stats
->dwForwDatagrams
= ip_stat
.ips_forward
;
694 stats
->dwInUnknownProtos
= ip_stat
.ips_noproto
;
695 stats
->dwInDiscards
= ip_stat
.ips_fragdropped
;
696 stats
->dwOutDiscards
= ip_stat
.ips_odropped
;
697 stats
->dwReasmOks
= ip_stat
.ips_reassembled
;
698 stats
->dwFragOks
= ip_stat
.ips_fragmented
;
699 stats
->dwFragFails
= ip_stat
.ips_cantfrag
;
700 stats
->dwReasmTimeout
= ip_stat
.ips_fragtimeout
;
701 stats
->dwOutNoRoutes
= ip_stat
.ips_noroute
;
702 stats
->dwOutRequests
= ip_stat
.ips_localout
;
703 stats
->dwReasmReqds
= ip_stat
.ips_fragments
;
707 FIXME( "unimplemented\n" );
713 /******************************************************************
714 * GetTcpStatistics (IPHLPAPI.@)
716 * Get the TCP statistics for the local computer.
719 * stats [Out] buffer for TCP statistics
723 * Failure: error code from winerror.h
725 DWORD WINAPI
GetTcpStatistics(PMIB_TCPSTATS stats
)
727 DWORD ret
= ERROR_NOT_SUPPORTED
;
729 if (!stats
) return ERROR_INVALID_PARAMETER
;
730 memset( stats
, 0, sizeof(*stats
) );
736 if ((fp
= fopen("/proc/net/snmp", "r")))
738 static const char hdr
[] = "Tcp:";
739 MIB_TCPTABLE
*tcp_table
;
742 while ((ptr
= fgets(buf
, sizeof(buf
), fp
)))
744 if (strncasecmp(buf
, hdr
, sizeof(hdr
) - 1)) continue;
745 /* last line was a header, get another */
746 if (!(ptr
= fgets(buf
, sizeof(buf
), fp
))) break;
747 if (!strncasecmp(buf
, hdr
, sizeof(hdr
) - 1))
750 sscanf( ptr
, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u",
751 &stats
->dwRtoAlgorithm
,
755 &stats
->dwActiveOpens
,
756 &stats
->dwPassiveOpens
,
757 &stats
->dwAttemptFails
,
758 &stats
->dwEstabResets
,
762 &stats
->dwRetransSegs
,
768 if (!AllocateAndGetTcpTableFromStack( &tcp_table
, FALSE
, GetProcessHeap(), 0 ))
770 stats
->dwNumConns
= tcp_table
->dwNumEntries
;
771 HeapFree( GetProcessHeap(), 0, tcp_table
);
777 #elif defined(HAVE_LIBKSTAT)
779 static char tcp
[] = "tcp";
783 if ((kc
= kstat_open()) &&
784 (ksp
= kstat_lookup( kc
, tcp
, 0, tcp
)) &&
785 kstat_read( kc
, ksp
, NULL
) != -1 &&
786 ksp
->ks_type
== KSTAT_TYPE_NAMED
)
788 stats
->dwRtoAlgorithm
= kstat_get_ui32( ksp
, "rtoAlgorithm" );
789 stats
->dwRtoMin
= kstat_get_ui32( ksp
, "rtoMin" );
790 stats
->dwRtoMax
= kstat_get_ui32( ksp
, "rtoMax" );
791 stats
->dwMaxConn
= kstat_get_ui32( ksp
, "maxConn" );
792 stats
->dwActiveOpens
= kstat_get_ui32( ksp
, "activeOpens" );
793 stats
->dwPassiveOpens
= kstat_get_ui32( ksp
, "passiveOpens" );
794 stats
->dwAttemptFails
= kstat_get_ui32( ksp
, "attemptFails" );
795 stats
->dwEstabResets
= kstat_get_ui32( ksp
, "estabResets" );
796 stats
->dwCurrEstab
= kstat_get_ui32( ksp
, "currEstab" );
797 stats
->dwInSegs
= kstat_get_ui32( ksp
, "inSegs" );
798 stats
->dwOutSegs
= kstat_get_ui32( ksp
, "outSegs" );
799 stats
->dwRetransSegs
= kstat_get_ui32( ksp
, "retransSegs" );
800 stats
->dwInErrs
= kstat_get_ui32( ksp
, "inErrs" );
801 stats
->dwOutRsts
= kstat_get_ui32( ksp
, "outRsts" );
802 stats
->dwNumConns
= kstat_get_ui32( ksp
, "connTableSize" );
805 if (kc
) kstat_close( kc
);
807 #elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS)
809 #ifndef TCPTV_MIN /* got removed in Mac OS X for some reason */
811 #define TCPTV_REXMTMAX 128
813 int mib
[] = {CTL_NET
, PF_INET
, IPPROTO_TCP
, TCPCTL_STATS
};
814 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
816 struct tcpstat tcp_stat
;
817 size_t needed
= sizeof(tcp_stat
);
819 if(sysctl(mib
, MIB_LEN
, &tcp_stat
, &needed
, NULL
, 0) != -1)
821 stats
->dwRtoAlgorithm
= MIB_TCP_RTO_VANJ
;
822 stats
->dwRtoMin
= TCPTV_MIN
;
823 stats
->dwRtoMax
= TCPTV_REXMTMAX
;
824 stats
->dwMaxConn
= -1;
825 stats
->dwActiveOpens
= tcp_stat
.tcps_connattempt
;
826 stats
->dwPassiveOpens
= tcp_stat
.tcps_accepts
;
827 stats
->dwAttemptFails
= tcp_stat
.tcps_conndrops
;
828 stats
->dwEstabResets
= tcp_stat
.tcps_drops
;
829 stats
->dwCurrEstab
= 0;
830 stats
->dwInSegs
= tcp_stat
.tcps_rcvtotal
;
831 stats
->dwOutSegs
= tcp_stat
.tcps_sndtotal
- tcp_stat
.tcps_sndrexmitpack
;
832 stats
->dwRetransSegs
= tcp_stat
.tcps_sndrexmitpack
;
833 stats
->dwInErrs
= tcp_stat
.tcps_rcvbadsum
+ tcp_stat
.tcps_rcvbadoff
+ tcp_stat
.tcps_rcvmemdrop
+ tcp_stat
.tcps_rcvshort
;
834 stats
->dwOutRsts
= tcp_stat
.tcps_sndctrl
- tcp_stat
.tcps_closed
;
835 stats
->dwNumConns
= tcp_stat
.tcps_connects
;
838 else ERR ("failed to get tcpstat\n");
841 FIXME( "unimplemented\n" );
847 /******************************************************************
848 * GetUdpStatistics (IPHLPAPI.@)
850 * Get the UDP statistics for the local computer.
853 * stats [Out] buffer for UDP statistics
857 * Failure: error code from winerror.h
859 DWORD WINAPI
GetUdpStatistics(PMIB_UDPSTATS stats
)
861 DWORD ret
= ERROR_NOT_SUPPORTED
;
863 if (!stats
) return ERROR_INVALID_PARAMETER
;
864 memset( stats
, 0, sizeof(*stats
) );
870 if ((fp
= fopen("/proc/net/snmp", "r")))
872 static const char hdr
[] = "Udp:";
875 while ((ptr
= fgets(buf
, sizeof(buf
), fp
)))
877 if (strncasecmp(buf
, hdr
, sizeof(hdr
) - 1)) continue;
878 /* last line was a header, get another */
879 if (!(ptr
= fgets(buf
, sizeof(buf
), fp
))) break;
880 if (!strncasecmp(buf
, hdr
, sizeof(hdr
) - 1))
883 sscanf( ptr
, "%u %u %u %u %u",
884 &stats
->dwInDatagrams
, &stats
->dwNoPorts
,
885 &stats
->dwInErrors
, &stats
->dwOutDatagrams
, &stats
->dwNumAddrs
);
893 #elif defined(HAVE_LIBKSTAT)
895 static char udp
[] = "udp";
898 MIB_UDPTABLE
*udp_table
;
900 if ((kc
= kstat_open()) &&
901 (ksp
= kstat_lookup( kc
, udp
, 0, udp
)) &&
902 kstat_read( kc
, ksp
, NULL
) != -1 &&
903 ksp
->ks_type
== KSTAT_TYPE_NAMED
)
905 stats
->dwInDatagrams
= kstat_get_ui32( ksp
, "inDatagrams" );
906 stats
->dwNoPorts
= 0; /* FIXME */
907 stats
->dwInErrors
= kstat_get_ui32( ksp
, "inErrors" );
908 stats
->dwOutDatagrams
= kstat_get_ui32( ksp
, "outDatagrams" );
909 if (!AllocateAndGetUdpTableFromStack( &udp_table
, FALSE
, GetProcessHeap(), 0 ))
911 stats
->dwNumAddrs
= udp_table
->dwNumEntries
;
912 HeapFree( GetProcessHeap(), 0, udp_table
);
916 if (kc
) kstat_close( kc
);
918 #elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS)
920 int mib
[] = {CTL_NET
, PF_INET
, IPPROTO_UDP
, UDPCTL_STATS
};
921 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
922 struct udpstat udp_stat
;
923 MIB_UDPTABLE
*udp_table
;
924 size_t needed
= sizeof(udp_stat
);
926 if(sysctl(mib
, MIB_LEN
, &udp_stat
, &needed
, NULL
, 0) != -1)
928 stats
->dwInDatagrams
= udp_stat
.udps_ipackets
;
929 stats
->dwOutDatagrams
= udp_stat
.udps_opackets
;
930 stats
->dwNoPorts
= udp_stat
.udps_noport
;
931 stats
->dwInErrors
= udp_stat
.udps_hdrops
+ udp_stat
.udps_badsum
+ udp_stat
.udps_fullsock
+ udp_stat
.udps_badlen
;
932 if (!AllocateAndGetUdpTableFromStack( &udp_table
, FALSE
, GetProcessHeap(), 0 ))
934 stats
->dwNumAddrs
= udp_table
->dwNumEntries
;
935 HeapFree( GetProcessHeap(), 0, udp_table
);
939 else ERR ("failed to get udpstat\n");
942 FIXME( "unimplemented\n" );
948 static MIB_IPFORWARDTABLE
*append_ipforward_row( HANDLE heap
, DWORD flags
, MIB_IPFORWARDTABLE
*table
,
949 DWORD
*count
, const MIB_IPFORWARDROW
*row
)
951 if (table
->dwNumEntries
>= *count
)
953 MIB_IPFORWARDTABLE
*new_table
;
954 DWORD new_count
= table
->dwNumEntries
* 2;
956 if (!(new_table
= HeapReAlloc( heap
, flags
, table
,
957 FIELD_OFFSET(MIB_IPFORWARDTABLE
, table
[new_count
] ))))
959 HeapFree( heap
, 0, table
);
965 memcpy( &table
->table
[table
->dwNumEntries
++], row
, sizeof(*row
) );
969 static int compare_ipforward_rows(const void *a
, const void *b
)
971 const MIB_IPFORWARDROW
*rowA
= a
;
972 const MIB_IPFORWARDROW
*rowB
= b
;
975 if ((ret
= rowA
->dwForwardDest
- rowB
->dwForwardDest
) != 0) return ret
;
976 if ((ret
= rowA
->dwForwardProto
- rowB
->dwForwardProto
) != 0) return ret
;
977 if ((ret
= rowA
->dwForwardPolicy
- rowB
->dwForwardPolicy
) != 0) return ret
;
978 return rowA
->dwForwardNextHop
- rowB
->dwForwardNextHop
;
981 /******************************************************************
982 * AllocateAndGetIpForwardTableFromStack (IPHLPAPI.@)
984 * Get the route table.
985 * Like GetIpForwardTable(), but allocate the returned table from heap.
988 * ppIpForwardTable [Out] pointer into which the MIB_IPFORWARDTABLE is
989 * allocated and returned.
990 * bOrder [In] whether to sort the table
991 * heap [In] heap from which the table is allocated
992 * flags [In] flags to HeapAlloc
995 * ERROR_INVALID_PARAMETER if ppIfTable is NULL, other error codes
996 * on failure, NO_ERROR on success.
998 DWORD WINAPI
AllocateAndGetIpForwardTableFromStack(PMIB_IPFORWARDTABLE
*ppIpForwardTable
, BOOL bOrder
,
999 HANDLE heap
, DWORD flags
)
1001 MIB_IPFORWARDTABLE
*table
;
1002 MIB_IPFORWARDROW row
;
1003 DWORD ret
= NO_ERROR
, count
= 16;
1005 TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppIpForwardTable
, bOrder
, heap
, flags
);
1007 if (!ppIpForwardTable
) return ERROR_INVALID_PARAMETER
;
1009 if (!(table
= HeapAlloc( heap
, flags
, FIELD_OFFSET(MIB_IPFORWARDTABLE
, table
[count
] ))))
1010 return ERROR_OUTOFMEMORY
;
1012 table
->dwNumEntries
= 0;
1018 if ((fp
= fopen("/proc/net/route", "r")))
1020 char buf
[512], *ptr
;
1023 /* skip header line */
1024 ptr
= fgets(buf
, sizeof(buf
), fp
);
1025 while ((ptr
= fgets(buf
, sizeof(buf
), fp
)))
1027 memset( &row
, 0, sizeof(row
) );
1029 while (!isspace(*ptr
)) ptr
++;
1031 if (getInterfaceIndexByName(buf
, &row
.dwForwardIfIndex
) != NO_ERROR
)
1034 row
.dwForwardDest
= strtoul(ptr
, &ptr
, 16);
1035 row
.dwForwardNextHop
= strtoul(ptr
+ 1, &ptr
, 16);
1036 flags
= strtoul(ptr
+ 1, &ptr
, 16);
1038 if (!(flags
& RTF_UP
)) row
.dwForwardType
= MIB_IPROUTE_TYPE_INVALID
;
1039 else if (flags
& RTF_GATEWAY
) row
.dwForwardType
= MIB_IPROUTE_TYPE_INDIRECT
;
1040 else row
.dwForwardType
= MIB_IPROUTE_TYPE_DIRECT
;
1042 strtoul(ptr
+ 1, &ptr
, 16); /* refcount, skip */
1043 strtoul(ptr
+ 1, &ptr
, 16); /* use, skip */
1044 row
.dwForwardMetric1
= strtoul(ptr
+ 1, &ptr
, 16);
1045 row
.dwForwardMask
= strtoul(ptr
+ 1, &ptr
, 16);
1046 /* FIXME: other protos might be appropriate, e.g. the default
1047 * route is typically set with MIB_IPPROTO_NETMGMT instead */
1048 row
.dwForwardProto
= MIB_IPPROTO_LOCAL
;
1050 if (!(table
= append_ipforward_row( heap
, flags
, table
, &count
, &row
)))
1055 else ret
= ERROR_NOT_SUPPORTED
;
1057 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1060 int fd
, len
, namelen
;
1061 mib2_ipRouteEntry_t
*entry
;
1064 if ((fd
= open_streams_mib( NULL
)) != -1)
1066 if ((data
= read_mib_entry( fd
, MIB2_IP
, MIB2_IP_ROUTE
, &len
)))
1068 for (entry
= data
; (char *)(entry
+ 1) <= (char *)data
+ len
; entry
++)
1070 row
.dwForwardDest
= entry
->ipRouteDest
;
1071 row
.dwForwardMask
= entry
->ipRouteMask
;
1072 row
.dwForwardPolicy
= 0;
1073 row
.dwForwardNextHop
= entry
->ipRouteNextHop
;
1074 row
.dwForwardType
= entry
->ipRouteType
;
1075 row
.dwForwardProto
= entry
->ipRouteProto
;
1076 row
.dwForwardAge
= entry
->ipRouteAge
;
1077 row
.dwForwardNextHopAS
= 0;
1078 row
.dwForwardMetric1
= entry
->ipRouteMetric1
;
1079 row
.dwForwardMetric2
= entry
->ipRouteMetric2
;
1080 row
.dwForwardMetric3
= entry
->ipRouteMetric3
;
1081 row
.dwForwardMetric4
= entry
->ipRouteMetric4
;
1082 row
.dwForwardMetric5
= entry
->ipRouteMetric5
;
1083 namelen
= min( sizeof(name
) - 1, entry
->ipRouteIfIndex
.o_length
);
1084 memcpy( name
, entry
->ipRouteIfIndex
.o_bytes
, namelen
);
1086 getInterfaceIndexByName( name
, &row
.dwForwardIfIndex
);
1087 if (!(table
= append_ipforward_row( heap
, flags
, table
, &count
, &row
))) break;
1089 HeapFree( GetProcessHeap(), 0, data
);
1093 else ret
= ERROR_NOT_SUPPORTED
;
1095 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1097 int mib
[6] = {CTL_NET
, PF_ROUTE
, 0, PF_INET
, NET_RT_DUMP
, 0};
1099 char *buf
= NULL
, *lim
, *next
, *addrPtr
;
1100 struct rt_msghdr
*rtm
;
1102 if (sysctl (mib
, 6, NULL
, &needed
, NULL
, 0) < 0)
1104 ERR ("sysctl 1 failed!\n");
1105 ret
= ERROR_NOT_SUPPORTED
;
1109 buf
= HeapAlloc (GetProcessHeap (), 0, needed
);
1112 ret
= ERROR_OUTOFMEMORY
;
1116 if (sysctl (mib
, 6, buf
, &needed
, NULL
, 0) < 0)
1118 ret
= ERROR_NOT_SUPPORTED
;
1123 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
)
1127 rtm
= (struct rt_msghdr
*)next
;
1129 if (rtm
->rtm_type
!= RTM_GET
)
1131 WARN ("Got unexpected message type 0x%x!\n",
1136 /* Ignore all entries except for gateway routes which aren't
1138 if (!(rtm
->rtm_flags
& RTF_GATEWAY
) ||
1139 (rtm
->rtm_flags
& RTF_MULTICAST
))
1142 memset( &row
, 0, sizeof(row
) );
1143 row
.dwForwardIfIndex
= rtm
->rtm_index
;
1144 row
.dwForwardType
= MIB_IPROUTE_TYPE_INDIRECT
;
1145 row
.dwForwardMetric1
= rtm
->rtm_rmx
.rmx_hopcount
;
1146 row
.dwForwardProto
= MIB_IPPROTO_LOCAL
;
1148 addrPtr
= (char *)(rtm
+ 1);
1150 for (i
= 1; i
; i
<<= 1)
1152 struct sockaddr
*sa
;
1155 if (!(i
& rtm
->rtm_addrs
))
1158 sa
= (struct sockaddr
*)addrPtr
;
1159 ADVANCE (addrPtr
, sa
);
1161 /* default routes are encoded by length-zero sockaddr */
1162 if (sa
->sa_len
== 0)
1164 else if (sa
->sa_family
!= AF_INET
)
1166 WARN ("Received unsupported sockaddr family 0x%x\n",
1172 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
1174 addr
= sin
->sin_addr
.s_addr
;
1179 case RTA_DST
: row
.dwForwardDest
= addr
; break;
1180 case RTA_GATEWAY
: row
.dwForwardNextHop
= addr
; break;
1181 case RTA_NETMASK
: row
.dwForwardMask
= addr
; break;
1183 WARN ("Unexpected address type 0x%x\n", i
);
1187 if (!(table
= append_ipforward_row( heap
, flags
, table
, &count
, &row
)))
1191 HeapFree( GetProcessHeap (), 0, buf
);
1194 FIXME( "not implemented\n" );
1195 ret
= ERROR_NOT_SUPPORTED
;
1198 if (!table
) return ERROR_OUTOFMEMORY
;
1201 if (bOrder
&& table
->dwNumEntries
)
1202 qsort( table
->table
, table
->dwNumEntries
, sizeof(row
), compare_ipforward_rows
);
1203 *ppIpForwardTable
= table
;
1205 else HeapFree( heap
, flags
, table
);
1206 TRACE( "returning ret %u table %p\n", ret
, table
);
1210 static MIB_IPNETTABLE
*append_ipnet_row( HANDLE heap
, DWORD flags
, MIB_IPNETTABLE
*table
,
1211 DWORD
*count
, const MIB_IPNETROW
*row
)
1213 if (table
->dwNumEntries
>= *count
)
1215 MIB_IPNETTABLE
*new_table
;
1216 DWORD new_count
= table
->dwNumEntries
* 2;
1218 if (!(new_table
= HeapReAlloc( heap
, flags
, table
,
1219 FIELD_OFFSET(MIB_IPNETTABLE
, table
[new_count
] ))))
1221 HeapFree( heap
, 0, table
);
1227 memcpy( &table
->table
[table
->dwNumEntries
++], row
, sizeof(*row
) );
1231 static int compare_ipnet_rows(const void *a
, const void *b
)
1233 const MIB_IPNETROW
*rowA
= a
;
1234 const MIB_IPNETROW
*rowB
= b
;
1236 return ntohl(rowA
->dwAddr
) - ntohl(rowB
->dwAddr
);
1240 /******************************************************************
1241 * AllocateAndGetIpNetTableFromStack (IPHLPAPI.@)
1243 * Get the IP-to-physical address mapping table.
1244 * Like GetIpNetTable(), but allocate the returned table from heap.
1247 * ppIpNetTable [Out] pointer into which the MIB_IPNETTABLE is
1248 * allocated and returned.
1249 * bOrder [In] whether to sort the table
1250 * heap [In] heap from which the table is allocated
1251 * flags [In] flags to HeapAlloc
1254 * ERROR_INVALID_PARAMETER if ppIpNetTable is NULL, other error codes
1255 * on failure, NO_ERROR on success.
1257 DWORD WINAPI
AllocateAndGetIpNetTableFromStack(PMIB_IPNETTABLE
*ppIpNetTable
, BOOL bOrder
,
1258 HANDLE heap
, DWORD flags
)
1260 MIB_IPNETTABLE
*table
;
1262 DWORD ret
= NO_ERROR
, count
= 16;
1264 TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppIpNetTable
, bOrder
, heap
, flags
);
1266 if (!ppIpNetTable
) return ERROR_INVALID_PARAMETER
;
1268 if (!(table
= HeapAlloc( heap
, flags
, FIELD_OFFSET(MIB_IPNETTABLE
, table
[count
] ))))
1269 return ERROR_OUTOFMEMORY
;
1271 table
->dwNumEntries
= 0;
1277 if ((fp
= fopen("/proc/net/arp", "r")))
1279 char buf
[512], *ptr
;
1282 /* skip header line */
1283 ptr
= fgets(buf
, sizeof(buf
), fp
);
1284 while ((ptr
= fgets(buf
, sizeof(buf
), fp
)))
1286 memset( &row
, 0, sizeof(row
) );
1288 row
.dwAddr
= inet_addr(ptr
);
1289 while (*ptr
&& !isspace(*ptr
)) ptr
++;
1290 strtoul(ptr
+ 1, &ptr
, 16); /* hw type (skip) */
1291 flags
= strtoul(ptr
+ 1, &ptr
, 16);
1294 if (flags
& ATF_COM
) row
.dwType
= MIB_IPNET_TYPE_DYNAMIC
;
1298 if (flags
& ATF_PERM
) row
.dwType
= MIB_IPNET_TYPE_STATIC
;
1301 row
.dwType
= MIB_IPNET_TYPE_OTHER
;
1303 while (*ptr
&& isspace(*ptr
)) ptr
++;
1304 while (*ptr
&& !isspace(*ptr
))
1306 row
.bPhysAddr
[row
.dwPhysAddrLen
++] = strtoul(ptr
, &ptr
, 16);
1309 while (*ptr
&& isspace(*ptr
)) ptr
++;
1310 while (*ptr
&& !isspace(*ptr
)) ptr
++; /* mask (skip) */
1311 while (*ptr
&& isspace(*ptr
)) ptr
++;
1312 getInterfaceIndexByName(ptr
, &row
.dwIndex
);
1314 if (!(table
= append_ipnet_row( heap
, flags
, table
, &count
, &row
)))
1319 else ret
= ERROR_NOT_SUPPORTED
;
1321 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1324 int fd
, len
, namelen
;
1325 mib2_ipNetToMediaEntry_t
*entry
;
1328 if ((fd
= open_streams_mib( NULL
)) != -1)
1330 if ((data
= read_mib_entry( fd
, MIB2_IP
, MIB2_IP_MEDIA
, &len
)))
1332 for (entry
= data
; (char *)(entry
+ 1) <= (char *)data
+ len
; entry
++)
1334 row
.dwPhysAddrLen
= min( entry
->ipNetToMediaPhysAddress
.o_length
, MAXLEN_PHYSADDR
);
1335 memcpy( row
.bPhysAddr
, entry
->ipNetToMediaPhysAddress
.o_bytes
, row
.dwPhysAddrLen
);
1336 row
.dwAddr
= entry
->ipNetToMediaNetAddress
;
1337 row
.dwType
= entry
->ipNetToMediaType
;
1338 namelen
= min( sizeof(name
) - 1, entry
->ipNetToMediaIfIndex
.o_length
);
1339 memcpy( name
, entry
->ipNetToMediaIfIndex
.o_bytes
, namelen
);
1341 getInterfaceIndexByName( name
, &row
.dwIndex
);
1342 if (!(table
= append_ipnet_row( heap
, flags
, table
, &count
, &row
))) break;
1344 HeapFree( GetProcessHeap(), 0, data
);
1348 else ret
= ERROR_NOT_SUPPORTED
;
1350 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1352 int mib
[] = {CTL_NET
, PF_ROUTE
, 0, AF_INET
, NET_RT_FLAGS
, RTF_LLINFO
};
1353 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
1355 char *buf
= NULL
, *lim
, *next
;
1356 struct rt_msghdr
*rtm
;
1357 struct sockaddr_inarp
*sinarp
;
1358 struct sockaddr_dl
*sdl
;
1360 if (sysctl (mib
, MIB_LEN
, NULL
, &needed
, NULL
, 0) == -1)
1362 ERR ("failed to get arp table\n");
1363 ret
= ERROR_NOT_SUPPORTED
;
1367 buf
= HeapAlloc (GetProcessHeap (), 0, needed
);
1370 ret
= ERROR_OUTOFMEMORY
;
1374 if (sysctl (mib
, MIB_LEN
, buf
, &needed
, NULL
, 0) == -1)
1376 ret
= ERROR_NOT_SUPPORTED
;
1384 rtm
= (struct rt_msghdr
*)next
;
1385 sinarp
=(struct sockaddr_inarp
*)(rtm
+ 1);
1386 sdl
= (struct sockaddr_dl
*)((char *)sinarp
+ ROUNDUP(sinarp
->sin_len
));
1387 if(sdl
->sdl_alen
) /* arp entry */
1389 memset( &row
, 0, sizeof(row
) );
1390 row
.dwAddr
= sinarp
->sin_addr
.s_addr
;
1391 row
.dwIndex
= sdl
->sdl_index
;
1392 row
.dwPhysAddrLen
= min( 8, sdl
->sdl_alen
);
1393 memcpy( row
.bPhysAddr
, &sdl
->sdl_data
[sdl
->sdl_nlen
], row
.dwPhysAddrLen
);
1394 if(rtm
->rtm_rmx
.rmx_expire
== 0) row
.dwType
= MIB_IPNET_TYPE_STATIC
;
1395 else if(sinarp
->sin_other
& SIN_PROXY
) row
.dwType
= MIB_IPNET_TYPE_OTHER
;
1396 else if(rtm
->rtm_rmx
.rmx_expire
!= 0) row
.dwType
= MIB_IPNET_TYPE_DYNAMIC
;
1397 else row
.dwType
= MIB_IPNET_TYPE_INVALID
;
1399 if (!(table
= append_ipnet_row( heap
, flags
, table
, &count
, &row
)))
1402 next
+= rtm
->rtm_msglen
;
1405 HeapFree( GetProcessHeap (), 0, buf
);
1408 FIXME( "not implemented\n" );
1409 ret
= ERROR_NOT_SUPPORTED
;
1412 if (!table
) return ERROR_OUTOFMEMORY
;
1415 if (bOrder
&& table
->dwNumEntries
)
1416 qsort( table
->table
, table
->dwNumEntries
, sizeof(row
), compare_ipnet_rows
);
1417 *ppIpNetTable
= table
;
1419 else HeapFree( heap
, flags
, table
);
1420 TRACE( "returning ret %u table %p\n", ret
, table
);
1425 static MIB_UDPTABLE
*append_udp_row( HANDLE heap
, DWORD flags
, MIB_UDPTABLE
*table
,
1426 DWORD
*count
, const MIB_UDPROW
*row
)
1428 if (table
->dwNumEntries
>= *count
)
1430 MIB_UDPTABLE
*new_table
;
1431 DWORD new_count
= table
->dwNumEntries
* 2;
1433 if (!(new_table
= HeapReAlloc( heap
, flags
, table
, FIELD_OFFSET(MIB_UDPTABLE
, table
[new_count
] ))))
1435 HeapFree( heap
, 0, table
);
1441 memcpy( &table
->table
[table
->dwNumEntries
++], row
, sizeof(*row
) );
1445 static int compare_udp_rows(const void *a
, const void *b
)
1447 const MIB_UDPROW
*rowA
= a
;
1448 const MIB_UDPROW
*rowB
= b
;
1451 if ((ret
= rowA
->dwLocalAddr
- rowB
->dwLocalAddr
) != 0) return ret
;
1452 return rowA
->dwLocalPort
- rowB
->dwLocalPort
;
1456 /******************************************************************
1457 * AllocateAndGetUdpTableFromStack (IPHLPAPI.@)
1459 * Get the UDP listener table.
1460 * Like GetUdpTable(), but allocate the returned table from heap.
1463 * ppUdpTable [Out] pointer into which the MIB_UDPTABLE is
1464 * allocated and returned.
1465 * bOrder [In] whether to sort the table
1466 * heap [In] heap from which the table is allocated
1467 * flags [In] flags to HeapAlloc
1470 * ERROR_INVALID_PARAMETER if ppUdpTable is NULL, whatever GetUdpTable()
1471 * returns otherwise.
1473 DWORD WINAPI
AllocateAndGetUdpTableFromStack(PMIB_UDPTABLE
*ppUdpTable
, BOOL bOrder
,
1474 HANDLE heap
, DWORD flags
)
1476 MIB_UDPTABLE
*table
;
1478 DWORD ret
= NO_ERROR
, count
= 16;
1480 TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppUdpTable
, bOrder
, heap
, flags
);
1482 if (!ppUdpTable
) return ERROR_INVALID_PARAMETER
;
1484 if (!(table
= HeapAlloc( heap
, flags
, FIELD_OFFSET(MIB_UDPTABLE
, table
[count
] ))))
1485 return ERROR_OUTOFMEMORY
;
1487 table
->dwNumEntries
= 0;
1493 if ((fp
= fopen("/proc/net/udp", "r")))
1495 char buf
[512], *ptr
;
1498 /* skip header line */
1499 ptr
= fgets(buf
, sizeof(buf
), fp
);
1500 while ((ptr
= fgets(buf
, sizeof(buf
), fp
)))
1502 if (sscanf( ptr
, "%u: %x:%x", &dummy
, &row
.dwLocalAddr
, &row
.dwLocalPort
) != 3)
1504 row
.dwLocalPort
= htons( row
.dwLocalPort
);
1505 if (!(table
= append_udp_row( heap
, flags
, table
, &count
, &row
)))
1510 else ret
= ERROR_NOT_SUPPORTED
;
1512 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1516 mib2_udpEntry_t
*entry
;
1518 if ((fd
= open_streams_mib( "udp" )) != -1)
1520 if ((data
= read_mib_entry( fd
, MIB2_UDP
, MIB2_UDP_ENTRY
, &len
)))
1522 for (entry
= data
; (char *)(entry
+ 1) <= (char *)data
+ len
; entry
++)
1524 row
.dwLocalAddr
= entry
->udpLocalAddress
;
1525 row
.dwLocalPort
= htons( entry
->udpLocalPort
);
1526 if (!(table
= append_udp_row( heap
, flags
, table
, &count
, &row
))) break;
1528 HeapFree( GetProcessHeap(), 0, data
);
1532 else ret
= ERROR_NOT_SUPPORTED
;
1535 FIXME( "not implemented\n" );
1536 ret
= ERROR_NOT_SUPPORTED
;
1539 if (!table
) return ERROR_OUTOFMEMORY
;
1542 if (bOrder
&& table
->dwNumEntries
)
1543 qsort( table
->table
, table
->dwNumEntries
, sizeof(row
), compare_udp_rows
);
1544 *ppUdpTable
= table
;
1546 else HeapFree( heap
, flags
, table
);
1547 TRACE( "returning ret %u table %p\n", ret
, table
);
1552 static MIB_TCPTABLE
*append_tcp_row( HANDLE heap
, DWORD flags
, MIB_TCPTABLE
*table
,
1553 DWORD
*count
, const MIB_TCPROW
*row
)
1555 if (table
->dwNumEntries
>= *count
)
1557 MIB_TCPTABLE
*new_table
;
1558 DWORD new_count
= table
->dwNumEntries
* 2;
1560 if (!(new_table
= HeapReAlloc( heap
, flags
, table
, FIELD_OFFSET(MIB_TCPTABLE
, table
[new_count
] ))))
1562 HeapFree( heap
, 0, table
);
1568 memcpy( &table
->table
[table
->dwNumEntries
++], row
, sizeof(*row
) );
1573 /* Why not a lookup table? Because the TCPS_* constants are different
1574 on different platforms */
1575 static inline DWORD
TCPStateToMIBState (int state
)
1579 case TCPS_ESTABLISHED
: return MIB_TCP_STATE_ESTAB
;
1580 case TCPS_SYN_SENT
: return MIB_TCP_STATE_SYN_SENT
;
1581 case TCPS_SYN_RECEIVED
: return MIB_TCP_STATE_SYN_RCVD
;
1582 case TCPS_FIN_WAIT_1
: return MIB_TCP_STATE_FIN_WAIT1
;
1583 case TCPS_FIN_WAIT_2
: return MIB_TCP_STATE_FIN_WAIT2
;
1584 case TCPS_TIME_WAIT
: return MIB_TCP_STATE_TIME_WAIT
;
1585 case TCPS_CLOSE_WAIT
: return MIB_TCP_STATE_CLOSE_WAIT
;
1586 case TCPS_LAST_ACK
: return MIB_TCP_STATE_LAST_ACK
;
1587 case TCPS_LISTEN
: return MIB_TCP_STATE_LISTEN
;
1588 case TCPS_CLOSING
: return MIB_TCP_STATE_CLOSING
;
1590 case TCPS_CLOSED
: return MIB_TCP_STATE_CLOSED
;
1595 static int compare_tcp_rows(const void *a
, const void *b
)
1597 const MIB_TCPROW
*rowA
= a
;
1598 const MIB_TCPROW
*rowB
= b
;
1601 if ((ret
= ntohl (rowA
->dwLocalAddr
) - ntohl (rowB
->dwLocalAddr
)) != 0) return ret
;
1602 if ((ret
= ntohs ((unsigned short)rowA
->dwLocalPort
) -
1603 ntohs ((unsigned short)rowB
->dwLocalPort
)) != 0) return ret
;
1604 if ((ret
= ntohl (rowA
->dwRemoteAddr
) - ntohl (rowB
->dwRemoteAddr
)) != 0) return ret
;
1605 return ntohs ((unsigned short)rowA
->dwRemotePort
) - ntohs ((unsigned short)rowB
->dwRemotePort
);
1609 /******************************************************************
1610 * AllocateAndGetTcpTableFromStack (IPHLPAPI.@)
1612 * Get the TCP connection table.
1613 * Like GetTcpTable(), but allocate the returned table from heap.
1616 * ppTcpTable [Out] pointer into which the MIB_TCPTABLE is
1617 * allocated and returned.
1618 * bOrder [In] whether to sort the table
1619 * heap [In] heap from which the table is allocated
1620 * flags [In] flags to HeapAlloc
1623 * ERROR_INVALID_PARAMETER if ppTcpTable is NULL, whatever GetTcpTable()
1624 * returns otherwise.
1626 DWORD WINAPI
AllocateAndGetTcpTableFromStack( PMIB_TCPTABLE
*ppTcpTable
, BOOL bOrder
,
1627 HANDLE heap
, DWORD flags
)
1629 MIB_TCPTABLE
*table
;
1631 DWORD ret
= NO_ERROR
, count
= 16;
1633 TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppTcpTable
, bOrder
, heap
, flags
);
1635 if (!ppTcpTable
) return ERROR_INVALID_PARAMETER
;
1637 if (!(table
= HeapAlloc( heap
, flags
, FIELD_OFFSET(MIB_TCPTABLE
, table
[count
] ))))
1638 return ERROR_OUTOFMEMORY
;
1640 table
->dwNumEntries
= 0;
1646 if ((fp
= fopen("/proc/net/tcp", "r")))
1648 char buf
[512], *ptr
;
1651 /* skip header line */
1652 ptr
= fgets(buf
, sizeof(buf
), fp
);
1653 while ((ptr
= fgets(buf
, sizeof(buf
), fp
)))
1655 if (sscanf( ptr
, "%x: %x:%x %x:%x %x", &dummy
, &row
.dwLocalAddr
, &row
.dwLocalPort
,
1656 &row
.dwRemoteAddr
, &row
.dwRemotePort
, &row
.dwState
) != 6)
1658 row
.dwLocalPort
= htons( row
.dwLocalPort
);
1659 row
.dwRemotePort
= htons( row
.dwRemotePort
);
1660 row
.dwState
= TCPStateToMIBState( row
.dwState
);
1661 if (!(table
= append_tcp_row( heap
, flags
, table
, &count
, &row
)))
1666 else ret
= ERROR_NOT_SUPPORTED
;
1668 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1672 mib2_tcpConnEntry_t
*entry
;
1674 if ((fd
= open_streams_mib( "tcp" )) != -1)
1676 if ((data
= read_mib_entry( fd
, MIB2_TCP
, MIB2_TCP_CONN
, &len
)))
1678 for (entry
= data
; (char *)(entry
+ 1) <= (char *)data
+ len
; entry
++)
1680 row
.dwLocalAddr
= entry
->tcpConnLocalAddress
;
1681 row
.dwLocalPort
= htons( entry
->tcpConnLocalPort
);
1682 row
.dwRemoteAddr
= entry
->tcpConnRemAddress
;
1683 row
.dwRemotePort
= htons( entry
->tcpConnRemPort
);
1684 row
.dwState
= entry
->tcpConnState
;
1685 if (!(table
= append_tcp_row( heap
, flags
, table
, &count
, &row
))) break;
1687 HeapFree( GetProcessHeap(), 0, data
);
1691 else ret
= ERROR_NOT_SUPPORTED
;
1693 #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
1697 struct xinpgen
*pXIG
, *pOrigXIG
;
1699 if (sysctlbyname ("net.inet.tcp.pcblist", NULL
, &Len
, NULL
, 0) < 0)
1701 ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1702 ret
= ERROR_NOT_SUPPORTED
;
1706 Buf
= HeapAlloc (GetProcessHeap (), 0, Len
);
1709 ret
= ERROR_OUTOFMEMORY
;
1713 if (sysctlbyname ("net.inet.tcp.pcblist", Buf
, &Len
, NULL
, 0) < 0)
1715 ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1716 ret
= ERROR_NOT_SUPPORTED
;
1720 /* Might be nothing here; first entry is just a header it seems */
1721 if (Len
<= sizeof (struct xinpgen
)) goto done
;
1723 pOrigXIG
= (struct xinpgen
*)Buf
;
1726 for (pXIG
= (struct xinpgen
*)((char *)pXIG
+ pXIG
->xig_len
);
1727 pXIG
->xig_len
> sizeof (struct xinpgen
);
1728 pXIG
= (struct xinpgen
*)((char *)pXIG
+ pXIG
->xig_len
))
1730 struct tcpcb
*pTCPData
= NULL
;
1731 struct inpcb
*pINData
;
1732 struct xsocket
*pSockData
;
1734 pTCPData
= &((struct xtcpcb
*)pXIG
)->xt_tp
;
1735 pINData
= &((struct xtcpcb
*)pXIG
)->xt_inp
;
1736 pSockData
= &((struct xtcpcb
*)pXIG
)->xt_socket
;
1738 /* Ignore sockets for other protocols */
1739 if (pSockData
->xso_protocol
!= IPPROTO_TCP
)
1742 /* Ignore PCBs that were freed while generating the data */
1743 if (pINData
->inp_gencnt
> pOrigXIG
->xig_gen
)
1746 /* we're only interested in IPv4 addresses */
1747 if (!(pINData
->inp_vflag
& INP_IPV4
) ||
1748 (pINData
->inp_vflag
& INP_IPV6
))
1751 /* If all 0's, skip it */
1752 if (!pINData
->inp_laddr
.s_addr
&&
1753 !pINData
->inp_lport
&&
1754 !pINData
->inp_faddr
.s_addr
&&
1755 !pINData
->inp_fport
)
1758 /* Fill in structure details */
1759 row
.dwLocalAddr
= pINData
->inp_laddr
.s_addr
;
1760 row
.dwLocalPort
= pINData
->inp_lport
;
1761 row
.dwRemoteAddr
= pINData
->inp_faddr
.s_addr
;
1762 row
.dwRemotePort
= pINData
->inp_fport
;
1763 row
.dwState
= TCPStateToMIBState (pTCPData
->t_state
);
1764 if (!(table
= append_tcp_row( heap
, flags
, table
, &count
, &row
))) break;
1768 HeapFree (GetProcessHeap (), 0, Buf
);
1771 FIXME( "not implemented\n" );
1772 ret
= ERROR_NOT_SUPPORTED
;
1775 if (!table
) return ERROR_OUTOFMEMORY
;
1778 if (bOrder
&& table
->dwNumEntries
)
1779 qsort( table
->table
, table
->dwNumEntries
, sizeof(row
), compare_tcp_rows
);
1780 *ppTcpTable
= table
;
1782 else HeapFree( heap
, flags
, table
);
1783 TRACE( "returning ret %u table %p\n", ret
, table
);