Tutorial: The Basics, your first flow plugin

This tutorial gives you an introduction into plugin development for T2 in C. Why C? When you are in the business of time and memory performance, you have nothing to waste if you have to hold a million++ flows in memory. Sure, better would be assembler or HW. Actually we are in contact with HW developers such as NAPPA and TITAN IC.

Before we start, all unnecessary plugins should be deleted from the plugin folder ~/.tranalyzer/plugins. Then, basicFlow and txtSink must be compiled.

$ t2build -e
...
$ t2build tranalyzer2 basicFlow txtSink
...

If you didn’t read the tutorials before, here is the basis plugin which we will extend: tcpWin

Nevertheless, I’ll talk you through how to write a plugin from scratch. In the tcpWin all is set for successful compilation. I recommend to follow the steps in the next chapter so that you are able to produce new plugins yourself. Then compare with the tcpWin.

If you did not create a separate data and results directory yet, please do it now in another cmd window, that facilitates your workflow:

$ mkdir ~/data ~/results
$ cd data

The anonymized sample pcap can be downloaded here: annoloc2.pcap. Please extract it under your data folder: ~/data, if you haven’t already. Now you are all set for your first own plugin. Yeeeehaa!

Creating your Plugin from a template

Actually it is fairly simple to write plugins, if you have some knowledge about any programming language. Even if you write scripts, such as awk, you will quickly realize that T2 plugins are also organized in BEGIN, MIDDLE and END blocks.

In T2 documentation, you will find a chapter which explains all the nitty-gritty info about programming plugins. Here, we will concentrate on getting you started asap. For this reason, we produced a skeleton plugin: t2PSkel which compiles and produces output, and serves as a template for your own plugin. Let’s look at the skeleton under plugins. The alias tran moves to your T2 installation, where you started the setup.sh script.

$ tranpl
$ ls
arpDecode   binSink           dhcpDecode  fnameLabel   icmpDecode  lldpDecode   mysqlSink    nFrstPkts   pcapd           protoStats    regex_pcre  snmpDecode  sslDecode     tcpFlags      tp0f          wavelet
autogen.sh  cdpDecode         dnsDecode   ftpDecode    igmpDecode  macRecorder  natNudel     ntpDecode   pktSIATHisto    psqlSink      sctpDecode  socketSink  stpDecode     tcpStates     txtSink
basicFlow   connStat          entropy     geoip        ircDecode   modbus       nDPI         ospfDecode  popDecode       pwX           smbDecode   sqliteSink  syslogDecode  telnetDecode  voipDetector
basicStats  descriptiveStats  findexer    httpSniffer  jsonSink    mongoSink    netflowSink  p0f         portClassifier  radiusDecode  smtpDecode  sshDecode   t2PSkel       tftpDecode    vrrpDecode
$ cd t2PSkel/src
$ ls
Makefile.am  t2PSkel.c  t2PSkel.h

This is the simplest src plugin structure. The .c file containing the C code. The .h file containing all definitions, constants and includes and the Makefile.am file being used by the autotools to generate the makefiles. In the documentation (Appendix C: Creating a Custom Plugin), all details for plugin development are described. In this tutorial, we will only concentrate on the very basics to get you started.

Plugins are loaded by T2 according to a predefined order, which is defined by the so called plugin number. I extracted its definition from the documentation below.

plugin range    description
------------------------------------
000 - 099       Global
100 - 199       Basic L2/3/4 plugins
200 - 299       Service and routing
300 - 699       L7 protocols
700 - 799       Math and statistics
800 - 899       Classifier and AI
900 - 999       Output

Certain ranges are predefined to cluster plugins, to preserve some order. Nevertheless, you may pick any arbitrary number, but do NOT choose an already existing one. If you do, they are loaded in alphabetic order. The loading order is important for dependencies between plugins, and the processing order. So a plugin with number 103 can deliver results to a plugin with number 200, vice versa would require a time machine. Appendix E (Status) of the documentation details all the existing plugins, their status and their plugin numbers. So you can look it up and see which numbers are already taken (or use the $T2HOME/doc/list_plugin_number.sh script).

Let’s pick 150 because it is higher than the L4 plugins such as tcpFlags, which we will use later as a dependency (Dependencies, described in the Plugin dependencies tutorial are used to access results from other plugins). The new_plugin script helps you to produce a clone from the skeleton plugin where all the names and numbers match your choice.

$ cd ..
$ scripts/new_plugin
Usage: new_plugin plugin_name [plugin_number]
$ scripts/new_plugin tcpWin 150
Plugin 'tcpWin' created
$

The script reports if the number exists already, in which case we would need to choose a different number.

If you want to integrate your new plugin into the t2_aliases so that you can navigate just by invoking tcpWin then you need to invoke the following command

$ source t2_aliases
$ tcpWin
$ ls
AUTHORS  autogen.sh  ChangeLog  configure.ac  COPYING  doc  Makefile.am  NEWS  README  src  t2plconf  tests
$

Now you have a working plugin residing under the plugins directory and it is added into the t2aliases. I recommend that you remove the lines we do not need for this tutorial as we progress in the tutorial for simplicity and better bug detection.

The inner organs of a Plugin

Let’s start with the Makefile.am file. As outlined above, it contains compiler information. What is important for now is the _la_SOURCES keyword, which defines all files to be compiled and linked. So, if you write additional .c files for your plugin, then you have to add them. The rest is not important for now and will be discussed later.

$ cat Makefile.am
lib_LTLIBRARIES = libtcpWin.la

libtcpWin_la_SOURCES = tcpWin.c

#libtcpWin_la_CFLAGS =

The .c file of the plugin folder contains in general the following sections (one of T2_PLUGIN_INIT(...) or T2_PLUGIN_INIT_WITH_DEPS(...) MUST always be used, the rest is optional):

  • T2_PLUGIN_INIT("pluginName", "pluginVersion", t2_version_major, t2_version_minor) or
  • T2_PLUGIN_INIT_WITH_DEPS("pluginName", "pluginVersion", t2_version_major, t2_version_minor, "dep1,dep2")

  • initialize()
  • printHeader()
  • onFlowGenerated(packet_t *packet __attribute__((unused)), unsigned long flowIndex)
  • claimLayer2Information(packet_t *packet, unsigned long flowIndex)
  • claimLayer4Information(packet_t *packet, unsigned long flowIndex)
  • onFlowTerminate(unsigned long flowIndex)
  • onApplicationTerminate()

  • bufferToSink(outputBuffer_t *buffer)
  • void pluginReport(FILE *stream)
  • void monitoring(FILE *stream, uint8 state)

The last three are discussed in the Plugin Sinks, Adding plugin end report and Adding plugin monitoring tutorials. In this tutorial, we will focus on the basic first seven.

Writing a TCP flow control window size threshold detector

Let’s say you want to extract the window size from the TCP header and count the number of times the window size undercuts a certain threshold defined in your .h file.

So open tcpWin.c in your editor of choice.

First, your plugin must have a name, tcpWin, a version number, and an minimal required acceptance limit of the T2 core, in our case 0.8.7 The second number has to 0 and the third 8. If you put anything else the plugin will be rejected by the core, as our major version is 0.8. If you choose to have a 0.8.7 as a first paramter, no problem.

If dependencies are required, dependent plugin names have to be added such as tcpFlags or tcpStates, here we do not need any.

In order to initialize your structure defined in tcpWin.h implement the initialize() function. In t2PSkel.c you will find a good example, but we will concentrate only on the first necessary steps. T2 appends your structure to the flow structure of other plugins residing before in memory and multiplies it with the amount of the size of the total hachChainTableSize. The latter defines the maximum amount of flows which can be hold by T2 at any given time. It is defined in tranalyzer.h as HASHCHAINTABLE_BASE_SIZE and by a multiplication factor defined either by HASHFACTOR or the -f option. For the time being you do not need to worry about that, just use the calloc(...) function as shown below.

I deleted the packet mode and load the flow file part because it is simpler for starters to produce just a flow file. The packet mode is discussed thoroughly in the Adding plugin packet output tutorial. So this is what we like to see in the initialize callback:

So you are all set, your structure is initialized and good to go.

The top line of the flow file contains the names of the columns which are needed for post processing by tawk. Moreover, T2 supplies the **_headers.txt** file which contains a detailed description of each columns. Both information is to be defined in the printHeader() function. I removed in the tcpWin.c most of the lines in t2PSkel.c because we like to start simple.

For your window size undercut detector an eight bit hex status number and an uint32_t count variable are required. A uint64_t could also be used, but remember, every byte is multiplied by HASHCHAINTABLE_BASE_SIZE. You just have to handle the overflow condition, we come back to that later:

The printHeader() function should look like this, just delete the lines which are unnecessary from the skeleton code for clarity, or peek into the tcpWin.c:

Each bv_append_bv(...) function adds a new column in the *_flows.txt* file and a description in the *_headers.txt* file. The first parameter defines the full description in the *_headers.txt* file, the second the name of the column on top of the flows file. The third one is a repetition parameter, which defines that the following variables are of arbitrary repetitive nature. So it is 0. We’ll come back to this when discussing the output of vectors. The next number defines how many variables are incorporated in a column definition. In our case, 1.

We also wrote a few compiler functions which are more user friendly:

Now we need to tell T2 what should happen at the first packet of a flow. The function onFlowGenerated(packet_t *packet, unsigned long flowIndex) has access to the packet and flow structure defined in networkHeaders.h.

We will need the packet in this function, so we remove the __attribute__((unused)) tag from the skeleton. The flowIndex is the most important tool for flow handling, specifically an internal index to access the flow via the flows[flowIndex] array. The first line defines the pointer to your plugin structure of the newly created flow. Then, we need to clear your structure, because the memory will be reused, if this flow dies. Next, we need the pointer to the flow itself, defined at the bottom of networkHeaders.h. As we want to process L4 TCP only, we terminate further processing for layer 2 and non TCP Flows. Note that this is different from the skeleton code.

I left the flow pointer definition and the check for L2_FLOW status in the code, because later we need it. Currently these statements will be removed by the compiler, or you may delete them.

Whenever a packet for a flow arrives, your plugin receives a callback claimLayer4Information(packet_t *packet, unsigned long flowIndex). All operations on the packet have to happen here.

When the flow timeouts or the protocol demands an end, e.g. TCP FIN, RST, then your plugin receives an onFlowTerminate(unsigned long flowIndex) callback. Here, all final calculations can be implemented and the output is performed according to your definition in the printHeader() function. It begins with the standard pointer definition of flowP and your plugin struct.

The BLOCK_BUF can be used to prevent T2 from filling the output buffer. For example, if you are using, e.g., netflowSink (a plugin which creates its own buffer), then you do not need to fill in the main_output_buffer and can thus activate BLOCK_BUF. In order to honor this mode, surround your output code with the #if BLOCK_BUF == 0 and #endif // BLOCK_BUF == 0 pragmas. Note, that in the onFlowTerminate(...) callback, you do NOT have access to the packet structure anymore.

Another method is to use the new user friendly wrappers as follows. In this case, the #if BLOCK_BUF == 0 and #endif // BLOCK_BUF == 0 pragmas can be omitted:

I let you decide whether you want to be an old school or a new age t2 hacker.

Note, that if the output does not match your definition in printHeader(), then you end up with exceptions or output which looks all right but the numbers might be wrong. If you are thorough about this, you will have no problems in the future.

When T2 terminates, the plugin flow structure needs to be cleaned up to avoid memory leaks. Later, we will add monitoring and end report features in this function. For starters, a simple free(...) is enough.

Now we need to take care about the definitions in tcpWin.h. An extract is listed below. It includes global.h, which contains all definitions from the core about protocols, internal data structures and definitions. We define WINTHRES, so that we can change it without changing the code. The bits of the status variable are defined below, only one bit. It denotes that packets of the flow undershot our threshold defined in WINTHRES.

The variables in the flow structure of the plugin tcpWinFlow_t should always be arranged according to their size to reduce memory fragmentation. So all uint64_t and double are located before float and uint32_t. And last, but not least, come the uint8_t, char and char []. The extern assures that if your plugin is a dependence of another plugin, it can include your .h file and have access to data tcpWin created at runtime.

After you edited the skeleton code you should compare your implementation with tcpWin. It is always better to write code yourself to get acquainted with a new type of SW.

Compile, run, analyze and extend

Now your plugin is ready to compile with t2build or autogen.sh:

$ t2build tcpWin

Plugin 'tcpWin'

make: Entering directory '/home/wurst/tranalyzer2-0.8.7/plugins/tcpWin/doc'
make: Nothing to be done for 'clean'.
make: Leaving directory '/home/wurst/tranalyzer2-0.8.7/plugins/tcpWin/doc'
libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, 'build-aux'.
...
make[2]: Leaving directory '/home/wurst/tranalyzer2-0.8.7/plugins/tcpWin/src'
make[2]: Entering directory '/home/wurst/tranalyzer2/plugins/tcpWin'
make[2]: Leaving directory '/home/wurst/tranalyzer2/plugins/tcpWin'
make[1]: Leaving directory '/home/wurst/tranalyzer2/plugins/tcpWin'

Plugin tcpWin copied into /home/wurst/.tranalyzer/plugins


BUILD SUCCESSFUL

$

Now use the alias .tran, to move to the current plugin folder, by default under .tranalyzer/plugins. You will see your plugin. You actually did it, congratulations! Then move back with tran, they tranpl and try to move directly to your plugin with tcpWin

$ .tran
$ ls
100_basicFlow.so  150_tcpWin.so  901_txtSink.so  subnets4_HLP.bin  subnets6_HLP.bin
$ tran
$ ls
autogen.sh  ChangeLog  doc  plugins  README.md  scripts  setup.sh  tests  tranalyzer2  utils
$ tranpl
ls
arpDecode   binSink           dhcpDecode  fnameLabel   icmpDecode  lldpDecode   mysqlSink    ntpDecode   pktSIATHisto    psqlSink      sctpDecode  socketSink  stpDecode     tcpFlags      tftpDecode    vrrpDecode
autogen.sh  cdpDecode         dnsDecode   ftpDecode    igmpDecode  macRecorder  nDPI         ospfDecode  popDecode       pwX           smbDecode   sqliteSink  stunDecode    tcpStates     tp0f          wavelet
basicFlow   connStat          entropy     geoip        ircDecode   modbus       netflowSink  p0f         portClassifier  radiusDecode  smtpDecode  sshDecode   syslogDecode  tcpWin        txtSink
basicStats  descriptiveStats  findexer    httpSniffer  jsonSink    mongoSink    nFrstPkts    pcapd       protoStats      regex_pcre    snmpDecode  sslDecode   t2PSkel       telnetDecode  voipDetector
$ tcpWin
AUTHORS  autogen.sh  ChangeLog  configure.ac  COPYING  doc  Makefile.am  NEWS  README  src  t2plconf  tests
$

Everything worked? Nice! Now invoke t2 with the downloaded pcap

$ t2 -r ~/data/annoloc2.pcap -w ~/results
...
$

Open another bash window, cd to your results folder and look into the flow file:

$ cd ~/results
$ tcol annoloc2_flows.txt
%dir  flowInd  flowStat            timeFirst          timeLast           duration   numHdrDesc  numHdrs  hdrDesc                ethVlanID  srcIP                                    srcIPCC  srcIPWho                srcPort  dstIP                                    dstIPCC  dstIPWho                dstPort  l4Proto  tcpWinStat  tcpWinThCnt
A     59       0x0000000200004000  1022171701.692762  1022171701.692762  0.000000   1           3        eth:ipv4:icmp             0          138.212.187.10                           jp       "asahi kasei corpora"   0        201.116.148.149                          mx       "--"                    0        1        0x00        0
A     108      0x0000000200004000  1022171701.700133  1022171701.700133  0.000000   1           3        eth:ipv4:udp              0          138.212.184.165                          jp       "asahi kasei corpora"   8889     19.112.107.128                           us       "--"                    2001     17       0x00        0
A     138      0x0000000000004000  1022171701.700983  1022171701.700983  0.000000   1           3        eth:ipv4:tcp              0          138.212.189.36                           jp       "asahi kasei corpora"   1044     205.25.217.73                            au       "not allocated by ap"   29981    6        0x00        0
A     193      0x0000000000004000  1022171701.704267  1022171701.704267  0.000000   1           3        eth:ipv4:tcp              0          138.212.190.87                           jp       "asahi kasei corpora"   1068     70.128.194.122                           us       "--"                    1863     6        0x00        0
A     245      0x0000000200004000  1022171701.706591  1022171701.706591  0.000000   1           3        eth:ipv4:udp              0          138.212.188.99                           jp       "asahi kasei corpora"   7778     83.221.58.33                             de       "--"                    2009     17       0x00        0
A     262      0x0000080200028000  1022171701.707777  1022171701.707777  0.000000   1           4        eth:ipv4:ipv6:UNK(168)    0          cfb6:1c18:5010:faf0:7f66:0:101:80a       --       "--"                    0        6c2:6a7f:1:384b::c100                    --       "--"                    0        168      0x00        0
...
A     3999     0x0000000200008000  1022171706.884790  1022171707.007311  0.122521   1           3        eth:ipv6:tcp          	   0	      2001:70e8:d3ce:e202::30:26               --       "--"                    2128     2001:70e8:d3ce:e200:deaf:b9ff:1f0e:becf  --       "--"                    6668     6        0x01        2
B     3999     0x0000000200008001  1022171706.922668  1022171706.955234  0.032566   1           3        eth:ipv6:tcp              0          2001:70e8:d3ce:e200:deaf:b9ff:1f0e:becf  --       "--"                    6668     2001:70e8:d3ce:e202::30:26               --       "--"                    2128     6        0x01        2
...
A     1041     0x0000000000004000  1022171701.876636  1022171726.639226  24.762590  1           3        eth:ipv4:tcp              0          133.26.84.187                            jp       "meiji university"      4766     138.212.187.109                          jp       "asahi kasei corpora"   80       6        0x01        6
B     1041     0x0000000200004001  1022171701.877349  1022171726.639232  24.761883  1           3        eth:ipv4:tcp              0          138.212.187.109                          jp       "asahi kasei corpora"   80       133.26.84.187                            jp       "meiji university"      4766     6        0x00        0
...

If you scroll to the right, you will see your output column. So the flows where the status bit 0 is set have indeed suffered from a window size crunch to 0, meaning that the machine cannot swallow any packets anymore. The counter indicates how often this happened during the flow. So add basicStats and rerun T2

$ t2build basicStats
...
$ t2 -r ~/data/annoloc2.pcap -w ~/results
...
$

Change to your results shell and think! What are we interested in now? We want all the flows which are greater than a tcpWinThCnt relative to the packets being sent back to the sender with the window size 0. But how greater? Let’s choose a tcpWinThCnt large enough so that we do not select al the short flows and the relative counts to 90%. So before you change the code of your plugin it is smart to understand the problem at hand and code first questions in tawk. The tawk would then look like this:

$ tawk '$tcpWinThCnt > 20 && ($tcpWinThCnt / $numPktsSnt) > 0.9' annoloc2_flows.txt | tcol
%dir  flowInd  flowStat            timeFirst          timeLast           duration   numHdrDesc  numHdrs  hdrDesc       ethVlanID  srcIP            srcIPCC  srcIPWho               srcPort  dstIP            dstIPCC  dstIPWho               dstPort  l4Proto  numPktsSnt  numPktsRcvd  numBytesSnt  numBytesRcvd  minPktSz  maxPktSz  avePktSize  stdPktSize  minIAT  maxIAT    aveIAT     stdIAT      pktps     bytps     pktAsm       bytAsm     tcpWinStat  tcpWinThCnt
B     636      0x0000000200004001  1022171701.743464  1022171720.404545  18.661081  1           3        eth:ipv4:tcp  0          138.212.187.203  jp       "asahi kasei corpora"  6699     19.123.222.7     us       "--"                   1430     6        72          72           22812        0             18        928       316.8333    207.2777    0       0.590107  0.2591818  0.0801546   3.858297  1222.437  0            1          0x01        72
B     151      0x0000000200004001  1022171701.801634  1022171726.463532  24.661898  1           3        eth:ipv4:tcp  0          36.152.156.46    cn       "--"                   3296     138.212.185.188  jp       "asahi kasei corpora"  6699     6        79          78           17495        37            0         1172      221.4557    243.5155    0       0.987917  0.3121759  0.119054    3.203322  709.3939  0.006369427  0.9957792  0x01        76
B     1151     0x0000000200004001  1022171701.939343  1022171726.416686  24.477343  1           3        eth:ipv4:tcp  0          138.212.187.203  jp       "asahi kasei corpora"  6699     68.239.17.59     us       "arin"                 4768     6        76          76           26384        0             11        1132      347.1579    237.4721    0       0.600271  0.3220703  0.07129291  3.104912  1077.895  0            1          0x01        76
$

Hmmm, only B Flows. The bytAsm is 1, so no content is sent from A to B. To see a clearer picture lets have a more general tawk and store the resulting flow index in an environment variable and use it in another tawk where we select all the relevant A/B flows.

$ f="$(tawk -H '$tcpWinThCnt > 20 && ($tcpWinThCnt / $numPktsSnt) > 0.9 { s = s ";" $flowInd } END { print substr(s,2 ) }' annoloc2_flows.txt)"
$ tawk -v f="$f" 'flow(f)' annoloc2_flows.txt | tcol
%dir  flowInd  flowStat            timeFirst          timeLast           duration   numHdrDesc  numHdrs  hdrDesc       ethVlanID  srcIP            srcIPCC  srcIPWho               srcPort  dstIP            dstIPCC  dstIPWho               dstPort  l4Proto  numPktsSnt  numPktsRcvd  numBytesSnt  numBytesRcvd  minPktSz  maxPktSz  avePktSize  stdPktSize  minIAT  maxIAT    aveIAT     stdIAT      pktps     bytps     pktAsm       bytAsm      tcpWinStat  tcpWinThCnt
A     636      0x0000000000004000  1022171701.743423  1022171720.669713  18.926290  1           3        eth:ipv4:tcp  0          19.123.222.7     us       "--"                   1430     138.212.187.203  jp       "asahi kasei corpora"  6699     6        72          72           0            22812         0         0         0           0           0       0.700571  0.2628651  0.1036983   3.804232  0         0              -1          0x01        1
B     636      0x0000000200004001  1022171701.743464  1022171720.404545  18.661081  1           3        eth:ipv4:tcp  0          138.212.187.203  jp       "asahi kasei corpora"  6699     19.123.222.7     us       "--"                   1430     6        72          72           22812        0             18        928       316.8333    207.2777    0       0.590107  0.2591818  0.0801546   3.858297  1222.437  0              1           0x01        72
A     151      0x0000000200004000  1022171701.701921  1022171725.949576  24.247655  1           3        eth:ipv4:tcp  0          138.212.185.188  jp       "asahi kasei corpora"  6699     36.152.156.46    cn       "--"                   3296     6        78          79           37           17495         0         36        0.474359    3.579886    0       0.901739  0.3108674  0.1130646   3.216806  1.525921  -0.00636427    -0.9957792  0x00        0
B     151      0x0000000200004001  1022171701.801634  1022171726.463532  24.661898  1           3        eth:ipv4:tcp  0          36.152.156.46    cn       "--"                   3296     138.212.185.188  jp       "asahi kasei corpora"  6699     6        79          78           17495        37            0         1172      221.4557    243.5155    0       0.987917  0.3121759  0.119054    3.203322  709.3939  0.006369427    0.9957792   0x01        76
A     1151     0x0000000000004000  1022171701.937172  1022171726.415550  24.478378  1           3        eth:ipv4:tcp  0          68.239.17.59     us       "arin"                 4768     138.212.187.203  jp       "asahi kasei corpora"  6699     6        76          76           0            26384         0         0         0           0           0       0.838332  0.322084   0.09550799  3.104781  0         0              -1          0x00        0
B     1151     0x0000000200004001  1022171701.939343  1022171726.416686  24.477343  1           3        eth:ipv4:tcp  0          138.212.187.203  jp       "asahi kasei corpora"  6699     68.239.17.59     us       "arin"                 4768     6        76          76           26384        0             11        1132      347.1579    237.4721    0       0.600271  0.3220703  0.07129291  3.104912  1077.895  0              1           0x01        76
$

That confirms the picture. That must be a streaming of data, where the request IP is told not to send any data. To effectively select such flows we need $tcpWinThCnt/$numPktsSnt column which tells us the packet rate relative to the total TCP packets. Moreover the initial window size might be beneficial to know, for two reasons. First, it gives us an indication of where the window size algorithm of a particular flow starts, and it contains information about the type of operations behind the flow or service. Second it also indicates, together with the TTL, the type of operating system, so let’s add this as well.

In order to do so, we need to extract the TTL and the window size behind the L2_FLOW check in the onFlowGenerated callback. As T2 is currently in IPv4/6 and L2 mode the L3 header is different for IPv4 or IPv6. In order to spare you guys some complicated if’s and pragmas, We wrote compiler functions which return the type of IP based on the current mode of T2 core. This is achieved by the command if (PACKET_IS_IPV6(packet)).

It is always good practice to cast the layer3Header from the packet structure to the appropriate constant ipHeader structure defined in tranalyzer2/networkHeaders.h. Then, we store the TTL into flow variable, later to be defined in the .h file. At last, only for TCP, we cast the layer4Header to the tcpHeader structure and store the window size into a yet not defined flow variable. Note, that we need to reverse the byte order, if you are working on a little endian machine, which is unfortunately 99% the case on this planet. Just add the lines marked with <--. Note that if you omit the TCP protocol check, then all other protocols will write to tcpWinInit, thus creating false output instead of 0.

You are all fixed for the first packet and the flow creation process. Sure, the TTL could change during the flow, in case of multiple path routing, lets ignore that for the moment. For our relative window threshold count, we need the count of all TCP packets. Just add a pktTcpCnt variable and increment it, see line marked with <--.

Next we need to add two columns marked by <-- in the flow file defined in printHeader(). A 32-bit unsigned integer for the window size and an 8-bit unsigned integer for the TTL.

At flow timeout or termination, we would like to see the TTL and the initial window size in the output. We do so by adding the lines marked by <--.

Open the tcpWin.h file and add the lines marked by <--.

Now you are all set, compile your plugin and rerun T2.

$ t2build tcpWin
...
$ t2 -r ~/data/annoloc2.pcap -w ~/results

Change to your results window and get the previous tawk commands. Change the variable in the tawk to the new column $tcpWinSzThRt > 0.9 and invoke both tawk commands again:

$ f="$(tawk -H '$tcpWinThCnt > 20 && $tcpWinSzThRt > 0.9 { s = s ";" $flowInd } END { print substr(s, 2) }' annoloc2_flows.txt)"
$ tawk -v f="$f" 'flow(f)' annoloc2_flows.txt | tcol
%dir  flowInd  flowStat            timeFirst          timeLast           duration   numHdrDesc  numHdrs  hdrDesc       ethVlanID  srcIP            srcIPCC  srcIPWho               srcPort  dstIP            dstIPCC  dstIPWho               dstPort  l4Proto  numPktsSnt  numPktsRcvd  numBytesSnt  numBytesRcvd  minPktSz  maxPktSz  avePktSize  stdPktSize  minIAT  maxIAT    aveIAT     stdIAT      pktps     bytps     pktAsm        bytAsm      tcpWinStat  ipTTL  tcpInitWinSz  tcpWinThCnt  tcpWinSzThRt
A     636      0x0000000000004000  1022171701.743423  1022171720.669713  18.926290  1           3        eth:ipv4:tcp  0          19.123.222.7     us       "--"                   1430     138.212.187.203  jp       "asahi kasei corpora"  6699     6        72          72           0            22812         0         0         0           0           0       0.700571  0.2628651  0.1036983   3.804232  0         0             -1          0x01        115    32378         1            0.01388889
B     636      0x0000000200004001  1022171701.743464  1022171720.404545  18.661081  1           3        eth:ipv4:tcp  0          138.212.187.203  jp       "asahi kasei corpora"  6699     19.123.222.7     us       "--"                   1430     6        72          72           22812        0             18        928       316.8333    207.2777    0       0.590107  0.2591818  0.0801546   3.858297  1222.437  0             1           0x01        128    0             72           1
A     151      0x0000000200004000  1022171701.701921  1022171725.949576  24.247655  1           3        eth:ipv4:tcp  0          138.212.185.188  jp       "asahi kasei corpora"  6699     36.152.156.46    cn       "--"                   3296     6        78          79           37           17495         0         36        0.474359    3.579886    0       0.901739  0.3108674  0.1130646   3.216806  1.525921  -0.006369427  -0.9957792  0x00        127    17520         0            0
B     151      0x0000000200004001  1022171701.801634  1022171726.463532  24.661898  1           3        eth:ipv4:tcp  0          36.152.156.46    cn       "--"                   3296     138.212.185.188  jp       "asahi kasei corpora"  6699     6        79          78           17495        37            0         1172      221.4557    243.5155    0       0.987917  0.3121759  0.119054    3.203322  709.3939  0.006369427   0.9957792   0x01        112    36            76           0.9620253
A     1151     0x0000000000004000  1022171701.937172  1022171726.415550  24.478378  1           3        eth:ipv4:tcp  0          68.239.17.59     us       "arin"                 4768     138.212.187.203  jp       "asahi kasei corpora"  6699     6        76          76           0            26384         0         0         0           0           0       0.838332  0.322084   0.09550799  3.104781  0         0             -1          0x00        113    17095         0            0
B     1151     0x0000000200004001  1022171701.939343  1022171726.416686  24.477343  1           3        eth:ipv4:tcp  0          138.212.187.203  jp       "asahi kasei corpora"  6699     68.239.17.59     us       "arin"                 4768     6        76          76           26384        0             11        1132      347.1579    237.4721    0       0.600271  0.3220703  0.07129291  3.104912  1077.895  0             1           0x01        128    0             76           1
$

You see that you have your first operational flow mode plugin which you can extend to your liking.

Have fun!

The next tutorial will teach you how to add plugin end report

See Also