06-28-04 01:59 PM
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 BUFFE
R>
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
[ Post a follow-up to this message ]
|