tcpdump mailing list archives

Re: Bug in libpcap: savefile.c / get_selectable_fd()


From: "Shaked, Nitzan" <nshaked () paypal com>
Date: Thu, 19 Mar 2009 09:39:46 -0000

Hello again

Thanks for the reply. With your permission, I'd like to keep this up a
little bit (I am actually quoting not in the original order, to cover
the "easy" part first):

If that means that you can't tell the difference between "end of file

on the pipe", "no more packets available right now", and "an error  
occurred while reading from the pipe", as might be the case, file a  
bug on that.

Actually, it *will* mean that. pcap_next_ex() calls pcap_offline_read()
(in the case of a savefile), which in turns calls sf_next_packet(),
which does the fread().

If we introduce the notion of non-blocking, then two things happen:

1) The translation of return values from sf_next_packet() and from
pcap_offline_read() cannot, indeed, distinguish between EOF / IO error /
truncated packet, and needs to be somewhat changed.
2) I believe the current code doesn't have the notion of "non-blocking",
which it doesn't have the notion of "I read only part of what was
request, maybe half a packet, or half a header", so there will have to
be a new return value just for that. See below for a more elaborate
suggestion.

I think you should put the pipe in non-blocking mode and, when  
select() says the descriptor is readable, keep calling pcap_next()  
until you get an error or EOF indication.

This seems like a good idea, and I'd love to actually do it. However,
there are currently several issues with it. I hope I am not overstepping
my boundaries here by listing my thoughts:

1) Encapsulation. From your suggestion to put the fd in non-blocking
mode, I understand you mean to actually call fcntl() myself (not from
inside libpcap, since pcap_setnonblock() does nothing on savefiles).
This means a flow which goes something like this:

if ( is_tty(filename) || is_pipe(filename) || is_socket(filename) ... )
{
        fp = fopen( ... );
        fcntl( fp, ... );
        pcap_fopen_offline( fp, ... );
}

... I can't use pcap_open_offline(), since it will do its own fopen()
and I'll lose the non-blocking effect. So in effect pcap_open_offline()
becomes redundant. Everybody will have to know about the special
non-blocking thing, do the logic I just mentioned above, and call
pcap_fopen_offline().

2) Bug (?) in the current code. In savefile.c, in pcap_fopen_offline(),
there's an fread() to read the savefile's header. The fread goes
something like "amt_read = fread( &hdr, 1, sizeof(hfr), fp )", and then
asking "if ( amt_read != sizeof(hdr) )". So we're asking to read
sizeof(hdr) chars (portability issue: not all platforms have 8bit
chars). If we're in non-blocking mode, and reading from a pipe, say, it
could be that there are only 4 bytes available right now, but there will
be more available later. The current code will give an error (truncated
header), because the amount returned will be 4. This raises 2 issues:

2.1) The code perhaps should do headers_read = fread( &hfr, sizeof(hdr),
1, fp ) and then ask whether headers_read==1 or not.
2.2) A more general issue, see below in #3.

3) The "non-blocking may return less than what you asked for" beast will
also manifest in all other freads. For example, the fread() to read a
packet header. Or the subsequent fread() to read the packet itself. In
those cases, maybe half the packet is available now and the other half
will be available in a subsequent call to fread(). This will happen
"all" the time (well, often enough), and so cannot result in an error.
Something like a state should be added here, saying "I am currently
reading a packet, and expecting 200 more bytes". If you get less than
200 bytes (say 50), return a new error code (something like
ERR_SF_AGAIN), and save those 50 bytes. Next time you are called
continue from there until you've read all 200 (or got a "real" error, or
EOF). Of course the same goes for reading the sf's header, and each
packet's header.

4) Regarding the SF_ERR_AGAIN I mention above, and regarding the
translation of return values from sf_next_packet() and
pcap_offline_read() I mention: once we introduce the notion of
non-blocking IO, it seems to me that we need to introduce new return
values all across:

4.1) sf_next_packet() should return:
        SF_OK (value: 1), in case everything went well
        SF_ERR (value: -1), in case of an IO error or a truncated
header/packet
        SF_EOF (value: 0), in case of EOF
        SF_AGAIN (value: -2), in case... well... that not enough data is
available right now, but might be available later.

4.2) pcap_offline_read() should return:
        "n" (n>0), in case everything went well, return the number of
packets read
        OFFLINE_READ_EOF (value: 0), in case sf_next_packet() returns
SF_EOF
        OFFLINE_READ_ERR (value: -1), in case sf_next_packet() returns
SF_ERR
        OFFLINE_READ_AGAIN (value: -2), in case sf_next_packet() returns
SF_AGAIN
        OFFLINE_READ_BREAK_LOOP (value: -3), if we were told to break
out of the loop

4.3) pcap_next_ex(), should return:
        PCAP_NEXT_OK (value: 1), in case everything went well
        PCAP_NEXT_AGAIN (value: 0), in case pcap_offline_read() returned
OFFLINE_READ_AGAIN (when reading from a savefile), or read_op() returned
0 (no packets before timeout, when reading from a live capture)
        PCAP_NEXT_ERR (value: -1), in case pcap_offline_read() returned
OFFLINE_READ_ERR (when reading from savefile), or read_op() returned -1
        PCAP_NEXT_EOF (value: -2), in case pcap_offline_read() returned
OFFLINE_READ_EOF

Note that a return value of 0 from pcap_next_ex(), according to my
suggestion, merges "no packets before timeout" when reading from live,
with "no data in non-blocking" when reading a savefile. I think they
have the same meaning to the application (basically: select() again and
call pcap_next_ex() the next time you can).

Anxiously awaiting comments,
Nitzan
-
This is the tcpdump-workers list.
Visit https://cod.sandelman.ca/ to unsubscribe.


Current thread: