Wireshark-dev: Re: [Wireshark-dev] Obtaining protocol offsets from dissection results

From: Eloy Paris <peloy@xxxxxxxxxx>
Date: Fri, 6 Jun 2008 13:36:47 -0400
Hi Guy,

On Thu, Jun 05, 2008 at 10:10:48PM -0700, Guy Harris wrote:

> > For each layer (protocol) in a packet I need to obtain the offset into
> > the packet. For example, for "eth:ip:icmp:data", the offsets would be:
> > 
> >     eth:   0
> >     ip:   14 (IP with no options)
> >     icmp: 34 (ICMP echo request)
> >     data: 42
> > 
> > I have been using the value of the "start" field of "struct field_info"
> > (epan/proto.h). However, I just found out that in some cases "start" can
> > be zero.
> 
> "Some cases" includes any case where you have reassembly - whether 
> IPv4/v6 fragmentation reassembly, reassembly of packet chunks in a TCP 
> stream, etc..

Oh, thanks, that explains it.

> It also includes cases where you have compressed packet data that's 
> decompressed before dissection (in which case it's not clear what the 
> offset would mean) or encrypted packet data that's decrypted before 
> dissection.
> 
> I.e., the general problem is insoluble.  What is it you're trying to do?

When netexpect dissects packets using libwireshark it tries to provide
as much information as possible to the user via Tcl variables so the
user can use those variables in scripts and do stuff. Regular fields
like ip.src, tcp.srcport, etc. are made available via Tcl variables with
the same name. However, to be able to do some things it is necessary to
go beyond the regular fields that standard packet dissection produces.
Offsets into a packet for the different protocols is one example.
Protocol names is another. Size of protocol headers is yet another.

Netexpect puts this non-standard information in a Tcl array called
"pdu". Here's an example of how one of those looks like:

netexpect> parray pdu
pdu(0,hdr_len) = 60
pdu(0,offset)  = 0
pdu(0,proto)   = frame
pdu(1,hdr_len) = 14
pdu(1,offset)  = 0
pdu(1,proto)   = eth
pdu(2,hdr_len) = 20
pdu(2,offset)  = 14
pdu(2,proto)   = ip
pdu(3,hdr_len) = 17
pdu(3,offset)  = 34
pdu(3,proto)   = icmp
pdu(4,hdr_len) = 9
pdu(4,offset)  = 42
pdu(4,proto)   = data
pdu(list)      = frame eth ip icmp data

With these additional variables, a script can do things like
"if {$pdu{2,proto} == "ip"} {do something with IP packet}", or
"set ippayload [barray _(packet) slice pdu(3,offset):", or the following
more complicated example that determines whether an ICMP message is an
error carrying an embedded IP datagram, or just an informational
message, like an ICMP echo request:

    if {[lindex $pdu(list) [expr [lsearch $pdu(list) icmp] + 1]] == "ip"} {
        set pdu_type "icmperror"
    } else {
        set pdu_type "icmp"
    }

I think the problem is insoluble depending on how much work you want
the libwireshark dissector to do. In other words, if you do not want
libwireshark to perform reassembly then it is easy to obtain the
offsets since a fragmented IP packet, for example, will be dissected as
eth:ip:data, with protocols offsets 0, 14, and 34. In this case, the
complicated analysis (reassembly, decompression, decryption) would have
to be done by the user's script. If you do want libwireshark to use full
analysis force then you hit the problem I experienced and that you so
eloquently explained.

Fortunately you guys have provided knobs for some of these things so
after I read your explanation I was able to get my offsets by disabling
reassembly of IPv6 packets.

In the future I may have to think about ways to make available to the
user the results of a packet dissection performed by libwireshark with
full analysis force enabled. For example, it'd be very nice to let
libwireshark perform full reassembly and provide the resulting packet to
the user so it can be used in scripts.

Thanks again for the help.

Cheers,

Eloy Paris.-
netexpect.org