[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Understanding the Decode subsystem and Writing Decode Plugins.



Hi,

I just checked the following file in to the subversion repository. It's
a quick howto for writing new protocols for firestorm. It will be in the
next release, and online eventually.

========================================================================
Firestorm v0.5.x - Programmers Manual. Part 1.
Understanding the Decode subsystem and Writing Decode Plugins.
Copyright (c) 2003 Gianni Tedesco <gianni at scaramanga dot co dot uk>
========================================================================

THE THEORY
==========

A packet contains many headers one encapsulated inside the other. Like
this:

 +--------------+
 + Ethernet Hdr +      Lower memory addresses
 +--------------+        (start of packet)
 +  IP Header   +
 +--------------+                |
 +  TCP Header  +                |
 +--------------+                V
 + HTTP Headers +
 +--------------+
 +              +
 +    data...   +      Higher memory addresses
 +              +         (end of packet)
 +--------------+

The decode information can be represented as a directed acyclic graph
like this:

    ether -> ip -> tcp -> http -> data

Because a network protocol can only encapsulate certain other specific
protocols within them, one can represent all possible packet decodes as
a bigger graph, and the individual packet decode is a subgraph of this.

    ether -> ip -> tcp -> smtp
          |     |      `-> http
          |     |      `-> pop3
          |     |      `-> telnet
          |     |
          |     `-> udp -> dns
          |     `->icmp
          `-> arp
          `-> ipv6

Eeach of these nodes is a 'struct proto', the subgraph (or path) through
here is stored inside 'struct packet' in the 'layer' array/vector.

Each node in the decode graph contains a linked list of all the nodes
that are possible to come after it (the 'children' member). For our
example the IP protocol will will contain links to tcp, then udp then
icmp.

Most network protocol structures have fields which tell us which
protocol is encapsulated within. For example IP has this field set to 6
for TCP and 17 for UDP and so on. These ID numbers are used to decide
which child we will call.

This tree lends itself well to a recursive algorithm. Infact the problem
is essentially recursive and there is no escaping it. Firestorm starts
the
decode process in the capdev plugins. The capture device knows what it
is
capturing from (eg: if the interface is an ethernet network card, then
we
are pretty sure that what we are getting is ethernet.

The decode plugins all do pretty much the same thing:
  1. What is the protocol field set to in this header?
  2. Look in our list for a matching protocol, if not found, terminate.
  3. If found, call the function pointer for that protocol (ie: recurse)

Infact the only reason decode functions are needed is because each
protocol header has different layout and different ways of encoding
which protocol is to come next. Some protocols have no children and
thus always terminate.


THE PRACTICE
============

Plugin decode plugins, the order of things:
 1. Check the packet space is big enough for the minimum size of
    your header, if not return.
 2. If your header is variable sized, take the size of the header,
    check there is enough room for it. If not return.
 3. Validate any other important fields such as checksums. Alert,
    if anything is really suspicious.
 4. Set the pointer in the next layer and increment pkt->llen.
 5. If p->llen < PKT_LAYERS, pass on to children remembering to
    blank the flags and session fields, if nothing is
    passed on, then call dispatch(pkt);

A sample plugin for foo protocol follows:

void foo_decode(struct packet *p)
{
	struct layer *l = &p->layer[p->llen].h.raw;
	struct foo_hdr *hdr = (struct foo_hdr *)p->layer[p->llen].h.raw;
	struct proto_child *pc;

	if ( l->h.raw + sizeof(struct foo_hdr) > p->end )
		return;

	if ( l->h.raw + ntohs(foo->len) > p->end )
		return;

	if ( !foo_csum(foo) ) {
		alert(&bad_csum, p);
		return;
	}

	p->layer[++p->llen].h.raw = l->h.raw + ntohs(foo->len);
	if ( p->llen >= PKT_LAYERS )
		goto finish;

	for(pc=l->proto->children; pc; pc=pc->next) {
		if ( l->h.proto == pc->id ) {
			p->layer[p->llen].flags = 0;
			p->layer[p->llen].session = 0;
			p->layer[p->llen].proto = pc->proto;
			pc->proto->decode(p);
			return;
		}
	}
finish:
	dispatch(p);
}

-- 
// Gianni Tedesco (gianni at scaramanga dot co dot uk)
lynx --source www.scaramanga.co.uk/scaramanga.asc | gpg --import
8646BE7D: 6D9F 2287 870E A2C9 8F60 3A3C 91B5 7669 8646 BE7D

Attachment: signature.asc
Description: This is a digitally signed message part