Unix Programming - Parsing packets from BPF

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > June 2004 > Parsing packets from BPF





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author Parsing packets from BPF
chris

2004-06-26, 10:11 am

I can now capture packets via the BPF so I need to begin parsing them.

Do I do something like this?

struct bpfhdr *bpfHdr;
struct iphdr *ipHdr;

read(bpf, buf, len)
bpfHdr = (struct bpfhdr *) buf;
ipHdr = (struct ipHdr *) buf + bpfHdr->bh_hdrlen;

Lev Walkin

2004-06-26, 10:11 am

chris wrote:
> I can now capture packets via the BPF so I need to begin parsing them.
>
> Do I do something like this?
>
> struct bpfhdr *bpfHdr;
> struct iphdr *ipHdr;
>
> read(bpf, buf, len)
> bpfHdr = (struct bpfhdr *) buf;
> ipHdr = (struct ipHdr *) buf + bpfHdr->bh_hdrlen;



/* Read data */
read_len = read(bpf_fd, ps->buf, ps->buf_size);

/* Process data */
for(packet = ps->buf; (packet - ps->buf) < read_len;
packet += BPF_WORDALIGN(bpf_header->bh_hdrlen
+ bpf_header->bh_caplen) )
{
void *packet_start = packet + bpf_header->bh_hdrlen;
int toend_size = rd-((char *)ip_start-(char *)ps->buf);
int captured_size = bpf_header->bh_caplen;
if(toend_size < captured_size) /* Paranoya */
captured_size = toend_size;

process_packet(packet_start, captured_size);
}


--
Lev Walkin
vlm@lionet.info
chris

2004-06-26, 10:38 pm

Lev Walkin wrote:

> chris wrote:
>
>
>
>
> /* Read data */
> read_len = read(bpf_fd, ps->buf, ps->buf_size);
>
> /* Process data */
> for(packet = ps->buf; (packet - ps->buf) < read_len;
> packet += BPF_WORDALIGN(bpf_header->bh_hdrlen
> + bpf_header->bh_caplen) )
> {
> void *packet_start = packet + bpf_header->bh_hdrlen;
> int toend_size = rd-((char *)ip_start-(char *)ps->buf);
> int captured_size = bpf_header->bh_caplen;
> if(toend_size < captured_size) /* Paranoya */
> captured_size = toend_size;
>
> process_packet(packet_start, captured_size);
> }
>
>


Thanks for the code, I just have a few questions: How come bpf_header is
never incremented? Isn't there a bpfhdr for each packet (since there
can be more than one packet per read())? Could you also explain the
toend_size stuff?

Thanks again.
Lev Walkin

2004-06-28, 8:59 am

chris wrote:
> Lev Walkin wrote:
>
>
> Thanks for the code, I just have a few questions: How come bpf_header is
> never incremented?


I wanted it to be our little secret ;)

#define bpf_header ((struct bpf_hdr *)packet)

> Isn't there a bpfhdr for each packet (since there
> can be more than one packet per read())? Could you also explain the
> toend_size stuff?


The BPF user supplies the buffer reasonably big to accomodate several
packets. The kernel has similar buffer too.

Ideally, a kernel would never add another packet into its own buffer,
when there isn't enough space for the whole packet. It would just
drop the packet.

In addition to that, it would be ideal if the userspace buffer is
equal or bigger than the kernel buffer.

Given these two conditions, it is clear to see that the simple loop
over buffer's contents is completely safe:

for(packet = ps->buf; (packet - ps->buf) < read_len;
packet += BPF_WORALIGN(
((struct bpf_header *)packet)->bh_hdrlen
+ ((struct bpf_header *)packet)->bh_caplen) ) {
/*
* Please note that a packet pointer here points
* to the packet completely contained within
* the allocated buffer.
*/
}

However, unless these conditions are met (i.e., kernel does not throw
away packet when it can't be fully accomodated by the free kernel buffer
space, or the user-level buffer size is less than the kernel-buffer one),
there is no trust for the (packet) pointer. It can point to the start
of the packet which is not completely contained within the buffer space.
So, if one would want to print the packet contents, a buffer overrun
may occur.

For example, let's take an example of the kernel buffer larger than
the user space one:

Kernel buffer:
[packet-1][packet-2][packet-3][some-free-space]<END OF BUFFER>
User-space buffer:
[packet-1][packet-2][pack<END OF BUFFER>

Although kernel is presumably cautious as to not to put packet-4 into
its own buffer because the free space is too short for packet-4, the
user-space buffer will be filled UP TO the
MIN(kernel-buffer-useful-content-size, user-space-buffer-size).
When user-space-buffer-size is less than kernel-buffer-useful-content-size,
as shown above, the for() loop will yield a (packet) pointer pointing
to the start of packet-3. It is clear that the user space buffer does not
contain the whole packet.

It should be obvious by now that inside the for() loop one should check
that the end of packet is contained within the buffer. Hence, the
"toend_size" usage. That variable holds the amount of space between
the current start-of-a-packet pointer and the end of the allocated
buffer space (or the minimum between the allocated buffer space and
the number of bytes available in that buffer, {read_len|rd}, as shown
in the original example).

It should also be obvious, that I've made a mistake there by not checking
if the "bpf_header->bh_caplen" is fully contained inside the buffer space
in the first place. The correct loop would look like this:

#define bpf_header ((struct bpf_hdr *)packet)
size_t buf_size = ...;
char buffer[buf_size];
char *packet;
char *safe_end_of_data;
ssize_t read_len;

/* Read data */
read_len = read(bpf_fd, buffer, buf_size);

/* Make sure there is at least one complete bpf header */
safe_end_of_data = buffer + (read_len - sizeof(struct bpf_hdr));

/* Process data */
for(packet = buffer; packet < safe_end_of_data;
packet += BPF_WORDALIGN(bpf_header->bh_hdrlen
+ bpf_header->bh_caplen) )
{
void *packet_start = packet + bpf_header->bh_hdrlen;
int toend_size = read_len - (packet - buffer);
int captured_size = bpf_header->bh_caplen;

if(toend_size < captured_size) /* Paranoya */
captured_size = toend_size;

process_packet(packet_start, captured_size);
}


> Thanks again.



--
Lev Walkin
vlm@lionet.info
Nils O. Selåsdal

2004-06-29, 8:45 pm

On Fri, 25 Jun 2004 18:45:23 +0000, chris wrote:

> I can now capture packets via the BPF so I need to begin parsing them.
>
> Do I do something like this?
>
> struct bpfhdr *bpfHdr;
> struct iphdr *ipHdr;
>
> read(bpf, buf, len)
> bpfHdr = (struct bpfhdr *) buf;
> ipHdr = (struct ipHdr *) buf + bpfHdr->bh_hdrlen;

Just as a side note,have you considered the pcap library ?
(www.tcpdump.org), it makes writing network sniffers real easy.

Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com