Plugin summary files

plugin development


When all data is processed or T2 is interrupted by Ctrl+C or a signal, then the end report is printed. At the same time certain global information can be printed by your plugin.

Getting started

Create folders for your data and results

If you have not created a separate data and results directory yet, please do it now. This will greatly facilitate your workflow:

mkdir ~/data ~/results

Reset tranalyzer2 and the plugins configuration

If you have followed the other tutorials, you may have modified some of the core and plugins configuration. To ensure your results match those in this tutorial, make sure to reset everything:

t2conf -a --reset

You can also clean all build files:

t2build -a -c

Empty the plugin folder

To ensure we are not left with some unneeded plugins or plugins which were built using different core configuration, it is safer to empty the plugins folder:

t2build -e -y

Are you sure you want to empty the plugin folder '/home/user/.tranalyzer/plugins' (y/N)? yes
Plugin folder emptied

Download the PCAP file

The PCAP file used in this tutorial can be downloaded here:

Please save it in your ~/data folder:

wget --no-check-certificate -P ~/data

Build tranalyzer2 and the required plugins

For this tutorial, we will need to build the core (tranalyzer2) and the following plugin:

We do not need flow output, so no sink plugin is needed.

As you may have modified some of the automatically generated files, it is safer to use the -r and -f options.

t2build -r -f tranalyzer2 basicFlow txtSink



Source code

In this tutorial, we will extend tcpWin06.tar.gz, the final version of the previous tutorial (Plugin Packet Mode).

If you are impatient, you can download the final versions of the tcpWin plugin we will develop in this tutorial.

To use one of those plugins, just unpack it in the plugins folder of your T2 installation.


tar -xf ~/Downloads/tcpWin07.tar.gz

And let t2_aliases know about it:

source "$T2HOME/scripts/t2_aliases"

Summary files

Every plugin can produce a summary of traffic content. Let’s generate a file which lists all IPs with their maximal winThCnt, so we could find the IP having the largest L4 trouble.

In order to produce just summary files, no t2OnLayer2(...)/t2OnLayer4(...) callback is needed. You could do everything in a packet callback, but that would be inefficient, as you go over the code for every packet. If it is possible, it is always good practice to move heavy processing to t2OnNewFlow(...)/t2OnFlowTerminate(...) callbacks, as it is only called once per flow.

For the sake of simplicity, we just have arrays and a global index as defined in the global section of the plugin.

So move to the tcpWin plugin and open tcpWin.h. Add the maximal size of the global IP/window size count array and the appendix of the global file. Just add the lines marked by // <-- (including the typedef gwz_t)


vi src/tcpWin.h


/* ========================================================================== */
/* ------------------------ USER CONFIGURATION FLAGS ------------------------ */
/* ========================================================================== */

#define TCPWIN_THRES      1 // TCP window size threshold undershoot flag
#define TCPWIN_MINPKTS   50 // <-- Summary file: minimal TCP packets seen to start saving process
#define TCPWIN_MAXWSCNT 100 // <-- Summary file: maximal number of window size threshold count array elements

#define TCPWIN_SUFFIX "_tcpwin.txt"  // <-- Summary file: file name suffix

/* ========================================================================== */
/* ------------------------- DO NOT EDIT BELOW HERE ------------------------- */
/* ========================================================================== */

// plugin defines

// tcpWinStat status variable
#define TCPWIN_STAT_THU 0x01 // TCP window size threshold undershoot

typedef struct {                      // <--
    ipAddr_t wzip  [TCPWIN_MAXWSCNT]; // <-- IP address array
    uint32_t tcpCnt[TCPWIN_MAXWSCNT]; // <-- TCP packet count
    float    wzCnt [TCPWIN_MAXWSCNT]; // <-- window size count
    int      wzi;                     // <-- window size index
} gwz_t;                              // <--

// Plugin structure


Note that we only select flows which contain more than 50 packets, to avoid small flow clutter.

So you are all set for the header file. Let’s now move to tcpWin.c, where we first need to define all necessary variables global to the plugin.

vi src/tcpWin.c


 * Plugin variables that may be used by other plugins (MUST be declared in
 * the header file as 'extern tcpWinFlow_t *tcpWinFlows;'

tcpWinFlow_t *tcpWinFlows;

 * Static variables are only visible in this file

// window size counts
static gwz_t  gwz;                      // <-- global window size structure

static uint32_t pktTcpCnt;              // Aggregated TCP packet count
static uint32_t winThCntG, winThCntG0;  // Aggregated win threshold count and variable for the last threshold count
static uint8_t  tcpWinStat;             // Aggregated status


Now go to the t2OnFlowTerminate(...) callback. First we need the pointer to the flow structure. To show the principal we just do a linear search whether we already have the IP stored and check whether its counter is greater than the potential existing, then we store the new data. Yes, it can be more elegant, using a hash or a tree. We will discuss T2 support for that matter in tutorial block T2 Kung Fu. Note that the following code is only for IPv4, as we want to start simple.

vi src/tcpWin.c

void t2OnFlowTerminate(unsigned long flowIndex, outputBuffer_t *buf) {
    const tcpWinFlow_t * const tcpWinFlowP = &tcpWinFlows[flowIndex];


    // <-- Add all the code below

    const flow_t * const flowP = &flows[flowIndex];
    if (!FLOW_IS_IPV4(flowP)) return;  // IPv4 only

    if (tcpWinFlowP->winThCnt == 0 || tcpWinFlowP->pktTcpCnt < TCPWIN_MINPKTS) return;

    const int wzi = gwz.wzi; // store element count in const local variable, makes the compiler happy
    if (wzi >= TCPWIN_MAXWSCNT) return;  // If array full, stop saving

    int i;
    for (i = 0; i < wzi; i++) {
        if (gwz.wzip[i].IPv4.s_addr == flowP->srcIP.IPv4.s_addr) break; // does IP exist?

    if (f > gwz.wzCnt[i]) {                     // only update if count is greater than the previous one
        gwz.tcpCnt[i] = tcpWinFlowP->pktTcpCnt; // update TCP packet count
        gwz.wzCnt[i] = f;                       // update relative count
        if (i == wzi) {                         // new one?
            gwz.wzip[i] = flowP->srcIP;         // save new IP
            gwz.wzi++;                          // increment global window size counter

The t2Finalize() callback not only serves to free flow memory but also to produce all kinds of global output into specialized files. The code below stores the IP and its count. Just add it after the free(...) command.

void t2Finalize() {
    free(tcpWinFlows); // free the tcpWinFlows

    // open tcpWin statistics file
    FILE *fp = t2_fopen_with_suffix(baseFileName, TCPWIN_SUFFIX, "w");
    if (UNLIKELY(!fp)) return;  // return if file could not be opened

    fprintf(fp, "# IP\tpktTcpCnt\twinRelThCnt\n"); // print header

    char srcIP[INET_ADDRSTRLEN];
    for (int i = 0; i < gwz.wzi; i++) {
        T2_IP_TO_STR(gwz.wzip[i], 4, srcIP, INET_ADDRSTRLEN);                     // convert IP to string
        fprintf(fp, "%s\t%" PRIu32 "\t%f\n", srcIP, gwz.tcpCnt[i], gwz.wzCnt[i]); // print in file


After you edited the skeleton code you should compare your implementation with tcpWin07.tar.gz.

Compile and run t2:

t2build tcpWin

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

Number of processed      flows: 17100 (17.10 K)
Number of processed L2   flows: 99 [0.58%]
Number of processed IPv4 flows: 16937 (16.94 K) [99.05%]
Number of processed IPv6 flows: 64 [0.37%]
Number of processed A    flows: 9719 (9.72 K) [56.84%]
Number of processed B    flows: 7381 (7.38 K) [43.16%]
Number of request        flows: 9676 (9.68 K) [56.58%]
Number of reply          flows: 7424 (7.42 K) [43.42%]
Total   A/B    flow asymmetry: 0.14
Total req/rply flow asymmetry: 0.13
Number of processed   packets/flows: 71.29
Number of processed A packets/flows: 58.05
Number of processed B packets/flows: 88.71
Number of processed total packets/s: 48859.83 (48.86 K)
Number of processed A+B   packets/s: 48859.83 (48.86 K)
Number of processed A     packets/s: 22615.05 (22.61 K)
Number of processed   B   packets/s: 26244.78 (26.24 K)

Open your results cmd window and look at what t2 produced

ls ~/results/


So the *_tcpwin.txt* file contains the summary of all IP and their counts being below the window size threshold. If you are interested in the IP with the worst connection sort the *_tcpwin.txt* file according to the 3rd column.

tawk -s '#' 't2sort(winRelThCnt)' ~/results/annoloc2_tcpwin.txt | tcol

# IP             pktTcpCnt  winRelThCnt  72         1.000000    79         0.962025  429        0.489510  52         0.269231  251        0.247012  195        0.246154  205        0.229268   162        0.203704   315        0.196825     388        0.164948    95         0.157895   140        0.150000     197        0.142132      235        0.140426     194        0.123711     78         0.115385  74         0.094595   131        0.045802   340        0.041176   847        0.035419   133        0.030075    87         0.022989    111        0.018018    365        0.016438     62         0.016129    250        0.016000     72         0.013889  73         0.013699    843        0.011862   92         0.010870    2079       0.009620     114        0.008772    133        0.007519   957        0.005225   244        0.004098   249        0.004016    719        0.002782      415        0.002410   977        0.002047     538        0.001859   1081       0.001850    618        0.001618   670        0.001493   1134       0.000882     1332       0.000751  2309       0.000433

So in all packets the host asked the opposite IP to stop sending packets. If you increase TCPWIN_MINPKTS what will happen?

Then add the destination address. Does that change much? Look at the flow asymmetry. Why not improve the measure by adding the TCP WS option. These are your jobs now. Let yourself be inspired by the tcpFlags plugin of T2.


You can download the final version of the tcpWin plugin.

The next tutorial will teach you geo-whois-labeling.

Have fun writing plugins!

See also