NetFlow: weird TCP flags in FlowViewer and flow-print?

Working with FlowViewer and flow-print (from the flow-tools suite), if you filter some NetFlow data by TCP flags you may notice a weird behaviour, like the one in the following picture:

FlowViewer - TCP Flags

Here I applied a filter on TCP Flags = 27, but on the output I had the “Fl” (Flag) column reporting 3! What’s up? Is 3 a kind of alias for 27? Is this a math puzzle? None of this!


First off, why did I filter for 27?

Well, NetFlow records contain a field reporting the cumulative OR-ed TCP flags seen on the flow. For example, in a regular TCP connection a client would send a SYN, then an ACK, then other optional PSHs and ACKs and finally a FIN. The table on the left reports a summary of TCP flags with their binary and decimal values.

If we “sum” (using the OR binary operator) all the flags used in a TCP connection (SYN [2] + ACK [16] + PSH [8] + FIN [1]) we have 27.

For example, if we try to connect to a port where no daemon is listening on, our client would send a SYN packet while the server would reply with a RST/ACK packet: in this scenario we would have 2 flows:

1) client-to-server: TCP Flags = 2 (just SYN [2]);
2) server-to-client: TCP Flags = 20 (RST [4] + ACK [16])

Now we know how TCP Flags work in NetFlow, but why 27 = 3???

Let’s start debugging FlowViewer using the DEBUG_VIEWER file into the Flow_Working directory; it tells us what FlowViewer executes in the background:

NETFLOWBOX:/usr/lib/cgi-bin/FlowViewer_3.3.1# cat Flow_Working/DEBUG_VIEWER

The output tells us the “hidden” command it uses, and we try to run it:

NETFLOWBOX:/usr/lib/cgi-bin/FlowViewer_3.3.1# /usr/bin/flow-cat -t "03/08/2010 08:44:59" -T "03/08/2010 16:50:01" /var/flows/MYROUTER/2010/2010-03/2010-03-08 | /usr/bin/flow-nfilter -f /usr/lib/cgi-bin/FlowViewer_3.3.1/Flow_Working/FlowViewer_filter_165206 -FFlow_Filter | /usr/bin/flow-print -f5
Start End Sif SrcIPaddress SrcP DIf DstIPaddress DstP P Fl Pkts Octets

0308.10:34:43.799 0308.10:34:44.995 66 192.168.0.154 1888 59 192.168.1.184 1433 6 3 15 2032
0308.11:00:31.339 0308.11:00:32.419 66 192.168.0.154 2472 59 192.168.1.184 1433 6 3 15 2034
0308.11:03:15.899 0308.11:03:16.715 66 192.168.0.154 2533 59 192.168.1.184 1433 6 3 15 2034
0308.11:18:00.811 0308.11:18:01.719 66 192.168.0.154 2877 59 192.168.1.184 1433 6 3 15 2042
0308.11:22:52.407 0308.11:22:53.483 66 192.168.0.154 3004 59 192.168.1.184 1433 6 3 15 2034
0308.11:26:47.751 0308.11:27:28.367 66 192.168.0.154 3107 59 192.168.1.184 1433 6 3 1420 1963241
0308.11:36:46.107 0308.11:36:47.195 66 192.168.0.154 3287 59 192.168.1.184 1433 6 3 15 2034
0308.11:41:42.115 0308.11:41:43.815 66 192.168.0.154 3297 59 192.168.1.184 1433 6 3 15 2034

Here it is, the antepenultimate column is the “Fl” (Flag), and it is = 3!

So, let’s go deep in the command; it uses flow-cat to merge some NetFlow data files, then it passes the output to the flow-nfilter and, finally, it runs flow-print with the -f5 argument.
The f option just tells flow-print how to format data… Well, let’s try to use another format (man flow-print is our friend here!): let’s switch to -f1!

NETFLOWBOX:/usr/lib/cgi-bin/FlowViewer_3.3.1# /usr/bin/flow-cat -t "03/08/2010 08:44:59" -T "03/08/2010 16:50:01" /var/flows/MYROUTER/2010/2010-03/2010-03-08 | /usr/bin/flow-nfilter -f /usr/lib/cgi-bin/FlowViewer_3.3.1/Flow_Working/FlowViewer_filter_165206 -FFlow_Filter | /usr/bin/flow-print -f1
Sif SrcIPaddress DIf DstIPaddress Pr SrcP DstP Pkts Octets
StartTime EndTime Active B/Pk Ts Fl

0042 192.168.0.154 003b 192.168.1.184 06 760 599 15 2032
0308.10:34:43.799 0308.10:34:44.995 1.196 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 9a8 599 15 2034
0308.11:00:31.339 0308.11:00:32.419 1.080 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 9e5 599 15 2034
0308.11:03:15.899 0308.11:03:16.715 0.816 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 b3d 599 15 2042
0308.11:18:00.811 0308.11:18:01.719 0.908 136 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 bbc 599 15 2034
0308.11:22:52.407 0308.11:22:53.483 1.076 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 c23 599 1420 1963241
0308.11:26:47.751 0308.11:27:28.367 40.616 1382 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 cd7 599 15 2034
0308.11:36:46.107 0308.11:36:47.195 1.088 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 ce1 599 15 2034
0308.11:41:42.115 0308.11:41:43.815 1.700 135 00 1b

This format is not on a single line, but two lines are used for each NetFlow record; the TCP Flags values are in the second line of each record, in the last field, and they are in hex, and … well, they are set on “1b” = 27 (decimal)! So, it’s “just” an output issue. If we check the source code, we can understand why we see 3 instead of 27:

from flow-print.c:

int format5(struct ftio *ftio, int options)
{

[...]

  while ((rec = ftio_read(ftio))) {

    [...]

    cur.tcp_flags = ((u_int8*)(rec+fo.tcp_flags));

    [...]

    printf("%-5u %-15.15s %-5u %-5u %-15.15s %-5u %-3u %-2d %-10lu %-10lun",

           (u_int)*cur.input, fmt_buf1, (u_int)*cur.srcport,
           (u_int)*cur.output, fmt_buf2, (u_int)*cur.dstport,
           (u_int)*cur.prot,
           (u_int)*cur.tcp_flags & 0x7,
           (u_long)*cur.dPkts,
           (u_long)*cur.dOctets);

This is the format5 routine used to print NetFlow data when the -f5 option is given; please note line 19. It takes the full TCP Flags value, applies a 0x7 mask with the AND binary operator and finally it prints the result. The 0x7 value in binary is 0000 0111; it means that, whatever original flag is, the routine always uses the last 3 bit only. Here it is the “27 = 3” puzzle’s solution: 27 (decimal) = 0001 1011, so it only considers last 3 bits, 011 = 3 (decimal).

Back to our previous examples…

A client connecting to a “closed” port will produce a flow record with just a SYN: binary 0000 0010, decimal 2, no problems here, 3 bits are enough for a full representation even in flow-print.

The server receiving a connection request for a closed port will produce a flow with a RST + ACK: binary 0000 0100 + 0001 0000 = 0001 0100, decimal 20; here we’ll have just the 3-bits view of the number, so flow-print will report decimal 4 (binary 100).

Of course, this output format issue may make troubleshooting and analysis quite difficult; every suspect behaviour has to be analyzed through the command line with the -f1 option of flow-print.

UPDATE: you may be interested in FlowGraph too, a tool that allows to dynamically build graphs based on previously collected netflow data and to use them in a web-based front-end, adding details about Autonomous System Number holders, IPv4 and IPv6 prefixes, inet(6)num objects, netnames from RIPE Stat.

The following two tabs change content below.
Italian, born in 1980, I started working in the IT area in the late '90s; I'm now a system and network administrator with a deep knowledge of the global Internet and its core architectures.

3 Comments

  1. Jake Wilson says:

    For NetFlow reporting, please consider Scrutinizer as well from plixer.com

    Jake

  2. dreamhunter says:

    Man… You’re awesome!

  3. Valentin says:

    Thanks wonderful post and guide helped me a lot.

Leave a Reply