Complete your first analyzer - RIP in bro

Introduce

The best way to understand the structure of the sources code is to write some modules by yourself. Let’s us to write an Analyzer in the Bro by Binpac. We choose RIP v2 to write the first protocol.

How to Use Binpac quick start to create the relative file to develop an analyzer?

Download Binpac Quick Start

git clone https://github.com/grigorescu/binpac_quickstart.git When you finish download, you can enter the binpac_quickstart directory, then you will find there are some files in this directory as blow.

Guoze@node-0:~/00_Workbench$ cd binpac_quickstart/
Guoze@node-0:~/00_Workbench/binpac_quickstart$ ls -al
total 32
drwxr-xr-x  4 Guoze senfv-PG0 4096 May 22 19:30 .
drwxr-xr-x 11 Guoze senfv-PG0 4096 May 23 12:15 ..
drwxr-xr-x  8 Guoze senfv-PG0 4096 May 22 19:30 .git
-rw-r--r--  1 Guoze senfv-PG0   21 May 22 19:30 __init__.py
-rw-r--r--  1 Guoze senfv-PG0  128 May 22 19:30 README.md
-rwxr-xr-x  1 Guoze senfv-PG0 7151 May 22 19:30 start.py
drwxr-xr-x  2 Guoze senfv-PG0 4096 May 22 19:30 templates

User start.pyto create the directory structure in Bro

start.py is the script which who can use to build a directory structure to develp the RIP analyzer. We can use this script as following.

Guoze@node-0:~/00_Workbench/binpac_quickstart$ ./start.py 
Usage:
    start.py NAME DESCRIPTION PATH_TO_BRO_SRC (--tcp|--udp) [--buffered] [--plugin]

Example: ./start.py RIP "Routing Internet Protocl" ../bro --udp Procotol name:RIP; Procoto description:Routing Internet Protocl The absolute path of the Bro source code:../bro The type of Network Portocol which the RIP use:–udp

Use this script to built RIP work directory in the Bro source code file. If you don’t know RIP protocol before, please read the RIP protocol documents.

In the terminal, we just need to input the command as follows to create our work directory. ./start.py RIP "Routing Internet Protocl" ../bro --udp

After complete, It will create some files in the bro/scripts/base/protocols/rip/ directory and bro/src/analyzer/protocol/rip/ directory

  1. bro/scripts/base/protocols/rip/
Guoze@node-0:~/00_Workbench/bro/scripts/base/protocols/rip$ ls -l
total 12
-rw-r--r-- 1 Guoze senfv-PG0  245 May 23 12:49 dpd.sig
-rw-r--r-- 1 Guoze senfv-PG0   66 May 23 12:49 __load__.bro
-rw-r--r-- 1 Guoze senfv-PG0 1327 May 23 12:49 main.bro

__load__.bro: This allows all the contents of the directory to be loaded via @load base/protocols/sip. dpd.sig: This file contains a signature that can be used to attach the analyzer to connections if their content matches. main.bro: Contains the base script-layer functionality for processing events emitted from the analyzer.

  1. src/analyzer/protocol/sip/
Guoze@node-0:~/00_Workbench/bro/src/analyzer/protocol/rip$ ls -l
total 32
-rw-r--r-- 1 Guoze senfv-PG0  301 May 23 12:50 CMakeLists.txt
-rw-r--r-- 1 Guoze senfv-PG0  472 May 23 12:50 events.bif
-rw-r--r-- 1 Guoze senfv-PG0  480 May 23 12:50 Plugin.cc
-rw-r--r-- 1 Guoze senfv-PG0  738 May 23 15:27 rip-analyzer.pac
-rw-r--r-- 1 Guoze senfv-PG0  721 May 23 12:50 RIP.cc
-rw-r--r-- 1 Guoze senfv-PG0  715 May 23 12:50 RIP.h
-rw-r--r-- 1 Guoze senfv-PG0 1005 May 23 12:50 rip.pac
-rw-r--r-- 1 Guoze senfv-PG0  939 May 23 15:03 rip-protocol.pac

CMakeLists.txt: Informs the CMake build system how to compile the analyzer. Plugin.cc: Analyzers in Bro are a type of plugin. This file does what’s necessary to register the new analyzer plugin with Bro. RIP.h: Defines the API for the new analyzer which derives from one of Bro’s already-existing analyzer classes. RIP.cc: Implementation of the analyzer. It’s mostly just responsible for handing off data to the protocol parser that’s been generated by BinPAC. events.bif: Defines events that the analyzer will generate. rip.pac: The main entry point for the BinPAC definition of the protocol that you want to parse. rip-protocol.pac: Where the message format is defined.
rip-analyzer.pac: Defines a connection, flow, and other processing functions for the analyzer.

If you just want to use Binpac to compile your c++ file, you just need to write these files:

  • events.bif
  • rip.pac
  • rip-protocol.pac
  • rip-analyzer.pac

Implementation

Implement the rip-protocol.pac file

This file just defines each part in the RIP protocol. In the code, you will find the Binpac language use keywords type to express a data structure in the RIP protocol. The RIP_PDUfuntion which is the lastest type in this file defines the data structure which user want to transfer when the RIP event happens.

The RIP packet format is:

   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |  command (1)  |  version (1)  |       must be zero (2)        |
  +---------------+---------------+-------------------------------+
  |                                                               |
  ~                         RIP Entry (20)                        ~
  |                                                               |
  +---------------+---------------+---------------+---------------+

There may be between 1 and 25 (inclusive) RIP entries. A RIP-1 entry has the following format:

   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | address family identifier (2) |      must be zero (2)         |
  +-------------------------------+-------------------------------+
  |                        IPv4 address (4)                       |
  +---------------------------------------------------------------+
  |                        must be zero (4)                       |
  +---------------------------------------------------------------+
  |                        must be zero (4)                       |
  +---------------------------------------------------------------+
  |                           metric (4)                          |
  +---------------------------------------------------------------+

Use this file to design the protocol structures of RIP in our case. Just take the structures from the protocol and write the code in this file to parse it.

   Protocol Extensions
   RIP-2 is:
   0                   1                   2                   3 3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Address Family Identifier (2) |        Route Tag (2)          |
   +-------------------------------+-------------------------------+
   |                         IP Address (4)                        |
   +---------------------------------------------------------------+
   |                         Subnet Mask (4)                       |
   +---------------------------------------------------------------+
   |                         Next Hop (4)                          |
   +---------------------------------------------------------------+
   |                         Metric (4)                            |
   +---------------------------------------------------------------+

The Address Family Identifier, IP Address, and Metric all have the meanings defined in section 3.4. The Version field will specify

1   # Generated by binpac_quickstart                                                                                                                                                      
  1                                                                                                                                                                                       
  2 # ## TODO: Add your protocol structures in here.                                                                                                                                      
  3 # ## some examples:                                                                                                                                                                   
  4                                                                                                                                                                                       
  5 # Types are your basic building blocks.                                                                                                                                               
  6 # There are some builtins, or you can define your own.                                                                                                                                
  7 # Here's a definition for a regular expression:                                                                                                                                       
  8 # type RIP_WHITESPACE = RE/[ \t]*/;                                                                                                                                                   
  9                                                                                                                                                                                       
 10 # A record is a collection of types.                                                                                                                                                  
 11 # Here's one with the built-in types                                                                                                                                                  
 12                                                                                                                                                                                       
 13 enum Rip_Command {                                                                                                                                                                    
 14     RIP_REQUEST     = 1,                                                                                                                                                              
 15     RIP_RESPONSE    = 2,                                                                                                                                                              
 16 }                                                                                                                                                                                     
 17                                                                                                                                                                                       
 18 enum Rip_Version {                                                                                                                                                                    
 19     RIP_V1   = 1,                                                                                                                                                                     
 20     RIP_V2   = 2,                                                                                                                                                                     
 21 }                                                                                                                                                                                     
 22                                                                                                                                                                                       
 23 type Rip_Message = record {    //Base Message                                                                                                                                                           
 24     command : uint8;                                                                                                                                                                  
 25     version : uint8;                                                                                                                                                                  
 26     pad     : padding[2];         //must be zero                                                                                                                                                    
 27     entry   : Rip_Entry[] &until($input.length()) == 0;                                                                                                                                
 28 };                                                                                                                                                                                    
 29                                                                                                                                                                                       
 30 type Rip_Entry = record {
 31     af      : uint16;                                                                                                                                                                 
 32     rt      : uint16;                                                                                                                                                                 
 33     ip      : uint32;                                                                                                                                                                 
 34     mask    : uint32;                                                                                                                                                                 
 35     gateway : uint32;                                                                                                                                                                 
 36     metric  : uint32;                                                                                                                                                                 
 37 };                                                                                                                                                                                    
 38                                                                                                                                                                                       
 39 type RIP_PDU(is_orig: bool) = record {                                                                                                                                                
 40     command : uint8;                                                                                                                                                                  
 41     version : uint8;                                                                                                                                                                  
 42     pad     : padding[2];                                                                                                                                                             
 43 } &byteorder=bigendian;                                                                                                                                                               

RIP_PDU structure: pass to the user land, just focus which you want to pass.

Implement the rip_analyzer.pac file

This file mainly finishes the processing and analyzing of the data which was passed by the RIP_PDU function to this file. If there is a RIP event happens, then it enters this processing flow.

The BifEvent::generate_rip_request function illustrates how to generate an request event. We use this event to pass the data structure which is important in the next process (the data structure created in rip_protocol.pac). The main thing we need to define is what we want to pass. Such as: msg.command, msg.version

%code{ … %} : Copy C++ code to the generated source file

1   # Generated by binpac_quickstart                                                                                                                                                      
  1             
  2 refine flow RIP_Flow += {                                                                                                                                                             
  3     function proc_rip_message(msg: RIP_PDU): bool                                                                                                                                     
  4         %{  
  5         // Check for RIP commands                                                                                                                                                     
  6         if ( ${msg.command} == RIP_REQUEST) {                                                                                                                                         
  7             BifEvent::generate_rip_request(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),
  8             ${msg.command},                      //Pass more thing                                                                                                                                       
  9             ${msg.version});                                                                                                                                                          
 10             return true;                                                                                                                                                              
 11         }   
 12         if ( ${msg.command} == RIP_RESPONSE) {                                                                                                                                        
 13             BifEvent::generate_rip_response(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),                                                                       
 14             ${msg.command},                                                                                                                                                           
 15             ${msg.version});                                                                                                                                                          
 16             return true;                                                                                                                                                              
 17         }   
 18         %}  
 19 };          
 20             
 21 refine typeattr RIP_PDU += &let {                                                                                                                                                     
 22     proc: bool = $context.flow.proc_rip_message(this);                                                                                                                                
 23 };          
~

function proc_rip_message(msg: RIP_PDU): bool just like to say please use that RIP_PDU data structure that was defined in the earlier file.

Implement the events.bif file

1   # Generated by binpac_quickstart                                                                                                                                                      
  1  
  2 # In this file, you'll define the events that your analyzer will                                                                                                                      
  3 # generate. A sample event is included.                                                                                                                                               
  4  
  5 # ## TODO: Edit the sample event, and add more events.                                                                                                                                
  6  
  7 ## Generated for RIP connections                                                                                                                                                      
  8 ##                                                                                                                                                                                    
  9 ## See `Google <http://lmgtfy.com/?q=RIP>`__ for more information about RIP                                                                                                           
 10 ##                                                                                                                                                                                    
 11 ## c: The connection                                                                                                                                                                  
 12 ##                                                                                                                                                                                    
 13 event rip_request%(c: connection, command: count, version: count%);                                                                                                                   
 14 event rip_response%(c: connection, command: count, version: count%);

RIP just have two types of command:

  • rip_request
  • rip_response

Test

Configure Bro’s Working Environment

Firstly, we need to configure the bro’s path to execute it. The executable path of the Bro is bro/build/src/bro.

Guoze@node-0:~/00_Workbench/bro$ ./build/src/bro -h
bro version 2.5-598
usage: ./build/src/bro [options] [file ...]
    <file>                         | policy file, or read stdin
    -a|--parse-only                | exit immediately after parsing scripts
    -b|--bare-mode                 | don't load scripts from the base/ directory
    -d|--debug-policy              | activate policy file debugging
    -e|--exec <bro code>           | augment loaded policies by given code
    -f|--filter <filter>           | tcpdump filter

Bro has provided an executable script for you. As a result, you can easily configure environment variables. We just need to execute the configuration script. Use the shell script for configuring environment variables is as follows:

Guoze@node-0:~/00_Workbench/bro/build$ cat bro-path-dev.sh 
export BROPATH=`/users/Guoze/00_Workbench/bro/build/bro-path-dev`
export BRO_PLUGIN_PATH="/users/Guoze/00_Workbench/bro/build/src":
export PATH="/users/Guoze/00_Workbench/bro/build/src":$PATH
Guoze@node-0:~/00_Workbench/bro$ source ./build/bro-path-dev.sh

If you want to check the execution status of the script and determine whether it has completed the configuration, you can enter: bro -hin the terminal. If the execution result is as same as the result of ./build/src/bro -h, then it means that the configuration has been configured successfully.

Guoze@node-0:~/00_Workbench/bro$ bro -h
bro version 2.5-598
usage: bro [options] [file ...]
    <file>                         | policy file, or read stdin
    -a|--parse-only                | exit immediately after parsing scripts
    -b|--bare-mode                 | don't load scripts from the base/ directory
    -d|--debug-policy              | activate policy file debugging
    -e|--exec <bro code>           | augment loaded policies by given code
    -f|--filter <filter>           | tcpdump filter
    -g|--dump-config               | dump current config into .state dir
    -h|--help|-?                   | command line help

Test RIP parser

Download RIP Network data

We can directly download a RIPv2 packet which can be used for testing on Internet. $ wget http://packetlife.net/captures/RIPv2.cap

After download, use the command ‘tcpdump’ to determine whether the data packet contains RIP data.

Guoze@node-0:~/00_Workbench$ tcpdump -nr RIPv2.cap
reading from file RIPv2.cap, link-type EN10MB (Ethernet)
23:06:26.942558 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:06:30.158769 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:06:52.663855 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:06:58.416478 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:07:19.709681 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:07:24.974047 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:07:45.389720 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:07:53.891896 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:08:14.625084 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:08:21.933550 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:08:41.410659 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:08:47.731064 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84

Add some print information

 12 refine flow RIP_Flow += {          
 11     function proc_rip_message(msg: RIP_PDU): bool
 10         %{                         
  9         // Check for RIP commands  
  8         if ( ${msg.command} == RIP_REQUEST) { 
  7             printf("In rip_request\n");
  6             BifEvent::generate_rip_request(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),
  5             ${msg.command},        
  4             ${msg.version});       
  3             return true;           
  2         }                          
  1         if ( ${msg.command} == RIP_RESPONSE) {
15              printf("In rip_response\n");                                                                                                                                              
  1             BifEvent::generate_rip_response(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),
  2             ${msg.command},        
  3             ${msg.version});       
  4             return true;           
  5         }                          
  6         %}

After change, make the code again.

Guoze@node-0:~/00_Workbench/bro$ sudo make
make -C build all
make[1]: Entering directory '/users/Guoze/00_Workbench/bro/build'
make[2]: Entering directory '/users/Guoze/00_Workbench/bro/build'
make[3]: Entering directory '/users/Guoze/00_Workbench/bro/build'
make[3]: Leaving directory '/users/Guoze/00_Workbench/bro/build'
........
........
[100%] Built target rst
make[2]: Leaving directory '/users/Guoze/00_Workbench/bro/build'
make[1]: Leaving directory '/users/Guoze/00_Workbench/bro/build'

Use Network Capture Data for Testing

Guoze@node-0:~/00_Workbench$ bro -r RIPv2.cap 
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
Terry Tang
Terry Tang
Software Development Engineer

My research interests include distributed robotics, mobile computing and programmable matter.

comments powered by Disqus

Related