Wrapping WinPCap

S

Shaun Baker

Hi,

I'm a bit of a novice at this but i'm running into a few problems
trying to wrap winpcap in C#. I know I can use a special socket flag
to pick up all packets across an interface but this solution needs to
work w/o an IP bound to the socket so winpcap is the only way to go
(and so I can get it working in mono). I have imported the following
functions.

[DllImport("wpcap.dll")]
public static unsafe extern IntPtr pcap_open_live(char* source, int
snaplen, int flags, int read_timeout, char* errbuf);

[DllImport("wpcap.dll")]
public static unsafe extern char* pcap_lookupdev(char* errbuff);

[DllImport("wpcap.dll")]
public static extern int pcap_next_ex(IntPtr p, ref pcap_pkthdr
pkt_header, StringBuilder pkt_data);

with the structs:

[StructLayout( LayoutKind.Sequential )]
public struct pcap_pkthdr
{
public timeval test;
public UInt32 caplen;
public UInt32 len;
}

[StructLayout( LayoutKind.Sequential )]
public struct timeval
{
public long tv_sec;
public long tv_usec;
}

The problem is twofold: first my pcap_pkthdr struct comes back all out
of whack (ie, everything is zero but tv_sec which is filled with a
huge garbage number. And the string returned seems to just be junk
(around five bytes worth). I am a floundering undergrad but this is my
first attempt at interop with unmanaged code and I was wondering if
anyone sees anything I'm doing. I sincerely appriciate any help anyone
can offer.

Respectfully,
CDT Shaun Baker
(e-mail address removed)
 
B

BMermuys

Hi,
inline

Shaun Baker said:
Hi,

I'm a bit of a novice at this but i'm running into a few problems
trying to wrap winpcap in C#. I know I can use a special socket flag
to pick up all packets across an interface but this solution needs to
work w/o an IP bound to the socket so winpcap is the only way to go
(and so I can get it working in mono). I have imported the following
functions.

[DllImport("wpcap.dll")]
public static unsafe extern IntPtr pcap_open_live(char* source, int
snaplen, int flags, int read_timeout, char* errbuf);

[DllImport("wpcap.dll", CharSet=CharSet.Ansi)]
public static extern IntPtr pcap_open_live(string source, int snaplen, int
flags, int read_timeout, StringBuffer errbuf);

Call like this:
const int PCAP_ERRBUF_SIZE = ...; // check the header files
StringBuilder errbuf = new StringBuilder(PCAP_ERRBUF_SIZE);
IntPtr handle = pcap_open_live (...,...,...,...,errbuf);
if (handle == IntPtr.Zero) Console.WriteLine( errbuf.ToString() );
[DllImport("wpcap.dll")]
public static unsafe extern char* pcap_lookupdev(char* errbuff);

[DllImport("wpcap.dll", CharSet=CharSet.Ansi)]
public static extern IntPtr pcap_lookupdev(StringBuilder errbuf);

Call like this:
StringBuilder errbuf = new StringBuilder(PCAP_ERRBUF_SIZE);
IntPtr pDev = pcap_lookupdev(errbuf);
string dev;
if (pDev != IntPtr.Zero) dev = Marshal.PtrToStringAnsi(pDev);
else Console.WriteLine ( errbuf.ToString() );
[DllImport("wpcap.dll")]
public static extern int pcap_next_ex(IntPtr p, ref pcap_pkthdr
pkt_header, StringBuilder pkt_data);

ref pcap_pkthdr means that the function can change it, but it doesn't want
to, it wants to set a pointer to a pcap_pkthdr. To be able to do that, give
a IntPtr as ref...

[DllImport("wpcap.dll")]
public static extern int pcap_next_ex(IntPtr handle, ref IntPtr pHeader, ref
IntPtr pData );

Call like this:
IntPtr pHeader = IntPtr.Zero; // pointer to header, will be set by function
IntPtr pData = IntPtr.Zero; // pointer will be set by function

int ret = pcap_next_ex( handle, ref pHeader, ref pData );
byte [] data;
pcap_pkthdr header;
if ( ret >=0 )
{
// copy structure pointed by pHeader to managed structure
Marshal.PtrToStruct ( pHeader, header );

// copy data pointed by pData to managed byte array
data = new byte[ header.len ];
Marshal.Copy ( pData, data, 0, header.len );
}
with the structs:

[StructLayout( LayoutKind.Sequential )]
public struct pcap_pkthdr
{
public timeval test;
public uint caplen;
public uint len;
}

long in c = int in c# (32bits)
[StructLayout( LayoutKind.Sequential )]
public struct timeval
{
public long tv_sec;
should be public int tv_sec;
public long tv_usec;
should be public int tv_usec;
 
G

Guest

BMermuys

Thank you for your help, that short amount of code really did teach me a lot of what I was doing adn what I was doing wrong. My biggest source of confusion was the pkt_hdr** passing. I thought I needed to declare the pkt_hdr and I didn't quite realize that the function did the malloc'ing for me. Other than a inspection of the code, should the fact that it wanted a pointer to a pointer tip me off that it was going to handle the allocation and instantiation of the actual datastructure? I would assume otherwise it would just request a pointer to it instead of a pointer-to-a-pointer. Thank you for your time, the long time in ada,c# and java has really rustied up my C skills

Respectfully
Shaun Baker
 
B

BMermuys

Hi,

Shaun Baker said:
Thank you for your help, that short amount of code really did teach me a lot of
what I was doing adn what I was doing wrong. My biggest source of confusion
was the pkt_hdr** passing. I thought I needed to declare the pkt_hdr and I
didn't quite realize that the function did the malloc'ing for me. Other than a
inspection of the code, should the fact that it wanted a pointer to a pointer tip
me off that it was going to handle the allocation and instantiation of the
actual datastructure?

Yes. But the best way to know is looking at c examples that use pcap
functions. The problem with signatures like pkt_hdr** is that you still
don't know who owns the memory. In case of pcap it seems that pcap always
owns the memory hence we don't free memory ourselfs...
I would assume otherwise it would just request a pointer
to it instead of a pointer-to-a-pointer. Thank you for your time, the long time
in ada,c# and java has really rustied up my C skills.

Funny you mention java, because the first prototype I gave, there is a
stringbuffer which ofcourse should be a stringbuilder :)

hth,
greetings
 
G

Guest

Thank you very much for your help. I got it working perfectly now. I just wanted to post a few changes that I did to get it working just in case someone has the same problem and is reading this post later.

1. Stringbuffer = StringBuilde
2. In the usage of pcap_next_ex, to convert the pHeader IntPtr to a usable instance of a header I had to use the object = Marshal.PtrToStructure(IntPtr,Type) call or I would receiver a value type exception, by doing Pcap_PktHeader header = (Pcap_PktHeader)Marshal.PtrToStructure(pHeader,typeof(Pcap_PktHeader)); this problem went away
3. When using pcap_lookupdev(...), when trying to use Marshal.PtrToAnsiString, I kept getting a bad dev string b/c after each letter it had a '\0' null character in the middle, very odd and I'm not sure why that happened. Insetad I just passed the returned pointer to pcap_open_live cal

Thank you agian for your help

Respectfully
Shaun Baker
 
G

Guest

I promise this is my last question but when i try and do the pcap_loop (pcap_next_ex works great) it will work a couple times and then return with an "object reference not set to an instance" exception.

I have defined the pcap_loop function as

[DllImport("wpcap.dll",CharSet=CharSet.Ansi)
public static extern void pcap_loop(IntPtr p, int cnt,pcap_callback theCallback, StringBuilder user)

where the original wa
int pcap_loop (pcap_t *p, int cnt, pcap_handler callback, u_char *user)

and my callback pcap_handler is
public delegate void pcap_callback(IntPtr user, IntPtr pkt_header, IntPtr pkt_data)

where the original wa
typedef void(* pcap_handler )(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data)

I tried to use StringBuilder instead of IntPtr but the pkt_data that I got back (when I got it didn't crash, was just a null reference), so I changed the u_char* to IntPtr and the allocate a byte array of header.len size and convert that to a string. I can't seem to figure out why it would work sometimes, it almost seems as if it's randomly screwing up the callback pointer. Thank you very much for the continued help, I am learning quite a bit about how this all works

Respectfully
CDT Shaun Bake
 
B

BMermuys

Hi,

Shaun Baker said:
Thank you very much for your help. I got it working perfectly now. I just
wanted to post a few changes that I did to get it working just in case
someone has the same problem and is reading this post later.
1. Stringbuffer = StringBuilder
yes, I think it's java that's got a stringbuffer
2. In the usage of pcap_next_ex, to convert the pHeader IntPtr to a usable
instance of a header I had to use the object =
Marshal.PtrToStructure(IntPtr,Type) call or I would receiver a value type
exception, by doing Pcap_PktHeader header =
(Pcap_PktHeader)Marshal.PtrToStructure(pHeader,typeof(Pcap_PktHeader)); this
problem went away.
3. When using pcap_lookupdev(...), when trying to use
Marshal.PtrToAnsiString, I kept getting a bad dev string b/c after each
letter it had a '\0' null character in the middle, very odd and I'm not sure
why that happened. Insetad I just passed the returned pointer to
pcap_open_live call

If there is a \0 after each character then this would indicate the string is
unicode. Try using PtrToStringUni, nevertheless it might be a good idea to
pass the pointer you got from open_live to lookup_dev.
Thank you agian for your help.

Glad I could help,
greetings
 
B

BMermuys

Hi,
inline

Shaun Baker said:
I promise this is my last question but when i try and

You may ask as much as you want. Offcourse we won't always be able to help.
And it's a good idea to search google newsgroup archive first.
do the pcap_loop (pcap_next_ex works great) it will work a couple times
and then return with an "object reference not set to an instance" exception.

I have defined the pcap_loop function as:

[DllImport("wpcap.dll",CharSet=CharSet.Ansi)]
public static extern void pcap_loop(IntPtr p, int cnt,pcap_callback
theCallback, StringBuilder user);
where the original was
int pcap_loop (pcap_t *p, int cnt, pcap_handler callback, u_char *user)

and my callback pcap_handler is:
public delegate void pcap_callback(IntPtr user, IntPtr pkt_header, IntPtr pkt_data);

where the original was
typedef void(* pcap_handler )(u_char *user, const struct pcap_pkthdr
*pkt_header, const u_char *pkt_data)
I tried to use StringBuilder instead of IntPtr but the pkt_data that I got back
(when I >got it didn't crash, was just a null reference), so I changed the

You should use an IntPtr because the framework has no idea that the size of
the data is inside header.len. It can't pin or copy by itself.
u_char* to IntPtr and the allocate a byte array of header.len size and convert
that to a string. I can't seem to figure out why it would work sometimes, it almost
seems as if it's randomly screwing up the callback pointer. Thank you very much
for the continued help, I am learning quite a bit about how this all works.

You don't tell us how you use the pcap_loop function. The only reason I
know this could happen is because you create a delegate instance inside the
function call. The managed code should keep a reference otherwise the GC
will cleanup the delegate instance...

pcap_loop (...., ..., new pcap_callback(...), ...); // bad

// pcap_callback_del would be a class variable or static variable
pcap_callback_del = new pcap_callback(...);
pcap_loop (...,..., pcap_callback_del, ..., ..);

HTH,
greetings
 
G

Guest

You called it, I should have realized that when I realized it when the pointer was getting seemingly 'lost'. Thank you again for everything. Have a nice night, look forward to being able to field answers to these types of questions one day.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top