Plugin geo labeling

geoip labeling plugin development

Introduction

Since version 0.8.8, all subnet processing is provided by the core. Meaning that every plugin can now profit from its services and no dependencies to basicFlow are necessary anymore. Thus, it became easier to write your own geo plugins. This is what we will do now in this tutorial. Moreover an introduction to the IPv4/6 flow processing is integrated in the tutorial.

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 https://tranalyzer.com/download/data/annoloc2.pcap

Build tranalyzer2 and the required plugins

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

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

...

BUILDING SUCCESSFUL

Source code

In this tutorial, we will extend tcpWin07.tar.gz, the final version of the previous tutorial (Plugin summary files).

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.

tranpl

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

And let t2_aliases know about it:

source "$T2HOME/scripts/t2_aliases"

The core subnet processing

As subnet processing is now part of the core, the flow and packet structure provides an index to the global IPv4/6 subnet table. That means we have now also to take care of different IP versions and core configuration, which I avoided so far for simplicity.

tranalyzer2

vi src/flow.h

...

#if SUBNET_INIT != 0
    uint32_t subnetNrSrc;  // <--
    uint32_t subnetNrDst;  // <--
#endif // SUBNET_INIT != 0

...

Hence, the user can access the subnet index for the src and dest address via the flow structure:

  • flowP->subnetNrSrc
  • flowP->subnetNrDst

The same is accessible in the packet structure, see // <--

vi src/packet.h

...

#if ((SUBNET_INIT != 0) || (AGGREGATIONFLAG & (SUBNET | SRCIP | DSTIP)))
#if IPV6_ACTIVATE > 0
    ipAddr_t                srcIPC;
    ipAddr_t                dstIPC;
#else // IPV6_ACTIVATE == 0
    ip4Addr_t               srcIPC;
    ip4Addr_t               dstIPC;
#endif // IPV6_ACTIVATE == 0

    uint32_t                subnetNrSrc;
    uint32_t                subnetNrDst;
    uint16_t                srcPortC;
    uint16_t                dstPortC;
    uint8_t                 l4ProtoC;
#endif // ((SUBNET_INIT != 0) || (AGGREGATIONFLAG & (SUBNET | SRCIP | DSTIP)))

...

In your program, e.g., at the t2OnLayer4() callback, you access the index via the packet structure:

  • packet->subnetNrSrc
  • packet->subnetNrDst

That’s it. So how do I convert this ominous index to viable subnet information? Easy, use the following macros (defined in $T2HOME/utils/subnetHL.h):

const uint32_t num = packet->subnetNrSrc;
const uint_fast8_t ipver = PACKET_IPVER(packet);

SUBNET_ASN(dest, ipver, num)    // Autonomous System Number
SUBNET_LOC(dest, ipver, num)    // Country
SUBNET_CNTY(dest, ipver, num)   // County
SUBNET_CTY(dest, ipver, num)    // City
SUBNET_ORG(dest, ipver, num)    // Organization
SUBNET_NETID(dest, ipver, num)  // Hex code
SUBNET_LAT(dest, ipver, num)    // Latitude
SUBNET_LNG(dest, ipver, num)    // Longitude
SUBNET_PREC(dest, ipver, num)   // Precision

Your code must react to the following core switches, in order to avoid compiler errors or unexpected results if you reconfigure tranalyzer core.

It is advisable to read the following tutorials first, if you feel uneasy about these core configurations:

Enabling switches reside in tranalyzer.h and main.h:

Flag Description File
IPV6_ACTIVATE Controls the focus on IPv4/6 or both tranalyzer.h
SUBNET_ON Enables the subnet functions in the core main.h
AGGREGATIONFLAG Controls aggregation modes of flows. main.h
The code SUBNET=0x80 enables also subnet functions like SUBNET_ON. main.h
SUBNET_INIT Switch for plugin programmers enabling subnet functions. main.h
A logical OR of SUBNET_ON and SUBNET. main.h

tranalyzer2

vi src/networkHeaders.h

...

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

#define IPV6_ACTIVATE     2 // 0: IPv4 only
                            // 1: IPv6 only
                            // 2: dual mode
...

vi src/tranalyzer.h

...

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

...

#define SUBNET_ON       1 // Core control of subnet function for plugins

/* -------------------------------------------------------------------------- */
/* -------------------- DO NOT EDIT THE FOLLOWING BLOCKS -------------------- */
/* -------------------------------------------------------------------------- */

// Aggregation modes
#define L4PROT  0x01
#define DSTPORT 0x02
#define SRCPORT 0x04
#define DSTIP   0x08
#define SRCIP   0x10
#define VLANID  0x20
#define SUBNET  0x80

// SUBNET mode: IP flow aggregation network masks
#define CNTRY_MSK 0xff800000
#define TOR_MSK   0x00400000
#define ORG_MSK   0x003fffff

#define NETIDMSK  (CNTRY_MSK | ORG_MSK) // netID mask

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

// Flow Aggregation
#define AGGREGATIONFLAG 0x00 // each bit: 1: aggregation activated
                             // (see aggregation modes defined above)

...

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

...

#define SUBNET_INIT (SUBNET_ON | (AGGREGATIONFLAG & SUBNET))

#endif // T2_TRANALYZER_H_INCLUDED

We leave everything at default.

An important part for a programmer are the macros T2 supplies to facilitate subnet tests and information extraction. They also shield the user from the global table pointers defined in the core main.c. These pointers contain the IPv4/6 subnet file information according to the users configuration in subnetHL.h.

utils

vi src/subnetHL.h

...
#if SUBNET_INIT != 0
void *subnetTableP[2];
#if IPV6_ACTIVATE == 0 || IPV6_ACTIVATE == 2
subnettable4_t *subnetTable4P;
#endif
#if IPV6_ACTIVATE > 0
subnettable6_t *subnetTable6P;
#endif
#endif // SUBNET_INIT != 0
...

The definition of the IPv4/6 structs is located at the end of the subnetHL.h in the utils folder. Note that CNTYCTY is switched off by default, saving space in the binary subnet file.

The said macros are defined in the middle part of subnetHL.h. Nevertheless, you may use the global pointers to access any info using the supplied index. Your choice.

utils

vi subnetHL.h

...

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

#define SUBRNG       0 // IP range definition: 0: CIDR only, 1: Begin-End
#define CNTYCTY      0 // 1: add county, city
#define WHOADDR      0 // 1: add whois address info
#define SUB_MAP      1 // 1: mmap subnet, 0: normal read

#define CNTYLEN     14 // length of County record
#define CTYLEN      14 // length of City record
#define WHOLEN      30 // length of Organization record
#define ADDRLEN     30 // length of Address record

/* +++++++++++++++++++++ ENV / RUNTIME - conf Variables +++++++++++++++++++++ */

#define SUBNET_UNK "-" // Representation of unknown locations

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

...

That is all you need to know for now. Let’s implement geo stuff now.

Implementing IPv4/6 switches in t2OnFlowTerminate()

Open tcpGeoWin.c and scroll to t2OnFlowTerminate(). The other callbacks before are the same as in previous tutorials.

We want to detect all packets where the window size is below TCPWIN_THRES and store it in the gwz structure if the count for an IP address is higher than the previous one.

As indicated by // <-- the flowP->subnetNrSrc contains the subnet index for the specific IP address provided by the core. That is all you need to access. Easy he?

We store it in the gwz.sID[i] for later processing. The macro FLOW_IS_IPV4 tests the flow status for the IPv4/6 bit. We need it in order to store the IPv4/6 address appropriately. The switch IPV6_ACTIVATE > 0 covers the different modes of tranalyzer IPv4 only, IPv6 only or both. Same for the storage of the IP’s below.

The switch SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0 activates the code only if core processes subnets and no aggregation mode is on, as we only want the standard case here, where we have only one IP per flow. Yes I could produce a simpler switch for that, will do that later.

tcpWin

vi src/tcpWin.c

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

    float f = 0.0;
    if (tcpWinFlowP->pktTcpCnt) {
        f = (float)tcpWinFlowP->winThCnt/(float)tcpWinFlowP->pktTcpCnt;
        pktTcpCnt  += tcpWinFlowP->pktTcpCnt;  // Count all TCP packets
    }

    OUTBUF_APPEND_U8(buf, tcpWinFlowP->stat);
    OUTBUF_APPEND_U8(buf, tcpWinFlowP->ttl);
    OUTBUF_APPEND_U32(buf, tcpWinFlowP->tcpWinInit);
    OUTBUF_APPEND_U32(buf, tcpWinFlowP->winThCnt);
    OUTBUF_APPEND_FLT(buf, f);

    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
#if SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0       // <--
            gwz.sID[i] = flowP->subnetNrSrc;                // <-- save subnetID from core
#endif // SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0 // <--
            gwz.wzi++;                          // increment global window size counter
        }
    }
}

One flow variable access and you have all subnet info you need.

The configuration is located in tcpGeoWin.h. Most of it we already know from earlier tutorials. New is the subnet ID sID in the gwz_t struct. Look for the // <--

vi src/tcpWin.h

...

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

#define TCPWIN_THRES      1 // TCP window threshold for packet counts
#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 0x1 // TCP window threshold undershoot


// Structure

typedef struct {
    ipAddr_t wzip  [TCPWIN_MAXWSCNT]; // IP address array
#if SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0       // <--
    uint32_t sID   [TCPWIN_MAXWSCNT];                       // <-- subnetID
#endif // SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0 // <--
    uint32_t tcpCnt[TCPWIN_MAXWSCNT]; // TCP packet count
    float    wzCnt [TCPWIN_MAXWSCNT]; // window size count
    int wzi;                          // window size index
} gwz_t;


// Plugin structure

typedef struct { // always large variables first to limit memory fragmentation
        uint32_t pktTcpCnt;        // flow TCP packet count
        uint32_t tcpWinInit;       // initial window size
        uint32_t winThCnt;         // packet count window size below threshold
        uint8_t ttl;               // TTL
        uint8_t stat;              // status
} tcpWinFlow_t;

...

Adding subnet info to the summary file

After the free call for that tcpWinFlows struct we open the file TCPWIN_SUFFIX. As above we select the output of subnet info only if subnet activated and standard flow aggregation. The macro T2_IP_TO_STR converts IPv4/6 addresses to human readable strings. SUBNET_LOC and SUBNET_WHO select the country code and the organization given the stored subnet index. That is all you need, and you are done. Look for // <--

vi src/tcpWin.c

void t2Finalize() {
    free(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

#if SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0             // <-- compile only if SUBNET core is on and no aggregation mode
    fprintf(fp, "# IP\tCntry\tOrg\tpktTcpCnt\twinRelThCnt\n");    // <-- print header
#else // SUBNET_ON == 0 || (AGGREGATIONFLAG & SUBNET) != 0        // <--
    fprintf(fp, "# IP\tpktTcpCnt\twinRelThCnt\n"); // print header
#endif // SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0       // <-- compile only if SUBNET core is on and no aggregation mode

    char srcIP[INET_ADDRSTRLEN];
    for (int i = 0; i < gwz.wzi; i++) {
        t2_ipv4_to_str(gwz.wzip[i].IPv4, srcIP, INET_ADDRSTRLEN); // convert IP to string
#if SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0             // <-- compile only if SUBNET core is on and no aggregation mode
        char *loc, *org;                                          // <--
        SUBNET_LOC(loc, 4, gwz.sID[i]);                           // <-- get country for IP
        SUBNET_ORG(org, 4, gwz.sID[i]);                           // <-- get organization for IP
        fprintf(fp, "%s\t%s\t%s\t%" PRIu32 "\t%f\n",              // <-- print in file
                srcIP, loc, org, gwz.tcpCnt[i], gwz.wzCnt);       // <--
#else // many IP's / flow                                         // <--
        fprintf(fp, "%s\t%" PRIu32 "\t%f\n", // print in file
                srcIP, gwz.tcpCnt[i], gwz.wzCnt[i]);
#endif // SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0       // <--
    }

    fclose(fp);
}

Two macros and you are subnet ready.

Adding subnet info to t2PluginReport() callback

Adding subnet info in the end report is straightforward, using the same switches as above.

void t2PluginReport(FILE *stream) {
    if (winThCntG) {
        T2_FPLOG_AGGR_HEX(stream, plugin_name, tcpWinStat);
        T2_FPLOG_NUMP(stream, plugin_name,
                "Number of TCP winsize packets below threshold " STR(TCPWIN_THRES),
                winThCntG, pktTcpCnt);
        // <-- Add the following 'if'
        if (gwz.wzi) {
            uint32_t gzCM = gwz.wzCnt[0];
            uint32_t gzi = 0;

            // find IP with largest ipCnt
            for (int i = 0; i < gwz.wzi; i++) {
                if (gwz.wzCnt[i] > gzCM) {
                    gzCM = gwz.wzCnt[i];
                    gzi = i;
                }
            }

            char srcIP[INET_ADDRSTRLEN];
            t2_ipv4_to_str(gwz.wzip[gzi].IPv4, srcIP, sizeof(srcIP));

#if SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0
            char *loc, *org;
            SUBNET_LOC(loc, ipver, gwz.sID[gzi]);  // <-- get country for IP
            SUBNET_ORG(org, ipver, gwz.sID[gzi]);  // <-- get organization for IP
            T2_FPLOG(stream, plugin_name, "IP: %s, country: %s, org: %s", srcIP, loc, org);
#else // many IP's / flow
            T2_FPLOG(stream, plugin_name, "IP: %s", srcIP);
#endif // SUBNET_ON != 0 && (AGGREGATIONFLAG & SUBNET) == 0
        }
    }
}

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

So you are all set. Compile and run t2:

t2build tcpWin

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

================================================================================
Tranalyzer 0.9.0 (Anteater), Cobra. PID: 17736, SID: 666
================================================================================
[INF] Creating flows for L2, IPv4, IPv6
Active plugins:
    01: basicFlow, 0.9.0
    02: tcpGeoWin, 0.9.0
    03: txtSink, 0.9.0
[INF] IPv4 Ver: 5, Rev: 09082023, Range Mode: 0, subnet ranges loaded: 481503 (481.50 K)
[INF] IPv6 Ver: 5, Rev: 09082023, Range Mode: 0, subnet ranges loaded: 41497 (41.50 K)
Processing file: /home/wurst/data/annoloc2.pcap
Link layer type: Ethernet [EN10MB/1]
Snapshot length: 66
Dump start: 1022171701.691172000 sec (Thu 23 May 2002 16:35:01 GMT)
[WRN] snapL2Length: 54 - snapL3Length: 40 - IP length in header: 1500
...
--------------------------------------------------------------------------------
tcpWin: Aggregated tcpWinStat=0x01
tcpWin: Number of TCP winsize packets below threshold 1: 2415 (2.42 K) [0.25%]
tcpWin: IP: 138.212.187.203, country: jp, org: ASAHI KASEI CORPORATION
--------------------------------------------------------------------------------
...

So you have the basicFlow geo location output and the tcpWin output in the end report as well as additional info in the *_tcpwin.txt* summary file.

If the file is too big you may sort by the 4th column, the TCP window threshold count (winRelThCnt).

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

# IP             Country  Org                             pktTcpCnt  winRelThCnt
138.212.187.203  jp       ASAHI KASEI CORPORATION         72         1.000000
36.152.156.46    cn       China Mobile Communications Co  79         0.962025
216.237.125.166  us       Infortech Corporation           429        0.489510
216.217.165.245  us       Windstream Communications LLC   52         0.269231
138.212.186.191  jp       ASAHI KASEI CORPORATION         251        0.247012
138.212.185.150  jp       ASAHI KASEI CORPORATION         195        0.246154
138.212.186.160  jp       ASAHI KASEI CORPORATION         205        0.229268
138.212.186.52   jp       ASAHI KASEI CORPORATION         162        0.203704
200.44.192.225   ve       CANTV Servicios                 315        0.196825
201.98.31.61     mx       Uninet SA de CV                 388        0.164948
216.56.159.22    us       WiscNet                         95         0.157895
138.212.186.60   jp       ASAHI KASEI CORPORATION         140        0.150000
201.9.136.60     br       Telemar Norte Leste SA          197        0.142132
193.87.5.62      sk       AS2607-MNT                      235        0.140426
201.9.140.14     br       Telemar Norte Leste SA          194        0.123711
19.112.1.129     us       MAINT-APNIC-AP                  78         0.115385
138.212.185.186  jp       ASAHI KASEI CORPORATION         74         0.094595
212.88.230.156   be       TELENET-OPS-MNT                 131        0.045802
201.123.124.98   mx       Gestión de direccionamiento    340        0.041176
193.86.108.236   cz       AS13036-MNT                     847        0.035419
138.212.185.98   jp       ASAHI KASEI CORPORATION         133        0.030075
200.50.55.138    cl       TELEFONICA CHILE SA             87         0.022989
200.50.132.98    bb       Infinetworx Caribbean Inc       111        0.018018
193.87.239.57    sk       Provider Local Registry         365        0.016438
83.42.68.176     es       Telefonica de Espana SAU        62         0.016129
200.32.26.254    ar       Telecom Argentina SA            250        0.016000
19.123.222.7     us       MAINT-APNIC-AP                  72         0.013889
138.212.188.204  jp       ASAHI KASEI CORPORATION         73         0.013699
193.87.97.162    sk       AS2607-MNT                      843        0.011862
138.212.191.84   jp       ASAHI KASEI CORPORATION         92         0.010870
133.26.84.187    jp       Meiji University                2079       0.009620
209.132.7.75     us       Simple Network Communications   114        0.008772
193.104.31.16    fr       OVER-LINK-MNT                   133        0.007519
193.87.112.223   sk       AS2607-MNT                      957        0.005225
209.147.223.37   us       Institutions of Higher Learnin  244        0.004098
138.212.188.66   jp       ASAHI KASEI CORPORATION         249        0.004016
192.224.45.42    us       CDK Global                      719        0.002782
70.5.118.83      us       Sprint Fort Worth POP           415        0.002410
83.128.136.224   nl       CAIW Internet                   977        0.002047
36.89.79.225     id       PT Telekomunikasi Indonesia     538        0.001859
138.212.184.48   jp       ASAHI KASEI CORPORATION         1081       0.001850
201.98.215.67    mx       Uninet SA de CV                 618        0.001618
219.127.165.87   jp       IPSTAR Company Limited          670        0.001493
138.212.188.99   jp       ASAHI KASEI CORPORATION         1134       0.000882
201.9.148.42     br       Telemar Norte Leste SA          1332       0.000751
138.212.190.117  jp       ASAHI KASEI CORPORATION         2309       0.000433

If you like to access the county and city information, you need to switch on CNTYCTY in subnetHL.h and recompile with t2build -R -f.

Exercise: Add County and City info to the end report. What do you need to configure in the core so that this info is added to the subnet table?

Query the IP tables

Assume that a protocol response contains an IP, such as DNS. From that IP you want to know whois behind it, then you need to query the IP tables yourself.

In order to do so you have to call the core functions subnet_testHL4 and subnet_testHL6 for IPv4 and 6 addresses shown below. The return value is the subnetID you can use to fetch the geo and whois data as already indicated above.

if (subnetTable4P) {
    ipAddr_t ip;
    const uint32_t subnetID1 = subnet_testHL4(subnetTable4P, ip.IPv4x[0]); // subnet test on IPv4 address

    ip4Addr_t ip4;
    const uint32_t subnetID2 = subnet_testHL4(subnetTable4P, ip4]); // subnet test on IPv4 address
}
if (subnetTable6P) {
    ipAddr_t ip;
    uint32_t subnetID = subnet_testHL6(subnetTable6P, ip); // subnet test on IPv6 address
}

As you can see in the code above, subnetTable[46]P may not exist depending on the configuration of T2, so to make things simpler, just use the following macros instead:

uint32_t subnetID;

ipAddr_t ip;
ip4Addr_t ip4;

SUBNET_TEST_IP4(subnetID, ip4);
SUBNET_TEST_IP4(subnetID, ip);

SUBNET_TEST_IP6(subnetID, ip);

SUBNET_TEST_IP(subnetID, ip, 4); // For IPv4
SUBNET_TEST_IP(subnetID, ip, 6); // For IPv6

It is beneficial to store IPv4 and 6 addresses in one structure, if in dual mode. On the other hand if only IPv4 addresses are processed the ip4Addr_t structure does not waste 48 bits. I advise to use the t2 structures as the macros rely on them.

The relevant address structures are defined in ipaddr.h, as shown in the extract below.

tranalyzer2

vi src/ipaddr.h

...

typedef union {
    uint32_t        IPv4x[4];
    struct in_addr  IPv4;       // IPv4 address
    struct in6_addr IPv6;       // IPv6 address
    uint64_t        IPv6L[2];   // IPv6 address 2*64 bit max chunk for masking ops
} __attribute__((packed)) ipAddr_t;

typedef struct {
    uint8_t  ver;    // version
    ipAddr_t addr;
} ipVAddr_t;

typedef union {
    uint32_t       IPv4x[1];
    struct in_addr IPv4;
} __attribute__((packed)) ip4Addr_t;

...

Instead of using the subnet indexes from the packet structure you can now define your own and use the SUBNET_TEST_IP[46]() macros or the subnet_testHL[46]() functions. Try it on tcpWin, it should produce the same output.

Conclusion

Have fun experimenting with subnet info!

You can download the final version of the tcpWin plugin.

The next tutorial will teach you all about plugin dependencies

Have fun writing plugins!

See also