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.

Reply via email to