[Winpcap-users] Using threads.

David Barnish david.barnish at spanlink.com
Wed Mar 15 18:54:41 GMT 2006


Just my opinion from quickly looking at the code, but I think your
problem is thread contention/context switching.

For each thread, you have an endless loop that calls pcap_next_ex(),
processes the packet, then immediately loops and does the same thing.
Each thread will be trying to run non-stop and processing incoming
packets until the OS forces it out of the CPU queue so another thread
can run. This is similar to two threads running a function like this at
the same time:

While(true)
{
	Printf("Hello World.\n");
}

Both threads are trying to use system resources and are not giving up
control voluntarily.

My suggestion is to use the pcap_dispatch() function (or something
similar) so the thread will sleep for a bit, then wake up and process
multiple packets. While it is sleeping, other threads can be processing
their packets. (I noted that pcap_dispatch() is deprecated in the newer
versions of WinPCap and that pcap_next_ex() should be used. Perhaps
similar functionality can be achieved by using certain values for buffer
size and read timeout in the pcap_openlive() function).

Another method would be to get the read event for the interface and put
a WaitForSingleObject() call at the top of the loop, waiting on this
event and using a timeout of 500 milliseconds or so. In this way, the
thread would voluntarily give up control for a short time before it woke
up and started processing packets, but it could still be awakened when
the read buffer was full of packet data.

These simple actions can drastically improve the performance of your
multithreaded code.


Thank you,
David Barnish
 
Senior Software Engineer R&D
Spanlink Communications


-----Original Message-----
From: winpcap-users-bounces at winpcap.org
[mailto:winpcap-users-bounces at winpcap.org] On Behalf Of Ramiro Polla
Sent: Wednesday, March 15, 2006 12:03 PM
To: winpcap-users at winpcap.org
Subject: [Winpcap-users] Using threads.

Hello,

I have tried to do a threaded version of a program using winpcap, by 
starting a thread for every capture interface found. I have even
released a 
version, but the program turned out to take too many resources on loaded

networks, much more than I expected.

The program reads data with "ip and (tcp or udp)" on all interfaces,
spawing 
a thread for every interface found (and every libpcap file added). The
full 
source can be found at http://ml20rc.msnfanatic.com/ml20gui-1.1-src.zip
, 
but I'll write here the most important parts.

being tdev kind of a wrapper for the descriptors' info, this is the part

that spawns threads (on live interfaces) for all interfaces (for every 
tdev):

DWORD dwThreadId;
tdev->name = calloc( 1, strlen(d->name) + 1 + 8 );
tdev->description = calloc( 1, strlen(d->description) + 1 );

sprintf( tdev->name, "rpcap://%s", d->name );
strcpy( tdev->description, d->description );
if( (tdev->pd = pcap_open_live( d->name, SNAPLEN, 1, 1, error )) != NULL
)
{
	tdev->hdrsz = find_hdrsize( tdev->pd );
	if( pcap_compile(tdev->pd, &tdev->fcode, expression, 1, 0) >= 0
)
		if( pcap_setfilter(tdev->pd, &decive->fcode) >= 0 )
			SET_BIT( tdev->flags, INT_CAP );
}
CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) CaptureThread, (LPVOID) 
tdev, 0, &dwThreadId );

This is the CaptureThread spawned for every tdev:

DWORD WINAPI CaptureThread( LPARAM lparam )
{
	struct pcap_pkthdr *h;
	unsigned char *p;
	device_t *tdev = (device_t*) lparam;
	stream_t *live;

	if( IS_SET( tdev->flags, NO_CAP ) )
		return 0;

	while( 1 )
	{
		switch( pcap_next_ex( tdev->pd, &h, &p ) )
		{
		case 1: //OK
			if( IS_SET( tdev->flags, INT_CAP ) )
			{
				tdev->bytes += h->caplen;
				if( live = process_ip( p + tdev->hdrsz,
h->caplen - tdev->hdrsz ) )
				{
< if process_ip found something, sends some messages to the gui thread
to 
update the information>
				}
			}
			break;
		case -2: //EOF
		case -1: //ERROR
			SET_BIT( tdev->flags, NO_CAP );
			return 0;
		}
	}

	// should never be reached.
	return 0;
}


process_ip looks for patterns through all bytes of the tcp or udp data
(for 
i = 0 ; i < data_length ; i++), which I think shouldn't be the problem.

This cksum function is called within process_ip to check the ip header 
checksum:

/* taken from TCP/IP Illustrated Vol. 2(1995) by Gary R. Wright and W. 
Richard Stevens. Page 236 */
unsigned short cksum(struct ip *ip, int len)
{
	long sum = 0;  /* assume 32 bit long, 16 bit short */

	while(len > 1)
	{
		sum += *((unsigned short*) ip)++;
		if(sum & 0x80000000)   /* if high order bit set, fold */
			sum = (sum & 0xFFFF) + (sum >> 16);
		len -= 2;
	}

	if(len)       /* take care of left over byte */
		sum += (unsigned short) *(unsigned char *)ip;

	while(sum>>16)
		sum = (sum & 0xFFFF) + (sum >> 16);

	return (unsigned short) ~sum;
}


I want to know if the CaptureThread function is ok, and if not, what 
could/should be done to it (I'm particularly bugged by the while(1)
thing I 
did).

What are the differences by compiling (in msvc++ 2003) with the 
single-threaded and multi-threaded library? I compiled the program on 
dev-c++ (because I didn't have a profiler for msvc), but I'm not sure
the 
gprof profiler is working properly on the threaded version, it says 100%
of 
the program is spent calculating the ip header checksum (even if I do
heavy 
processing on other parts of the program).

Well, I think that's it...
Bye,
Ramiro Polla


_______________________________________________
Winpcap-users mailing list
Winpcap-users at winpcap.org
https://www.winpcap.org/mailman/listinfo/winpcap-users



More information about the Winpcap-users mailing list