<!doctype linuxdoc system>
<article>
<!-- title information -->
<title>IPTables U32 Match Tutorial</title>
<!-- table of contents -->
<toc>

<!-- begin the document -->

<sect2>Introduction

<p>
IPTables has always been a relatively flexible and modular
firewall; if it can't currently test for a particular packet
characteristic, you have the option of writing a test or modifying an
existing test.  The catch - as with so much of open source - is that
most of us aren't programmers.

<p>
It turns out we do have an option that doesn't require
programming.  Don Cohen was kind enough to write an IPTables module that
pulls any bytes you'd like out of the packet, does some manipulation,
and sees if the result is in a particular range.  For example, I can
grab the Fragmentation information out of the IP header, throw away
everything except the More Fragments flag, and see if that flag is
set.

<p>
Without writing any C code at all.  :-)

<p>
What I'll do is introduce the core concepts here, and put in
hopefully enough annotated examples that you'll be able to write your
own tests.

<p>
I won't be focusing on <i>what</i> these fields are, or why
you'd want to test them; there are lots of (warning - shameless plug for
my employer ahead!) <url url="http://www.sans.org/" name="resources"> for
doing that.  If you simply need a quick reference for the packet
headers, see <url url="http://www.sans.org/resources/tcpip.pdf" name="tcpip.pdf">.

<p>
All byte positions in this article start counting at 0 as the
first byte of the header.  For example, in the IP header, byte "0" holds
the 4 bit "Version" and 4 bit "IP Header Length", byte "1" holds the
"TOS" field, etc.

<sect3>Check the value of a 2 byte field

<p>
In it's simplest form, u32 grabs a block of 4 bytes starting
at Start, applies a mask of Mask to it, and compares the result to
Range.  Here's the syntax we'll use for our first examples:

<tscreen><verb>
iptables -m u32 --u32 "Start&Mask=Range"
</verb></tscreen>

<p>
We'll generally pick a "Start" value that's 3 less than the
last byte in which you're interested.  So, if you want bytes 4 and 5 of
the IP header (the IP ID field), Start needs to be 5-3 = 2.  Mask strips
out all the stuff you don't want; it's a bitmask that can be as large as
0xFFFFFFFF.  To get to our target of bytes 4 or 5, we have to discard
bytes 2 and 3.  Here's the mask we'll use:  0x0000FFFF .  We'll actually
use the shorter, and equivalent, 0xFFFF instead.

<p>
So, to test for <b>IPID</b>'s from 2 to 256, the iptables
command line is:

<tscreen><verb>
iptables -m u32 --u32 "2&0xFFFF=0x2:0x0100"
</verb></tscreen>

<p>
To read this off from left to right: "Load the u32 module,
and perform the following u32 tests on this packet; grab the 4 bytes
starting with byte 2 (bytes 2 and 3 are the Total Length field, and
bytes 4 and 5 are the IPID), apply a mask of 0x0000FFFF (which sets the
first two bytes to all zeroes, leaving the last two bytes untouched),
and see if that value - the IPID - falls between 2 and 256 inclusive; if
so, return true, otherwise false."

<p>
There is no standalone IPID check in IPTables, but this is
the equivalent of the "ip[2:2] >= 2 and ip[2:2] <= 256" tcpdump/bpf filter.

<p>
I leave off actions in these examples, but you can add things
like:

<tscreen><verb>
-j LOG --log-prefix "ID-in-2-256 "<br>
-j DROP
</verb></tscreen>

<p>
or any other action.  You can also add other tests, as we'll
do in a minute.

<p>
Don offers this test to see if the <b>total packet length</b>
is greater than or equal to 256.  The total length field is bytes 2 and
3 of the IP header, so our starting position is 3-3 = 0.  Since we're
pulling out two bytes again, the mask will be 0xFFFF here as well.  The
final test is:

<tscreen><verb>
iptables -m u32 --u32 "0&0xFFFF=0x100:0xFFFF"
</verb></tscreen>

<p>
This is the same as:

<tscreen><verb>
iptables -m length --length 256:65535<br>
	or the bpf filter<br>
"len >= 256"
</verb></tscreen>

<sect3>Check the value of a 1 byte field

<p>
Much the same, except we'll use a mask of 0x000000FF (or it's
shorter equivalent 0xFF) to pull out a single byte from the 4 bytes u32
initially hands us.  Let's say I want to test the <b>TTL</b> field for
TTL's below 3 to find people tracerouting to us.  Yes, there's a ttl
module, but let's see how this would be done in u32.

<p>
I want to end up with byte 8 of the IP header, so my starting
position is 8-3 = 5.  Here's the test:

<tscreen><verb>
iptables -m u32 --u32 "5&0xFF=0:3"
</verb></tscreen>

<p>
Which is equivalent to:

<tscreen><verb>
iptables -m ttl --ttl-lt 4<br>
	or the bpf filter<br>
"ip[8] <= 3"
</verb></tscreen>

<!-- #ZZZZ FIXME - add bpf equivalents to these from here on. -->
<sect3>Looking at 4 bytes at once

<p>
To check a complete destination </b>IP address</b>, we'll
inspect bytes 16-19.  Because we want all 4 bytes, we don't need a mask
at all.  Let's see if the destination address is 224.0.0.1:

<tscreen><verb>
iptables -m u32 --u32 "16=0xE0000001"
</verb></tscreen>

<p>
This is equivalent to:

<tscreen><verb>
iptables -d 224.0.0.1/32
</verb></tscreen>

<p>
If we only want to look at the first three bytes (to check if
a source address is part of a given class C network), we'll need to use
a mask again.  The mask we'll use is 0xFFFFFF00 , which throws away the
last octet.  Let's check if the source address (from bytes 12-15,
although we'll ignore byte 15 with the mask) is in the class C network
192.168.15.0 (0xC0A80F00):

<tscreen><verb>
iptables -m u32 --u32 "12&0xFFFFFF00=0xC0A80F00"
</verb></tscreen>

<p>
Which is the same as:

<tscreen><verb>
iptables -s 192.168.15.0/24
</verb></tscreen>


<sect3>Inspecting early bytes in the header 

<!-- FIXME: Oooh, we can do negatives.  *grin*  -->

<p>
Obviously, if I want to look at the <b>TOS</b> field (byte 1 of the
IP header), I can't start at byte 1-3 = -2.  What we'll do instead is
start at byte 0, pull out the byte we want, and then move it down to the
last position for easy testing.  This isn't the only way we could do
this, but it helps demonstrate a technique we'll need in a minute.

<p>
To pull out the TOS field, I first ask u32 to give me bytes
0-3 by using an offset of 0.  Now, I pull out byte 1 (the second byte in
that block) with a mask of 0x00FF0000 .  I need to shift the TOS value
down to the far right position for easy comparison.  To do this, I use a
technique called, unsuprisingly, "right shift".  The symbol for right
shift is ">>"; this is followed by the number of bits right to move the
data.  If you're unfamiliar with right shift, take a look at this
<url url="http://www.harpercollege.edu/bus-ss/cis/166/mmckenzi/lect19/l19n.htm" name="tutorial from Harper College">.



<p>
I want to move TOS two bytes  - or 16 bits - to the right. 
This is done with ">>16".  Now that we have TOS in the correct position,
we compare it to 0x08 (Maximize Throughput):

<tscreen><verb>
iptables -m u32 --u32 "0&0x00FF0000>>16=0x08"
</verb></tscreen>



<p>
which is the equivalent of:

<tscreen><verb>
iptables -m ttl --tos 8
</verb></tscreen>


<sect3>Inspecting individual bits

<p>
I'd like to look at the "<b>More Fragments</b>" flag - a flag
which has no existing test in iptables (-f matches 2nd and further
fragments, I want to match all fragments except the last).  Byte 6
contains this, so I'll start with offset 3 and throw away bytes 3-5. 
Normally this would use a mask of 0x000000FF, but I also want to discard
the other bits in that last byte.  The only bit I want to keep is the
third from the top (0010 0000), so the mask I'll use is 0x00000020 . 
Now I have two choices; move that bit down to the lowest position and
compare, or leave it in its current position and compare.

<p>
To move it down, we'll right shift 5 bits.  The final test
is:

<tscreen><verb>
iptables -m u32 --u32 "3&0x20>>5=1"
</verb></tscreen>

<p>
If I take the other approach of leaving the bit where it is,
I need to be careful about the compare value on the right.  If that bit
is turned on, the compare value needs to be 0x20 as well.

<tscreen><verb>
iptables -m u32 --u32 "3&0x20=0x20"
</verb></tscreen>

<p>
Both approaches return true if the More Fragments flag is
turned on.


<sect3>Combining tests

<p>
If you want to inspect more than one aspect of a packet, use:

<tscreen><verb>
&&
</verb></tscreen>

<p>
between each test.


<sect3>Moving on to the TCP header

<p>
This is a little tricky.  Let's say I'd like to look at bytes
4-7 of the TCP header (the <b>TCP sequence number</b>).  Let's take the
simple approach first, and then look at some ways to improve this.

<p>
For our first version, let's assume that the IP header is 20
bytes long - usually a good guess.  Our starting point is byte 4 of the
tcp header that immediately follows the IP header.  Our simplistic test
for whether the sequence number is 41 (hex 29) might look like this:

<tscreen><verb>
iptables -m u32 --u32 "24=0x29"
</verb></tscreen>

<p>
For packets where the IP header length is 20, this will
actually work, but there are a few problems.  Let's fix them one by
one.

<p>
First, we never check to see if the packet is even a TCP
packet.  This is stored in byte 9 of the IP header, so we'll pull 4
bytes starting at byte 6, drop 6-8, and check to see if it's 6.  The new 
rule that first checks if this is a TCP packet at all and also checks
that the Sequence Number is 41 is:

<tscreen><verb>
iptables -m u32 --u32 "6&0xFF=0x6 && 24=0x29"
</verb></tscreen>

<p>
The second problem we've momentarily ignored is the IP header
length.  True, it usually is 20 bytes long, but it <i>can</i> be longer,
if IP options are used.

<!-- FIXME: explain header length encoding -->

<p>
Here are the steps.  We pull the IP header length (a nibble
that shows how many 4 bytes words there are in the header, usually 5)
out of the IP header.  We multiply it by 4 to get the number of bytes in
the IP header.  We use this number to say how many bytes to jump to get
to the beginning of the TCP header, and jump 4 more bytes to get to the
Sequence number.

<p>
To get the header length, we need the first byte:
<tscreen><verb>"0>>24"</verb></tscreen>, but we need to only grab the lower nibble and we
need to multiply that number by 4 to get the actual number of bytes in
the header.  To do the multiply, we'll right shift 22 instead of 24. 
With this shift, we'll need to use a mask of 0x3C instead of the 0x0F we
would have used.  The expression so far is: <tscreen><verb>"0>>22&0x3C"</verb></tscreen>. 
On an IP header with no options, that expression returns 20; just what
we'd expect.  Now we need to tell u32 to <i>use</i> that number and make
a jump that many bytes into the packet, a step performed by the "@"
operator.

<tscreen><verb>
iptables -m u32 --u32 "6&0xFF=0x6 && 0>>22&0x3C@4=0x29"
</verb></tscreen>

<p>
The "@" grabs the number we created on its left (20,
normally) and jumps that many bytes forward (we can even do this more
than once - see the TCP payload section below).  The 4 to its right
tells u32 to grab bytes 4-7, but u32 knows to pull them relative to the
20 bytes it skipped over.  This gives us the Sequence Number, even if
the IP header grows because of options.  *phew*!

<p>
The last quirk to handle is fragments.  When we were only
working with the IP header, this wasn't an issue; IP is designed in such
a way that the IP header itself can never be fragmented.  The TCP header
and application payload technically might be, and if we're handed the
second or further fragment, we might be looking not at the Sequence
Number in bytes 4-7, but perhaps some other part of the TCP header, or
more likely, some application layer data.

<p>
What we'll do is check that this is the first fragment (or an
unfragmented packet, the test won't care), so that we're sure we're
looking at tcp header info.  To do this, we test the fragment offset in
most (we discard the top three flag bits) of bytes 6 and 7 of the IP
header to make sure the offset is 0.  The test is:
<tscreen><verb>"4&0x1FFF=0"</verb></tscreen>

<p>
The final expression (check for TCP, check for unfragmented
packet or first fragment, and jump over the IP header, checking that
bytes 4-7 of the TCP header are equal to 41) is:

<tscreen><verb>
iptables -m u32 --u32 "6&0xFF=0x6 && 4&0x1FFF=0 && 0>>22&0x3C@4=0x29"
</verb></tscreen>

<p>
If the packet is, in fact, fragmented, we have one more
consideration; the fragment might be so small that the field we're
testing might have been put in a future fragment!  In this one case,
it's not an issue because every IP link should handle packets of at
least 68 bytes; even if the IP header was at its maximum of 60 bytes,
the first 8 bytes of the TCP header should be included in that first
fragment.

<p>
When we start testing for things further in to the packet,
we'll have to depend on u32's ability to simply return false if we ever
try to ask for a value that falls outside of the packet being
inspected.


<sect3>Checking for values in the ICMP header

<p>
Let's look for ICMP Host Unreachables (ICMP, type 3, code 1). 
Just as in the above example, we need to check for the Protocol field
(Protocol 1 = ICMP this time) and that we're looking at a complete
packet or at least the first fragment: <tscreen><verb>"6&0xFF=1 &&
4&0x1FFF=0"</verb></tscreen>

<p>
To check for the ICMP Type and Code, we skip over the IP
header again (<tscreen><verb>"0>>22&0x3C@..."</verb></tscreen>).  To grab the first two
bytes, we'll start at offset 0 and just right shift 16 bits.  The final
test is:

<tscreen><verb>
iptables -m u32 --u32 "6&0xFF=1 && 4&0x1FFF=0 && 0>>22&0x3C@0>>16=0x0301"
</verb></tscreen>


<sect3>Checking for values in the UDP payload

<p>
Lets try going all the way into the packet payload now, and
match packets that are UDP DNS queries.  Here we're not only going to
check for destination port 53, but we're also going to test the top bit
of byte 2 of the payload; if set, this is a DNS query.

<p>
We start by checking that this is a UDP packet:
<tscreen><verb>"6&0xFF=17"</verb></tscreen>.  We add the now familiar check for first
fragment: <tscreen><verb>"4&0x1FFF=0"</verb></tscreen>.

<p>
To test the destination port, we grab bytes 2 and 3 from the
udp header (after jumping over the IP header as in the previous
examples): <tscreen><verb>"0>>22&0x3C@0&0xFFFF=53"</verb></tscreen>.

<p>
If the packet has passed all of the above, we go back to
check the payload (remember we have to jump over the variable-length IP
and 8 byte UDP headers <tscreen><verb>"0>>22&0x3C@8 ..."</verb></tscreen>) to make sure
this is a DNS <i>query</i> rather than a response.  To grab the high bit
from byte 2, I'll use offset 8 to grab the first 4 payload bytes, right
shift 15 bits to deposit the Query bit in the lowest position, and throw
away all the rest of the bits with a mask of 0x01:
<tscreen><verb>"0>>22&0x3C@8>>15&0x01=1"</verb></tscreen>

<p>
The final test is:

<tscreen><verb>
iptables -m u32 --u32 "6&0xFF=17 && 4&0x1FFF=0 && 0>>22&0x3C@0&0xFFFF=53 && 0>>22&0x3C@8>>15&0x01=1"
</verb></tscreen>

<p>
Ugh.  I've seen stellar noise that had less entropy  :-) 
Note that we're doing the whole thing with u32 checks; we could pull out
the "udp", "first/no fragment" and "port 53" checks into other modules,
and end up with this slightly more readable version:

<tscreen><verb>
iptables -p udp --dport 53 \! -f -m u32 --u32 "0>>22&0x3C@8>>15&0x01=1"
</verb></tscreen>


<!-- #ZZZZ pull filters from here down to recap area -->



<sect3>Tests

<p>
First, a recap of the above, then some additional tests.

<descrip>

<tag><tscreen><verb>"2&0xFFFF=0x2:0x0100"</verb></tscreen>

</>Test for IPID's between 2 and 256

<tag><tscreen><verb>"0&0xFFFF=0x100:0xFFFF"</verb></tscreen>

</>Check for packets with 256 or more bytes.

<tag><tscreen><verb>"5&0xFF=0:3"</verb></tscreen>

</>Match packets with a TTL of 3 or less.

<tag><tscreen><verb>"16=0xE0000001"</verb></tscreen>

</>Destination IP address is 224.0.0.1

<tag><tscreen><verb>"12&0xFFFFFF00=0xC0A80F00"</verb></tscreen>

</>Source IP is in the 192.168.15.X class C network.

<tag><tscreen><verb>0&0x00FF0000>>16=0x08</verb></tscreen>

</>Is the TOS field 8 (Maximize Throughput)?

<tag><tscreen><verb>"3&0x20>>5=1"</verb></tscreen>

</>Is the More Fragments flag set?

<tag><tscreen><verb>"6&0xFF=0x6"</verb></tscreen>

</>Is the packet a TCP packet?

<tag><tscreen><verb>"4&0x1FFF=0"</verb></tscreen>

</>Is the fragment offset 0?  (If so, this is either an unfragmented
packet or the first fragment).

<tag><tscreen><verb>"0>>22&0x3C@4=0x29"</verb></tscreen>

</>Is the TCP Sequence number 41?  (This requires the previous two
checks for TCP and First Fragment as well)

<tag><tscreen><verb>"0>>22&0x3C@0>>16=0x0301"</verb></tscreen>

</>Check for ICMP type=3 and code=1 (needs UDP and first fragment tests too)

<tag><tscreen><verb>"0>>22&0x3C@0&0xFFFF=53"</verb></tscreen>

</>Is the UDP destination port 53?  (Check for udp and first/no
fragment first)

<tag><tscreen><verb>"0>>22&0x3C@8>>15&0x01=1"</verb></tscreen>

</>Check that the UDP DNS Query bit is set (again, check for UDP,
first/no fragment, and dest port 53 first).







































<p>
And now, some new tests:

<tag><tscreen><verb>"6&0xFF=1"</verb></tscreen>

</>Is this an ICMP packet?  (From Don Cohen's documentation)

<tag><tscreen><verb>"6&0xFF=17"</verb></tscreen>

</>Is this a UDP packet?

<tag><tscreen><verb>"4&0x3FFF=0"</verb></tscreen>

</>Is the fragment offset 0 <i>and</i> MF cleared?  (If so, this is an
unfragmented packet).

<tag><tscreen><verb>"4&0x3FFF=1:0x3FFF"</verb></tscreen>

</>Is the fragment offset greater than 0 <i>or</i> MF set?  (If so,
this is a fragment).

<tag><tscreen><verb>0>>22&0x3C@12>>26&0x3C@-3&0xFF=0:255</verb></tscreen>

</>Is there any payload on this tcp packet (check for tcp and not
fragmented first)?  This elegant test was contributed by Don Cohen as I
fumbled for a way to look for payload on a syn packet.  By simply
testing to see if payload byte 0 has a value between 0 and 255, we get
true if payload byte 0 exists (read: "if there is any payload at all"),
and false if we've gone beyond the end of the packet (read: "if there is
no payload").

<tag><tscreen><verb></verb></tscreen>

</>

<tag><tscreen><verb></verb></tscreen>

</>

<tag><tscreen><verb></verb></tscreen>

</>

<tag><tscreen><verb></verb></tscreen>

</>




</dl>


<hr>

<p>
Don Cohen wrote the u32 module, and also wrote some (if
you'll forgive me) somewhat cryptic documentation inside the source code
for the module.  William Stearns wrote this text, which borrows some
examples and concepts from Don's documentation.  Many thanks to Don for
reviewing an early draft of this article.  Thanks also to Gary Kessler
and Sans for making the TCP/IP pocket reference guide freely
available.

<!-- FIXME: add comma seperated and ranges (up to 10) on right of = -->

</article>























