Python on Windows: How to get network protocol statistics

doug
Thu, 23 Jul 2009 17:05:32 +0000

Just something I was playing around with today that I thought I might share.

I was curious about how to get network statistics in windows so I've been reading MSDN all day trying to work it out. I've written a little bit of python using the ctypes module to access the API on network statistics. All I can say is that doing these things on Linux is so much easier as you can just access the /proc directory and parse the system files there that contain such information.

Here's the snippet, please read the comment header as there is some funky behaviour I haven't quite understood yet.

# -------------------------------------------------
# Description: Prints out a nice report of protocol
#              statistics
#
# Author:      doug@neverfear.org
# Platform:    Windows XP/Vista
#
# Notes:
# This code snippet demonstrates how to access the
# GetTcpStatisticsEx WINAPI routines located in
# iphlpapi.dll using the ctypes python module.
#
# There is odd behaviour that I haven't quite
# figured out. The script is safe but for some
# unexplained reason on occasion this script hangs
# after completing it's task successfully or the
# python executable crashes. I have a gut feeling
# it's something to do with the C-style workings of
# the ctypes library and pythons garbage collection
# tripping up over one another. I've yet to locate
# the issue with sufficient certainty to say.
#
# If anybody knows why, mail me: doug@neverfear.org
# -------------------------------------------------



from ctypes import *
from ctypes.wintypes import DWORD
import socket, sys

Iphlpapi = windll.Iphlpapi

class PrintableStruct(Structure):

    def __dict__(self):
        l = {}
        for field, type in self._fields_:
            l[field] = repr(self.__getattribute__(field))
        return l

    def __str__(self):
        return str(self.__dict__())

    def __repr__(self):
        return repr(self.__dict__())

    def formatted_string(self):
        for field, type in self._fields_:
            value = self.__getattribute__(field)
            print "% 20s = %s" % (field, repr(value))





class TCPSTATS(PrintableStruct):
    """
    Modelled after the MIB_TCPSTATS structure.
    For more information:
    http://msdn.microsoft.com/en-us/library/aa366915(VS.85).aspx
    (Visited 2009-07-23)
    """
    _fields_ = [
        ("dwRtoAlgorithm", DWORD),
        ("dwRtoMin", DWORD),
        ("dwRtoMax", DWORD),
        ("dwMaxConn", DWORD),
        ("dwActiveOpens", DWORD),
        ("dwPassiveOpens", DWORD),
        ("dwAttemptFails", DWORD),
        ("dwEstabResets", DWORD),
        ("dwCurrEstab", DWORD),
        ("dwInSegs", DWORD),
        ("dwOutSegs", DWORD),
        ("dwRetransSegs", DWORD),
        ("dwInErrs", DWORD),
        ("dwOutRsts", DWORD),
        ("dwNumConns", DWORD)
    ]


class ICMPSTATS_EX(PrintableStruct):
    """
    Modelled after the MIBICMPSTATS_EX structure.
    For more information:
    http://msdn.microsoft.com/en-us/library/aa366819(VS.85).aspx
    (Visited 2009-07-23)
    """
    _fields_ = [
        ("dwMsgs", DWORD),
        ("dwErrors", DWORD),
        ("rgdwTypeCount", DWORD)
    ]


class ICMP_EX(PrintableStruct):
    """
    Modelled after the MIB_ICMP_EX structure.
    For more information:
    http://msdn.microsoft.com/en-us/library/aa366832(VS.85).aspx
    (Visited 2009-07-23)
    """
    _fields_ = [
        ("icmpInStats", ICMPSTATS_EX),
        ("icmpOutStats", ICMPSTATS_EX)
    ]

class IPSTATS(PrintableStruct):
    """
    Modelled after the MIB_IPSTATS structure.
    For more information:
    http://msdn.microsoft.com/en-us/library/aa366871(VS.85).aspx
    (Visited 2009-07-23)
    """
    _fields_ = [
        ("dwForwarding", DWORD),
        ("dwDefaultTTL", DWORD),
        ("dwInReceives", DWORD),
        ("dwInHdrErrors", DWORD),
        ("dwInAddrErrors", DWORD),
        ("dwForwDatagrams", DWORD),
        ("dwInUnknownProtos", DWORD),
        ("dwInDiscards", DWORD),
        ("dwInDelivers", DWORD),
        ("dwOutRequests", DWORD),
        ("dwRoutingDiscards", DWORD),
        ("dwOutDiscards", DWORD),
        ("dwOutNoRoutes", DWORD),
        ("dwReasmTimeout", DWORD),
        ("dwReasmReqds", DWORD),
        ("dwReasmOks", DWORD),
        ("dwReasmFails", DWORD),
        ("dwFragOks", DWORD),
        ("dwFragFails", DWORD),
        ("dwFragCreates", DWORD),
        ("dwNumIf", DWORD),
        ("dwNumAddr", DWORD),
        ("dwNumRoutes", DWORD)
    ]

class UDPSTATS(PrintableStruct):
    """
    Modelled after the MIB_UDPSTATS structure.
    For more information:
    http://msdn.microsoft.com/en-us/library/aa366929(VS.85).aspx
    (Visited 2009-07-23)
    """
    _fields_ = [
        ("dwInDatagrams", DWORD),
        ("dwNoPorts", DWORD),
        ("dwInErrors", DWORD),
        ("dwOutDatagrams", DWORD),
        ("dwNumAddrs", DWORD)
    ]

StatsFuncs = [
    # Protocol: (Statistics function pointer, statistics structure class)
    ("ICMP", Iphlpapi.GetIcmpStatisticsEx, ICMP_EX),
    ("TCP", Iphlpapi.GetTcpStatisticsEx, TCPSTATS),
    ("IP", Iphlpapi.GetIpStatisticsEx, IPSTATS),
    ("UDP", Iphlpapi.GetUdpStatisticsEx, UDPSTATS)
]


# Basic usage for IPv4 and IPv6 protocols (for reference):
#
# stats = TCPSTATS()
# print "IPv4 statistics:",
# if Iphlpapi.GetTcpStatisticsEx(byref(stats), socket.AF_INET) == 0:
#   print str(stats)
# else:
#   print "Error getting statistics for IPv4"
# print
# stats = TCPSTATS()
# print "IPv6 statistics:",
# if Iphlpapi.GetTcpStatisticsEx(byref(stats), socket.AF_INET6) == 0:
#   print str(stats)
# else:
#   print "Error getting statistics for IPv6"

for (Protocol, GetProtocolStats, StructType) in StatsFuncs:
    print "-" * 30
    print Protocol, "statistics"
    print "-" * 30
    print

    stats = StructType()
    if GetProtocolStats(byref(stats), socket.AF_INET):
        print "Error getting", Protocol, "v4 statistics"
    else:
        print Protocol, "v4 statistics"
        print "-" * 20
        stats.formatted_string()
    print
    if GetProtocolStats(byref(stats), socket.AF_INET6):
        print "Error getting", Protocol, "v6 statistics"
    else:
        print Protocol, "v6 statistics:"
        print "-" * 20
        stats.formatted_string()
    print

sys.exit(0)