Chapter 13. Gory Details (or the Life of a Packet in Snort)

Internal Details (or The Life of a Packet Inside Snort)

This section describes, in depth, what happens to a packet inside snort. It is not required to understand anything in this section to use snort effectively. However, if you plan on adding functionality to snort (or are just a code junkie), then this chapter will provide lots of useful information. Truthfully, this is one of the earlier chapters that I have written and is probably the most interesting to me. Of course I also really like the chapters on plugins (even though I have not written them all yet) and the chapter on writing plugins (even though I have not even started on it yet). But enough explanation onto the details.

The description of what happens to a packet when it enters snort starts where snort is actually capturing packets. It ignores all the boring details of parsing the commandline and reading the rules file. It skips over initializing the preprocessor and output plugins. It even bypasses all the gore of parsing the rules file and building the rule trees. While all of this stuff is important, it usually happens only once (when snort is first started), while the detection engine happens once per packet (and there can be thousands of these (?) per second. This is where the real power of snort lies, not in the rules parser. Anyone, even Marty, can write a rules parser and handle some rudimentary error checking.

The one line that makes it all happen is:

     pcap_loop(pd, pv.pkt_cnt, (pcap_handler)ProcessPacket, NULL)


This statement calls ProcessPacket for each packet that the pcap subsystem captures. So lets look at what ProcessPacket does with that packet.

In the most often used scenario (we are NOT being verbose and we ARE using rules), ProcessPacket just passes the packet onto the preprocessors. The preprocessors are each given a chance to look at the packet (one at a time) and do anything that they may need to. This can include rewriting the packet, writing data to the alert file, logging the packet, or toggling the detection flag. One interesting thing to note is that all of the preprocessors must look at the packet. There is no way for a preprocessor to signal that it considers the packet done and nobody else needs to look at it. So, if you load up a lot of preprocessors that do intensive work, then you will slow everything down. One good design criteria for a preprocessor is to quickly determine whether you want the packet or not. The quicker the preprocessor can decide if it wants the packet, the quicker the packet will pass through the system.

Once the preprocessors are done with the packet (and none of them turned off the detection flag), the packet is passes on to the detection system. The detection system then evaluates the packets against each of the three different rule trees. The three rules trees correspond to the three different types of rules that can be used. These are alert, pass, and log. The order these are checked against is determined by a command line option. By default the order is Alert->Pass->Log. This can be changed to Pass->Alert->Log by including -o on the command line.

Since the same technique is used for matching a packet against a rule despite the action the rule designates, I will only describe it once. For consistency, let us assume that we are doing a match against the alert tree.