Projects STRLCPY 5GReplay Commits 95106144
🤬
Showing first 29 files as there are too many
  • ■ ■ ■ ■ ■ ■
    .gitignore
     1 +# Log files
     2 +*.log
     3 + 
     4 +# Maven
     5 +target/
     6 +dist/
     7 + 
     8 + 
     9 +# Generated by MacOS
     10 +.DS_Store
     11 + 
     12 +# Generated by Windows
     13 +Thumbs.db
     14 +rsync.sh
     15 + 
     16 +# Applications
     17 +*.app
     18 +*.exe
     19 +*.war
     20 + 
     21 +# Large media files
     22 +*.mp4
     23 +*.tiff
     24 +*.avi
     25 +*.flv
     26 +*.mov
     27 +*.wmv
     28 + 
     29 +/build/
     30 + 
  • ■ ■ ■ ■ ■ ■
    Makefile
     1 +CC = gcc
     2 +RM = rm -rf
     3 +MKDIR = mkdir -p
     4 +CP = cp
     5 +MV = mv
     6 +LN = ln -s
     7 + 
     8 +#name of executable file to generate
     9 +OUTPUT = mmt-5greplay
     10 + 
     11 +#directory where probe will be installed on
     12 +ifndef MMT_BASE
     13 + MMT_BASE := /opt/mmt
     14 + NEED_ROOT_PERMISSION := 1
     15 +else
     16 + $(info INFO: Set default folder of MMT to $(MMT_BASE))
     17 +endif
     18 + 
     19 +INSTALL_DIR := $(MMT_BASE)/5greplay
     20 + 
     21 +# directory where MMT-DPI was installed
     22 +MMT_DPI_DIR := $(MMT_BASE)/dpi
     23 + 
     24 + 
     25 +#get git version abbrev
     26 +GIT_VERSION := $(shell git log --format="%h" -n 1)
     27 +VERSION := 0.0.1
     28 + 
     29 +CACHE_LINESIZE := 64 #$(shell getconf LEVEL1_DCACHE_LINESIZE)
     30 + 
     31 +#set of library
     32 +LIBS += -ldl -lpthread -lxml2 -lconfuse -lsctp
     33 + 
     34 +#Jul. 19 2021: we currently use directly the function inside libmmt_tmobile.so
     35 +LIBS += -lmmt_tmobile
     36 + 
     37 +CFLAGS += -fPIC -Wall -DINSTALL_DIR=\"$(INSTALL_DIR)\" -DVERSION_NUMBER=\"$(VERSION)\" -DGIT_VERSION=\"$(GIT_VERSION)\" -DLEVEL1_DCACHE_LINESIZE=$(CACHE_LINESIZE) \
     38 + -Wno-unused-variable -Wno-unused-function -Wuninitialized\
     39 + -I/usr/include/libxml2/ -I$(MMT_DPI_DIR)/include
     40 + 
     41 +CLDFLAGS += -L$(MMT_DPI_DIR)/lib -L/usr/local/lib
     42 + 
     43 +#a specific flag for each .o file
     44 +CFLAGS += $(CFLAGS-$@)
     45 + 
     46 +#for debuging
     47 +ifdef DEBUG
     48 + CFLAGS += -g -DDEBUG_MODE -O0 -fstack-protector-all
     49 +else
     50 + CFLAGS += -O3
     51 +endif
     52 + 
     53 +ifdef VALGRIND
     54 + CFLAGS += -DVALGRIND_MODE
     55 +endif
     56 + 
     57 +#Enable update_rules if this parameter is different 0
     58 +ifndef UPDATE_RULES
     59 +else
     60 + ifneq "$(UPDATE_RULES)" "0"
     61 + CFLAGS += -DMODULE_ADD_OR_RM_RULES_RUNTIME
     62 + endif
     63 +endif
     64 + 
     65 +#folders containing source files
     66 +SRCDIR := $(abspath ./src )
     67 + 
     68 +#list all .c files inside ./src and its sub-folders
     69 +LIB_SRCS := $(shell find $(SRCDIR)/ -type f -name '*.c')
     70 +#exclude main.c
     71 +#LIB_SRCS := $(filter-out $(SRCDIR)/main.c, $(LIB_SRCS))
     72 +LIB_OBJS := $(patsubst %.c,%.o, $(LIB_SRCS))
     73 + 
     74 +ifndef VERBOSE
     75 + QUIET := @
     76 +endif
     77 + 
     78 +#make starts with the first target (not targets whose names start with ‘.’).
     79 +# This is called the default goal.
     80 +# https://www.gnu.org/software/make/manual/html_node/How-Make-Works.html
     81 +all: main
     82 + 
     83 + 
     84 +#this is useful when running the tools, such as, gen_dpi, compile_rule
     85 +# but libmmt_core, ... are not found by ldd
     86 +export LD_LIBRARY_PATH=$(MMT_DPI_DIR)/lib
     87 + 
     88 +# check if there exists the folder of MMT-DPI
     89 +$(MMT_DPI_DIR):
     90 + @echo "ERROR: Not found MMT-DPI at folder $(MMT_DPI_DIR).\n"
     91 + @exit 1
     92 +
     93 +%.o: %.c
     94 + @echo "[COMPILE] $(notdir $@)"
     95 + $(QUIET) $(CC) $(CFLAGS) $(CLDFLAGS) -c -o $@ $<
     96 +
     97 +main: $(MMT_DPI_DIR) $(LIB_OBJS) --refresh-plugin-engine
     98 + @echo "[COMPILE] $@"
     99 + $(QUIET) $(CC) -Wl,--export-dynamic -o $(OUTPUT) $(CLDFLAGS) $(LIB_OBJS) $(RULE_OBJS) $(LIBS) -lpcap -l:libmmt_core.so
     100 + 
     101 + 
     102 +RULE_XML := $(sort $(wildcard rules/*.xml))
     103 + 
     104 +ifdef STATIC_LINK
     105 +#this block is for statically linking rules into libmmt_security.a
     106 + 
     107 +#here we got a list of rule files
     108 +RULE_OBJS := $(patsubst %.xml,%.o, $(RULE_XML))
     109 + 
     110 +#Generate code C of a rule
     111 +rules/%.c: main
     112 + $(QUIET) echo [COMPILE] rules/$*.xml
     113 + $(QUIET) ./$(OUTPUT) compile $@ rules/$*.xml -c > /dev/null 2>&1
     114 + 
     115 +#Compile code C of a rule and add a RULE_SUFFIX is the name of the rule
     116 +# (we replace the non-alphanumeric characters by underscores)
     117 +rules/%.o: rules/%.c
     118 + @#replace non-alphanumeric characters in the file name of rule(s) by underscores
     119 + $(eval RULE_SUFFIX=$(shell echo $* | sed "s/[^[:alnum:]]/_/g"))
     120 +
     121 + $(QUIET) $(CC) $(CFLAGS) $(CLDFLAGS) -I./src/lib -I./src/dpi -c -o $@ $< -DRULE_SUFFIX=_$(RULE_SUFFIX)
     122 + @#we do not need its C code any more, delete it
     123 + $(QUIET) $(RM) $<
     124 + @#remember the list of suffix of compiled rules
     125 + $(eval STATIC_RULES_SUFFIX_LIST += SUFFIX($(RULE_SUFFIX)))
     126 + 
     127 + 
     128 +#update the CFLAG for plugins_engine.so
     129 +CFLAG-PLUGINS-ENGINE += -DSTATIC_RULES_SUFFIX_LIST="$(STATIC_RULES_SUFFIX_LIST)"
     130 +
     131 +SAMPLE_RULES :=
     132 +else
     133 + SAMPLE_RULES := $(patsubst %.xml,%.so, $(RULE_XML))
     134 + RULE_OBJS :=
     135 +endif
     136 + 
     137 +# This target is to deal with the issue when user uses
     138 +# 2 differrent values of INSTALL_DIR for "make" and "make install"
     139 +# Ex: make; sudo make install INSTALL_DIR=/tmp/mmt/security
     140 +# - the first "make" will set in the codes MMT_SEC_PLUGINS_REPOSITORY_OPT to /opt/mmt/security/rules
     141 +# - while the second "make install" will install to /tmp/mmt
     142 +# Thus we need to recompile the codes that use MMT_SEC_PLUGINS_REPOSITORY_OPT to update the new directory.
     143 +# The following target will remove the object files of the codes, thus it will trigger to recompile them.
     144 +# So, in the example above, the MMT_SEC_PLUGINS_REPOSITORY_OPT will be update to /tmp/mmt/security/rules.
     145 +
     146 +--refresh-plugin-engine:
     147 + $(QUIET) echo [RE-COMPILE] plugins_engine.o
     148 + $(QUIET) $(CC) $(CFLAGS) $(CLDFLAGS) $(CFLAG-PLUGINS-ENGINE) -c -o $(SRCDIR)/engine/plugins_engine.o $(SRCDIR)/engine/plugins_engine.c
     149 + 
     150 + 
     151 +uninstall:
     152 + $(QUIET) $(RM) $(INSTALL_DIR)
     153 +
     154 +rules/%.so: main
     155 + $(QUIET) ./$(OUTPUT) compile rules/$*.so rules/$*.xml
     156 +
     157 +sample-rules: $(SAMPLE_RULES)
     158 + 
     159 +clean-rules:
     160 + $(QUIET) $(RM) rules/*.so rules/*.o rules/*.c
     161 +clean: clean-rules
     162 + $(QUIET) $(RM) $(MAIN_OBJS) $(LIB_OBJS) $(OUTPUT) test.* \
     163 + $(RULE_OBJS) $(TMP_DIR)
     164 +
     165 +clean-all: clean
     166 + $(QUIET) $(RM) $(MMT_DPI_HEADER)
     167 +
  • ■ ■ ■ ■ ■ ■
    README.md
    1  -# 5GReplay
    2  -Tool for repalying and modifying 5G protocol network traffic
     1 +# MMT-5Greplay
     2 + 
     3 +This repository contains the following folders:
     4 + 
     5 +- `src` : C code of mmt-5greplay
     6 +- `rules`: set of official XML rules. An encoded version (*.so) of these rules will be distributed with mmt-sec when using `make deb`. All rules (official and for testing purposes) are stored in rules/properties_all
     7 +- `check`: sample pcap files and expected results to validate mmt-5greplay
     8 +- `docs`: [documentation](docs/_index.md)
     9 +- `test`: diversity of testing code
     10 + 
     11 +# Build
     12 + 
     13 +## Pre-requires
     14 + 
     15 +Suppose on your machine, you have:
     16 + 
     17 +- *libxml2-dev, libpcap-dev, libconfuse-dev* : `sudo apt-get install libxml2-dev libpcap-dev libconfuse-dev`
     18 + 
     19 +- `mmt-sdk`: `sudo dpkg -i lib/mmt-dpi*.deb`
     20 + 
     21 +## Clean
     22 + 
     23 +- Do `make clean` to clean compiled objects
     24 + 
     25 + 
     26 +## Compile
     27 + 
     28 + 
     29 +- compile on its local directory: `make`
     30 + 
     31 +- compile sample rules existing in `rules` folder: `make sample-rule`
     32 + 
     33 +- enable debug using gdb: `make DEBUG=1`
     34 + 
     35 + 
     36 +- if you want to use Valgrind DRD or Helgrind, you should do `make DEBUG=1 VALGRIND=1`. The option `VALGRIND=1` adds some instruction allowing Valgrind bypass atomic operations that usually causes false positive errors in Valgrind.
     37 + 
     38 +# Execution
     39 + 
     40 +mmt-5greplay command uses the following form: `command [option]`
     41 + 
     42 + - command : is one of the following `compile`, `info`, `replay`
     43 + - option : run "./mmt-5greplay command -h" to get option of each command
     44 +
     45 + 
     46 +## compile
     47 +This command parses rules in .xml file, then compile to a plugin .so file.
     48 + 
     49 +```bash
     50 +#to generate .so file
     51 +./mmt-5greplay compile rules/40.TCP_SYN_scan.so rules/40.TCP_SYN_scan.xml
     52 +
     53 +#to generate code c (for debug)
     54 +./mmt-5greplay compile rules/40.TCP_SYN_scan.c rules/40.TCP_SYN_scan.xml -c
     55 + 
     56 +```
     57 + 
     58 +To compile all rules existing in the folder `rules`, use the following command: `make sample-rules`
     59 + 
     60 +## info
     61 + 
     62 +This command prints information of rules encoded in a binary file (.so).
     63 + 
     64 +```bash
     65 +#print information of all available plugins
     66 +./mmt-5greplay info
     67 +#print information of rules encoded in `rules/nas-smc-replay-attack.so`
     68 +./mmt-5greplay info rules/nas-smc-replay-attack.so
     69 +```
     70 + 
     71 +## replay
     72 + 
     73 +This command can analyze
     74 +
     75 +- either real-time traffic by monitoring a NIC,
     76 +- or traffic saved in a pcap file. The verdicts will be printed to the current screen.
     77 + 
     78 +```bash
     79 +./mmt-5greplay replay [<options>]
     80 +Option:
     81 + -v : Print version information, then exits.
     82 + -c <config file> : Gives the path to the configuration file (default: ./mmt-5greplay.conf).
     83 + -t <trace file> : Gives the trace file for offline analyse.
     84 + -i <interface> : Gives the interface name for live traffic analysis.
     85 + -X attr=value : Override configuration attributes.
     86 + For example "-X output.enable=true -Xoutput.output-dir=/tmp/" will enable output to file and change output directory to /tmp.
     87 + This parameter can appear several times.
     88 + -x : Prints list of configuration attributes being able to be used with -X, then exits.
     89 + -h : Prints this help, then exits.
     90 +
     91 +#online analysis on eth0
     92 +./mmt-5greplay replay -i eth0
     93 +#to see all parameters, run ./mmt-5greplay replay -h
     94 +#verify a pcap file
     95 +./mmt-5greplay replay -t check/pcap/16.two_successive_SYN.pcap
    3 96   
     97 +```
     98 + 
     99 + 
     100 +# Documentation
     101 + 
     102 +[docs](docs/)
  • ■ ■ ■ ■ ■ ■
    docs/add_rm_rules_at_runtime.md
     1 + 
     2 +## 1. Add new rules
     3 +The new rules in XML format must be compiled (e.g., using `compile_rule`) to obtain the rules in .so format.
     4 +The compiled .so files must be then moved to rules folder (either `/opt/mmt/security/rules` or `./rules`).
     5 +Subsequently, mmt-security works only on these .so files.
     6 + 
     7 + 
     8 +The functions used in the following procedures should be called from the main thread (that is the one it calls `mmt_sec_init`).
     9 + 
     10 +### 1.1 Register the new rules to mmt-security
     11 +To inform mmt-security to take into account the new rules, call the following function
     12 + 
     13 +```C
     14 +size_t mmt_sec_add_rules( const char *rules_mask )
     15 +```
     16 + 
     17 +- `rules_mask` parameter is a string wrt the [rules mask](./rules_mask.md) syntax. It distributes the new rules into the existing threads.
     18 +Currently, this function does not allow to create a new thread to process new rules.
     19 + 
     20 +- The function returns number of new rules to be added. A new rule is not added if one of the following conditions is met:
     21 + 
     22 + - it has the same ID with the one of a rule being verified.
     23 + - it is assigned to a non-existing thread.
     24 +
     25 +If the current execution contains only a single mmt-security thread, the new rules will be added to this thread for any `thead_id` existing in `rules_mask`.
     26 + 
     27 +### 1.2 Register to MMT-DPI the new protocols/attributes to be extracted
     28 +The new rules may use protocols/attributes that never been used by the existing rules.
     29 +Consequently one need to register to MMT-DPI these new protocols/attributes to be able to extract their data.
     30 + 
     31 +The following funtion allows to retire a list of unique attributes of protocols that are currently being used by mmt-security:
     32 + 
     33 +```C
     34 +size_t mmt_sec_get_unique_protocol_attributes( proto_attribute_t const*const** proto_atts_array );
     35 +```
     36 + 
     37 +For example:
     38 + 
     39 +```C
     40 +size proto_atts_count, i;
     41 +proto_attribute_t const*const* proto_atts;
     42 +//get list of unique proto_attr
     43 +proto_atts_count = mmt_sec_get_unique_protocol_attributes( & proto_atts );
     44 +//visite the list
     45 +for( i=0; i<proto_atts_count; i++ )
     46 + DEBUG( "Attribute: %s.%s (%d.%d)",
     47 + proto_atts[i]->proto, proto_atts[i]->att,
     48 + proto_atts[i]->proto_id, proto_atts[i]->att_id );
     49 +```
     50 + 
     51 +## 2. Remove rules
     52 + 
     53 +To remove rules from mmt-security, use the following function:
     54 + 
     55 +```C
     56 +size_t mmt_sec_remove_rules( size_t rules_count, const uint32_t* rules_id_set );
     57 +```
     58 + 
     59 +- `rules_count` is number of elements of `rules_id_set`
     60 +- `rules_id_set` is an array of rules IDs
     61 +- the function returns number of rules being removed.
     62 +A rule is not removed if it is not being verified by mmt-security.
     63 + 
     64 +For example:
     65 + 
     66 +```C
     67 +uint32_t rm_rules_arr[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
     68 +size_t count = mmt_sec_remove_rules(20, rm_rules_arr );
     69 +DEBUG("Removed %zu rules", count);
     70 +```
     71 + 
     72 +After removing rules, some protocols/attributes may not be needed. One can unregister them from MMT-DPI to increase DPI performance.
     73 + 
     74 +## 3. Example
     75 + 
     76 +See [../src/main_sec_standalone.c](../src/main_sec_standalone.c)
  • ■ ■ ■ ■ ■ ■
    docs/hash.md
     1 + 
     2 +This document explains how we can check quickly a rule that can be verified when we know a message `msg`.
     3 +We know that a rule contains a set of events. Each event contains a set of proto.atts.
     4 +A rule is verified when we have a message that contains at least one event.
     5 + 
     6 +# Problem statement
     7 + 
     8 +Let examine the following rule:
     9 + 
     10 +```xml
     11 +<property value="THEN" delay_units="s" delay_min="0+" delay_max="6" property_id="3" type_property="ATTACK"
     12 + description="TCP SYN requests on microsoft-ds port 445 with SYN ACK.">
     13 + <event value="COMPUTE" event_id="1"
     14 + description="SYN request"
     15 + boolean_expression="((tcp.flags == 2)&amp;&amp;(tcp.dest_port == 445))"/>
     16 + <event value="COMPUTE" event_id="2"
     17 + description="SYN ACK reply"
     18 + boolean_expression="((tcp.flags == 18)&amp;&amp;(ip.src == ip.dst.1))"/>
     19 +</property>
     20 +```
     21 + 
     22 +This rule has 2 events.
     23 +The first event, having `event_id=1`, requires the present of `tcp.flags`, `tcp.dest_port` and `ip.dst` (it will be used in event 2).
     24 +The second one requires `tcp.flags`, `ip.src`.
     25 +This rule is verified against a message `msg` if the message contains:
     26 + 
     27 +- either `set_1` = ( `tcp.flags`, `tcp.dest_port`, `ip.dst`)
     28 +- or `set_2` = (`tcp.flags`, `ip.src`)
     29 +- or `set_3` = `set_1` v `set_2`
     30 + 
     31 +Thus, given a message `msg` having a set of any proto.atts. We need to find the fastest way to check if the message containing one of the 3 sets above.
     32 + 
     33 +# Solution
     34 + 
     35 +Using bit hash to check the present of each proto.att in message. Specifically,
     36 + 
     37 +- Each proto.att is represented by a unique id starting from 0.
     38 +
     39 + This assignment is done when calling `mmt_sec_init` function. At that moment, MMT-5Greplay knows the set of rules to verify, thus the set of proto.atts presented in these rules. The set of proto.atts is sorted by ascending of ID of protocols and attributes, then indexed from 0 to the last proto.atts. The ID numbers are designed by MMT-DPI.
     40 +
     41 +
     42 + For example, the ids of the proto.att above are as the following:
     43 +
     44 + | 0 | 1 | 2 | 3 |
     45 + | -------:| --------:| ---------------:| -----------:|
     46 + |`ip.src` | `ip.dst` | `tcp.dest_port` | `tcp.flags` |
     47 +
     48 +- A `unint_64 hash` number is used to present a set of proto.atts by turning on the corresponding bit of each proto.att.
     49 + 
     50 + Each message `msg` has a hash number, `msg->hash`, representing its set of proto.atts. The hash number is updated each time a new proto.att is added into `msg` by the function `set_element_data_message_t`.
     51 +
     52 + For example, to represent the presence of `tcp.dest_port` and `tcp.flags` of message `msg`, we have `msg->hash = (2^2 + 2^3) = 12`
     53 +
     54 + Each rule `r` has an array of hash numbers, `r->events_hash`, represent its requirement set of proto.atts.
     55 + This is calculated only once, when initializing MMT-5Greplay, by the function `_calculate_hash_number`.
     56 +
     57 + For example, to represent the requirement `set_1` of event 1, we have `r->event_hash[1] = (2^1 + 2^2 + 2^3) = 14`
     58 +
     59 + The message `m` can be used to verify the event 1 if `(msg->hash & r->events_hash[1]) == r->events_hash[1]`
     60 + 
     61 + 
     62 +- A rule has a hash number representing its requirement of proto.atts.
     63 + This number is calculated only once, when initializing MMT-5Greplay, inside the function `mmt_single_sec_register`.
     64 +
     65 + For example, `hash = (r->event_hash[1] | r->event_hash[2]) = (14 | 9) = 15`
     66 +
     67 + A message `msg` can be verified against a rule `r` if `msg` contains at least one proto.att required by `r`. Consequently, the condition is `(hash & msg->hash != 0)`.
     68 +
     69 + 
     70 +## Limit
     71 + As `uint64_t` is used to represent hash of a message, a message can contain a set of maximal 64 proto.atts.
     72 +
  • ■ ■ ■ ■ ■ ■
    docs/readme.md
     1 +- [What's new in this version?](./versions.md)
     2 +- [Workflow to use MMT-5Greplay](./smp_workflow.md)
     3 +- [How to write an embedded function](./rule.md)
     4 +- [Verdict decision of rule types](./verdict.md)
     5 +- [Hash function to access quickly of a message element](./hash.md)
     6 + 
  • ■ ■ ■ ■ ■ ■
    docs/rule.md
     1 +# 1. Sample rules
     2 + 
     3 +There are sample rules in [../rules/](../rules/)
     4 + 
     5 + 
     6 +# 2. Embedded functions
     7 + 
     8 +Embedded functions are functions that allow implementing calculations
     9 +that are too complicated to define using only classical operators on fields in the Boolean expressions of security rules.
     10 +One can either use existing embedded functions or implement a new function.
     11 +In both cases, they can be used in the Boolean expressions by using the syntax:
     12 + 
     13 + `#<name_of_function>(<list of parameters>)`
     14 + 
     15 +For example:
     16 + 
     17 + `(#em_is_search_engine( http.user_agent ) == true)`
     18 +
     19 +where `http` is the protocol name and `user_agent` is the attribute name (i.e., packet meta-data).
     20 + 
     21 +**To avoid any confusion**, a new embedded function name should start by a prefix `em_`.
     22 + 
     23 +## 2.1. Special terms
     24 + 
     25 +1. `true` will be replaced by the number 1. For example
     26 + 
     27 + `#em_check( tcp.src_port ) == true`
     28 + 
     29 +2. `false` will be replaced by the number 0. For example
     30 + 
     31 + `#em_check( tcp.src_port ) == false`
     32 + 
     33 + 
     34 +## 2.2 Implement a new embedded function
     35 +In each rule file, there exists a section allowing user to add an embedded function.
     36 + 
     37 +```xml
     38 +<embedded_functions><![CDATA[
     39 +//code C
     40 +static inline bool em_check( double port ){
     41 + if( port == 80 || port == 8080 )
     42 + return true;
     43 + return false;
     44 +}
     45 +]]></embedded_functions>
     46 +```
     47 + 
     48 +In side this tag, one can also implement 2 other functions as the followings:
     49 + 
     50 +1. `void on_load(){ ... }` being called when the rules inside the xml file being loaded into MMT-5Greplay
     51 + 
     52 +2. `void on_unload(){ ... }` being called when exiting MMT-5Greplay
     53 + 
     54 + 
     55 +## 2.3 Pre-installed embedded functions
     56 + 
     57 +In boolean expressions of rules, one can use one or many embedded functions
     58 + 
     59 +1. `is_exist( proto.att )` checks whether an event has an attribute of a protocol, e.g., `is_exist( http.method )` will return `true` if the current event contains protocol `HTTP` and attribute method has a non-null value, otherwise it will return `false`.
     60 + 
     61 + Normally MMT has a filter that allow an event in a rule to be verified only if any proto.att used in its boolean expression contains value. If one of them has not, the rule will not be verified. This allows to reduce number of verification of boolean expression, thus increases the performance.
     62 +
     63 + For example, given an event having the following boolean expression:
     64 +
     65 + `((ip.src != ip.dst) && (#em_check_URI(http.uri) == 1))`
     66 +
     67 + This event is verified only if `ip.src` and `ip.dst` and `http.uri` are not null, hence only HTTP packets are verified (it does not verify every IP packets).
     68 +
     69 + However, if one use the following expression, that is totally having the same meaning with the previous one:
     70 +
     71 + `((ip.src != ip.dst) && ((#is_exist(http.uri) == true ) && (#em_check_URI(http.uri) == 1)))`
     72 +
     73 + MMT need to verify the expression against any IP packet as `is_exist` tell MMT to exclude `http.uri` from its filter.
     74 +
     75 + 
     76 +2. `is_empty( proto.att )`, e.g., `is_empty(http.uri)` checks whether the string value is empty, i.e., its length is zero.
     77 + 
     78 +3. User can use any standard C functions as embedded function, e.g., `(#strstr( http.user_agent, 'robot') != 0)` to check if `http.user_agent` contains a sub-string `"robot"`.
     79 + 
     80 + Please note that, before using a C function the library containing that embedded functions need to be included.
     81 + The following libraries have been pre-included:
     82 +
     83 +```C
     84 +#include <string.h>
     85 +#include <stdio.h>
     86 +#include <stdlib.h>
     87 +#include "mmt_lib.h"
     88 +#include "pre_embedded_functions.h"
     89 +```
     90 + 
     91 + Thus when using a function that does not defined inside these libraries, one need to include its library. For example:
     92 +
     93 +```xml
     94 +<embedded_functions><![CDATA[
     95 +#include <math.h>
     96 + 
     97 +static inline bool function em_check( double port ){
     98 + double x = sqrt( port );
     99 + ...
     100 +}
     101 +]]></embedded_functions>
     102 +```
     103 +
     104 +# 3. Reactive functions
     105 +
     106 +Reactive functions allow user perform some action when a rule is satisfied.
     107 +The functions will be called each time their rules are satisfied.
     108 +When a security and attack rules are satisfied, they will give `not_respected` and `detected` verdicts respectively.
     109 + 
     110 +To implement and use a reactive function, one need:
     111 + 
     112 +- implement a C function inside `embedded_functions` tag. The function name should be prefixed by `em_` to avoid any confusion with the ones existing in MMT.
     113 + 
     114 + The function has the following format:
     115 + 
     116 +```C
     117 +typedef void (*mmt_rule_satisfied_callback)(
     118 + const rule_t *rule, //rule being validated
     119 + int verdict, //DETECTED, NOT_RESPECTED
     120 + uint64_t timestamp, //moment (by time) the rule is validated
     121 + uint64_t counter, //moment (by order of message) the rule is validated
     122 + const mmt_array_t * const trace //historic of messages that validates the rule
     123 +);
     124 +```
     125 +
     126 +- put the function name in attribute `if_satisfied` of the rule you want to react. For example: `if_satisfied="em_print_out"`
     127 + 
     128 + 
     129 +```xml
     130 +<beginning>
     131 +<embedded_functions><![CDATA[
     132 +static void em_print_out(
     133 + const rule_info_t *rule, int verdict, uint64_t timestamp,
     134 + uint64_t counter, const mmt_array_t * const trace ){
     135 + const char* trace_str = mmt_convert_execution_trace_to_json_string( trace, rule );
     136 + printf( "detect rule %d\n%s\n", rule->id, trace_str );
     137 + //you can call a system command, for example:
     138 + //char command[1001];
     139 + //snprintf( command, 1000, "echo rule %d is validated by %s", rule->id, trace_str );
     140 + //system( command );
     141 +}
     142 +]]></embedded_functions>
     143 + 
     144 +<!-- Property 10: HTTP using a port different from 80 and 8080.-->
     145 +<property value="THEN" delay_units="s" delay_min="0" delay_max="0" property_id="10" type_property="EVASION"
     146 + description="HTTP using a port different from 80 and 8080." if_satisfied="em_print_out">
     147 + <event value="COMPUTE" event_id="1"
     148 + description="HTTP packet using a port different from 80 and 8080"
     149 + boolean_expression="((http.method != '')&amp;&amp;((tcp.dest_port != 80)&amp;&amp;(tcp.dest_port != 8080)))"/>
     150 + <event value="COMPUTE" event_id="2"
     151 + description="HTTP packet"
     152 + boolean_expression="(ip.src != ip.dst)"/>
     153 +</property>
     154 +</beginning>
     155 +```
     156 + 
     157 +# 4. Compile rules
     158 + 
     159 +MMT-5Greplay rules are specified in plain text following a XML format.
     160 +These rules need to be encoded in a suitable format, that is a dynamic C library, before being able to use by MMT-5Greplay.
     161 + 
     162 +The compiled rules must put in either `./rules` or `/opt/mmt/security/rules`.
     163 +The former has higher priority and only one of them will be taken into account by MMT-5Greplay.
     164 +Specifically, if MMT-5Greplay found `./rules` folder in the current folder executing, it will use the rules inside this folder and
     165 +it does not take into account the rules in `/opt/mmt/security/rules`.
     166 + 
     167 +## 4.1 Compile rules
     168 +MMT-5Greplay provides a compiler to do the such of task. Its source code is in [`src/main_gen_plugin.c`](../src/main_gen_plugin.c) file.
     169 + 
     170 +- use `make compile_rule` to compile this program to get an executable program `compile_rule`
     171 + 
     172 +- use `compile_rule` to compile rules in a XML file. For example:
     173 + 
     174 + `./compile_rule rules/1.so rules/1.ssh.xml`
     175 +
     176 + The program uses 3 parameters in form: `./compile_rule output_file property_file [gcc parameters]`
     177 + 
     178 + where:
     179 + 
     180 + - `output_file`: is the path of file containing the result that can be either a .c file or .so file.
     181 + - `property_file`: is the path where the property file can be found.
     182 + - `options`:
     183 +
     184 + - `-c`: will generate only the C code. This option allows manually modifying the generated code before compiling it.
     185 + After generating the C code, the tool prints out the command that needs to be executed for compiling it.
     186 +
     187 + - gcc parameters: used to generate the C code, and compile it to obtain the .so file.
     188 + These parameters will be directly transmitted to the gcc compiler, for example, `"-I /tmp -lmath"`
     189 +
     190 +Please note that, the compiled rules must be put in the directory `./rules` or `/opt/mmt/security/rules/`
     191 + 
     192 +## 4.2 Obtain information inside compiled rules
     193 + 
     194 +To get some basic information about a compiled rule (such as, ID, description) MMT provides a tool:
     195 +[`src/main_plugin_info.c`](../src/main_plugin_info.c)
     196 + 
     197 +- use `make rule_info` to obtain its executable program
     198 +- use `./rule_info` to print information of all compiled rules in `./rules` or `/opt/mmt/security/rules/`
     199 + 
     200 + By default, the tool will print out the information on all rules, for example:
     201 +
     202 +```
     203 +./rule_info
     204 +Found 37 rules.
     205 +1 - Rule id: 1
     206 + - type : attack
     207 + - events_count : 4
     208 + - variables_count : 5
     209 + - variables : ip.dst (178.13), ip.src (178.12), tcp.dest_port (354.2), tcp.flags (354.6), tcp.src_port (354.1)
     210 + - description : Several attempts to connect via ssh (brute force attack). Source address is either infected machine or attacker (no spoofing is possible).
     211 + - if_satisfied : (nil)
     212 + - version : 1.1.5 (70a367f - 2017-6-8 12:42:25), dpi version 1.6.8.0 (b3e727b)
     213 +2 - Rule id: 10
     214 + - type : evasion
     215 + - events_count : 2
     216 + ...
     217 +...
     218 +```
     219 + 
     220 +- The tool can also be used to inspect a specific compiled rule by giving the rule path as parameter, for example:
     221 + 
     222 +```
     223 +./rule_info /opt/mmt/security/rules/4.arp.so
     224 +Found 1 rule.
     225 +1 - Rule id: 4
     226 + - type : attack
     227 + - events_count : 3
     228 + - variables_count : 7
     229 + - variables : arp.ar_op (30.5), arp.ar_sha (30.6), arp.ar_sip (30.7), arp.ar_tip (30.9), ethernet.dst (99.2), ethernet.packet_count (99.4099), ethernet.src (99.3)
     230 + - description : IPv4 address conflict detection (RFC5227). Possible arp poisoning.
     231 + - if_satisfied : (nil)
     232 + - version : 1.1.5 (70a367f - 2017-6-8 12:42:28), dpi version 1.6.8.0 (b3e727b)
     233 +```
     234 +
     235 +# 5. Default values
     236 + 
     237 +In the XML file of a rule, if an attribute is absent then its value is set by default:
     238 + 
     239 +- `property`, `operator`:
     240 + + `value` : `COMPUTE` (=> only one `<event>` is required. Thus `delay_min` and `delay_max` must be 0)
     241 + + `delay_units`: `s`
     242 + + `delay_min`: 0
     243 + + `delay_max`: 0
     244 +
     245 +An example of `COMPUTE`rule:
     246 + 
     247 +```XML
     248 +<property property_id="11" type_property="EVASION"
     249 + description="IP packet size incorrect">
     250 + <event value="COMPUTE" event_id="1"
     251 + description="Packet size incorrect"
     252 + boolean_expression="((meta.packet_len &lt; 34)"/>
     253 +</property>
     254 +```
     255 + 
     256 +
     257 +# 6. Write a high performance rule
     258 +
     259 + To write a rule having a high performance one need to:
     260 +
     261 + - use only the proto.att in boolean expression when need.
     262 + 
     263 + - Please refer to the usage of function `is_exist` in section 2.3 to get an example.
     264 + - Use explicitly the following tcp flags to filter out unwanted verification: tcp.fin, tcp.syn, tcp.rst, tcp.psh, tcp.ack, tcp.urg, tcp.ece, tcp.cwr.
     265 +
     266 + For example, the 2 following boolean expressions have the same meaning:
     267 +
     268 + `(tcp.flags == 4)` and `((tcp.flags == 4) && (tcp.rst == 1))`
     269 +
     270 + They both return `true` when only RST flag of a TCP packet is on, but the latter is better as MMT verifies its rule only when `tcp.flags` and `tcp.rst` are not zero. Usually less than about 1% packets having `tcp.rst != 0`, consequently the rule using the second expression will be verified against only 1% packets.
     271 + 
     272 + - reduce `delay_max` of a rule to a suitable value.
     273 + 
     274 + When having a higher value of `delay_max` MMT-5Greplay creates more rule instances to correlate different events of different packets. When `delay_max` is zero, the rule is call simple rule, MMT-5Greplay verifies the rule and gives verdict immediately without creating any rule instances. A simple rule is verified much faster than a complex one that has non-zero `delay_max`.
     275 +
     276 + At 10Gbps, MMT-5Greplay can verify 12400 simple rules or 600 complex rules.
     277 +
     278 + - optimize implementation of embedded functions.
     279 + 
     280 + - The embedded functions are called each time their boolean expressions are verified. Consequently, rather than initialize something, for example, connection to database, inside these functions, one can do such a task, only once, inside function `on_load` then store the connection into a static local variable that will be used inside the embedded functions.
     281 +
     282 + - alway use embedded function with `static inline` keyword. For more information about advantage of `inline`, please refer to document of gcc: [An Inline Function is As Fast As a Macro](https://gcc.gnu.org/onlinedocs/gcc/Inline.html)
     283 + 
  • ■ ■ ■ ■ ■ ■
    docs/rules_mask.md
     1 +Rules mask is a string. It is used to distribute rules on each thread.
     2 + 
     3 +## Syntax
     4 +It respects the following BNF syntax:
     5 + 
     6 +```
     7 +rules_mask :== (thread_id:rule_id_range)+
     8 +thread_id := number
     9 +rule_id_range := number | number-rule_id_range | number,rule_id_range
     10 +```
     11 + 
     12 +`thread_id` must be an integer and starts from 1
     13 + 
     14 +### Example
     15 + 
     16 +`
     17 +"(1:1-4,6)(2:5,7-10)"
     18 +`
     19 + 
     20 +This rules mask will attribute rules 1,2,3,4,6 to the first thread, rules 5,7,8,9,10 to the second thread.
  • ■ ■ ■ ■ ■ ■
    docs/smp_workflow.md
     1 +# 1. Introduction
     2 +Generally, MMT-5Greplay takes as input a sequence of message and output alerts. It loads first a set of rules to be verified and be compiled as dynamic libraries. Each time a message coming, it verifies the message against the rules. If some rules are satisfied, it output alerts.
     3 + 
     4 +```
     5 + rules.so ----> --------------
     6 + | |
     7 +==== messages ===> | MMT-5Greplay | ==== alerts ===>
     8 + | |
     9 + --------------
     10 +```
     11 + 
     12 +# 2. Workflow
     13 +The workflow of MMT-5Greplay multi-threading is represented as Figure below. Details of the functions will be introduced subsequently.
     14 +![Workflow](smp_workflow.svg)
     15 + 
     16 +[Edit](https://knsv.github.io/mermaid/live_editor/#/edit/c2VxdWVuY2VEaWFncmFtClVzZXItPj4gbW10X3NtcF9zZWN1cml0eTogbW10X3NtcF9zZWNfcmVnaXN0ZXIoKQphY3RpdmF0ZSBtbXRfc21wX3NlY3VyaXR5Cmxvb3AgZm9yIGVhY2ggdGhyZWFkCm1tdF9zbXBfc2VjdXJpdHktPj4gbW10X3NpbmdsZV9zZWN1cml0eTogbW10X3NpbmdsZV9zZWNfcmVnaXN0ZXIKYWN0aXZhdGUgbW10X3NpbmdsZV9zZWN1cml0eQptbXRfc2luZ2xlX3NlY3VyaXR5LT4-IHJ1bGVfdmVyaWZfZW5naW5lOiBydWxlX2VuZ2luZV9pbml0CmFjdGl2YXRlIHJ1bGVfdmVyaWZfZW5naW5lCmVuZAoKVXNlci0-PiBtbXRfc21wX3NlY3VyaXR5OiBtbXRfc21wX3NlY19wcm9jZXNzIChtZXNzYWdlKQptbXRfc21wX3NlY3VyaXR5IC0-PiBtbXRfc21wX3NlY3VyaXR5OiByaW5nX3B1c2goIG1lc3NhZ2UgKQoKbG9vcCBmb3JldmVyCm1tdF9zaW5nbGVfc2VjdXJpdHkgLT4-IG1tdF9zbXBfc2VjdXJpdHk6IHJpbmdfcG9wX2J1cnN0KCBtZXNzYWdlcyApCgpsb29wIGZvciBlYWNoIG0gaW4gbWVzc2FnZXMKbW10X3NtcF9zZWN1cml0eSAtPj4gbW10X3NpbmdsZV9zZWN1cml0eTogbW10X3NpbmdsZV9zZWNfcHJvY2VzcyggbSApCm1tdF9zaW5nbGVfc2VjdXJpdHkgLT4-IHJ1bGVfdmVyaWZfZW5naW5lOiBydWxlX2VuZ2luZV9wcm9jZXNzKCBtICkKcnVsZV92ZXJpZl9lbmdpbmUgLS0-PiBtbXRfc2luZ2xlX3NlY3VyaXR5OiB2ZXJkaWN0CgpvcHQgdmVyZGljdCAhPSB1bmtub3duCm1tdF9zaW5nbGVfc2VjdXJpdHkgLT4-IG1tdF9zaW5nbGVfc2VjdXJpdHk6IGNhbGxiYWNrKCB2ZXJkaWN0ICkKZW5kCgplbmQKZW5kCgpVc2VyLT4-IG1tdF9zbXBfc2VjdXJpdHk6IG1tdF9zbXBfc2VjX3VucmVnaXN0ZXIKbG9vcCBmb3IgZWFjaCB0aHJlYWQKbW10X3NtcF9zZWN1cml0eS0-PiBtbXRfc2luZ2xlX3NlY3VyaXR5OiBtbXRfc2luZ2xlX3NlY191bnJlZ2lzdGVyCm1tdF9zaW5nbGVfc2VjdXJpdHktPj4gcnVsZV92ZXJpZl9lbmdpbmU6IHJ1bGVfZW5naW5lX2ZyZWUKZGVhY3RpdmF0ZSBydWxlX3ZlcmlmX2VuZ2luZQptbXRfc2luZ2xlX3NlY3VyaXR5IC0tPj4gbW10X3NtcF9zZWN1cml0eTogYWxlcnRzX2NvdW50IG9mIGVhY2ggcnVsZQpkZWFjdGl2YXRlIG1tdF9zaW5nbGVfc2VjdXJpdHkKZW5kCgptbXRfc21wX3NlY3VyaXR5IC0tPj4gVXNlcjogYWxlcnRzX2NvdW50CmRlYWN0aXZhdGUgbW10X3NtcF9zZWN1cml0eQ)
     17 + 
     18 +## 2.1. Initialize
     19 + 
     20 +This process need to be done only once before calling any other functions.
     21 + 
     22 +### 2.2.1 Load rules
     23 + 
     24 +````C
     25 +int mmt_sec_init( const char * excluded_rules_id )
     26 +````
     27 + 
     28 +- **Description**:
     29 + This function initialises MMT-5Greplay to analyse a set of rules. By defaults, these rules must be compiled and located on the folder `./rules` or `/opt/mmt/security/rules`.
     30 + 
     31 +- **Input**:
     32 + This function allows to exclude some rules from verification. The ID of excluded rules are set by the parameter `excluded_rules_id`. The parameter is a null-terminated byte string
     33 + 
     34 + The IDs are separated by comma `,`. A continue range of IDs is represented by the smallest and the biggest ID and they are separated by a dash `-`.
     35 + 
     36 + For example, `mmt_sec_init("1-5,7,9")`, will exclude rules having id 1,2,3,4,5,7 and 9 from verification.
     37 + 
     38 +- **Return**:
     39 + - 0 if no error
     40 + - 1 if MMT-5Greplay found no rules to verify
     41 + 
     42 + 
     43 +### 2.2.2 Register
     44 + 
     45 +```C
     46 +// A function to be called when a rule is validated
     47 +typedef void (*mmt_sec_callback)(
     48 + const rule_info_t *rule, //rule being validated
     49 + enum verdict_type verdict, //DETECTED, NOT_RESPECTED
     50 + uint64_t timestamp, //moment (by time) the rule is validated
     51 + uint64_t counter, //moment (by order of packet) the rule is validated
     52 + const mmt_array_t * const trace, //historic of messages that validates the rule
     53 + void *user_data //#user-data being given in mmt_sec_register_rules
     54 +);
     55 +mmt_sec_handler_t* mmt_sec_register( size_t threads_count, const uint32_t *cores_id,
     56 + const char *rules_mask, bool verbose, mmt_sec_callback callback, void *args );
     57 +```
     58 + 
     59 +- **Description**: Register some rules to validate.
     60 +
     61 + After calling this function, depending on number of threads given in `threads_count` parameter, MMT-5Greplay can create several threads and starts them. The processing of the threads is represented by the loop `forever` block in Figure above. This block always check the MMT-5Greplay buffer to get out a message to verify. Each time it found a rule being validated, it will call the function given by `callback` parameter. It is stoped by calling `mmt_sec_unregister` function.
     62 + 
     63 +- **Input**:
     64 + + `threads_count`: number of threads
     65 + + `core_mask` : a string indicating logical cores to be used,
     66 + e.g., "1-4,11-12,19" => we use cores 1,2,3,4,11,12,19. Core number starts from 1.
     67 + + `rule_mask` : a string indicating special rules being attributed to special threads
     68 + e.g., "(1:10-13)(2:50)(4:1007-1010)".
     69 + The other rules will be attributed equally to the rest of threads.
     70 + + `callback` : a function to be called when a rules is validated
     71 + + `user_data` : data will be passed to the `callback`
     72 +- **Return**:
     73 + + `NULL` if there are error, e.g., insufficient memory, parameters are incorrect
     74 + + a handler pointer, otherwise
     75 +- **Note**:
     76 + The function callback can be called from different threads. (Thus if it accesses to a global variable or a static one, the access to these variables must be synchronous)
     77 + 
     78 + 
     79 + 
     80 + 
     81 +## 2.2. Process
     82 + 
     83 +This function need to call every time having a message to be verified.
     84 + 
     85 +```C
     86 +void mmt_sec_process( mmt_sec_handler_t *handler, message_t *msg );
     87 +```
     88 + 
     89 +This function simply put the message into a buffer of MMT-5Greplay. If the buffer is not full, the function return immediately, otherwise it will always verify the buffer until having an available place to put the message.
     90 + 
     91 +## 2.3. Finish
     92 + 
     93 +This process need to be done once only when finishing.
     94 + 
     95 +### 2.3.1 Unrgister
     96 +```C
     97 +size_t mmt_sec_unregister( mmt_sec_handler_t* );
     98 +```
     99 + - **Description**: Stop and free every threads used by MMT-5Greplay.
     100 + - **Input**: a pointer points to the handler being created by `mmt_sec_register`
     101 + - **Return**: Number of alerts generated
     102 + 
     103 +### 2.3.2 Unload rules
     104 + 
     105 +The following function will unload rules. This is called only at the end of processing. After this function, no more mmt-security functions can be called.
     106 + 
     107 +```C
     108 +void mmt_sec_close();
     109 +```
  • docs/smp_workflow.svg
  • ■ ■ ■ ■ ■ ■
    docs/verdict.md
     1 + 
     2 +Given a property `if A then B`,
     3 + 
     4 +- `FSM_ERROR_STATE_REACHED` if `A` is validated but not `B` (or we have not `B` in a given delay)
     5 + 
     6 +- `FSM_FINAL_STATE_REACHED` if both `A` and `B` are validated
     7 + 
     8 +```c
     9 +static inline enum verdict_type _get_verdict( int rule_type, enum fsm_handle_event_value result ){
     10 + switch ( rule_type ) {
     11 + case RULE_TYPE_TEST:
     12 + switch( result ){
     13 + case FSM_ERROR_STATE_REACHED:
     14 + return VERDICT_NOT_RESPECTED;
     15 + case FSM_FINAL_STATE_REACHED:
     16 + return VERDICT_RESPECTED;
     17 + default:
     18 + return VERDICT_UNKNOWN;
     19 + }
     20 + break;
     21 + 
     22 + case RULE_TYPE_SECURITY:
     23 + switch( result ){
     24 + case FSM_ERROR_STATE_REACHED:
     25 + return VERDICT_NOT_RESPECTED;
     26 + case FSM_FINAL_STATE_REACHED:
     27 +// return VERDICT_RESPECTED;
     28 + return VERDICT_UNKNOWN;
     29 + default:
     30 + return VERDICT_UNKNOWN;
     31 + }
     32 + break;
     33 + case RULE_TYPE_ATTACK:
     34 + case RULE_TYPE_EVASION:
     35 + switch( result ){
     36 + case FSM_ERROR_STATE_REACHED:
     37 + return VERDICT_UNKNOWN; //VERDICT_NOT_DETECTED;
     38 + case FSM_FINAL_STATE_REACHED:
     39 + return VERDICT_DETECTED;
     40 + default:
     41 + return VERDICT_UNKNOWN;
     42 + }
     43 + break;
     44 + default:
     45 + ABORT("Error 22: Property type should be a security rule or an attack.\n");
     46 + }//end of switch
     47 + return VERDICT_UNKNOWN;
     48 +}
     49 +```
  • ■ ■ ■ ■ ■
    docs/versions.md
     1 + 
     2 + 
     3 +## Version 0.0.1
     4 + 
     5 +1. Restructure source code
  • lib/mmt-dpi_1.7.0.0_a8ad3c2_Linux_x86_64.deb
    Binary file.
  • ■ ■ ■ ■ ■ ■
    mmt-5greplay.conf
     1 +# option only when using DPDK to capture packets or to forwarding packets
     2 +dpdk-option = "--syslog=syslog --log-level=5 -c 0x55555555555"
     3 + 
     4 +#root stack of network protocol
     5 +# for Ethernet: 1
     6 +# for ieee802154: 800
     7 +# for Linux cooked capture: 624
     8 +stack-type = 1
     9 + 
     10 +input {
     11 + # in case for PCAP the input mode can be ONLINE or OFFLINE, however for DPDK it's only ONLINE
     12 + mode = ONLINE
     13 +
     14 + # input source for PCAP online mode (interface name) and for offline mode (pcap name),
     15 + # however for DPDK its interface port number
     16 + # in DPDK mode, MMT supports also multi-port inputs,
     17 + # - e.g., source="0,1" will tell MMT to capture packets on port 0 and 1
     18 + # - MMT will aggregate traffic on these 2 ports, thus 2 packets of one flow can be received on 2 different ports
     19 + source = "lo"
     20 +
     21 + # maximal size of a packet
     22 + snap-len = 65535 #
     23 +}
     24 + 
     25 +output {
     26 + enable = true
     27 + output-dir = "./" # Location where files are written:
     28 + sample-interval = 5 #a new sample file is created each x seconds given by output.cache-period
     29 + report-description = true # true to include rule's description into the alert reports,
     30 + # otherwise it will be excluded (thus rules's descriptions will be an empty string in the reports)
     31 + # Excluding rules's descriptions will reduce the size of reports.
     32 +}
     33 + 
     34 +engine {
     35 + thread-nb = 0 # the number of security threads per one probe thread , e .g . , if we have 16 probe threads and thread-nb = x ,
     36 + # then x*16 security threads will be used .
     37 + # If set to zero this means that the security analysis will be done by the threads of the probe .
     38 +
     39 + exclude-rules = "" # Range of rules to be excluded from the verification
     40 + rules-mask = "" # Mapping of rules to the security threads:
     41 + # Format: rules-mask = (thread-index:rule-range);
     42 + # thread-index = a number greater than 0
     43 + # rule-range = number greater than 0, or a range of numbers greater than 0.
     44 + # Example: If we have thread-nb = 3 and "(1:1,2,4-6)(2:3)" ,
     45 + # this means that:
     46 + # thread 1 verifies rules 1 ,2 ,4 ,5 ,6;
     47 + # thread 2 verifies only rule 3; and
     48 + # thread 3 verifies the rest
     49 + # Note: if we have thread-nb = 2 and "(1:1)(2:3)", then only rules 1 and 3 are verified (the others are not)
     50 +
     51 + ip-encapsulation-index = LAST # If traffic is ip-in-ip, this option selects which IP will be analysed.
     52 + # - FIRST: first ip in the protocol hierarchy
     53 + # - LAST: last ip in the protocol hierarchy
     54 + # - i: i-th ip in ther protocol hierarchy.
     55 + # For example, given ETH.IP.UDP.GTP.IP.TCP.VPN.IP.SSL,
     56 + # - FIRST, or 1, indicates IP after ETH
     57 + # - LAST, or any number >= 3, indicates IP after VPN
     58 + # - 2 indicates IP after GTP
     59 + # NOTE: this option will be ignored in non ip-in-ip traffic
     60 + 
     61 + # number of fsm instances of one rule
     62 + max-instances = 100000
     63 +}
     64 + 
     65 +# A mem_pool contains several pools. Each pool stores several blocks of memory
     66 +# having the same size.
     67 +# This parameter set the maximum elements of a pool.
     68 +mempool {
     69 + # This parameter set the Maximum bytes of a pool: 2 GBytes
     70 + max-bytes = 2000000000
     71 + # Max number of elements in a pool
     72 + max-elements = 1000
     73 + # maximum size, in bytes, of a report received from mmt-probe
     74 + max-message-size = 3000
     75 +
     76 + # Number of reports can be stored in a ring buffer
     77 + smp-ring-size = 1000
     78 +}
     79 + 
     80 + 
     81 +forward
     82 +{
     83 + enable = true
     84 + output-nic = "lo"
     85 + nb-copies = 2 #number of copies of a packet to be sent
     86 + snap-len = 0 #specifies the snapshot length to be set on the handle.
     87 + promisc = 1 #specifies whether the interface is to be put into promiscuous mode. If promisc is non-zero, promiscuous mode will be set, otherwise it will not be set.
     88 + default = DROP #default action when packets are not selected/satisfied by any rule
     89 + # either FORWARD to forward the packets or DROP to drop the packets
     90 +
     91 + #forward packets to a target using SCTP protocol: MMT will be a SCTP client,
     92 + # - it connects to the given "sctp-host" at "sctp-port"
     93 + # - the SCTP packets' payload will be sent to the target using this SCTP connection
     94 + target-protocols = { SCTP, UDP}
     95 + target-hosts = { "127.0.0.5", "127.0.0.7" }
     96 + target-ports = { 38412, 2152 }
     97 +}
     98 + 
  • ■ ■ ■ ■ ■ ■
    rules/forward-localhost.xml
     1 +<beginning>
     2 + 
     3 +<property property_id="103" type_property="FORWARD"
     4 + description="Inject only SCTP packets from UE -> Core but not inversed direction">
     5 + <event description="From UE and NAS-5G packets"
     6 + boolean_expression="( (sctp.dest_port == 38412 ) &amp;&amp;(sctp.ch_type == 0))"/>
     7 +</property>
     8 + 
     9 +<property property_id="104" type_property="FORWARD"
     10 + description="Inject only UDP packets from UE -> Core but not inversed direction">
     11 + <event description="From UE and GTP packets"
     12 + boolean_expression="( (udp.dest_port == 2152 ) )"/>
     13 +</property>
     14 + 
     15 +</beginning>
  • ■ ■ ■ ■ ■ ■
    rules/nas-smc-replay-attack.xml
     1 +<beginning>
     2 +<!-- Property 100: Forwarding NAS security mode COMPLETE that answers to NAS security mode COMMAND.-->
     3 +<property value="THEN" delay_units="ms" delay_min="0" delay_max="0+" property_id="100" type_property="FORWARD"
     4 + description="Forwarding NAS security mode COMPLETE that answers to NAS security mode COMMAND ">
     5 + <event value="COMPUTE" event_id="1"
     6 + description="NAS Security mode COMMAND"
     7 + boolean_expression="(nas_5g.message_type == 93)"/>
     8 + <event value="COMPUTE" event_id="2"
     9 + description="NAS Security mode COMPLETE"
     10 + boolean_expression="(nas_5g.security_type == 3)"/>
     11 +</property>
     12 +</beginning>
  • ■ ■ ■ ■ ■ ■
    rules/security-rule-forwarding.xml
     1 +<beginning>
     2 +<embedded_functions><![CDATA[
     3 + 
     4 +static void em_forward(
     5 + const rule_info_t *rule, int verdict, uint64_t timestamp,
     6 + uint64_t counter, const mmt_array_t * const trace ){
     7 + const char* trace_str = mmt_convert_execution_trace_to_json_string( trace, rule );
     8 + forward_packet();
     9 + //old ran_ue_id
     10 + uint64_t ran_ue_id = get_numeric_value( PROTO_NGAP, NGAP_ATT_RAN_UE_ID, 1, trace );
     11 + for( int i=0; i<1; i++ ){
     12 + set_numeric_value( PROTO_NGAP, NGAP_ATT_RAN_UE_ID, ran_ue_id + i + 100 );
     13 + forward_packet();
     14 + }
     15 +}
     16 + 
     17 +void on_load(){
     18 + printf("Loaded successfully rule 1");
     19 +}
     20 + 
     21 +void on_unload(){
     22 + printf("Unloaded successfully rule 1");
     23 +}
     24 + 
     25 +bool em_check(){
     26 + sleep(1);
     27 + printf("sleep 1 second");
     28 + return true;
     29 +}
     30 + 
     31 + 
     32 +]]></embedded_functions>
     33 + 
     34 +<!--
     35 +<property value="THEN" delay_units="s" delay_min="0+" delay_max="1" property_id="100" type_property="FORWARD"
     36 + description="Forwarding NAS security mode COMPLETE that answers to NAS security mode COMMAND "
     37 + _if_satisfied="#update(ngap.procedure_code, (ngap.procedure_code.1 + 40)"
     38 + if_satisfied="em_forward"
     39 + >
     40 + <event value="COMPUTE" event_id="1"
     41 + description="NAS Security mode COMMAND"
     42 + boolean_expression="(nas_5g.message_type == 93)"/>
     43 + <event value="COMPUTE" event_id="2"
     44 + description="NAS Security mode COMPLETE"
     45 + boolean_expression="(nas_5g.security_type != 3)"/>
     46 +</property>
     47 + -->
     48 + 
     49 + 
     50 +
     51 +<property property_id="103" type_property="FORWARD"
     52 + description="Inject only packet from UE -> Core but not inversed direction">
     53 + <event description="From UE and NAS-5G packets"
     54 + boolean_expression="( ( #is_same_ipv4(ip.src, '127.0.0.1') ) &amp;&amp; ((sctp.dest_port == 38412 ) &amp;&amp;(sctp.ch_type == 0)) )"/>
     55 +</property>
     56 + 
     57 +<!--
     58 +<property property_id="104" type_property="FORWARD"
     59 + description="SCTP INIT">
     60 + <event description="From UE and NAS-5G packets"
     61 + boolean_expression="( #is_same_ipv4(ip.src, '10.1.0.3') &amp;&amp; #em_check())"/>
     62 +</property>
     63 + -->
     64 + 
     65 +<!--
     66 +<property value="COMPUTE" property_id="101" type_property="FORWARD"
     67 + description="5G testing" if_satisfied="#update(ngap.procedure_code, (ngap.procedure_code.1 + 40)">
     68 + <event value="COMPUTE" event_id="1"
     69 + description="Authentication response, Uplink NAS Transport"
     70 + boolean_expression="((ngap.procedure_code == 46) &amp;&amp; (nas_5g.message_type == 87))"/>
     71 +</property>
     72 + -->
     73 +
     74 +<!--
     75 +<property value="THEN" property_id="102" type_property="FORWARD"
     76 + description="5G testing" if_satisfied="em_forward">
     77 + <event value="COMPUTE" event_id="1"
     78 + description="Authentication response, Uplink NAS Transport"
     79 + boolean_expression="((ngap.procedure_code == 46) &amp;&amp; (nas_5g.message_type == 87))"/>
     80 + <event value="COMPUTE" event_id="2"
     81 + description="Nothing"
     82 + boolean_expression="(ngap.ran_ue_id != 0)"/>
     83 +</property>
     84 + -->
     85 + 
     86 +<!--
     87 + 
     88 +<property value="COMPUTE" property_id="103" type_property="FORWARD"
     89 + description="Drop no-5G traffic" if_satisfied="#drop()">
     90 + <event description="registration request"
     91 + boolean_expression="(ethernet.proto == 16393"/>
     92 +</property>
     93 + 
     94 +<property value="THEN" delay_units="s" delay_min="0" delay_max="1" property_id="104" type_property="ATTACK"
     95 + description="5G UE authentication hijack: UE IDs in Authentication Response is different with the one of Authetication Request">
     96 + <event value="COMPUTE" event_id="1"
     97 + description="Authentication request, Downlink NAS Transport"
     98 + boolean_expression="((ngap.procedure_code == 4) &amp;&amp; (nas_5g.message_type == 86))"/>
     99 +
     100 + <event value="COMPUTE" event_id="2"
     101 + description="In Authentication Response of the same SCTP stream, same AMF-UE-ID but different RAN-UE-ID"
     102 + boolean_expression="((sctp_data.data_stream == sctp_data.data_stream.1)
     103 + &amp;&amp; ((((ngap.procedure_code == 46)
     104 + &amp;&amp; (((nas_5g.message_type == 87)
     105 + &amp;&amp; ((ngap.amf_ue_id == ngap.amf_ue_id.1)
     106 + &amp;&amp; (ngap.ran_ue_id != ngap.ran_ue_id.1) )))))))"/>
     107 +</property>
     108 + 
     109 + -->
     110 +</beginning>
  • ■ ■ ■ ■ ■ ■
    src/command/compile.c
     1 +/*
     2 + * main_gen_plugin.c
     3 + *
     4 + * Created on: 26 sept. 2016
     5 + * Created by: Huu Nghia NGUYEN <[email protected]>
     6 + *
     7 + * Parse rules in .xml file, then generate .c file, then compile to a plugin .so file.
     8 + */
     9 + 
     10 +#include <mmt_core.h>
     11 +#include "../lib/mmt_lib.h"
     12 +#include "../engine/expression.h"
     13 +#include "../engine/rule.h"
     14 +#include "../engine/gen_code.h"
     15 + 
     16 +#define MAX_STRING_LEN 10000
     17 + 
     18 +int compile( int argc, char** argv ){
     19 + rule_t **rule_list;
     20 + size_t rule_count, i;
     21 + char c_file[MAX_STRING_LEN];
     22 + char gcc_param[MAX_STRING_LEN];
     23 + 
     24 + const char* xml_file, *output_file;
     25 + char *embedded_functions;
     26 + int ret;
     27 + 
     28 + if( argc != 3 && argc != 4){
     29 + fprintf( stderr, "Usage: %s output_file property_file option", argv[0] );
     30 + fprintf( stderr, "\n - output_file : path of file containing result that can be either a .c file or .so file");
     31 + fprintf( stderr, "\n - property_file : path to property file to read");
     32 + fprintf( stderr, "\n - option : ");
     33 + fprintf( stderr, "\n + \"-c\" : generate only code c" );
     34 + fprintf( stderr, "\n + otherwise: generate code c, then compile to .so file.");
     35 + fprintf( stderr, "\n This option will be transfered to gcc, for example, \"-I /tmp/include -lmath\"");
     36 + fprintf( stderr, "\n");
     37 + return 1;
     38 + }
     39 + xml_file = argv[2];
     40 + output_file = argv[1];
     41 + 
     42 + //check output file
     43 + if( argc == 4 && strcmp( argv[3], "-c") == 0 ){
     44 + if( !str_end_with( output_file, ".c" ) ){
     45 + log_write( LOG_ERR, "output_file must be end with .c");
     46 + return 1;
     47 + }
     48 + 
     49 + snprintf(c_file, sizeof( c_file), "%s", output_file );
     50 + }else{
     51 + if( !str_end_with( output_file, ".so" ) ){
     52 + log_write( LOG_ERR, "output_file must be end with .so");
     53 + return 1;
     54 + }
     55 + snprintf(c_file, sizeof( c_file), "%s.c", output_file );
     56 + }
     57 + 
     58 + //init mmt_dpi extraction to load list of avaiable protocols
     59 + init_extraction();
     60 + 
     61 + //read rule from .xml file
     62 + rule_count = read_rules_from_file( xml_file, &rule_list, &embedded_functions );
     63 + 
     64 + //generate rules to .c code
     65 + generate_fsm( c_file, rule_list, rule_count, embedded_functions );
     66 + 
     67 + if( argc == 4 && strcmp( argv[3], "-c") == 0 ){
     68 + log_write( LOG_INFO, "Encoded %zu rules from \"%s\" to \"%s\"", rule_count, xml_file, c_file );
     69 + log_write( LOG_INFO, "To compile, use: /usr/bin/gcc -fPIC -shared %s -o output.so", c_file );
     70 + }else{
     71 + //compile code file
     72 + if( argc == 3 )
     73 + ret = compile_gen_code(output_file, c_file,"./src/lib -I./src/engine -I ./src/dpi -I/opt/mmt/dpi/include" );
     74 + else{
     75 + snprintf( gcc_param, sizeof( gcc_param), "./src/lib -I./src/engine -I ./src/dpi -I/opt/mmt/dpi/include %s", argv[3] );
     76 + ret = compile_gen_code(output_file, c_file, gcc_param );
     77 + }
     78 + 
     79 + //DEBUG( "ret = %d", ret );
     80 + if( ret == 0 ){
     81 + log_write( LOG_INFO, "Encoded %zu rules from \"%s\" to \"%s\"", rule_count, xml_file, output_file );
     82 + 
     83 +#ifndef DEBUG_MODE
     84 + //delete .c file
     85 + remove( c_file );
     86 +#endif
     87 + }else
     88 + log_write( LOG_ERR, "Cannot encode rule \"%s\". Check options.", xml_file );
     89 + }
     90 + 
     91 + //free each rule
     92 + for( i=0; i<rule_count; i++ )
     93 + free_a_rule( rule_list[i], true);
     94 + 
     95 + mmt_mem_free( rule_list );
     96 + mmt_mem_free( embedded_functions );
     97 + close_extraction();// close mmt_dpi
     98 + return 0;
     99 +}
     100 + 
  • ■ ■ ■ ■ ■ ■
    src/command/info.c
     1 +/*
     2 + * main_plugin_info.c
     3 + *
     4 + * Created on: Oct 10, 2016
     5 + * Created by: Huu Nghia NGUYEN <[email protected]>
     6 + *
     7 + * Get information of rules encoded in a binary file (.so)
     8 + */
     9 +#include "../lib/base.h"
     10 +#include "../lib/mmt_log.h"
     11 +#include "../lib/mmt_alloc.h"
     12 +#include "../engine/plugins_engine.h"
     13 +#include <dirent.h>
     14 +#include <dlfcn.h>
     15 + 
     16 +int info( int argc, char** argv ){
     17 + const rule_info_t *const*rules_arr;
     18 + size_t i, j, n;
     19 + struct tm tm;
     20 + 
     21 + ASSERT( argc <= 2, "Usage: %s [lib_file.so]", argv[0] );
     22 + 
     23 + if( argc == 1)
     24 + //load all plugins from default folder:
     25 + // - /opt/mmt/engine/rules
     26 + // - ./rules
     27 + n = load_mmt_sec_rules( &rules_arr );
     28 + else{
     29 + if( strcmp(argv[1], "-h") == 0 ){
     30 + fprintf( stderr, "Usage: %s [lib_file.so]", argv[0] );
     31 + fprintf( stderr, "\n" );
     32 + return 0;
     33 + }
     34 + //load only one plugin given by argv[1]
     35 + n = load_mmt_sec_rule( &rules_arr, argv[1] );
     36 + }
     37 + 
     38 + //print rules' information
     39 + printf("Found %zu rule%s", n, n<=1? ".": "s." );
     40 + 
     41 + for( i=0; i<n; i++ ){
     42 + printf("\n%zu - Rule id: %d", (i+1), rules_arr[i]->id );
     43 + printf("\n\t- type : %s", rules_arr[i]->type_string );
     44 + printf("\n\t- events_count : %d", rules_arr[i]->events_count );
     45 + printf("\n\t- variables_count : %d", rules_arr[i]->proto_atts_count );
     46 + printf("\n\t- variables : " );
     47 + for( j=0; j<rules_arr[i]->proto_atts_count; j++ )
     48 + printf( "%s%s.%s (%d.%d)",
     49 + j==0? "":", ",
     50 + rules_arr[i]->proto_atts[j].proto, rules_arr[i]->proto_atts[j].att,
     51 + rules_arr[i]->proto_atts[j].proto_id, rules_arr[i]->proto_atts[j].att_id);
     52 + 
     53 + printf("\n\t- description : %s", rules_arr[i]->description );
     54 + printf("\n\t- if_satisfied : %p", rules_arr[i]->if_satisfied );
     55 + //printf("\n\t- create_instance : %pF", rules_arr[i]->create_instance );
     56 + //printf("\n\t- hash_message : %pF", rules_arr[i]->hash_message );
     57 + tm = *localtime(& rules_arr[i]->version->created_date );
     58 + printf("\n\t- version : %s (%s - %d-%d-%d %d:%d:%d), dpi version %s", rules_arr[i]->version->number,
     59 + rules_arr[i]->version->hash,
     60 + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
     61 + rules_arr[i]->version->dpi );
     62 + }
     63 + printf("\n");
     64 + return 0;
     65 +}
     66 + 
  • ■ ■ ■ ■ ■ ■
    src/command/replay.c
     1 +/*
     2 + * main_sec_standalone.c
     3 + *
     4 + * Created on: 18 oct. 2016
     5 + * Created by: Huu Nghia NGUYEN <[email protected]>
     6 + *
     7 + * Standalone mmt-engine application.
     8 + * This application can analyze (1) real-time traffic by monitoring a NIC or (2)
     9 + * traffic saved in a pcap file. The verdicts will be printed to the current screen.
     10 + */
     11 +#include <stdio.h>
     12 +#include <stdlib.h>
     13 +#include <pcap.h>
     14 +#include <string.h>
     15 +#include <unistd.h>
     16 +#include <sys/types.h>
     17 +#include <sys/stat.h>
     18 +#include <sys/time.h>
     19 +#include <fcntl.h>
     20 +#include <getopt.h>
     21 +#include <signal.h>
     22 +#include <errno.h>
     23 + 
     24 +#include "../lib/mmt_lib.h"
     25 +#include "../engine/dpi_message_t.h"
     26 +#include "../engine/mmt_security.h"
     27 +#include "../engine/verdict_printer.h"
     28 +#include "../engine/rule.h"
     29 +#include "../forward/forward_packet.h"
     30 + 
     31 +typedef struct context_struct{
     32 + mmt_sec_handler_t *sec_handler;
     33 + config_t *config;
     34 + forward_packet_context_t *forward_context;
     35 +}context_t;
     36 + 
     37 +//Statistic
     38 +static size_t total_received_reports = 0;
     39 +static size_t total_received_packets = 0;
     40 +static size_t proto_atts_count = 0;
     41 +proto_attribute_t const *const*proto_atts = NULL;
     42 + 
     43 +static pcap_t *pcap;
     44 + 
     45 +//handler of MMT-SEC
     46 +static mmt_sec_handler_t *sec_handler = NULL;
     47 +//handler of MMT-DPI
     48 +static mmt_handler_t *mmt_dpi_handler = NULL;
     49 +static config_t *config = NULL;
     50 + 
     51 +#define DEFAULT_CONFIG_FILE "./mmt-5greplay.conf"
     52 +void usage(const char * prg_name) {
     53 + printf("%s [<Option>]\n", prg_name);
     54 + printf("Option:\n");
     55 + printf("\t-v : Print version information, then exits.\n");
     56 + printf("\t-c <config file> : Gives the path to the configuration file (default: %s).\n",
     57 + DEFAULT_CONFIG_FILE);
     58 + printf("\t-t <trace file> : Gives the trace file for offline analyse.\n");
     59 + printf("\t-i <interface> : Gives the interface name for live traffic analysis.\n");
     60 + printf("\t-X attr=value : Override configuration attributes.\n");
     61 + printf("\t For example \"-X output.enable=true -Xoutput.output-dir=/tmp/\" will enable output to file and change output directory to /tmp.\n");
     62 + printf("\t This parameter can appear several times.\n");
     63 + printf("\t-x : Prints list of configuration attributes being able to be used with -X, then exits.\n");
     64 + printf("\t-h : Prints this help, then exits.\n");
     65 +}
     66 + 
     67 +/**
     68 + * Free a string pointer before clone data to it
     69 + */
     70 +static inline void _override_string_conf( char **conf, const char*new_val ){
     71 + mmt_mem_free( *conf );
     72 + *conf = mmt_strdup( new_val );
     73 +}
     74 + 
     75 + 
     76 +static inline config_t *_parse_options(int argc, char ** argv ) {
     77 + int opt;
     78 + const char *config_file = DEFAULT_CONFIG_FILE;
     79 + config_t *conf = NULL;
     80 + const char *options = "t:i:c:X:xh";
     81 + 
     82 + //to get config
     83 + extern char *optarg;
     84 + extern int optind;
     85 + 
     86 + char *string_att, *string_val;
     87 + 
     88 + bool is_user_gives_conf_file = false;
     89 + 
     90 + while ((opt = getopt(argc, argv, options)) != EOF) {
     91 + switch (opt) {
     92 + case 'c':
     93 + config_file = optarg;
     94 + break;
     95 + case 'x':
     96 + conf_print_identities_list();
     97 + exit( EXIT_SUCCESS );
     98 + break;
     99 + case 'X':
     100 + case 't':
     101 + case 'i':
     102 + break;
     103 + case 'h':
     104 + default:
     105 + usage(argv[0]);
     106 + exit( EXIT_SUCCESS );
     107 + 
     108 + }
     109 + }
     110 + 
     111 + conf = conf_load_from_file( config_file );
     112 + if( conf == NULL ){
     113 + log_write_dual(LOG_ERR, "Cannot read configuration file from \"%s\"\n", config_file );
     114 + exit( EXIT_FAILURE );
     115 + }
     116 + 
     117 + //reset getopt function
     118 + optind = 0;
     119 + 
     120 + //override some options inside the configuration
     121 + while ((opt = getopt(argc, argv, options)) != EOF) {
     122 + switch (opt) {
     123 + //trace file
     124 + case 't':
     125 + _override_string_conf( &conf->input->input_source, optarg );
     126 + //switch to offline mode
     127 + conf->input->input_mode = OFFLINE_ANALYSIS;
     128 + break;
     129 + //input interface
     130 + case 'i':
     131 + _override_string_conf( &conf->input->input_source, optarg );
     132 + //switch to online mode
     133 + conf->input->input_mode = ONLINE_ANALYSIS;
     134 + break;
     135 + 
     136 + case 'X':
     137 + //example: -X file-output.enable=true
     138 + //we will separate the phrase "file-output.enable=true" into 2
     139 + // to expect:
     140 + // string_att = "file-output.enable"
     141 + // string_val = "true"
     142 + string_att = optarg;
     143 + string_val = optarg;
     144 + while( *string_val != '\0' ){
     145 + //separated by = character
     146 + if( *string_val == '=' ){
     147 + *string_val = '\0'; //NULL ended for attribute
     148 + //jump to the part after = character
     149 + string_val ++;
     150 + break;
     151 + }
     152 + string_val ++;
     153 + }
     154 + //not found = character
     155 + if( *string_val == '\0' )
     156 + log_write( LOG_WARNING, "Input parameter '%s' is not well-formatted (must be in format parameter=value). Ignored it.", string_att );
     157 + 
     158 + switch( conf_override_element(conf, string_att, string_val) ){
     159 + case 0:
     160 + //log_write( LOG_INFO, "Overridden value of configuration parameter '%s' by '%s'", string_att, string_val );
     161 + break;
     162 + case -1:
     163 + log_write_dual(LOG_ERR, "Unknown parameter identity %s\n", string_att );
     164 + exit( EXIT_FAILURE );
     165 + }
     166 + 
     167 + }
     168 + }
     169 + 
     170 + if( conf_validate(conf) ){
     171 + abort();
     172 + }
     173 + 
     174 + return conf;
     175 +}
     176 + 
     177 + 
     178 +/**
     179 + * A function to be called when a rule is validated
     180 + * Note: this function can be called from one or many different threads,
     181 + * ==> be carefully when using static or shared variables inside it
     182 + */
     183 +static void _print_security_verdict(
     184 + const rule_info_t *rule, //rule being validated
     185 + enum verdict_type verdict, //DETECTED, NOT_RESPECTED
     186 + uint64_t timestamp, //moment (by time) the rule is validated
     187 + uint64_t counter, //moment (by order of packet) the rule is validated
     188 + const mmt_array_t * trace, //historic messages that validates the rule
     189 + void *user_data //#user-data being given in register_security
     190 +){
     191 + forward_packet_context_t *context = (forward_packet_context_t *) user_data;
     192 + char message[10000];
     193 + size_t len;
     194 + 
     195 + //Special processing when the rule is FORWARD
     196 + if( rule->type_id == RULE_TYPE_FORWARD && context ){
     197 + //mark that there exists a FORWARD rule that is satisfied,
     198 + // thus do not need to peform default action (forward/drop) on the current packet
     199 + // because this action is decided by user in the satisfied rule
     200 + forward_packet_mark_being_satisfied( context );
     201 + }
     202 + 
     203 + if( config->output->is_enable ){
     204 + const char *exec_trace = mmt_convert_execution_trace_to_json_string( trace, rule );
     205 + len = snprintf( message, sizeof( message ), "%ld,%"PRIu32",\"%s\",\"%s\",\"%s\",%s",
     206 + time( NULL ),
     207 + rule->id,
     208 + verdict_type_string[verdict],
     209 + rule->type_string,
     210 + (config->output->is_report_description? rule->description : ""),
     211 + exec_trace );
     212 + message[len] = '\0';
     213 + verdict_printer_send( message );
     214 + }
     215 +}
     216 + 
     217 +/**
     218 + * Convert a pcap packet to a message being understandable by mmt-engine.
     219 + * The function returns NULL if the packet contains no interested information.
     220 + * Otherwise it creates a new memory segment to store the result message. One need
     221 + * to use #free_message_t to free the message.
     222 + */
     223 +static inline message_t* _get_packet_info( const ipacket_t *pkt ){
     224 + int i;
     225 + void *data;
     226 + int type;
     227 + 
     228 + message_t *msg = create_message_t();
     229 + msg->timestamp = mmt_sec_encode_timeval( &pkt->p_hdr->ts );
     230 + msg->counter = pkt->packet_id;
     231 + msg->flow_id = get_session_id_from_packet( pkt );
     232 + //get a list of proto/attributes being used by mmt-engine
     233 + for( i=0; i<proto_atts_count; i++ )
     234 + dpi_message_set_data( pkt, proto_atts[i]->dpi_type, msg, proto_atts[i]->proto_id, proto_atts[i]->att_id );
     235 + 
     236 + if( likely( msg->elements_count ))
     237 + return msg;
     238 + 
     239 + //need to free #msg when the packet contains no-interested information
     240 + free_message_t( msg );
     241 + return NULL;
     242 +}
     243 + 
     244 + 
     245 +/**
     246 + * Register an attribute of a protocol to MMT-DPI. They are given by their IDs
     247 + * @param proto_id
     248 + * @param att_id
     249 + * @param verbose
     250 + * @return true if it is registered successfully
     251 + * false if it has been registered or it can not be registered
     252 + */
     253 +static inline bool _register_proto_att_to_mmt_dpi( uint32_t proto_id, uint32_t att_id, bool verbose ){
     254 + //is it registered?
     255 + if( is_registered_attribute( mmt_dpi_handler, proto_id, att_id ))
     256 + return 0;
     257 + if( register_extraction_attribute( mmt_dpi_handler, proto_id, att_id ) ){
     258 + DEBUG( "Registered attribute to extract: %"PRIu32".%"PRIu32, proto_id, att_id );
     259 + return 1;
     260 + }
     261 + return 0;
     262 +}
     263 + 
     264 +/**
     265 + * update of list of unique att_protos and register them to MMT-DPI
     266 + * @return number of att_protos being registered
     267 + */
     268 +static inline size_t _update_and_register_protocols_attributes_to_extract( bool verbose ){
     269 + int i;
     270 + size_t ret = 0;
     271 + proto_atts_count = mmt_sec_get_unique_protocol_attributes( & proto_atts );
     272 + 
     273 + for( i=0; i<proto_atts_count; i++ ){
     274 + 
     275 + ret += _register_proto_att_to_mmt_dpi( proto_atts[i]->proto_id, proto_atts[i]->att_id, verbose );
     276 + 
     277 + //tcp.p_payload => need payload_len
     278 + if( proto_atts[i]->proto_id == PROTO_TCP && proto_atts[i]->att_id == PROTO_PAYLOAD ){
     279 + //tcp.payload_len
     280 + ret += _register_proto_att_to_mmt_dpi( PROTO_TCP, TCP_PAYLOAD_LEN, verbose );
     281 + }else if( proto_atts[i]->proto_id == PROTO_IP && proto_atts[i]->att_id == IP_OPTS){
     282 + ret += _register_proto_att_to_mmt_dpi( PROTO_IP, IP_HEADER_LEN, verbose );
     283 + }
     284 + }
     285 + return ret;
     286 +}
     287 + 
     288 +#ifdef MODULE_ADD_OR_RM_RULES_RUNTIME
     289 +static inline void _print_add_rm_rules_instruction(){
     290 + log_write( LOG_INFO,"During runtime, user can add or remove some rules using the following commands:\n%s\n%s",
     291 + " - to add new rules: add rule_mask, for example: add (1:1-3)(2:4-6)",
     292 + " - to remove existing rules: rm rule_range, for example: rm 1-3");
     293 +}
     294 + 
     295 +/**
     296 + * Add rules to process and update DPI to extract the corresponding protos/atts
     297 + * @param rules_mask
     298 + * @return number of rules being added
     299 + */
     300 +static inline size_t _add_rules( const char* rules_mask ){
     301 + size_t ret = mmt_sec_add_rules(rules_mask);
     302 + //no new rules being added
     303 + if( ret == 0 )
     304 + return ret;
     305 + 
     306 + //register the new proto_atts if need
     307 + size_t count = _update_and_register_protocols_attributes_to_extract( false );
     308 + DEBUG( "Registered %zu new proto_atts", count );
     309 + 
     310 + return ret;
     311 +}
     312 + 
     313 + 
     314 +static inline size_t _remove_rules( size_t rules_count, const uint32_t *rules_ids_array ){
     315 + proto_attribute_t const*const* old_proto_atts;
     316 + proto_attribute_t const*const* new_proto_atts;
     317 + size_t old_proto_atts_count, new_proto_atts_count;
     318 + size_t i, j;
     319 + 
     320 + old_proto_atts_count = mmt_sec_get_unique_protocol_attributes( & old_proto_atts );
     321 + 
     322 + size_t ret = mmt_sec_remove_rules( rules_count, rules_ids_array );
     323 + //no rules being removed ???
     324 + if( ret == 0 )
     325 + return ret;
     326 + 
     327 + new_proto_atts_count = mmt_sec_get_unique_protocol_attributes( & new_proto_atts );
     328 + 
     329 + //set of proto_atts does not change after removing some rules => donot need to unregister any proto_att
     330 + if( old_proto_atts_count == new_proto_atts_count )
     331 + return ret;
     332 + 
     333 + //unregister the att_protos of rules being removed
     334 + //for each old protol_att
     335 + for( i=0; i<old_proto_atts_count; i++ ){
     336 + for( j=0; j<new_proto_atts_count; j++ )
     337 + if( old_proto_atts[i]->proto_id == new_proto_atts[i]->proto_id &&
     338 + old_proto_atts[i]->att_id == new_proto_atts[i]->att_id )
     339 + break; //this proto_att is still needed
     340 + //
     341 + if( j <= new_proto_atts_count )
     342 + continue;
     343 + //unregister this old proto_att
     344 + unregister_extraction_attribute(mmt_dpi_handler, old_proto_atts[i]->proto_id, old_proto_atts[i]->att_id );
     345 + DEBUG("Unregistered from mmt-dpi: %"PRIu32".%"PRIu32" (%s,%s)",
     346 + old_proto_atts[i]->proto_id, old_proto_atts[i]->att_id,
     347 + old_proto_atts[i]->proto, old_proto_atts[i]->att );
     348 + }
     349 + return ret;
     350 +}
     351 + 
     352 +/**
     353 + * This has to be called before any stdin input function.
     354 + * When I used std::cin before using this function, it never returned true again.
     355 + * @return true if user press some keys ended by Enter
     356 + */
     357 +static inline bool _is_user_press_keys(){
     358 + struct timeval tv;
     359 + fd_set fds;
     360 + tv.tv_sec = 0;
     361 + tv.tv_usec = 0;
     362 + FD_ZERO(&fds);
     363 + FD_SET(STDIN_FILENO, &fds); //add stdin to fsd, STDIN_FILENO is 0
     364 + select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
     365 + return ( FD_ISSET(STDIN_FILENO, &fds) != 0 );
     366 +}
     367 + 
     368 +void _add_or_remove_rules_if_need(){
     369 + const int len = 1000;
     370 + 
     371 + char buffer[ 1000 ], *c;
     372 + size_t count;
     373 + uint32_t *rules_id_to_rm_set;
     374 + //if user does not press any keys
     375 + if( _is_user_press_keys() == false )
     376 + return;
     377 + 
     378 + //get user's string
     379 + if( !fgets( buffer, len, stdin) )
     380 + return;
     381 + 
     382 + //as fgets add EOF or EOL at the end of buffer => we need to remove these special characters
     383 + c = buffer;
     384 + while( *c != '\0' ){
     385 + if( *c == EOF || *c == '\n' ){
     386 + *c = '\0';
     387 + break;
     388 + }
     389 + c++;
     390 + }
     391 + 
     392 + 
     393 + if( buffer[0] == '\0' )
     394 + return;
     395 + 
     396 + //add xxxx
     397 + if( buffer[0] == 'a' && buffer[1] == 'd' && buffer[2] == 'd' && buffer[3] == ' ' ){
     398 + log_write( LOG_INFO, "Added totally %zu rule(s)", _add_rules( &buffer[4] ));
     399 + return;
     400 + }else //rm xxx
     401 + if( buffer[0] == 'r' && buffer[1] == 'm' && buffer[2] == ' ' ){
     402 + count = expand_number_range( &buffer[3], &rules_id_to_rm_set );
     403 + if( count > 0 ){
     404 + count = _remove_rules( count, rules_id_to_rm_set);
     405 + }
     406 + log_write( LOG_INFO, "Removed totally %zu rule(s)", count);
     407 + 
     408 + //free memory allocated by expand_number_range
     409 + mmt_mem_free( rules_id_to_rm_set );
     410 + return;
     411 + }
     412 + 
     413 + log_write( LOG_WARNING,"Unknown command \"%s\"", buffer );
     414 + _print_add_rm_rules_instruction();
     415 +}
     416 +/* Returns an integer in the range [1, n].
     417 + *
     418 + * Uses rand(), and so is affected-by/affects the same seed.
     419 + */
     420 +static inline int _rand_int(unsigned int n) {
     421 + if ((n - 1) == RAND_MAX) {
     422 + return rand();
     423 + } else {
     424 + // Chop off all of the values that would cause skew...
     425 + long end = RAND_MAX / n; // truncate skew
     426 + end *= n;
     427 + 
     428 + // ... and ignore results from rand() that fall above that limit.
     429 + // (Worst case the loop condition should succeed 50% of the time,
     430 + // so we can expect to bail out of this loop pretty quickly.)
     431 + int r;
     432 + while ((r = rand()) >= end);
     433 + 
     434 + return r % n + 1;
     435 + }
     436 +}
     437 + 
     438 +static inline bool _rand_bool(){
     439 + return (_rand_int( 10 ) > 5);
     440 +}
     441 +#else
     442 +#define _add_or_remove_rules_if_need()
     443 +#define _print_add_rm_rules_instruction()
     444 +#endif
     445 + 
     446 + 
     447 + 
     448 +/**
     449 + * This function is called by mmt-dpi for each incoming packet containing registered proto/att.
     450 + * It gets interested information from the #ipkacet to a message then sends the
     451 + * message to mmt-engine.
     452 + */
     453 +static int _packet_handle( const ipacket_t *ipacket, void *args ) {
     454 + uint32_t rm_rules_arr[50];
     455 + char string[500], *ch = string;
     456 + int i;
     457 + 
     458 + context_t *context = (context_t *) args;
     459 + MUST_NOT_OCCUR( context == NULL, "args parameter must not be NULL"); //this must not happen
     460 + 
     461 + if( context->config->forward->is_enable )
     462 + forward_packet_on_receiving_packet_before_rule_processing( ipacket, context->forward_context );
     463 + 
     464 + uint64_t flow_id = get_session_id_from_packet( ipacket );
     465 + 
     466 + message_t *msg = _get_packet_info( ipacket );
     467 + 
     468 + total_received_packets ++;
     469 + 
     470 + //if there is no interested information
     471 + //TODO: to check if we still need to send timestamp/counter to mmt-sec?
     472 + if( unlikely( msg == NULL ))
     473 + return 1;
     474 + 
     475 + mmt_sec_process( context->sec_handler, msg );
     476 + 
     477 +//TODO: remve this block
     478 +//#ifdef MODULE_ADD_OR_RM_RULES_RUNTIME
     479 +// if( total_received_reports == 1000 ){
     480 +// DEBUG("Add %zu rules", _add_rules("(1:33,32,34)"));
     481 +// //need to add/rm or not?
     482 +// if( _rand_bool() ){
     483 +// printf("\n%zu\n", total_received_reports );
     484 +// //add or rm rules?
     485 +// if( _rand_bool() ){
     486 +// //rm random rules ID
     487 +// int nb_rules_to_rm = _rand_int( 5 );
     488 +// for( i=0; i<nb_rules_to_rm; i++ )
     489 +// rm_rules_arr[i] = _rand_int( 50 );
     490 +// mmt_sec_remove_rules( nb_rules_to_rm, rm_rules_arr );
     491 +// }else{
     492 +// //add
     493 +// int nb_rules_to_add = _rand_int( 5 );
     494 +// ch = string;
     495 +// ch += sprintf(string, "(%d:", _rand_int(9) );
     496 +// for( i=0; i<nb_rules_to_add; i++ )
     497 +// ch += sprintf(ch, "%d,", _rand_int( 50 ) );
     498 +// *ch = '\0';
     499 +// *(ch - 1) = ')';
     500 +//
     501 +// _add_rules( string );
     502 +//
     503 +// }
     504 +// }
     505 +// }
     506 +//#endif
     507 + 
     508 + total_received_reports ++;
     509 + 
     510 + return 0;
     511 +}
     512 + 
     513 +void live_capture_callback( u_char *user, const struct pcap_pkthdr *p_pkthdr, const u_char *data ){
     514 + mmt_handler_t *mmt = (mmt_handler_t*)user;
     515 + struct pkthdr header;
     516 + 
     517 + //allow user to add/rm rules
     518 + _add_or_remove_rules_if_need();
     519 + 
     520 + header.ts = p_pkthdr->ts;
     521 + header.caplen = p_pkthdr->caplen;
     522 + header.len = p_pkthdr->len;
     523 + if (!packet_process( mmt, &header, data )) {
     524 + fprintf(stderr, "Packet data extraction failure.\n");
     525 + }
     526 + //printf("."); fflush( stdout );
     527 +}
     528 + 
     529 + 
     530 +static inline void termination(){
     531 + struct pcap_stat pcs; /* packet capture filter stats */
     532 + size_t alerts_count;
     533 + 
     534 + pcap_breakloop( pcap );
     535 + 
     536 + alerts_count = mmt_sec_unregister( sec_handler );
     537 + 
     538 + if (pcap_stats(pcap, &pcs) < 0) {
     539 +// (void) fprintf(stderr, "pcap_stats: %s\n", pcap_geterr( pcap ));//Statistics aren't available from savefiles
     540 + }else{
     541 + (void) fprintf(stderr, "\n%12d packets received by filter\n", pcs.ps_recv);
     542 + (void) fprintf(stderr, "%12d packets dropped by interface\n", pcs.ps_ifdrop);
     543 + (void) fprintf(stderr, "%12d packets dropped by kernel (%3.2f%%)\n", pcs.ps_drop, pcs.ps_drop * 100.0 / pcs.ps_recv);
     544 + fflush(stderr);
     545 + }
     546 + 
     547 + fprintf(stderr, "%12zu packets received\n", total_received_packets );
     548 + fprintf(stderr, "%12zu messages received\n", total_received_reports );
     549 + fprintf(stderr, "%12zu alerts generated\n", alerts_count );
     550 + 
     551 + pcap_close( pcap );
     552 + 
     553 + if( config->output->is_enable )
     554 + verdict_printer_free();
     555 + 
     556 + mmt_sec_close(); // close mmt_security
     557 + close_extraction();// close mmt_dpi
     558 + conf_release( config );
     559 +}
     560 + 
     561 +void signal_handler_seg(int signal_type) {
     562 + log_write( LOG_ERR, "Interrupted by signal %d", signal_type );
     563 + log_execution_trace();
     564 + exit( signal_type );
     565 +}
     566 + 
     567 +void signal_handler(int signal_type) {
     568 + static volatile int times_counter = 0;
     569 + 
     570 + if( times_counter >= 1 ) exit( signal_type );
     571 + times_counter ++;
     572 + 
     573 + log_write( LOG_ERR, "Interrupted by signal %d", signal_type );
     574 + 
     575 + if( signal_type == SIGINT ){
     576 + log_write( LOG_ERR,"Releasing resource ... (press Ctrl+c again to exit immediately)");
     577 + signal(SIGINT, signal_handler);
     578 + }
     579 + exit( signal_type );
     580 +}
     581 + 
     582 + 
     583 + 
     584 +void register_signals(){
     585 +#ifndef DEBUG_MODE
     586 + signal(SIGSEGV, signal_handler_seg );
     587 +#endif
     588 + signal(SIGINT, signal_handler);
     589 + signal(SIGTERM, signal_handler);
     590 + signal(SIGABRT, signal_handler);
     591 +}
     592 + 
     593 +int replay(int argc, char** argv) {
     594 + char mmt_errbuf[1024];
     595 + 
     596 + const unsigned char *data;
     597 + struct pcap_pkthdr p_pkthdr;
     598 + char errbuf[1024];
     599 + int ret;
     600 + struct pkthdr header;
     601 + size_t i, j, size;
     602 + uint16_t *rules_id_filter = NULL;
     603 + context_t context;
     604 + 
     605 + register_signals();
     606 + 
     607 + config = _parse_options(argc, argv);
     608 + 
     609 + ret = mmt_sec_init( config->engine->excluded_rules );
     610 + if( ret != 0 ){
     611 + conf_release( config );
     612 + exit( EXIT_FAILURE );
     613 + }
     614 + 
     615 + if( config->output->is_enable )
     616 + verdict_printer_init( config->output->output_dir, config->output->sample_interval );
     617 + 
     618 + //init mmt_dpi extraction
     619 + init_extraction();
     620 + 
     621 + //Initialize dpi handler
     622 + mmt_dpi_handler = mmt_init_handler(DLT_EN10MB, 0, mmt_errbuf);
     623 + if (!mmt_dpi_handler) { /* pcap error ? */
     624 + fprintf(stderr, "MMT handler init failed for the following reason: %s\n", mmt_errbuf);
     625 + return EXIT_FAILURE;
     626 + }
     627 + 
     628 + forward_packet_context_t *forward_context = forward_packet_alloc( config, mmt_dpi_handler );
     629 + 
     630 + sec_handler = mmt_sec_register( config->engine->threads_size,
     631 + NULL, //core_id is NULL to allow OS arbitrarily arranging security threads on logical cores
     632 + config->engine->rules_mask,
     633 + true,
     634 + _print_security_verdict,
     635 + forward_context );
     636 + 
     637 + _update_and_register_protocols_attributes_to_extract( true );
     638 + 
     639 + context.config = config;
     640 + context.forward_context = forward_context;
     641 + context.sec_handler = sec_handler;
     642 + 
     643 + //Register a packet handler, it will be called for every processed packet
     644 + register_packet_handler(mmt_dpi_handler, 1, _packet_handle, &context );
     645 + 
     646 + _print_add_rm_rules_instruction();
     647 + 
     648 + if( config->input->input_mode == OFFLINE_ANALYSIS ) {
     649 + log_write( LOG_INFO,"Analyzing pcap file %s", config->input->input_source );
     650 + pcap = pcap_open_offline(config->input->input_source, errbuf); // open offline trace
     651 + if (!pcap) { /* pcap error ? */
     652 + ABORT("pcap_open failed for the following reason: %s\n", errbuf);
     653 + }
     654 + 
     655 + while ((data = pcap_next(pcap, &p_pkthdr)) ) {
     656 + //allow user to add/rm rules
     657 + _add_or_remove_rules_if_need();
     658 + 
     659 + 
     660 + header.ts = p_pkthdr.ts;
     661 + header.caplen = p_pkthdr.caplen;
     662 + header.len = p_pkthdr.len;
     663 + if (!packet_process(mmt_dpi_handler, &header, data))
     664 + log_write( LOG_ERR, "Packet data extraction failure.\n");
     665 + }
     666 + 
     667 + } else {
     668 + log_write( LOG_INFO,"Listening on interface %s", config->input->input_source );
     669 + 
     670 + pcap = pcap_create( config->input->input_source, errbuf);
     671 + if (pcap == NULL)
     672 + ABORT("Couldn't open device %s\n", errbuf);
     673 + 
     674 + pcap_set_snaplen(pcap, config->input->snap_len);
     675 + pcap_set_promisc(pcap, 1);
     676 + pcap_set_timeout(pcap, 0);
     677 + pcap_set_buffer_size(pcap, 100*1000*1000);
     678 + pcap_activate(pcap);
     679 + 
     680 + (void)pcap_loop( pcap, -1, &live_capture_callback, (u_char*)mmt_dpi_handler );
     681 + }
     682 + 
     683 + termination();
     684 + forward_packet_release(forward_context);
     685 + 
     686 + return EXIT_SUCCESS;
     687 +}
     688 + 
     689 + 
     690 +/**
     691 + * Public API
     692 + */
     693 +uint32_t conf_get_number_value( config_identity_t id ){
     694 + switch( id ){
     695 + case CONF_ATT__ENGINE__MAX_INSTANCES:
     696 + return config->engine->max_instances;
     697 + case CONF_ATT__MEMPOOL__MAX_BYTES:
     698 + return config->mempool->max_bytes;
     699 + case CONF_ATT__MEMPOOL__MAX_ELEMENTS:
     700 + return config->mempool->max_elements;
     701 + case CONF_ATT__MEMPOOL__MAX_MESSAGE_SIZE:
     702 + return config->mempool->max_message_size;
     703 + case CONF_ATT__MEMPOOL__SMP_RING_SIZE:
     704 + return config->mempool->smp_ring_size;
     705 + default:
     706 + ABORT("Does not support yet the config identity %d\n", id);
     707 + }
     708 + return 0;
     709 +}
     710 + 
  • ■ ■ ■ ■ ■ ■
    src/engine/config.c_
     1 +/*
     2 + * mmt_sec_config.c
     3 + *
     4 + * Created on: 23 nov. 2016
     5 + * Author: la_vinh
     6 + */
     7 +#include <stdlib.h>
     8 +#include <string.h>
     9 +#include <pthread.h>
     10 +
     11 +#include "config.h"
     12 +#include "mmt_lib.h"
     13 +#include "plugins_engine.h"
     14 +
     15 +static pthread_spinlock_t spin_lock;
     16 +
     17 +static const char *name[] = {
     18 + [MMT_SEC__CONFIG__INPUT__MAX_MESSAGE_SIZE] = "input.max_message_size",
     19 + [MMT_SEC__CONFIG__SECURITY__MAX_INSTANCES] = "security.max_instances",
     20 + [MMT_SEC__CONFIG__SECURITY__SMP__RING_SIZE] = "security.smp.ring_size",
     21 + [MMT_SEC__CONFIG__OUTPUT__INORGE_DESCRIPTION] = "output.inorge_description",
     22 + [MMT_SEC__CONFIG__MEMPOOL__MAX_BYTES] = "mempool.max_bytes",
     23 + [MMT_SEC__CONFIG__MEMPOOL__MAX_ELEMENTS_PER_POOL]= "mempool.max_elements_per_pool"
     24 +};
     25 +
     26 +//global configuration
     27 +static uint32_t config[] = {
     28 + [MMT_SEC__CONFIG__INPUT__MAX_MESSAGE_SIZE] = 3000,
     29 + [MMT_SEC__CONFIG__SECURITY__MAX_INSTANCES] = 100000,
     30 + [MMT_SEC__CONFIG__SECURITY__SMP__RING_SIZE] = 1000,
     31 + [MMT_SEC__CONFIG__OUTPUT__INORGE_DESCRIPTION] = 20,
     32 + [MMT_SEC__CONFIG__MEMPOOL__MAX_BYTES] = 2*1000*1000*1000, //2GB per thread
     33 + [MMT_SEC__CONFIG__MEMPOOL__MAX_ELEMENTS_PER_POOL]= 1000
     34 +};
     35 +
     36 +uint32_t mmt_sec_get_config( enum config_att att ){
     37 + uint32_t val;
     38 + if( pthread_spin_lock( &spin_lock ) == 0 ){
     39 + val = config[ att ];
     40 + pthread_spin_unlock( &spin_lock );
     41 + return val;
     42 + }
     43 + return 0;
     44 +}
     45 +
     46 +uint32_t mmt_sec_set_config( enum config_att att, uint32_t val ){
     47 + if( pthread_spin_lock( &spin_lock ) == 0 ){
     48 + config[ att ] = val;
     49 + pthread_spin_unlock( &spin_lock );
     50 + return val;
     51 + }
     52 + return 0;
     53 +}
     54 +
     55 +const char* mmt_sec_get_config_name( enum config_att att ){
     56 + return name[att];
     57 +}
     58 +
     59 +/**
     60 + * remove space leading of a string
     61 + */
     62 +#define _trim( x ) while( *x == ' ' || *x == '\t' ) x++;
     63 +/**
     64 + * Check whether #line in form of #variable_name = #value
     65 + * If yes, set #variable_ptr = #value
     66 + * @param variable_name
     67 + * @param variable_ptr
     68 + * @param line
     69 + * @return
     70 + */
     71 +static int _check_then_set_value( int index, const char *line ){
     72 + const char* variable_name = name[ index ];
     73 + long val;
     74 + uint32_t old_val;
     75 +
     76 + //jump over spaces
     77 + _trim( line );
     78 +
     79 + //comment
     80 + if( *line == '#' || *line == '\n' || *line == '\r' || *line == '\0' )
     81 + return 0;
     82 +
     83 + //compare
     84 + while( *variable_name == *line ){
     85 + variable_name ++;
     86 + line ++;
     87 + }
     88 + //first part is matched
     89 + if( *variable_name == '\0' && ( *line == ' ' || *line == '\t' || *line == '=' )){
     90 +
     91 + //jump over spaces
     92 + _trim( line );
     93 +
     94 + val = atol( line );
     95 + old_val = mmt_sec_get_config( index );
     96 + if( val == old_val )
     97 + return 0;
     98 +
     99 + log_write( LOG_INFO,"update \"%s\" from %d to %ld", name[ index ], old_val , val );
     100 +
     101 + mmt_sec_set_config( index, val );
     102 +
     103 + return 0;
     104 + }
     105 +
     106 + return -1;
     107 +}
     108 +
     109 +/**
     110 + * PUBLIC API
     111 + * Load config from file ./mmt-security.conf
     112 + * @return
     113 + */
     114 +__attribute__((constructor))
     115 +bool mmt_sec_load_default_config(){
     116 + char *line = NULL;
     117 + size_t len = 0;
     118 + int i, n = sizeof( config ) / sizeof( uint32_t );
     119 +
     120 + if( pthread_spin_init( &spin_lock, PTHREAD_PROCESS_PRIVATE ) != 0){
     121 + ABORT("Cannot init spin_lock");
     122 + }
     123 +
     124 + FILE *file = fopen( "./mmt-security.conf", "r" );
     125 + if( file == NULL )
     126 + file = fopen( INSTALL_DIR "/mmt-security.conf", "r" );
     127 +
     128 + if( file == NULL )
     129 + return false;
     130 +
     131 + while ( getline( &line, &len, file ) != -1) {
     132 +
     133 + for( i=0; i<n; i++ )
     134 + if( _check_then_set_value( i, line ) == 0 )
     135 + break;
     136 +
     137 + if( i == n )
     138 + log_write( LOG_WARNING,"mmt-security.conf is not correct. Unexpected \"%s\"", line );
     139 + }
     140 +
     141 + fclose( file );
     142 + free( line );
     143 +
     144 + return true;
     145 +}
     146 + 
  • ■ ■ ■ ■ ■ ■
    src/engine/config.h
     1 +/*
     2 + * mmt_sec_config.h
     3 + *
     4 + * Created on: 23 nov. 2016
     5 + * Author: la_vinh
     6 + */
     7 +
     8 +#ifndef MMT_SEC_CONFIG_H_
     9 +#define MMT_SEC_CONFIG_H_
     10 +#include <stdio.h>
     11 +#include <inttypes.h>
     12 +#include <stdbool.h>
     13 +
     14 +enum config_att{
     15 + /**
     16 + * maximum size, in bytes, of a report received from mmt-probe
     17 + */
     18 + MMT_SEC__CONFIG__INPUT__MAX_MESSAGE_SIZE,
     19 + /**
     20 + * number of fsm instances of one rule
     21 + */
     22 + MMT_SEC__CONFIG__SECURITY__MAX_INSTANCES,
     23 +
     24 + /**
     25 + * Multi thread: size of buffer
     26 + */
     27 + MMT_SEC__CONFIG__SECURITY__SMP__RING_SIZE,
     28 +
     29 + /**
     30 + * Number of consecutive alerts of one rule can be ignored its description.
     31 + * The first alert of a rule always contain description of its rule.
     32 + * However, to avoid a huge output, a number of consecutive alerts of that rule
     33 + * can be excluded the description.
     34 + *
     35 + * - set = 0 to include description to any alerts
     36 + */
     37 + MMT_SEC__CONFIG__OUTPUT__INORGE_DESCRIPTION,
     38 +
     39 + /**
     40 + * Memory pool
     41 + * When mmt-sec frees a block of memory, it will not call #free immediately.
     42 + * It will be stored by a memory pool (if the pool is not full).
     43 + * When mmt-sec requires a block of memory, it will call #malloc only if #mem_pool
     44 + * has no block having the same size.
     45 + *
     46 + * Maximum bytes a thread may be reserved by using mmt_mem_pool
     47 + */
     48 + MMT_SEC__CONFIG__MEMPOOL__MAX_BYTES,
     49 + /**
     50 + * maximum elements of a pool of a mem_pool
     51 + * A mem_pool contains several pools. Each pool stores several blocks of memory
     52 + * having the same size.
     53 + * This parameter set the maximum elements of a pool.
     54 + */
     55 + MMT_SEC__CONFIG__MEMPOOL__MAX_ELEMENTS_PER_POOL,
     56 +};
     57 +
     58 +bool mmt_sec_load_default_config();
     59 +
     60 +uint32_t mmt_sec_set_config( enum config_att att, uint32_t value );
     61 +
     62 +uint32_t mmt_sec_get_config( enum config_att att );
     63 +
     64 +const char* mmt_sec_get_config_name( enum config_att att );
     65 +
     66 +#endif /* MMT_SEC_CONFIG_H_ */
     67 + 
  • ■ ■ ■ ■ ■ ■
    src/engine/configure.c
     1 +/*
     2 + * configure.c
     3 + *
     4 + * Created on: Dec 12, 2017
     5 + * by: Huu Nghia
     6 + */
     7 + 
     8 +#include <confuse.h>
     9 +#include <stdio.h>
     10 +#include <string.h>
     11 +#include <assert.h>
     12 +#include <errno.h>
     13 +#include <stdbool.h>
     14 +#include <sys/stat.h>
     15 + 
     16 +#include "../lib/mmt_lib.h"
     17 +#include "configure.h"
     18 + 
     19 +#define DLT_EN10MB 1/**< Ethernet (10Mb) */
     20 + 
     21 +//parse forward default-aciton
     22 +bool conf_parse_forward_default_action(int *result, const char *value) {
     23 + if (IS_EQUAL_STRINGS(value, "FORWARD") )
     24 + *result = ACTION_FORWARD;
     25 + else if (IS_EQUAL_STRINGS(value, "DROP") )
     26 + *result = ACTION_DROP;
     27 + else {
     28 + return false;
     29 + }
     30 + return true;
     31 +}
     32 + 
     33 +static int _conf_parse_forward_default_action(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result) {
     34 + if ( conf_parse_forward_default_action( result, value) )
     35 + return 0;
     36 + cfg_error(cfg, "invalid value for option '%s': %s. Expected either FORWARD or DROP.", cfg_opt_name(opt), value);
     37 + return -1;
     38 +}
     39 + 
     40 +// parse values for the input-mode option
     41 +bool conf_parse_input_mode(int *result, const char *value) {
     42 + if (IS_EQUAL_STRINGS(value, "ONLINE") )
     43 + *result = ONLINE_ANALYSIS;
     44 + else if (IS_EQUAL_STRINGS(value, "OFFLINE") )
     45 + *result = OFFLINE_ANALYSIS;
     46 + else {
     47 + return false;
     48 + }
     49 + return true;
     50 +}
     51 + 
     52 +static int _conf_parse_input_mode(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result) {
     53 + if ( conf_parse_input_mode( result, value) )
     54 + return 0;
     55 + cfg_error(cfg, "invalid value for option '%s': %s. Expected either ONLINE or OFFLINE.", cfg_opt_name(opt), value);
     56 + return -1;
     57 +}
     58 + 
     59 +static int _conf_parse_ip_encapsulation_index(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result) {
     60 + if (IS_EQUAL_STRINGS(value, "FIRST") )
     61 + *(int *) result = CONF_IP_ENCAPSULATION_INDEX_FIRST;
     62 + else if (IS_EQUAL_STRINGS(value, "LAST") )
     63 + *(int *) result = CONF_IP_ENCAPSULATION_INDEX_LAST;
     64 + else{
     65 + int val = atoi( value );
     66 + if( val >= CONF_IP_ENCAPSULATION_INDEX_FIRST ){
     67 + if( val <= CONF_IP_ENCAPSULATION_INDEX_LAST )
     68 + *(int *) result = val;
     69 + else
     70 + *(int *) result = CONF_IP_ENCAPSULATION_INDEX_LAST;
     71 + }else{
     72 + cfg_error(cfg, "invalid value for option '%s': %s", cfg_opt_name(opt), value);
     73 + return -1;
     74 + }
     75 + }
     76 + DEBUG( "engine.ip-encapsulation-index = %d", *(int *) result );
     77 + return 0;
     78 +}
     79 + 
     80 +static inline cfg_t *_load_cfg_from_file(const char *filename) {
     81 + 
     82 + cfg_opt_t forward_packet_opts[] = {
     83 + CFG_BOOL("enable", true, CFGF_NONE),
     84 + CFG_STR("output-nic", "lo", CFGF_NONE),
     85 + CFG_INT("snap-len", 0, CFGF_NONE),
     86 + CFG_INT("nb-copies", 1, CFGF_NONE),
     87 + CFG_INT("promisc", 0, CFGF_NONE),
     88 + CFG_INT_CB("default", ACTION_DROP, CFGF_NONE, _conf_parse_forward_default_action),
     89 + CFG_STR_LIST("target-protocols", "{}", CFGF_NONE),
     90 + CFG_STR_LIST("target-hosts", "{}", CFGF_NONE),
     91 + CFG_STR_LIST("target-ports", "{}", CFGF_NONE),
     92 + CFG_END()
     93 + };
     94 + 
     95 + 
     96 + cfg_opt_t engine_opts[] = {
     97 + CFG_INT("thread-nb", 0, CFGF_NONE),
     98 + CFG_STR("rules-mask", NULL, CFGF_NONE),
     99 + CFG_STR("exclude-rules", NULL, CFGF_NONE),
     100 + CFG_INT("max-instances", 100000, CFGF_NONE),
     101 + CFG_INT_CB("ip-encapsulation-index", CONF_IP_ENCAPSULATION_INDEX_LAST, CFGF_NONE, _conf_parse_ip_encapsulation_index),
     102 + CFG_END()
     103 + };
     104 + 
     105 + cfg_opt_t mempool_opts[] = {
     106 + CFG_INT("max-bytes", 2000000000, CFGF_NONE), // 2GB
     107 + CFG_INT("max-elements", 1000, CFGF_NONE),
     108 + CFG_INT("max-message-size", 3000, CFGF_NONE),
     109 + CFG_INT("smp-ring-size", 1000, CFGF_NONE),
     110 + };
     111 + 
     112 + cfg_opt_t input_opts[] = {
     113 + CFG_INT_CB("mode", ONLINE_ANALYSIS, CFGF_NONE, _conf_parse_input_mode),
     114 + CFG_STR("source", "lo", CFGF_NONE),
     115 + CFG_INT("snap-len", 65535, CFGF_NONE),
     116 + CFG_END()
     117 + };
     118 + 
     119 + cfg_opt_t output_opts[] = {
     120 + CFG_BOOL("enable", false, CFGF_NONE),
     121 + CFG_STR("output-dir", "./reports/", CFGF_NONE),
     122 + CFG_INT("sample-interval", 5, CFGF_NONE),
     123 + CFG_BOOL("report-description", true, CFGF_NONE),
     124 + CFG_END()
     125 + };
     126 + 
     127 + cfg_opt_t opts[] = {
     128 + CFG_SEC("input", input_opts, CFGF_NONE),
     129 + CFG_SEC("output", output_opts, CFGF_NONE),
     130 + CFG_SEC("engine", engine_opts, CFGF_NONE),
     131 + CFG_SEC("mempool", mempool_opts, CFGF_NONE),
     132 + CFG_SEC("forward", forward_packet_opts, CFGF_NONE),
     133 + 
     134 + 
     135 + CFG_INT("stack-type", DLT_EN10MB, CFGF_NONE),
     136 + CFG_STR("dpdk-option", 0, CFGF_NONE),
     137 + 
     138 + CFG_END()
     139 + };
     140 + 
     141 + cfg_t *cfg = cfg_init(opts, CFGF_NONE);
     142 + switch (cfg_parse(cfg, filename)) {
     143 + case CFG_FILE_ERROR:
     144 + //log_write(LOG_ERR, "Error: configuration file '%s' could not be read: %s\n", filename, strerror(errno));
     145 + cfg_free( cfg );
     146 + return NULL;
     147 + case CFG_SUCCESS:
     148 + break;
     149 + case CFG_PARSE_ERROR:
     150 + log_write( LOG_ERR, "Error: configuration file '%s' could not be parsed.\n", filename );
     151 + cfg_free( cfg );
     152 + return NULL;
     153 + }
     154 + 
     155 + return cfg;
     156 +}
     157 + 
     158 +static inline char * _cfg_get_str( cfg_t *cfg, const char *header ){
     159 + const char *str = cfg_getstr( cfg, header );
     160 + if (str == NULL)
     161 + return NULL;
     162 + return mmt_strdup( str );
     163 +}
     164 + 
     165 +static inline char * _cfg_get_dir( cfg_t *cfg, const char *header ){
     166 + const char *str = cfg_getstr( cfg, header );
     167 + if (str == NULL)
     168 + return NULL;
     169 + size_t len = strlen( str );
     170 + //ensure that a directory path is always end by '/'
     171 + char *dir = mmt_mem_alloc( len + 1 + 1 ); //+1 for '\0'; +1 for eventually '/'
     172 + memcpy( dir, str, len + 1 ); //+1 for '\0'
     173 + 
     174 + if( dir[ len - 1 ] != '/' ){
     175 + dir[ len ] = '/'; //append '/' if it is not there
     176 + dir[ len + 1] = '\0'; //ensure NULL-terminated
     177 + }
     178 + return dir;
     179 +}
     180 + 
     181 +static inline cfg_t* _get_first_cfg_block( cfg_t *cfg, const char* block_name ){
     182 + if( ! cfg_size( cfg, block_name) )
     183 + return NULL;
     184 + //DEBUG( "Parsing block '%s'", block_name );
     185 + return cfg_getnsec( cfg, block_name, 0 );
     186 +}
     187 + 
     188 + 
     189 +static inline long int _cfg_getint( cfg_t *cfg, const char *ident, long int min, long int max, long int def_val ){
     190 + long int val = cfg_getint( cfg, ident );
     191 + if( val < min || val > max ){
     192 + log_write( LOG_INFO, "Not expected %ld for %s. Used default value %ld.", val, ident, def_val );
     193 + return def_val;
     194 + }
     195 + 
     196 + return val;
     197 +}
     198 + 
     199 +static inline input_source_conf_t * _parse_input_source( cfg_t *cfg ){
     200 + cfg = _get_first_cfg_block( cfg, "input" );
     201 + if( cfg == NULL )
     202 + return NULL;
     203 + 
     204 + input_source_conf_t *ret = mmt_mem_alloc( sizeof( input_source_conf_t ));
     205 + 
     206 + ret->input_mode = cfg_getint(cfg, "mode");
     207 + ret->input_source = _cfg_get_str(cfg, "source");
     208 + 
     209 +#ifdef DPDK_CAPTURE_MODULE
     210 + ret->capture_mode = DPDK_CAPTURE;
     211 +#else
     212 + ret->capture_mode = PCAP_CAPTURE;
     213 +#endif
     214 + 
     215 + ret->snap_len = cfg_getint( cfg, "snap-len" );
     216 + 
     217 + if( ret->snap_len == 0 )
     218 + ret->snap_len = UINT16_MAX;
     219 + 
     220 + return ret;
     221 +}
     222 + 
     223 +static inline output_conf_t * _parse_output( cfg_t *cfg ){
     224 + cfg = _get_first_cfg_block( cfg, "output" );
     225 + if( cfg == NULL )
     226 + return NULL;
     227 + 
     228 + output_conf_t *ret = mmt_mem_alloc( sizeof( output_conf_t ));
     229 + 
     230 + ret->is_enable = cfg_getbool(cfg, "enable");
     231 + ret->output_dir = _cfg_get_dir(cfg, "output-dir");
     232 + ret->sample_interval = _cfg_getint( cfg, "sample-interval", 0, 120, 5 );
     233 + ret->is_report_description = cfg_getbool(cfg, "report-description");
     234 + return ret;
     235 +}
     236 + 
     237 +static inline forward_packet_conf_t *_parse_forward_packet( cfg_t *cfg ){
     238 + cfg = _get_first_cfg_block( cfg, "forward" );
     239 + if( cfg == NULL )
     240 + return NULL;
     241 + 
     242 + forward_packet_conf_t *ret = mmt_mem_alloc( sizeof( forward_packet_conf_t ));
     243 + 
     244 + ret->is_enable = cfg_getbool( cfg, "enable" );
     245 + ret->output_nic = _cfg_get_str(cfg, "output-nic");
     246 + ret->snap_len = _cfg_getint( cfg, "snap-len", 0, 65535, 65535 );
     247 + ret->nb_copies = _cfg_getint( cfg, "nb-copies", 1, UINT32_MAX, 1 );
     248 + ret->promisc = _cfg_getint( cfg, "promisc", 0, 1, 1 );
     249 + ret->default_action = cfg_getint( cfg, "default" );
     250 + 
     251 + ret->target_size = cfg_size( cfg, "target-protocols");
     252 + ASSERT( ret->target_size == cfg_size( cfg, "target-hosts"), "Number of elements in target-protocols and target-hosts are different");
     253 + ASSERT( ret->target_size == cfg_size( cfg, "target-ports"), "Number of elements in target-protocols and target-ports are different");
     254 + 
     255 + ret->targets = mmt_mem_alloc( sizeof( forward_packet_target_conf_t ) * ret->target_size );
     256 + int i;
     257 + char *str;
     258 + for( i=0; i<ret->target_size; i++) {
     259 + //protocol
     260 + str = cfg_getnstr(cfg, "target-protocols", i);
     261 + if( IS_EQUAL_STRINGS(str, "SCTP") )
     262 + ret->targets[i].protocol = FORWARD_PACKET_PROTO_SCTP;
     263 + else if( IS_EQUAL_STRINGS(str, "UDP") )
     264 + ret->targets[i].protocol = FORWARD_PACKET_PROTO_UDP;
     265 + else
     266 + ABORT("Does not support yet the protocol: %s", str);
     267 + //host
     268 + str = cfg_getnstr(cfg, "target-hosts", i);
     269 + ret->targets[i].host = mmt_strdup( str );
     270 + //port
     271 + //ret->targets[i].port = cfg_getnint(cfg, "target-ports", i);
     272 + //do not understand why ret->targets[i].port is zero
     273 + str = cfg_getnstr(cfg, "target-ports", i);
     274 + ret->targets[i].port = atoi( str );
     275 + }
     276 + return ret;
     277 +}
     278 + 
     279 +size_t conf_parse_list( const char *string, char ***proto_lst ){
     280 + size_t ret = 0;
     281 + int i;
     282 + char **lst;
     283 + const char *str;
     284 + const size_t len = strlen(string) + sizeof("X={}");
     285 + char buffer[ len ];
     286 + ASSERT( proto_lst != NULL, "Must not be NULL" );
     287 + //put string in form
     288 + snprintf( buffer, len, "X={%s}", string );
     289 + 
     290 + cfg_opt_t opts[] = {
     291 + CFG_STR_LIST("X", "{}", CFGF_NONE),
     292 + CFG_END()
     293 + };
     294 + 
     295 + cfg_t *cfg = cfg_init( opts, CFGF_NONE );
     296 + if( cfg_parse_buf(cfg, buffer) == CFG_PARSE_ERROR )
     297 + log_write( LOG_ERR, "Error: protocols '%s' could not be parsed.", string );
     298 + else{
     299 + ret = cfg_size( cfg, "X");
     300 + 
     301 + lst = mmt_mem_alloc( sizeof( void* ) * ret );
     302 + 
     303 + for( i=0; i<ret; i++) {
     304 + str = cfg_getnstr(cfg, "X", i);
     305 + lst[i] = mmt_strdup( str );
     306 + }
     307 + *proto_lst = lst;
     308 + }
     309 + cfg_free( cfg );
     310 + return ret;
     311 +}
     312 + 
     313 +static inline engine_conf_t *_parse_engine_block( cfg_t *cfg ){
     314 + if( (cfg = _get_first_cfg_block( cfg, "engine")) == NULL )
     315 + return NULL;
     316 + 
     317 + engine_conf_t *ret = mmt_mem_alloc( sizeof( engine_conf_t ));
     318 + ret->threads_size = _cfg_getint( cfg, "thread-nb", 0, 128, 0 );
     319 + ret->excluded_rules = _cfg_get_str(cfg, "exclude-rules" );
     320 + ret->rules_mask = _cfg_get_str(cfg, "rules-mask" );
     321 + ret->max_instances = _cfg_getint( cfg, "max-instances", 1, UINT32_MAX, 100000 );
     322 + ret->ip_encapsulation_index = cfg_getint( cfg, "ip-encapsulation-index" );
     323 + 
     324 + return ret;
     325 +}
     326 + 
     327 +static inline mempool_conf_t *_parse_mempool_block( cfg_t *cfg ){
     328 + if( (cfg = _get_first_cfg_block( cfg, "mempool")) == NULL )
     329 + return NULL;
     330 + 
     331 + mempool_conf_t *ret = mmt_mem_alloc( sizeof( mempool_conf_t ));
     332 + ret->max_bytes = _cfg_getint( cfg, "max-bytes", 0, UINT32_MAX, 2000000000 );
     333 + ret->max_elements = _cfg_getint( cfg, "max-elements", 0, UINT16_MAX, 1000 );
     334 + ret->max_message_size = _cfg_getint( cfg, "max-message-size", 0, UINT16_MAX, 3000 );
     335 + ret->smp_ring_size = _cfg_getint( cfg, "max-bytes", 0, UINT32_MAX, 1000 );
     336 + 
     337 + return ret;
     338 +}
     339 + 
     340 +/**
     341 + * Public API
     342 + * @param filename
     343 + * @return
     344 + */
     345 +config_t* conf_load_from_file( const char* filename ){
     346 + const char *str;
     347 + int i;
     348 + cfg_t *cfg = _load_cfg_from_file( filename );
     349 + if( cfg == NULL )
     350 + return NULL;
     351 + 
     352 + config_t *conf = mmt_mem_alloc( sizeof( config_t ) );
     353 + 
     354 + conf->stack_type = cfg_getint(cfg, "stack-type");
     355 + conf->dpdk_options = _cfg_get_str(cfg, "dpdk-option" );
     356 + 
     357 + conf->input = _parse_input_source( cfg );
     358 + conf->output = _parse_output(cfg);
     359 + 
     360 + conf->engine = _parse_engine_block( cfg );
     361 + conf->forward = _parse_forward_packet(cfg);
     362 + conf->mempool = _parse_mempool_block( cfg );
     363 + cfg_free( cfg );
     364 + 
     365 + return conf;
     366 +}
     367 + 
     368 +/**
     369 + * Public API
     370 + * Free all memory allocated by @load_configuration_from_file
     371 + * @param
     372 + */
     373 +void conf_release( config_t *conf){
     374 + if( conf == NULL )
     375 + return;
     376 + 
     377 + int i;
     378 + if( conf->input ){
     379 + mmt_mem_free( conf->input->input_source );
     380 + mmt_mem_free( conf->input );
     381 + }
     382 + if( conf->output ){
     383 + mmt_mem_free( conf->output->output_dir );
     384 + mmt_mem_free( conf->output );
     385 + }
     386 + 
     387 + if( conf->engine ){
     388 + mmt_mem_free( conf->engine->excluded_rules );
     389 + mmt_mem_free( conf->engine->rules_mask );
     390 + mmt_mem_free( conf->engine );
     391 + }
     392 + 
     393 + if( conf->forward ){
     394 + mmt_mem_free( conf->forward->output_nic );
     395 + for( i=0; i<conf->forward->target_size; i++ ){
     396 + mmt_mem_free( conf->forward->targets[i].host );
     397 + }
     398 + mmt_mem_free( conf->forward->targets );
     399 + mmt_mem_free( conf->forward );
     400 + }
     401 + mmt_mem_free( conf->mempool );
     402 + mmt_mem_free( conf->dpdk_options );
     403 + mmt_mem_free( conf );
     404 +}
     405 + 
     406 +int conf_validate( config_t *conf ){
     407 + int ret = 0;
     408 + //forward packet requires engine
     409 + if( conf->forward->is_enable ){
     410 + bool is_enable_engine = false;
     411 + //when engine module is availabe inside mmt-probe => take into account its setting parameter
     412 + ASSERT( conf->forward->nb_copies > 0, "Number of copies of packet to be sent must be greater than 0: nb-copies > 0");
     413 + }
     414 + 
     415 +#ifdef DPDK_CAPTURE_MODULE
     416 + if( conf->input->input_mode == OFFLINE_ANALYSIS ){
     417 + log_write(LOG_ERR, "input.mode must be ONLINE in DPDK_CAPTURE mode");
     418 + ret ++;
     419 + }
     420 +#endif
     421 + 
     422 + if( conf->output->is_enable ){
     423 + //check output folder exist
     424 + struct stat sb;
     425 + 
     426 + if (stat( conf->output->output_dir, &sb) == 0 && S_ISDIR(sb.st_mode)){
     427 + //printf("YES\n");
     428 + //folder is existing
     429 + } else
     430 + ABORT( "Output folder %s does not exist. Need to update output.output-dir parameter.", conf->output->output_dir );
     431 + }
     432 + return ret;
     433 +}
     434 + 
  • ■ ■ ■ ■ ■ ■
    src/engine/configure.h
     1 +/*
     2 + * configure.h
     3 + *
     4 + * Created on: Dec 12, 2017
     5 + * by: Huu Nghia
     6 + */
     7 + 
     8 +#ifndef SRC_LIB_CONFIGURE_H_
     9 +#define SRC_LIB_CONFIGURE_H_
     10 + 
     11 +#include <stdlib.h>
     12 +#include <stdint.h>
     13 +#include <inttypes.h> //for uint64_t PRIu64
     14 +#include <stdbool.h>
     15 +#include <errno.h>
     16 + 
     17 +typedef struct internet_service_address_struct{
     18 + char *host_name;
     19 + uint16_t port_number;
     20 +}internet_service_address_t;
     21 + 
     22 +typedef struct input_source_conf_struct{
     23 + 
     24 + enum {ONLINE_ANALYSIS, OFFLINE_ANALYSIS} input_mode;
     25 + enum {DPDK_CAPTURE, PCAP_CAPTURE} capture_mode;
     26 + 
     27 + //input source for PCAP online mode (interface name) and for offline mode (pcap name), however for DPDK its interface port number
     28 + char *input_source;
     29 + uint16_t snap_len;
     30 +}input_source_conf_t;
     31 + 
     32 + 
     33 +typedef enum{
     34 + CONF_IP_ENCAPSULATION_INDEX_FIRST = 1,
     35 + CONF_IP_ENCAPSULATION_INDEX_SECOND ,
     36 + CONF_IP_ENCAPSULATION_INDEX_THIRD ,
     37 + CONF_IP_ENCAPSULATION_INDEX_FOURTH ,
     38 + CONF_IP_ENCAPSULATION_INDEX_FIFTH ,
     39 + CONF_IP_ENCAPSULATION_INDEX_SIXTH ,
     40 + CONF_IP_ENCAPSULATION_INDEX_SEVENTH ,
     41 + CONF_IP_ENCAPSULATION_INDEX_EIGHTH ,
     42 + CONF_IP_ENCAPSULATION_INDEX_NINTH ,
     43 + CONF_IP_ENCAPSULATION_INDEX_TENTH ,
     44 + CONF_IP_ENCAPSULATION_INDEX_ELEVENTH ,
     45 + CONF_IP_ENCAPSULATION_INDEX_TWELFTH ,
     46 + CONF_IP_ENCAPSULATION_INDEX_THIRTEENTH ,
     47 + CONF_IP_ENCAPSULATION_INDEX_FOURTEENTH ,
     48 + CONF_IP_ENCAPSULATION_INDEX_FIFTEENTH ,
     49 + CONF_IP_ENCAPSULATION_INDEX_LAST /*currently, MMT supports maximally 16 proto in hierarchies*/
     50 +}conf_ip_encapsulation_index_t;
     51 + 
     52 +typedef struct engine_conf_struct{
     53 + uint16_t threads_size;
     54 + char *excluded_rules;
     55 + char *rules_mask;
     56 + uint32_t max_instances;
     57 + //indicate which IP will be analyzed in case of IP-in-IP
     58 + conf_ip_encapsulation_index_t ip_encapsulation_index;
     59 +}engine_conf_t;
     60 + 
     61 + 
     62 +typedef struct output_conf_struct{
     63 + bool is_enable;
     64 + char *output_dir;
     65 + uint16_t sample_interval;
     66 + bool is_report_description;
     67 +}output_conf_t;
     68 + 
     69 +typedef enum {
     70 + ACTION_FORWARD,
     71 + ACTION_DROP
     72 +}forward_action_t;
     73 + 
     74 +typedef struct forward_packet_target_conf_struct{
     75 + enum{ FORWARD_PACKET_PROTO_SCTP, FORWARD_PACKET_PROTO_UDP } protocol;
     76 + char * host;
     77 + uint16_t port;
     78 +}forward_packet_target_conf_t;
     79 + 
     80 +typedef struct forward_packet_conf_struct{
     81 + bool is_enable;
     82 + char *output_nic;
     83 + uint16_t snap_len;
     84 + uint16_t promisc;
     85 + uint32_t nb_copies;
     86 + forward_action_t default_action;
     87 + 
     88 + forward_packet_target_conf_t *targets;
     89 + uint16_t target_size;
     90 +}forward_packet_conf_t;
     91 + 
     92 + 
     93 +typedef struct mempool_conf_struct{
     94 + uint32_t max_bytes;
     95 + uint16_t max_elements;
     96 + uint16_t max_message_size;
     97 + uint32_t smp_ring_size;
     98 +}mempool_conf_t;
     99 + 
     100 +/**
     101 + * Configuration of MMT-Probe
     102 + */
     103 +typedef struct config_struct{
     104 + 
     105 + uint32_t stack_type; //dpi stack type
     106 + char *dpdk_options;
     107 + 
     108 + input_source_conf_t *input;
     109 + output_conf_t *output;
     110 + 
     111 + forward_packet_conf_t *forward;
     112 + engine_conf_t *engine;
     113 + mempool_conf_t *mempool;
     114 +}config_t;
     115 + 
     116 + 
     117 +/**
     118 + * Load configuration from a file
     119 + * @param filename
     120 + * @return
     121 + */
     122 +config_t* conf_load_from_file( const char* filename );
     123 + 
     124 +/**
     125 + * Free all memory allocated by @load_configuration_from_file
     126 + * @param
     127 + */
     128 +void conf_release( config_t * );
     129 + 
     130 + 
     131 +/**
     132 + * Split a string into an array of string segments with separator is comma
     133 + * @param string
     134 + * @param proto_lst is a pointer to a the result array that will be created by the function.
     135 + * @return number of segments in proto_lst
     136 + * @note: user needs to free the segments in proto_lst and also proto_lst after using them
     137 + */
     138 +size_t conf_parse_list( const char *string, char ***proto_lst );
     139 + 
     140 +/**
     141 + * Validate a configuration to avoid any conflict among its parameters, such as,
     142 + * if you is_enable http reconstruction, then tcp reassembly must be enabled also.
     143 + * @param conf
     144 + * @return
     145 + */
     146 +int conf_validate( config_t *conf );
     147 + 
     148 +bool conf_parse_input_mode( int *result, const char *string );
     149 + 
     150 +#endif /* SRC_LIB_CONFIGURE_H_ */
     151 + 
  • ■ ■ ■ ■ ■ ■
    src/engine/configure_override.c
     1 +/*
     2 + * configure_override.c
     3 + *
     4 + * Created on: Apr 23, 2018
     5 + * by: Huu Nghia Nguyen
     6 + */
     7 + 
     8 +#include "../lib/mmt_lib.h"
     9 + 
     10 +#include "configure_override.h"
     11 + 
     12 +static bool _parse_bool( const char *value ){
     13 + if( IS_EQUAL_STRINGS( value, "true" ) )
     14 + return true;
     15 + if( IS_EQUAL_STRINGS( value, "false" ) )
     16 + return false;
     17 + return false;
     18 +}
     19 + 
     20 +const identity_t* conf_get_identity_from_string( const char * ident_str ){
     21 + const identity_t *identities;
     22 + size_t nb_parameters = conf_get_identities( &identities );
     23 + int i;
     24 + for( i=0; i<nb_parameters; i++ )
     25 + if( IS_EQUAL_STRINGS( ident_str, identities[i].ident ))
     26 + return &identities[i];
     27 + return NULL;
     28 +}
     29 + 
     30 + 
     31 +const char* conf_validate_data_value( const identity_t *ident, const char *data_value ){
     32 + static char error_reason[1000];
     33 + int i;
     34 + int enum_val = 0;
     35 + 
     36 + //special data type
     37 + switch( ident->val ){
     38 + //input
     39 + case CONF_ATT__INPUT__MODE:
     40 + if( conf_parse_input_mode( &enum_val, data_value ) )
     41 + return NULL;
     42 + else{
     43 + snprintf( error_reason, sizeof( error_reason), "Unexpected value [%s] for [%s]", data_value, ident->ident );
     44 + return error_reason;
     45 + }
     46 + break;
     47 +//#ifdef SECURITY_MODULE
     48 +// case CONF_ATT__SECURITY__INGORE_REMAIN_FLOW:
     49 +// if( conf_parse_security_ignore_mode( &enum_val, data_value ) )
     50 +// return NULL;
     51 +// else{
     52 +// snprintf( error_reason, sizeof( error_reason), "Unexpected value [%s] for [%s]", data_value, ident->ident );
     53 +// return error_reason;
     54 +// }
     55 +// break;
     56 +//#endif
     57 + default:
     58 + break;
     59 + }
     60 + 
     61 + //check value depending on data type of parameter
     62 + switch( ident->data_type ){
     63 + case BOOL:
     64 + if( IS_EQUAL_STRINGS( data_value, "true" ) )
     65 + break;
     66 + if( IS_EQUAL_STRINGS( data_value, "false" ) )
     67 + break;
     68 + 
     69 + snprintf( error_reason, sizeof(error_reason), "Expect either 'true' or 'false' as value of '%s' (not '%s')", ident->ident, data_value );
     70 + return error_reason;
     71 + break;
     72 + 
     73 + case UINT16_T:
     74 + case UINT32_T:
     75 + //check if data_value contains only the number
     76 + i = 0;
     77 + while( data_value[i] != '\0' ){
     78 + if( data_value[i] < '0' || data_value[i] > '9' ){
     79 + snprintf( error_reason, sizeof( error_reason), "Expect a number as value of '%s' (not '%s')", ident->ident, data_value );
     80 + return 0;
     81 + }
     82 + i ++;
     83 + }
     84 + break;
     85 + default:
     86 + break;
     87 + }
     88 + 
     89 + return NULL;
     90 +}
     91 + 
     92 +static inline bool _override_element_by_ident( config_t *conf, const identity_t *ident, const char *value_str ){
     93 + uint32_t int_val = 0;
     94 + int i;
     95 + DEBUG("Update %s to %s", ident->ident, value_str );
     96 + void *field_ptr = conf_get_ident_attribute_field(conf, ident->val );
     97 + int enum_val = 0;
     98 + 
     99 + if( field_ptr == NULL ){
     100 + log_write( LOG_INFO, "Have not supported yet for [%s]", ident->ident );
     101 + return false;
     102 + }
     103 + char **string_ptr;
     104 + 
     105 + //special data type
     106 + switch( ident->val ){
     107 + //input
     108 + case CONF_ATT__INPUT__MODE:
     109 + if( conf_parse_input_mode( &enum_val, value_str ) )
     110 + *((int *)field_ptr) = enum_val;
     111 + else{
     112 + log_write( LOG_INFO, "Unexpected value [%s] for [%s]", value_str, ident->ident );
     113 + return false;
     114 + }
     115 + log_write( LOG_INFO, "Overridden value of configuration parameter '%s' by '%d'", ident->ident, enum_val );
     116 + return true;
     117 + default:
     118 + break;
     119 + }
     120 + 
     121 + switch( ident->data_type ){
     122 + //update value depending on parameters
     123 + case NO_SUPPORT:
     124 + log_write( LOG_INFO, "Have not supported yet for [%s]", ident->ident );
     125 + return false;
     126 + case CHAR_STAR:
     127 + string_ptr = (char **) field_ptr;
     128 + //value does not change ==> do nothing
     129 + if( IS_EQUAL_STRINGS( *string_ptr, value_str ) )
     130 + return false;
     131 + mmt_mem_free( *string_ptr );
     132 + *string_ptr = mmt_strdup( value_str );
     133 + log_write( LOG_INFO, "Overridden value of configuration parameter '%s' by '%s'", ident->ident, *string_ptr );
     134 + return true;
     135 + case LIST:
     136 + /*
     137 + switch( ident->val ){
     138 + 
     139 + case CONF_ATT__RADIUS_REPORT__OUTPUT_CHANNEL:
     140 + int_val = conf_parse_output_channel( value_str );
     141 + if( int_val == *(output_channel_conf_t *) field_ptr )
     142 + return false;
     143 + 
     144 + (*(output_channel_conf_t *) field_ptr) = int_val;
     145 + log_write( LOG_INFO, "Overridden value of configuration parameter '%s' by '%d'",
     146 + ident->ident, *((output_channel_conf_t *)field_ptr) );
     147 + return true;
     148 + }
     149 + */
     150 + break;
     151 + case BOOL:
     152 + int_val = _parse_bool( value_str );
     153 + //value does not change => do nothing
     154 + if( int_val == *((bool *)field_ptr) )
     155 + return false;
     156 + 
     157 + //update value
     158 + *((bool *)field_ptr) = int_val;
     159 + log_write( LOG_INFO, "Overridden value of configuration parameter '%s' by '%d'",
     160 + ident->ident, *((bool *)field_ptr) );
     161 + return true;
     162 + 
     163 + 
     164 + case UINT16_T:
     165 + int_val = atoi( value_str );
     166 + //value does not change ==> do nothing
     167 + if( int_val == *((uint16_t *)field_ptr) )
     168 + return false;
     169 + 
     170 + *((uint16_t *)field_ptr) = int_val;
     171 + log_write( LOG_INFO, "Overridden value of configuration parameter '%s' by '%d'",
     172 + ident->ident, *((uint16_t *)field_ptr) );
     173 + return true;
     174 + 
     175 + case UINT32_T:
     176 + int_val = atol( value_str );
     177 + //value does not change ==> do nothing
     178 + if( int_val == *((uint32_t *)field_ptr) )
     179 + return false;
     180 + *((uint32_t *)field_ptr) = int_val;
     181 + log_write( LOG_INFO, "Overridden value of configuration parameter '%s' by '%d'",
     182 + ident->ident, *((uint16_t *)field_ptr) );
     183 + return true;
     184 + default:
     185 + break;
     186 + }
     187 + 
     188 + log_write( LOG_INFO, "Unknown identifier '%s'", ident->ident );
     189 + return false;
     190 +}
     191 + 
     192 + 
     193 +/**
     194 + * Update value of a configuration parameter.
     195 + * The new value is updated only if it is different with the current value of the parameter.
     196 + * @param conf
     197 + * @param ident
     198 + * @param value
     199 + * @return 0 if the new value is updated, otherwise false
     200 + */
     201 +int conf_override_element( config_t *conf, const char *ident_str, const char *value_str ){
     202 + const identity_t *ident = conf_get_identity_from_string( ident_str );
     203 + 
     204 + if( ident == NULL ){
     205 + log_write( LOG_INFO, "Unknown parameter identity [%s]", ident_str );
     206 + return -1;
     207 + }
     208 + if( _override_element_by_ident(conf, ident, value_str ) )
     209 + return 0;
     210 + return 1;
     211 +}
     212 + 
     213 +bool conf_override_element_by_id( config_t *conf, int ident_val, const char *value_str ){
     214 + const identity_t *ident = conf_get_identity_from_id( ident_val );
     215 + 
     216 + if( ident == NULL ){
     217 + log_write( LOG_INFO, "Unknown parameter identity [%d]", ident_val );
     218 + return false;
     219 + }
     220 + return _override_element_by_ident(conf, ident, value_str );
     221 +}
     222 + 
     223 + 
     224 +/**
     225 + * Public API
     226 + */
     227 +void conf_print_identities_list(){
     228 + int i;
     229 + const char *data_type_strings[] = {
     230 + "",
     231 + "boolean",
     232 + "uint16_t",
     233 + "uint32_t",
     234 + "string",
     235 + "string"
     236 + };
     237 + 
     238 + const identity_t *identities;
     239 + size_t nb_parameters = conf_get_identities( &identities );
     240 + 
     241 + for( i=0; i<nb_parameters; i++ )
     242 + if( identities[i].data_type !=NO_SUPPORT )
     243 + printf("%s (%s)\n", identities[i].ident, data_type_strings[identities[i].data_type]);
     244 +}
     245 + 
  • ■ ■ ■ ■ ■ ■
    src/engine/configure_override.h
     1 +/*
     2 + * configure_override.h
     3 + *
     4 + * Created on: Apr 23, 2018
     5 + * by: Huu Nghia Nguyen
     6 + */
     7 + 
     8 +#ifndef SRC_CONFIGURE_OVERRIDE_H_
     9 +#define SRC_CONFIGURE_OVERRIDE_H_
     10 + 
     11 +#include "configure.h"
     12 +#include "../lib/macro_apply.h"
     13 + 
     14 +/**
     15 + * Data type using by the attributes of the configuration
     16 + */
     17 +typedef enum{
     18 + NO_SUPPORT,
     19 + BOOL,
     20 + UINT16_T,
     21 + UINT32_T,
     22 + CHAR_STAR,
     23 + LIST
     24 +} data_type_t;
     25 + 
     26 +typedef struct identity_struct{
     27 + int val;
     28 + data_type_t data_type;
     29 + const char *ident;
     30 +}identity_t;
     31 + 
     32 +const identity_t* conf_get_identity_from_string( const char * ident_str );
     33 + 
     34 +/**
     35 + * Override an attribute in configuration.
     36 + * @param
     37 + * @param ident: identifier of element will be overridden.
     38 + * @param value: value will be overridden only if the value is different with the current one of the element.
     39 + * @return 0 if the value has been overridden, otherwise false
     40 + */
     41 +int conf_override_element( config_t*, const char* ident, const char *value );
     42 + 
     43 +/**
     44 + * Override an attribute in configuration.
     45 + * @param conf
     46 + * @param ident_val
     47 + * @param value_str
     48 + * @return
     49 + */
     50 +bool conf_override_element_by_id( config_t *conf, int ident_val, const char *value_str );
     51 + 
     52 +/**
     53 + * Check if data_value is suitable for an identity.
     54 + * @param ident
     55 + * @param data_value
     56 + * @return NULL if yes, otherwise, a text representing error reasons.
     57 + */
     58 +const char* conf_validate_data_value( const identity_t *ident, const char *data_value );
     59 + 
     60 +void conf_print_identities_list();
     61 + 
     62 + 
     63 + 
     64 +#define _FIRST( a, ... ) a
     65 +#define FIRST( a ) _FIRST a
     66 + 
     67 +#define _SECOND( a, b, c, d ) {.val = a, .data_type = d, .ident = b}
     68 +#define SECOND( a ) _SECOND a
     69 + 
     70 +#define _CASE( a, b, c, d ) case a: return c;
     71 +#define CASE( a ) _CASE a
     72 + 
     73 +#define COMMA() ,
     74 +#define EMPTY()
     75 + 
     76 +#define DECLARE_CONF_ATT( ... ) \
     77 + \
     78 +/*list of identities by number*/ \
     79 +typedef enum { \
     80 + APPLY( COMMA, FIRST, __VA_ARGS__ ) \
     81 +}config_identity_t; \
     82 + \
     83 +/*list of identities*/ \
     84 +static inline size_t conf_get_identities( const identity_t **lst ){ \
     85 + static identity_t identities[ COUNT_ARGS( __VA_ARGS__ ) ] = { \
     86 + APPLY( COMMA, SECOND, __VA_ARGS__ ) \
     87 + }; \
     88 + if( lst != NULL ) *lst = identities; \
     89 + return COUNT_ARGS( __VA_ARGS__ ); \
     90 +}; \
     91 + \
     92 +/*get a file of config_t by identities number */ \
     93 +static inline void* conf_get_ident_attribute_field( \
     94 + config_t *conf, config_identity_t x ){ \
     95 + switch( x ){ \
     96 + APPLY( EMPTY, CASE, __VA_ARGS__ ) \
     97 + } \
     98 + return NULL; \
     99 +}
     100 + 
     101 +/**
     102 + * In the following declaration, each line uses the structure:
     103 + * (ident-number, ident-string, pointer-field, data-type)
     104 + * - ident-number: is used to define enum element
     105 + * - ident-string: is string of configuration attribute.
     106 + * They are the same as in mmt-5greplay.conf. Its level is separated by dot, for example:
     107 + * "input.mode" will represent "mode" inside the "input" block
     108 + * - pointer-field: is a pointer pointing to a field of "conf" variable having "config_t" type
     109 + * - data-type: is data type of the attribute. It can be bool, uint16_t, uint32_t or char*
     110 + */
     111 +DECLARE_CONF_ATT(
     112 + (CONF_ATT__NONE, "no-support", NULL, NO_SUPPORT),
     113 + //general
     114 + (CONF_ATT__STACK_TYPE, "stack-type", &conf->stack_type, UINT32_T),
     115 + (CONF_ATT__INPUT__DPDK_OPTION, "dpdk-option", &conf->dpdk_options, CHAR_STAR),
     116 + 
     117 + //input
     118 + (CONF_ATT__INPUT__MODE, "input.mode", &conf->input->input_mode, UINT16_T),
     119 + (CONF_ATT__INPUT__SOURCE, "input.source", &conf->input->input_source, CHAR_STAR),
     120 + (CONF_ATT__INPUT__SNAP_LEN, "input.snap-len", &conf->input->snap_len, UINT16_T),
     121 + 
     122 + //output
     123 + (CONF_ATT__OUTPUT__ENABLE, "output.enable", &conf->output->is_enable, BOOL),
     124 + (CONF_ATT__OUTPUT__OUTPUT_DIR, "output.output-dir", &conf->output->output_dir, CHAR_STAR),
     125 + (CONF_ATT__OUTPUT__REPORT_DESCRIPTION,"output.report-description", &conf->output->is_report_description, BOOL),
     126 + (CONF_ATT__OUTPUT__SAMPLE_INTERVAL, "output.sample-interval", &conf->output->sample_interval, UINT16_T),
     127 + 
     128 + //engine
     129 + (CONF_ATT__ENGINE__THREAD_NB, "engine.thread-nb", &conf->engine->threads_size, UINT16_T),
     130 + (CONF_ATT__ENGINE__EXCLUDE_RULES, "engine.exclude-rules", &conf->engine->excluded_rules, CHAR_STAR),
     131 + (CONF_ATT__ENGINE__RULES_MASK, "engine.rules-mask", &conf->engine->rules_mask, CHAR_STAR),
     132 + (CONF_ATT__ENGINE__MAX_INSTANCES, "engine.max-instances", &conf->engine->max_instances, UINT32_T ),
     133 + 
     134 + (CONF_ATT__MEMPOOL__MAX_MESSAGE_SIZE, "mempool.max-message-size", &conf->mempool->max_message_size, UINT32_T),
     135 + (CONF_ATT__MEMPOOL__MAX_BYTES, "mempool.max-bytess", &conf->mempool->max_bytes, UINT16_T),
     136 + (CONF_ATT__MEMPOOL__MAX_ELEMENTS, "mempool.max-elements", &conf->mempool->max_elements, UINT16_T),
     137 + (CONF_ATT__MEMPOOL__SMP_RING_SIZE, "mempool.smp-ring-size", &conf->mempool->smp_ring_size, UINT32_T ),
     138 + 
     139 + (CONF_ATT__FORWARD__ENABLE, "forward.enable", &conf->forward->is_enable, BOOL),
     140 + (CONF_ATT__FORWARD__OUTPUT_NIC,"forward.output-nic", &conf->forward->output_nic, CHAR_STAR),
     141 + (CONF_ATT__FORWARD__SNAP_LEN, "forward.snap-len", &conf->forward->snap_len, UINT16_T),
     142 + (CONF_ATT__FORWARD__NB_COPIES, "forward.nb-copies", &conf->forward->nb_copies, UINT32_T),
     143 + (CONF_ATT__FORWARD__PROMISC, "forward.promisc", &conf->forward->promisc, UINT16_T)
     144 +)
     145 + 
     146 +/**
     147 + * Get identity_t object from a number ID.
     148 + * @param id
     149 + * @return
     150 + */
     151 +static inline const identity_t* conf_get_identity_from_id( int id ){
     152 + const identity_t *identities;
     153 + size_t nb_parameters = conf_get_identities( &identities );
     154 + 
     155 + if( id < 0 || id >= nb_parameters )
     156 + return NULL;
     157 + 
     158 + return &identities[ id ];
     159 +}
     160 + 
     161 + 
     162 +static inline const char* conf_get_name_from_id( config_identity_t id ){
     163 + return conf_get_identity_from_id( id )->ident;
     164 +}
     165 + 
     166 +uint32_t conf_get_number_value( config_identity_t id );
     167 + 
     168 +bool conf_load_config( const char *path );
     169 + 
     170 +const config_t *conf_get();
     171 + 
     172 +void conf_release();
     173 + 
     174 + 
     175 +#endif /* SRC_CONFIGURE_OVERRIDE_H_ */
     176 + 
  • ■ ■ ■ ■ ■ ■
    src/engine/dpi_message_t.h
     1 +/*
     2 + * dpi_message_t.h
     3 + *
     4 + * Bridging the gap between mmt-dpi data and mmt-engine message
     5 + *
     6 + * Created on: Mar 24, 2017
     7 + * Created by: Huu Nghia NGUYEN <[email protected]>
     8 + */
     9 + 
     10 +#ifndef SRC_LIB_DPI_MESSAGE_T_H_
     11 +#define SRC_LIB_DPI_MESSAGE_T_H_
     12 + 
     13 + 
     14 +#include <mmt_core.h>
     15 +#include <tcpip/mmt_tcpip.h>
     16 + 
     17 +#include "message_t.h"
     18 + 
     19 +#ifndef ftp_command_struct
     20 +/**
     21 + * FTP command structure: CMD PARAMETER
     22 + */
     23 +typedef struct ftp_command_struct{
     24 + uint16_t cmd;
     25 + char *str_cmd;
     26 + char *param;
     27 +}ftp_command_t;
     28 + 
     29 +/**
     30 + * FTP response structure
     31 + */
     32 +typedef struct ftp_response_struct{
     33 + uint16_t code;
     34 + char *str_code;
     35 + char *value;
     36 +}ftp_response_t;
     37 + 
     38 +#endif
     39 + 
     40 +/**
     41 + * Get length of payload of a protocol
     42 + * @param ipacket
     43 + * @param proto_id
     44 + * @return
     45 + */
     46 +static inline size_t dpi_get_payload_len(const ipacket_t * ipacket, uint32_t proto_id ){
     47 + int i = 0;
     48 + uint16_t length = 0;
     49 + uint16_t offset = 0;
     50 + 
     51 + for (i = 1; i < ipacket->proto_hierarchy->len; i++){
     52 + offset +=ipacket->proto_headers_offset->proto_path[i];
     53 + 
     54 + if ( ipacket->proto_hierarchy->proto_path[i] == proto_id ){
     55 + //get header offset of the proto after #proto_id
     56 + if ( (i+1) < ipacket->proto_hierarchy->len){
     57 + offset +=ipacket->proto_headers_offset->proto_path[i+1];
     58 + length = ipacket->p_hdr->caplen - offset;
     59 + 
     60 + return length;
     61 + }
     62 + return 0;
     63 + }
     64 + }
     65 + 
     66 + return 0;
     67 +}
     68 + 
     69 +/**
     70 + * Get length of data of a protocol
     71 + * @param ipacket
     72 + * @param proto_id
     73 + * @return
     74 + */
     75 +static inline size_t dpi_get_data_len( const ipacket_t * ipacket, uint32_t proto_id ){
     76 + int i = 0;
     77 + 
     78 + uint16_t length = 0;
     79 + uint16_t offset = 0;
     80 + 
     81 + for (i = 1; i < ipacket->proto_hierarchy->len; i++){
     82 + offset +=ipacket->proto_headers_offset->proto_path[i];
     83 + if ( ipacket->proto_hierarchy->proto_path[i] == proto_id ){
     84 + length = ipacket->p_hdr->caplen - offset;
     85 + 
     86 + return length;
     87 + }
     88 + }
     89 + return 0;
     90 +}
     91 + 
     92 +static inline size_t dpi_get_ip_option_len(const ipacket_t * ipacket ){
     93 + uint8_t *ip_header_len = (uint8_t *) get_attribute_extracted_data( ipacket,PROTO_IP,IP_HEADER_LEN );
     94 + 
     95 + if( likely( ip_header_len != NULL ))
     96 + return (*ip_header_len - 20); //IPv4 has 20 bytes of fixed header
     97 + return 0;
     98 +}
     99 + 
     100 +/**
     101 + * Public API
     102 + * Convert data in format of MMT-Probe to data in format of MMT-Sec
     103 + */
     104 +static inline int dpi_message_set_void_data( const ipacket_t *pkt, const void *data, message_t *msg, uint32_t proto_id, uint32_t att_id ){
     105 + const void *new_data = NULL;
     106 + size_t new_data_len = 0;
     107 + int new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     108 + 
     109 + if( unlikely( data == NULL ))
     110 + return 0;
     111 + 
     112 + switch( att_id ){
     113 + case PROTO_PAYLOAD:
     114 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     115 + new_data = data;
     116 + new_data_len = dpi_get_payload_len( pkt, proto_id );
     117 + break;
     118 + 
     119 + case PROTO_DATA:
     120 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     121 + new_data = data;
     122 + new_data_len = dpi_get_data_len( pkt, proto_id );
     123 + break;
     124 + 
     125 + case FTP_LAST_COMMAND:
     126 + if ( proto_id == PROTO_FTP ){
     127 + new_data_type = MMT_SEC_MSG_DATA_TYPE_STRING;
     128 + new_data = ((ftp_command_t *)data)->str_cmd;
     129 + new_data_len = strlen( ((ftp_command_t *)data)->str_cmd );
     130 + }
     131 + break;
     132 + 
     133 + case FTP_LAST_RESPONSE_CODE:
     134 + if ( proto_id == PROTO_FTP ){
     135 + new_data_type = MMT_SEC_MSG_DATA_TYPE_STRING;
     136 + new_data = ((ftp_response_t *)data)->str_code;
     137 + new_data_len = strlen( ((ftp_response_t *)data)->str_code );
     138 + }
     139 + break;
     140 + 
     141 + case IP_OPTS:
     142 + if( proto_id == PROTO_IP){
     143 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     144 + new_data = data;
     145 + new_data_len = dpi_get_ip_option_len( pkt );
     146 + }
     147 + break;
     148 + 
     149 + default:
     150 + log_write( LOG_WARNING,"Need to process attribute %d.%d for packet %"PRIu64, proto_id, att_id, pkt->packet_id );
     151 + break;
     152 + }//end of switch( att_id )
     153 + 
     154 + if( new_data_len == 0 || new_data == NULL )
     155 + return 0;
     156 + 
     157 + return set_element_data_message_t( msg, proto_id, att_id, new_data, new_data_type, new_data_len );
     158 +}
     159 + 
     160 + 
     161 +/**
     162 + * Convert data encoded by mmt-dpi to one element of message_t.
     163 + * - Input:
     164 + * + data : data to be converted
     165 + * + type : type of #data
     166 + * - Output:
     167 + * + el : element to be updated in message_t
     168 + * + msg : message containing el
     169 + * - return:
     170 + * + 0 if success
     171 + */
     172 +static inline int dpi_message_set_dpi_data( const void *data, int dpi_data_type, message_t *msg, uint32_t proto_id, uint32_t att_id ){
     173 + double number = 0;
     174 + const void *new_data= NULL;
     175 + size_t new_data_len = 0;
     176 + int new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     177 + 
     178 + //does not exist data for this proto_id and att_id
     179 + if( unlikely( data == NULL ))
     180 + return 1;
     181 + 
     182 + switch( dpi_data_type ){
     183 + case MMT_UNDEFINED_TYPE: /**< no type constant value */
     184 + break;
     185 + case MMT_DATA_CHAR: /**< 1 character constant value */
     186 + number = *(char *) data;
     187 + new_data_type = MMT_SEC_MSG_DATA_TYPE_NUMERIC;
     188 + new_data = &number;
     189 + new_data_len = sizeof( double );
     190 + break;
     191 + 
     192 + case MMT_U8_DATA: /**< unsigned 1-byte constant value */
     193 + number = *(uint8_t *) data;
     194 + new_data_type = MMT_SEC_MSG_DATA_TYPE_NUMERIC;
     195 + new_data = &number;
     196 + new_data_len = sizeof( double );
     197 + break;
     198 + 
     199 + case MMT_DATA_PORT: /**< tcp/udp port constant value */
     200 + case MMT_U16_DATA: /**< unsigned 2-bytes constant value */
     201 + number = *(uint16_t *) data;
     202 + new_data_type = MMT_SEC_MSG_DATA_TYPE_NUMERIC;
     203 + new_data = &number;
     204 + new_data_len = sizeof( double );
     205 + break;
     206 + 
     207 + case MMT_U32_DATA: /**< unsigned 4-bytes constant value */
     208 + number = *(uint32_t *) data;
     209 + new_data_type = MMT_SEC_MSG_DATA_TYPE_NUMERIC;
     210 + new_data = &number;
     211 + new_data_len = sizeof( double );
     212 + break;
     213 + 
     214 + case MMT_U64_DATA: /**< unsigned 8-bytes constant value */
     215 + number = *(uint64_t *) data;
     216 + new_data_type = MMT_SEC_MSG_DATA_TYPE_NUMERIC;
     217 + new_data = &number;
     218 + new_data_len = sizeof( double );
     219 + break;
     220 + 
     221 + case MMT_DATA_FLOAT: /**< float constant value */
     222 + number = *(float *) data;
     223 + new_data_type = MMT_SEC_MSG_DATA_TYPE_NUMERIC;
     224 + new_data = &number;
     225 + new_data_len = sizeof( double );
     226 + break;
     227 + 
     228 + case MMT_DATA_IP6_ADDR: /**< ip6 address constant value */
     229 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     230 + new_data = data;
     231 + new_data_len = 16;
     232 + break;
     233 + 
     234 + case MMT_DATA_MAC_ADDR: /**< ethernet mac address constant value */
     235 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     236 + new_data = data;
     237 + new_data_len = 6;
     238 + break;
     239 + 
     240 + case MMT_DATA_IP_NET: /**< ip network address constant value */
     241 + case MMT_DATA_IP_ADDR: /**< ip address constant value */
     242 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     243 + new_data = data;
     244 + new_data_len = 4;
     245 + break;
     246 + 
     247 + case MMT_DATA_TIMEVAL: /**< number of seconds and microseconds constant value */
     248 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     249 + new_data = data;
     250 + new_data_len = sizeof( struct timeval );
     251 + break;
     252 + 
     253 + //special cases: pointer is processed separately
     254 + //case MMT_DATA_POINTER: /**< pointer constant value (size is void *) */
     255 + 
     256 + case MMT_DATA_BUFFER: /**< binary buffer content */
     257 + case MMT_DATA_POINT: /**< point constant value */
     258 + case MMT_DATA_PORT_RANGE: /**< tcp/udp port range constant value */
     259 + case MMT_DATA_DATE: /**< date constant value */
     260 + case MMT_DATA_TIMEARG: /**< time argument constant value */
     261 + case MMT_DATA_STRING_INDEX: /**< string index constant value (an association between a string and an integer) */
     262 + case MMT_DATA_LAYERID: /**< Layer ID value */
     263 + case MMT_DATA_FILTER_STATE: /**< (filter_id: filter_state) */
     264 + case MMT_DATA_PARENT: /**< (filter_id: filter_state) */
     265 + case MMT_STATS: /**< pointer to MMT Protocol statistics */
     266 + printf("WARN: does not support MMT-DPI data type %d\n", dpi_data_type );
     267 + break;
     268 + 
     269 + case MMT_BINARY_DATA: /**< binary constant value */
     270 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     271 + new_data = data;
     272 + new_data_len = BINARY_64DATA_TYPE_LEN;
     273 + break;
     274 + 
     275 + case MMT_BINARY_VAR_DATA: /**< binary constant value with variable size given by function getExtractionDataSizeByProtocolAndFieldIds */
     276 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     277 + new_data = data;
     278 + new_data_len = BINARY_1024DATA_TYPE_LEN;
     279 + break;
     280 + 
     281 + case MMT_STRING_DATA: /**< text string data constant value. Len plus data. Data is expected to be '\0' terminated and maximum BINARY_64DATA_LEN long */
     282 + case MMT_STRING_LONG_DATA: /**< text string data constant value. Len plus data. Data is expected to be '\0' terminated and maximum STRING_DATA_LEN long */
     283 + new_data_type = MMT_SEC_MSG_DATA_TYPE_STRING;
     284 + new_data = ((mmt_binary_var_data_t *)data)->data;
     285 + new_data_len = ((mmt_binary_var_data_t *)data)->len;
     286 + break;
     287 + 
     288 + 
     289 + case MMT_HEADER_LINE: /**< string pointer value with a variable size. The string is not necessary null terminating */
     290 + new_data_type = MMT_SEC_MSG_DATA_TYPE_STRING;
     291 + new_data = ((mmt_header_line_t *)data)->ptr;
     292 + new_data_len = ((mmt_header_line_t *)data)->len;
     293 + break;
     294 + 
     295 + case MMT_GENERIC_HEADER_LINE: /**< structure representing an RFC2822 header line with null terminating field and value elements. */
     296 + case MMT_STRING_DATA_POINTER: /**< pointer constant value (size is void *). The data pointed to is of type string with null terminating character included */
     297 + new_data_type = MMT_SEC_MSG_DATA_TYPE_STRING;
     298 + new_data = data;
     299 + new_data_len = strlen( (char*) data);
     300 + break;
     301 + 
     302 + case MMT_DATA_PATH: /**< protocol path constatn value */
     303 + new_data_type = MMT_SEC_MSG_DATA_TYPE_BINARY;
     304 + new_data = data;
     305 + new_data_len = sizeof( proto_hierarchy_t );
     306 + break;
     307 + 
     308 + default:
     309 + break;
     310 + }
     311 + 
     312 + if( unlikely( new_data_len == 0 || new_data == NULL ))
     313 + return 0;
     314 + 
     315 + return set_element_data_message_t( msg, proto_id, att_id, new_data, new_data_type, new_data_len );
     316 +}
     317 + 
     318 + 
     319 +/**
     320 + * Convert data encoded in a pcap packet to readable data that is either a double
     321 + * or a string ending by '\0'.
     322 + * This function will create a new memory segment to store its result.
     323 + */
     324 +static inline int dpi_message_set_data( const ipacket_t *pkt, int dpi_data_type, message_t *msg, uint32_t proto_id, uint32_t att_id ){
     325 + uint8_t *data = (uint8_t *) get_attribute_extracted_data( pkt, proto_id, att_id );
     326 + 
     327 + //does not exist data for this proto_id and att_id
     328 + if( data == NULL )
     329 + return 1;
     330 + 
     331 + if( dpi_data_type == MMT_DATA_POINTER ){
     332 + return dpi_message_set_void_data( pkt, data, msg, proto_id, att_id );
     333 + }
     334 + 
     335 + return dpi_message_set_dpi_data( data, dpi_data_type, msg, proto_id, att_id );
     336 +}
     337 + 
     338 +#endif /* SRC_LIB_DPI_MESSAGE_T_H_ */
     339 + 
  • ■ ■ ■ ■ ■ ■
    src/engine/expression.c
     1 +/*
     2 + * expression.c
     3 + *
     4 + * Created on: 21 sept. 2016
     5 + * Created by: Huu Nghia NGUYEN <[email protected]>
     6 + */
     7 + 
     8 +#include <ctype.h>
     9 +#include <stdlib.h>
     10 +#include <limits.h>
     11 +#include <mmt_core.h>
     12 + 
     13 +#include "../lib/mmt_lib.h"
     14 +#include "mmt_utils.h"
     15 +#include "expression.h"
     16 + 
     17 +#define MAX_STR_SIZE 10000
     18 + 
     19 +/**
     20 + * Convert from MMT_DPI_DATA_TYPE to MMT_SEC_DATA_TYPE that is either a MMT_SEC_MSG_DATA_TYPE_STRING or a MMT_SEC_MSG_DATA_TYPE_NUMERIC
     21 + */
     22 +inline int convert_data_type( int type ){
     23 + switch( type ){
     24 + case MMT_U8_DATA:
     25 + case MMT_U16_DATA:
     26 + case MMT_U32_DATA:
     27 + case MMT_U64_DATA:
     28 + case MMT_DATA_PORT:
     29 + case MMT_DATA_CHAR:
     30 + case MMT_DATA_FLOAT:
     31 + return MMT_SEC_MSG_DATA_TYPE_NUMERIC;
     32 + 
     33 + case MMT_DATA_MAC_ADDR:
     34 + case MMT_STRING_DATA:
     35 + case MMT_STRING_LONG_DATA:
     36 + case MMT_BINARY_VAR_DATA:
     37 + case MMT_BINARY_DATA:
     38 + case MMT_HEADER_LINE:
     39 + case MMT_DATA_IP_ADDR:
     40 + case MMT_DATA_IP6_ADDR:
     41 + case MMT_STRING_DATA_POINTER:
     42 + return MMT_SEC_MSG_DATA_TYPE_STRING;
     43 + 
     44 + case MMT_DATA_POINTER:
     45 + case MMT_DATA_PATH:
     46 + case MMT_DATA_TIMEVAL:
     47 + case MMT_DATA_BUFFER:
     48 + case MMT_DATA_POINT:
     49 + case MMT_DATA_PORT_RANGE:
     50 + case MMT_DATA_DATE:
     51 + case MMT_DATA_TIMEARG:
     52 + case MMT_DATA_STRING_INDEX:
     53 + case MMT_DATA_IP_NET:
     54 + case MMT_DATA_LAYERID:
     55 + case MMT_DATA_FILTER_STATE:
     56 + case MMT_DATA_PARENT:
     57 + case MMT_STATS:
     58 + return MMT_SEC_MSG_DATA_TYPE_BINARY;
     59 + default:
     60 + return UNKNOWN;
     61 + }
     62 + return MMT_SEC_MSG_DATA_TYPE_STRING;
     63 +}
     64 + 
     65 +size_t str_trim( uint8_t *string, size_t size ){
     66 + return size;
     67 +}
     68 +/**
     69 + * public API
     70 + */
     71 +size_t get_variables_inside_expression( const expression_t *expr ){
     72 + size_t size = 0;
     73 + return size;
     74 +}
     75 + 
     76 +/** Public API */
     77 +constant_t *expr_create_a_constant( enum data_type type, size_t data_size, void *data ){
     78 + constant_t *cont = mmt_mem_alloc( sizeof( constant_t ));
     79 + cont->data_type = type;
     80 + cont->data_size = data_size;
     81 + cont->data = data;
     82 + return cont;
     83 +}
     84 + 
     85 +static inline void _to_lower( char *str){
     86 + char *p;
     87 + //change name to lower case
     88 + for( p = str; *p != '\0'; p ++)
     89 + *p = tolower( *p );
     90 +}
     91 + 
     92 +/** Public API */
     93 +variable_t *expr_create_a_variable( char *proto, char *attr, uint16_t ref_index ){
     94 + variable_t *var = mmt_mem_alloc( sizeof( variable_t ));
     95 + //_to_lower( proto );
     96 + //_to_lower( attr );
     97 + var->proto = proto;
     98 + var->att = attr;
     99 + var->ref_index = ref_index;
     100 + var->proto_id = get_protocol_id_by_name( var->proto );
     101 + ASSERT( var->proto_id != ((uint32_t)-1), "Error: Unknown protocol \"%s\".", proto );
     102 + var->att_id = get_attribute_id_by_protocol_id_and_attribute_name( var->proto_id, var->att );
     103 + ASSERT( var->att_id != ((uint32_t)-1), "Error: Unknown attribute \"%s\" of protocol \"%s\".", attr, proto );
     104 + var->dpi_type = get_attribute_data_type( var->proto_id, var->att_id );
     105 + var->data_type = convert_data_type( var->dpi_type );
     106 + 
     107 + ASSERT( var->data_type != UNKNOWN, "Error 2: Data type for %s.%s has not implemented yet.", proto, attr);
     108 + 
     109 + return var;
     110 +}
     111 + 
     112 +/** Public API */
     113 +operation_t *expr_create_an_operation( char *name, enum operator operator ){
     114 + operation_t *opt = mmt_mem_alloc( sizeof( operation_t ));
     115 + opt->name = name;
     116 + opt->operator = operator;
     117 + opt->params_list = NULL;
     118 + opt->data_type = UNKNOWN;
     119 + opt->params_size = 0;
     120 + return opt;
     121 +}
     122 + 
     123 +/** Public API */
     124 +expression_t *expr_create_an_expression( enum expression type, void *data ){
     125 + expression_t *expr = mmt_mem_alloc( sizeof( expression_t));
     126 + expr->type = type;
     127 + expr->father = NULL;
     128 + switch( type ){
     129 + case VARIABLE:
     130 + expr->variable = data;
     131 + break;
     132 + case CONSTANT:
     133 + expr->constant = data;
     134 + break;
     135 + case OPERATION:
     136 + expr->operation = data;
     137 + break;
     138 + }
     139 + return expr;
     140 +}
     141 + 
     142 +/** Public API */
     143 +void expr_free_a_constant( constant_t *x, bool free_data){
     144 + if( free_data == YES )
     145 + mmt_free_and_assign_to_null( x->data );
     146 + mmt_free_and_assign_to_null( x );
     147 +}
     148 + 
     149 +/** Public API */
     150 +void expr_free_a_variable( variable_t *x, bool free_data){
     151 + if( free_data == YES ){
     152 + mmt_free_and_assign_to_null( x->proto );
     153 + mmt_free_and_assign_to_null( x->att );
     154 + }
     155 + mmt_free_and_assign_to_null( x );
     156 +}
     157 + 
     158 +/** Public API */
     159 +void expr_free_an_operation( operation_t *x, bool free_data){
     160 + link_node_t *ptr, *q;
     161 + //free data and parameters of this operation
     162 + if( free_data == YES){
     163 + mmt_free_and_assign_to_null( x->name );
     164 + 
     165 + ptr = x->params_list;
     166 + while( ptr != NULL ){
     167 + q = ptr;
     168 + ptr = ptr->next;
     169 + 
     170 + //free data of a node of linked-list
     171 + expr_free_an_expression( (expression_t *) q->data, free_data );
     172 + q->data = NULL;
     173 + //free a node of linked-list
     174 + mmt_free_and_assign_to_null( q );
     175 + q = NULL;
     176 + }
     177 + }
     178 + mmt_free_and_assign_to_null( x );
     179 +}
     180 + 
     181 +/** Public API */
     182 +void expr_free_an_expression( expression_t *expr, bool free_data){
     183 + switch( expr->type ){
     184 + case VARIABLE:
     185 + expr_free_a_variable( expr->variable, free_data);
     186 + break;
     187 + case CONSTANT:
     188 + expr_free_a_constant( expr->constant, free_data );
     189 + break;
     190 + case OPERATION:
     191 + expr_free_an_operation( expr->operation, free_data );
     192 + break;
     193 + }
     194 + mmt_free_and_assign_to_null( expr );
     195 +}
     196 + 
     197 +/**
     198 + * Get index of the first character in string such that it is not a space
     199 + * - Return:
     200 + * + 0 if there is no space
     201 + * + str_size if the string contains only space
     202 + * + index where the character is not a space
     203 + */
     204 +static inline size_t _jump_space( const char *string, size_t str_size ){
     205 + size_t i=0;
     206 + if( (string == NULL) || (str_size == 0) )
     207 + return 0;
     208 + //a pointer created by mmt_malloc always have \0 at the end
     209 + for( i=0; i<str_size; i++ )
     210 + if( ! isspace( string[i] ) )
     211 + return i;
     212 + return 0;
     213 +}
     214 + 
     215 + 
     216 +size_t _parse_a_proto_name( char **name, const char *string, size_t str_size, bool is_allow_number_prefix ){
     217 + size_t i = 0, index = 0;
     218 + const char *temp;
     219 + char *p;
     220 + *name = NULL;
     221 + if( string == NULL || str_size == 0 )
     222 + return 0;
     223 + 
     224 + index = _jump_space( string, str_size );
     225 + 
     226 + //all of string are spaces
     227 + if( index == str_size ) return index;
     228 + 
     229 + temp = string + index;
     230 + 
     231 + //whether allow a name start by: number/alphabet/_
     232 + if( !is_allow_number_prefix )
     233 + //first character must be an alphabet
     234 + if( ! (isalpha(*temp ) || *temp == '_') )
     235 + return index;
     236 + 
     237 + //the next characters can be alphabet or digit
     238 + while( (isalnum( *(temp+i) ) || *(temp +i) == '_' ) && index < str_size ){
     239 + i++;
     240 + index ++;
     241 + }
     242 + 
     243 + //duplicate the name
     244 + *name = mmt_mem_dup( temp, i );
     245 + 
     246 + return index;
     247 +}
     248 + 
     249 + 
     250 +size_t _parse_a_name( char **name, const char *string, size_t str_size ){
     251 + return _parse_a_proto_name( name, string, str_size, false );
     252 +}
     253 +/**
     254 + * Parse a string that is put inside by ' ' or " "
     255 + */
     256 +size_t _parse_a_string( char **name, const char *string, size_t str_size ){
     257 + size_t i = 0, index = 0;
     258 + const char *temp;
     259 + *name = NULL;
     260 + 
     261 + if( string == NULL || str_size == 0 )
     262 + return 0;
     263 + 
     264 + index = _jump_space( string, str_size );
     265 + 
     266 + //all of string are spaces
     267 + if( index == str_size ) return index;
     268 + 
     269 + temp = string + index;
     270 + 
     271 + //first character must be the open bracket: " or '
     272 + if( *temp != '"' && *temp != '\'')
     273 + return index;
     274 + 
     275 + i = 1;
     276 + index ++;
     277 + //find a close bracket
     278 + while( *(temp+i) != *temp && index < str_size ){
     279 + i++;
     280 + index ++;
     281 + }
     282 + 
     283 + //must be found the closer
     284 + ASSERT( index < str_size, "Error 3.a: String is in incorrect format: %s", temp );
     285 + 
     286 + //duplicate the name
     287 + if( i-1 == 0)
     288 + *name = mmt_mem_dup( "", 1 );
     289 + else
     290 + *name = mmt_mem_dup( temp + 1, i - 1 );
     291 + 
     292 + //jump over the closer
     293 + return index + 1;
     294 +}
     295 + 
     296 +/**
     297 + * Parse a number: integer or float
     298 + */
     299 +size_t _parse_a_number( double **num, const char *string, size_t str_size ){
     300 + size_t i = 0, index = 0;
     301 + bool has_dot = NO;
     302 + const char *temp;
     303 + char *str;
     304 + *num = NULL;
     305 + if( string == NULL || str_size == 0 )
     306 + return 0;
     307 + 
     308 + index = _jump_space( string, str_size );
     309 + 
     310 + //all of string are spaces
     311 + if( index == str_size ) return index;
     312 + 
     313 + temp = string + index;
     314 + //first character must be an number
     315 + if( ! isdigit(*temp ))
     316 + return index;
     317 + 
     318 + //the next characters can be a dot or a digit
     319 + while( (isdigit(*(temp+i)) || *(temp+i) == '.' ) && index < str_size ){
     320 + if( *(temp+i) == '.' ){
     321 + //already saw a dot then we break at the second dot
     322 + if( has_dot == YES )
     323 + break;
     324 + else
     325 + has_dot = YES;
     326 + }
     327 + 
     328 + i++;
     329 + index ++;
     330 + }
     331 + 
     332 + //duplicate the name
     333 + str = mmt_mem_dup( temp, i );
     334 + *num = mmt_mem_alloc( sizeof( double ));
     335 + **num = atof( str );
     336 + 
     337 + mmt_mem_free( str );
     338 + 
     339 + return index;
     340 +}
     341 + 
     342 +/**
     343 + * get number of digits of an integer
     344 + */
     345 +size_t _num_digits( int n ){
     346 + if (n < 0) return _num_digits ((n == INT_MIN) ? INT_MAX : -n);
     347 + if (n < 10) return 1;
     348 + return 1 + _num_digits (n / 10);
     349 +}
     350 + 
     351 +/**
     352 + * A constant is either a number or a string
     353 + */
     354 +size_t _parse_constant( constant_t **expr, const char *string, size_t str_size ){
     355 + size_t index;
     356 + char *name = NULL;
     357 + double *number = NULL;
     358 + *expr = NULL;
     359 + if( string == NULL || str_size == 0 )
     360 + return 0;
     361 + 
     362 + index = _parse_a_number( &number, string, str_size );
     363 + //found a number
     364 + if( number != NULL ){
     365 + *expr = expr_create_a_constant(MMT_SEC_MSG_DATA_TYPE_NUMERIC, sizeof( double), number);
     366 + return index;
     367 + }
     368 + 
     369 + //not found any number => find a string
     370 + index = _parse_a_string( &name, string, str_size );
     371 + if( name != NULL ){
     372 + *expr = expr_create_a_constant(MMT_SEC_MSG_DATA_TYPE_STRING, mmt_mem_size( name ), name);
     373 + return index;
     374 + }
     375 + return index;
     376 +}
     377 + 
     378 +/**
     379 + * Parse a variable that is in format: proto.att[.index], e.g., TCP.SRC or TCP.SRC.1
     380 + */
     381 +size_t _parse_variable( variable_t **expr, const char *string, size_t str_size ){
     382 + size_t index, old_index;
     383 + bool is_start_by_number;
     384 + char *str_1 = NULL, *str_2 = NULL;
     385 + uint16_t ref_index = UNKNOWN_REF_INDEX;
     386 + char const *temp;
     387 + double *num = NULL;
     388 + 
     389 + *expr = NULL;
     390 + 
     391 + index = _jump_space( string, str_size );
     392 + if( (string == NULL) || (str_size == 0) || index == str_size )
     393 + return index;
     394 + 
     395 + temp = string + index;
     396 + 
     397 + //mark: if started by a number => must be PROTO.ATT
     398 + is_start_by_number = isdigit( *temp );
     399 + old_index = index;
     400 + 
     401 + index = _parse_a_proto_name( &str_1, temp, str_size, true );
     402 + 
     403 + //has proto?
     404 + if( str_1 != NULL ){
     405 + //must have a dot
     406 + if( string[ index ] == '.' ){
     407 + index ++; //jump over this dot
     408 + temp = string + index ;
     409 + index += _parse_a_proto_name( &str_2, temp, str_size - index, true );
     410 + //has att ?
     411 + if( str_2 != NULL ){
     412 + //has index ?
     413 + if( string[ index ] == '.' ){
     414 + index ++; //jump over the second dot
     415 + temp = string + index; //2 dots
     416 + index += _parse_a_number( &num, temp, str_size - index );
     417 + ref_index = (uint16_t ) (*num);
     418 + mmt_free_and_assign_to_null( num );
     419 + }
     420 + 
     421 + *expr = expr_create_a_variable( str_1, str_2, ref_index );
     422 + return index;
     423 + }
     424 + else
     425 + mmt_free_and_assign_to_null( str_1 );
     426 + } else {
     427 + //this is not a proto.att
     428 + //=> do not allow to start by a number
     429 + if( is_start_by_number ){
     430 + mmt_free_and_assign_to_null( str_1 );
     431 + return old_index;
     432 + }
     433 + }
     434 + }
     435 + 
     436 + return index;
     437 +}
     438 + 
     439 +static inline char _get_the_next_char( const char *string ){
     440 + while( isspace( *string )) string ++;
     441 + return *string;
     442 +}
     443 + 
     444 +static inline bool _parse_a_boolean_expression( bool is_first_time, expression_t *expr, const char *string ){
     445 + //parse expression and create sub-tree, expr->operator = top operator
     446 + //((ARP.OPCODE == 2)&&(ARP.SRC_PROTO == ARP.SRC_PROTO.1))
     447 + //OR, AND, NEQ, EQ, GT, GTE, LT, LTE, THEN, COMPUTE, XC, XCE, XD, XDE, XE, ADD, SUB, MUL, DIV
     448 + size_t index;
     449 + 
     450 + const char *temp = string;
     451 + const char *temp2 = string;
     452 + double *new_number = NULL;
     453 + expression_t *new_expr;
     454 + operation_t *new_op;
     455 + variable_t *new_var;
     456 + char *new_string;
     457 + 
     458 + //jump
     459 + while ( isspace(*temp))temp++;
     460 + 
     461 + if (*temp == '(') {
     462 + temp2 = temp + 1;
     463 + if ( is_first_time == YES) {
     464 + _parse_a_boolean_expression(NO, expr, temp2);
     465 + } else {
     466 + //create new_expr
     467 + //we have not known yet the operator of new_op
     468 + //it will be determined after
     469 + new_op = expr_create_an_operation(NULL, UNKNOWN );
     470 + new_expr = expr_create_an_expression( OPERATION, new_op );
     471 + new_expr->father = expr;
     472 + //append new_expr to expr->params_list
     473 + expr->operation->params_list = append_node_to_link_list( expr->operation->params_list, new_expr );
     474 + expr->operation->params_size ++;
     475 + 
     476 + _parse_a_boolean_expression(NO, new_expr, temp2);
     477 + }
     478 + } else if (*temp == ')') {
     479 + ASSERT( expr->father != NULL || _get_the_next_char(temp + 1) == '\0', "Error 37d: Unexpected: %s", temp + 1 );
     480 + _parse_a_boolean_expression(NO, expr->father, temp + 1);
     481 + } else if (*temp == '\0') {
     482 + //do nothing
     483 + } else if (*temp == '\'') {
     484 + //a 'string'
     485 + index = _parse_a_string( &new_string, temp, MAX_STR_SIZE );
     486 + 
     487 + new_expr = expr_create_an_expression( CONSTANT,
     488 + expr_create_a_constant(MMT_SEC_MSG_DATA_TYPE_STRING, mmt_mem_size( new_string ), new_string) );
     489 + new_expr->father = expr;
     490 + 
     491 + //append new_expr to expr->params_list
     492 + expr->operation->params_list = append_node_to_link_list( expr->operation->params_list, new_expr );
     493 + expr->operation->params_size ++;
     494 + _parse_a_boolean_expression(NO, expr, temp + index);
     495 + } else if (isdigit(*temp) || isalpha(*temp) || *temp == '_') {
     496 + 
     497 + //a variable: PROTO.FIELD.EVENT
     498 + index = _parse_variable( &new_var, temp, MAX_STR_SIZE );
     499 + 
     500 + //not a variable
     501 + //=> give one more chance to check constant: (1) a number or (2) an id (true/false)
     502 + if( new_var == NULL ){
     503 + //(1) check number
     504 + if( isdigit( *temp )){
     505 + //a number 1.0
     506 + index = _parse_a_number( &new_number, temp, MAX_STR_SIZE );
     507 + //found a number
     508 + //create a new expression
     509 + new_expr = expr_create_an_expression( CONSTANT, expr_create_a_constant(MMT_SEC_MSG_DATA_TYPE_NUMERIC, sizeof( double), new_number) );
     510 + new_expr->father = expr;
     511 + //append new_expr to expr->params_list
     512 + expr->operation->params_list = append_node_to_link_list( expr->operation->params_list, new_expr );
     513 + expr->operation->params_size ++;
     514 + } else {
     515 + //(2) check id
     516 + index = _parse_a_name( &new_string, temp, MAX_STR_SIZE );
     517 + new_number = mmt_mem_alloc( sizeof( double ) );
     518 + if( strcmp( new_string, "true") == 0 ){
     519 + *new_number = true;
     520 + }else if( strcmp( new_string, "false") == 0 ){
     521 + *new_number = false;
     522 + }else {
     523 + mmt_mem_free( new_number );
     524 + ABORT("Error 37c: Illegal name:\"%s\".\nExpected either \"true\", \"false\", or proto.att or proto.att.ref", temp );
     525 + }
     526 + new_expr = expr_create_an_expression( CONSTANT, expr_create_a_constant(MMT_SEC_MSG_DATA_TYPE_NUMERIC, sizeof( double), new_number) );
     527 + new_expr->father = expr;
     528 + //append new_expr to expr->params_list
     529 + expr->operation->params_list = append_node_to_link_list( expr->operation->params_list, new_expr );
     530 + expr->operation->params_size ++;
     531 + }
     532 + }else{
     533 + new_expr = expr_create_an_expression( VARIABLE, new_var );
     534 + new_expr->father = expr;
     535 + //append new_expr to expr->params_list
     536 + expr->operation->params_list = append_node_to_link_list( expr->operation->params_list, new_expr );
     537 + expr->operation->params_size ++;
     538 + }
     539 + 
     540 + _parse_a_boolean_expression(NO, expr, temp + index);
     541 + 
     542 + } else if (*temp == '#') {
     543 + //an embedded function: #func(param_1, param_2)
     544 + temp ++;
     545 + index = _parse_a_name( &new_string, temp, MAX_STR_SIZE );
     546 + 
     547 + new_op = expr_create_an_operation( new_string, FUNCTION );
     548 + new_expr = expr_create_an_expression( OPERATION, new_op );
     549 + new_expr->father = expr;
     550 + 
     551 + //append new_expr to expr->params_list
     552 + expr->operation->params_list = append_node_to_link_list( expr->operation->params_list, new_expr );
     553 + expr->operation->params_size ++;
     554 + //parse the parameters of this function
     555 + _parse_a_boolean_expression(YES, new_expr, temp + index );
     556 + } else if (isdigit(*temp)) {
     557 + 
     558 + } else if (*temp == '&' && *(temp + 1) == '&') {
     559 + // &&
     560 + //must no change operator: either unknown for the first element in the list or the same
     561 + //=> to explain: a && b || c
     562 + // need ((a && b) || c)
     563 + ASSERT( (expr->type == OPERATION && (expr->operation->operator == AND || expr->operation->operator == UNKNOWN)),
     564 + "Error 39a: Unexpect \"%s\"\n", temp );
     565 + temp2 = temp + 2;
     566 + expr->type = OPERATION;
     567 + expr->operation->operator = AND;
     568 + expr->operation->name = mmt_mem_dup( "&&", 2);
     569 + _parse_a_boolean_expression(NO, expr, temp2);
     570 + } else if (*temp == '|' && *(temp + 1) == '|') {
     571 + // ||
     572 + ASSERT( (expr->type == OPERATION && (expr->operation->operator == OR || expr->operation->operator == UNKNOWN)),
     573 + "Error 39a: Unexpect \"%s\"\n", temp );
     574 + temp2 = temp + 2;
     575 + expr->type = OPERATION;
     576 + expr->operation->operator = OR;
     577 + expr->operation->name = mmt_mem_dup( "||", 2);
     578 + _parse_a_boolean_expression(NO, expr, temp2);
     579 + } else if (*temp == '=' && *(temp + 1) == '=') {
     580 + // ==
     581 + ASSERT( (expr->type == OPERATION && expr->operation->operator == UNKNOWN ),
     582 + "Error 39a: Unexpect \"%s\"\n", temp );
     583 + temp2 = temp + 2;
     584 + // set value
     585 + expr->type = OPERATION;
     586 + expr->operation->operator = EQ;
     587 + expr->operation->name = mmt_mem_dup( "==", 2);
     588 + //continue
     589 + _parse_a_boolean_expression(NO, expr, temp2);
     590 + } else if (*temp == '!' && *(temp + 1) == '=') {
     591 + // !=
     592 + ASSERT( (expr->type == OPERATION && expr->operation->operator == UNKNOWN ),
     593 + "Error 39a: Unexpect \"%s\"\n", temp );
     594 + temp2 = temp + 2;
     595 + expr->type = OPERATION;
     596 + expr->operation->operator = NEQ;
     597 + expr->operation->name = mmt_mem_dup( "!=", 2);
     598 + _parse_a_boolean_expression(NO, expr, temp2);
     599 + } else if (*temp == '>' && *(temp + 1) != '=') {
     600 + // >
     601 + ASSERT( (expr->type == OPERATION && expr->operation->operator == UNKNOWN ),
     602 + "Error 39a: Unexpect \"%s\"\n", temp );
     603 + temp2 = temp + 1;
     604 + expr->type = OPERATION;
     605 + expr->operation->operator = GT;
     606 + expr->operation->name = mmt_mem_dup( ">", 1);
     607 + _parse_a_boolean_expression(NO, expr, temp2);
     608 + } else if (*temp == '>' && *(temp + 1) == '=') {
     609 + // >=
     610 + ASSERT( (expr->type == OPERATION && expr->operation->operator == UNKNOWN ),
     611 + "Error 39a: Unexpect \"%s\"\n", temp );
     612 + temp2 = temp + 2;
     613 + expr->type = OPERATION;
     614 + expr->operation->operator = GTE;
     615 + expr->operation->name = mmt_mem_dup( ">=", 2);
     616 + _parse_a_boolean_expression(NO, expr, temp2);
     617 + } else if (*temp == '<' && *(temp + 1) != '=') {
     618 + // <
     619 + ASSERT( (expr->type == OPERATION && expr->operation->operator == UNKNOWN ),
     620 + "Error 39a: Unexpect \"%s\"\n", temp );
     621 + temp2 = temp + 1;
     622 + expr->type = OPERATION;
     623 + expr->operation->operator = LT;
     624 + expr->operation->name = mmt_mem_dup( "<", 1);
     625 + _parse_a_boolean_expression(NO, expr, temp2);
     626 + } else if (*temp == '<' && *(temp + 1) == '=') {
     627 + // <=
     628 + ASSERT( (expr->type == OPERATION && expr->operation->operator == UNKNOWN ),
     629 + "Error 39a: Unexpect \"%s\"\n", temp );
     630 + temp2 = temp + 2;
     631 + expr->type = OPERATION;
     632 + expr->operation->operator = LTE;
     633 + expr->operation->name = mmt_mem_dup( "<=", 2);
     634 + _parse_a_boolean_expression(NO, expr, temp2);
     635 + } else if (*temp == '+') {
     636 + // +
     637 + ASSERT( (expr->type == OPERATION && (expr->operation->operator == ADD || expr->operation->operator == UNKNOWN) ),
     638 + "Error 39a: Unexpect \"%s\"\n", temp );
     639 + temp2 = temp + 1;
     640 + expr->type = OPERATION;
     641 + expr->operation->operator = ADD;
     642 + expr->operation->name = mmt_mem_dup( "+", 1);
     643 + _parse_a_boolean_expression(NO, expr, temp2);
     644 + } else if (*temp == '-') {
     645 + // -
     646 + ASSERT( (expr->type == OPERATION && (expr->operation->operator == SUB || expr->operation->operator == UNKNOWN)),
     647 + "Error 39a: Unexpect \"%s\"\n", temp );
     648 + temp2 = temp + 1;
     649 + expr->type = OPERATION;
     650 + expr->operation->operator = SUB;
     651 + expr->operation->name = mmt_mem_dup( "-", 1);
     652 + _parse_a_boolean_expression(NO, expr, temp2);
     653 + } else if (*temp == '*') {
     654 + // *
     655 + ASSERT( (expr->type == OPERATION && (expr->operation->operator == MUL || expr->operation->operator == UNKNOWN)),
     656 + "Error 39a: Unexpect \"%s\"\n", temp );
     657 + temp2 = temp + 1;
     658 + expr->type = OPERATION;
     659 + expr->operation->operator = MUL;
     660 + expr->operation->name = mmt_mem_dup( "*", 1);
     661 + _parse_a_boolean_expression(NO, expr, temp2);
     662 + } else if (*temp == '/') {
     663 + // '/'
     664 + ASSERT( (expr->type == OPERATION && (expr->operation->operator == DIV || expr->operation->operator == UNKNOWN)),
     665 + "Error 39a: Unexpect \"%s\"\n", temp );
     666 + temp2 = temp + 1;
     667 + expr->type = OPERATION;
     668 + expr->operation->operator = DIV;
     669 + expr->operation->name = mmt_mem_dup( "/", 1);
     670 + _parse_a_boolean_expression(NO, expr, temp2);
     671 + } else if( *temp == ',' && expr->type == OPERATION && expr->operation->operator == FUNCTION ){
     672 + temp ++;
     673 + while( isspace( *temp)) temp ++;
     674 + //waiting for another parameter
     675 + ASSERT( *temp != ')', "Error 37b: Illegal delimiter of function %s: ,%c", expr->operation->name, *temp );
     676 + //*tmp != ')' ==> continue parsing the next parameter of function
     677 + _parse_a_boolean_expression(NO, expr, temp);
     678 + }else{
     679 + (void)fprintf(stderr, "Error 37: Illegal character found in boolean expression: %c%c.\n", *temp, *(temp + 1));
     680 + exit(-1);
     681 + }
     682 + return 0;
     683 +}
     684 + 
     685 +/**
     686 + * public API
     687 + */
     688 +int parse_expression( expression_t **expr, const char *string, size_t str_size ){
     689 + expression_t *ret = NULL;
     690 + operation_t *new_op;
     691 + *expr = NULL;
     692 + if( string == NULL )
     693 + return 0;
     694 + 
     695 + //we have not known yet the operator of new_op
     696 + //it will be determined after
     697 + new_op = expr_create_an_operation(NULL, UNKNOWN );
     698 + ret = expr_create_an_expression( OPERATION, new_op );
     699 + *expr = ret;
     700 + return _parse_a_boolean_expression( YES, ret, string );
     701 +}
     702 + 
     703 +/**
     704 + * public API
     705 + */
     706 +size_t expr_stringify_constant( char **string, const constant_t *expr){
     707 + char buff[ MAX_STR_SIZE ];
     708 + int size;
     709 + double d;
     710 + 
     711 + if( expr == NULL ){
     712 + string = NULL;
     713 + return 0;
     714 + }
     715 + 
     716 + if( expr->data_type == MMT_SEC_MSG_DATA_TYPE_NUMERIC ){
     717 + d = *(double *)expr->data;
     718 + //integer
     719 + size = snprintf(buff, sizeof(buff), "%.2f", d);
     720 + size --; //jump over last '\0';
     721 + //remove zero at the end, e.g., 10.00 ==> 10
     722 + while( size > 1 && buff[ size - 1 ] == '0' )
     723 + size --;
     724 + if( buff[ size - 1 ] == '.' ) size --;
     725 + }else if( expr->data_type == MMT_SEC_MSG_DATA_TYPE_STRING ){
     726 + size = snprintf( buff, sizeof(buff), "\"%s\"", (char *)expr->data );
     727 + }else{
     728 + size = snprintf( buff, sizeof(buff), "\"__na__\"");
     729 + }
     730 + 
     731 + *string = mmt_mem_dup( buff, size );
     732 + 
     733 + return size;
     734 +}
     735 + 
     736 +/**
     737 + * public API
     738 + */
     739 +size_t expr_stringify_variable( char **string, const variable_t *var){
     740 + size_t size = 0;
     741 + char buff[ 250 ];
     742 + *string = NULL;
     743 + 
     744 + if( var == NULL ){
     745 + return 0;
     746 + }
     747 + 
     748 + if( var->ref_index != UNKNOWN_REF_INDEX ){
     749 + size = snprintf(buff, sizeof( buff ), "_%s_%s_%d", var->proto, var->att, var->ref_index);
     750 + }else{
     751 + size = snprintf(buff, sizeof( buff ), "_%s_%s", var->proto, var->att );
     752 + }
     753 + *string = mmt_mem_dup( buff, size );
     754 + return size;
     755 +}
     756 + 
     757 +static inline bool _is_comparison_operator( int op ){
     758 + return op == NEQ || op == EQ || op == GT || op == GTE || op == LT || op == LTE;
     759 +}
     760 + 
     761 +static inline bool _is_string_variable( const operation_t *opt ){
     762 + link_node_t *ptr;
     763 + expression_t *expr;
     764 + ptr = opt->params_list;
     765 + while( ptr != NULL ){
     766 + expr = (expression_t *) ptr->data;
     767 + if( expr->type != VARIABLE || expr->variable->data_type != MMT_SEC_MSG_DATA_TYPE_STRING )
     768 + return NO;
     769 + ptr = ptr->next;
     770 + }
     771 + return YES;
     772 +}
     773 + 
     774 +static inline bool _is_string_param( const operation_t *opt ){
     775 + link_node_t *ptr;
     776 + expression_t *expr;
     777 + ptr = opt->params_list;
     778 + while( ptr != NULL ){
     779 + expr = (expression_t *) ptr->data;
     780 + if( expr->type == VARIABLE && expr->variable->data_type != MMT_SEC_MSG_DATA_TYPE_STRING )
     781 + return NO;
     782 + if( expr->type == CONSTANT && expr->variable->data_type != MMT_SEC_MSG_DATA_TYPE_STRING )
     783 + return NO;
     784 + ptr = ptr->next;
     785 + }
     786 + return YES;
     787 +}
     788 +/**
     789 + * Public API
     790 + */
     791 +size_t expr_stringify_operation( char **string, const operation_t *opt ){
     792 + char *tmp = NULL;
     793 + const char *delim;
     794 + link_node_t *node;
     795 + size_t index = 0;
     796 + expression_t *expr;
     797 + 
     798 + char str[ MAX_STR_SIZE ];
     799 + //change comparison of string to function: "a" == "b" ==> 0 == strcmp("a", "b")
     800 + if( _is_comparison_operator( opt->operator ) && _is_string_param ( opt ) ){
     801 + //delimiter
     802 + delim = ",";
     803 + index += snprintf( &str[ index ], MAX_STR_SIZE, "0 %s %s(", opt->name,
     804 + _is_string_variable( opt) ? "mmt_mem_cmp" : "strcmp" );
     805 + }else if( opt->operator == FUNCTION ){
     806 + delim = ",";
     807 + index += snprintf( &str[ index ], MAX_STR_SIZE, "%s(", opt->name );
     808 + }else{
     809 + delim = opt->name;
     810 + index += snprintf( &str[ index ], MAX_STR_SIZE, "(" );
     811 + }
     812 + 
     813 + //parameters
     814 + node = opt->params_list;
     815 + //no parameter
     816 + if( node == NULL ){
     817 + index += snprintf( &str[ index ], MAX_STR_SIZE - index, ")");
     818 + }
     819 + else while( node != NULL ){
     820 + expr = (expression_t *) node->data;
     821 + (void) expr_stringify_expression( &tmp, expr );
     822 + //the last parameter ==> no need delimiter but a close-bracket
     823 + if( node->next == NULL ){
     824 + index += snprintf( &str[ index ], MAX_STR_SIZE - index, "%s)", tmp);
     825 + }else{
     826 + index += snprintf( &str[ index ], MAX_STR_SIZE - index, "%s %s ", tmp , delim);
     827 + }
     828 + //tmp was created in expr_stringify_expression( &tmp ...
     829 + mmt_mem_free( tmp );
     830 + node = node->next;
     831 + };
     832 + 
     833 + //clone string
     834 + *string = mmt_mem_dup( str, index );
     835 + 
     836 + return index;
     837 +}
     838 +/**
     839 + * public API
     840 + */
     841 +size_t expr_stringify_expression( char **string, const expression_t *expr){
     842 + //nothing to do
     843 + if( expr == NULL ){
     844 + string = NULL;
     845 + return 0;
     846 + }
     847 + switch( expr->type ){
     848 + case CONSTANT:
     849 + return expr_stringify_constant( string, expr->constant );
     850 + case VARIABLE:
     851 + return expr_stringify_variable( string, expr->variable );
     852 + case OPERATION:
     853 + return expr_stringify_operation( string, expr->operation );
     854 + default:
     855 + DEBUG( "Undefined" );
     856 + return 0;
     857 + }
     858 +}
     859 + 
     860 + 
     861 +size_t _get_unique_variables_of_expression( const expression_t *expr, mmt_map_t *map ){
     862 + size_t var_count = 0;
     863 + void *ptr;
     864 + link_node_t *p;
     865 + 
     866 + if( expr == NULL ) return 0;
     867 + 
     868 + switch( expr->type ){
     869 + case VARIABLE:
     870 + ptr = mmt_map_set_data( map, expr->variable, expr->variable, NO );
     871 + if( ptr == NULL )
     872 + var_count ++;
     873 + break;
     874 + case CONSTANT:
     875 + break;
     876 + case OPERATION:
     877 + p = expr->operation->params_list;
     878 + //get variables in parameters of the operation
     879 + while( p != NULL ){
     880 + var_count += _get_unique_variables_of_expression( (expression_t *) p->data, map );
     881 + p = p->next;
     882 + }
     883 + break;
     884 + }
     885 + return var_count;
     886 +}
     887 + 
     888 +/**
     889 + * Public API
     890 + */
     891 +size_t get_unique_variables_of_expression( const expression_t *expr, mmt_map_t **variables_map, bool has_index ){
     892 + size_t var_count = 0;
     893 + mmt_map_t *map;
     894 + 
     895 + *variables_map = NULL;
     896 + if( expr == NULL ) return 0;
     897 + if( has_index == YES )
     898 + map = mmt_map_init( compare_variable_name_and_index );
     899 + else
     900 + map = mmt_map_init( compare_variable_name );
     901 + 
     902 + var_count = _get_unique_variables_of_expression( expr, map );
     903 + 
     904 + //free the map being allocated
     905 + if( var_count == 0 )
     906 + mmt_map_free( map, NO );
     907 + else
     908 + *variables_map = map;
     909 + 
     910 + return var_count;
     911 +}
     912 +/**
     913 + * public API
     914 + */
     915 +constant_t *evaluate_expression( const expression_t *expr, const constant_t **constants, size_t const_size ){
     916 + constant_t *ret = (constant_t *) mmt_mem_alloc( sizeof( constant_t ));
     917 + return ret;
     918 +}
     919 + 
     920 + 
     921 +void expr_update_data_type( expression_t *expr ){
     922 + 
     923 +}
     924 + 
  • ■ ■ ■ ■ ■ ■
    src/engine/expression.h
     1 +/*
     2 + * bool_expression.h
     3 + *
     4 + * Created on: 21 sept. 2016
     5 + * Created by: Huu Nghia NGUYEN <[email protected]>
     6 + *
     7 + * Boolean expression of an event
     8 + */
     9 + 
     10 +#ifndef SRC_LIB_EXPRESSION_H_
     11 +#define SRC_LIB_EXPRESSION_H_
     12 + 
     13 +#include <stdlib.h>
     14 +#include <stdint.h>
     15 +#include <string.h>
     16 + 
     17 +#include "../lib/mmt_lib.h"
     18 +#include "message_t.h"
     19 + 
     20 +#define UNKNOWN_REF_INDEX ((uint16_t)UNKNOWN)
     21 + 
     22 +/**
     23 + * Constant
     24 + */
     25 +typedef struct{
     26 + enum data_type data_type;
     27 + /**
     28 + * size of the pointer *data
     29 + */
     30 + size_t data_size;
     31 + void *data;
     32 +} constant_t;
     33 + 
     34 +/**
     35 + * Convert from data types from MMT_DPI to #data_type that is
     36 + * either a MMT_SEC_MSG_DATA_TYPE_NUMERIC or a MMT_SEC_MSG_DATA_TYPE_STRING
     37 + */
     38 +int convert_data_type( int mmt_dpi_data_type );
     39 + 
     40 +/**
     41 + * Variable
     42 + */
     43 +typedef struct{
     44 + int data_type;
     45 + int dpi_type;
     46 + //a variable: TCP.SRC or TCP.SRC.1
     47 + char *proto, *att;
     48 + uint32_t proto_id, att_id;//those are generated from their name: tcp => 354
     49 + uint16_t ref_index;
     50 +} variable_t;
     51 + 
     52 + 
     53 +enum operator{
     54 + //boolean operator: or, and
     55 + OR, AND,
     56 + NOT,
     57 + //comparison operators: not equal, equal, ...
     58 + NEQ, EQ, GT, GTE, LT, LTE,
     59 + //numeric operators: + - * /
     60 + ADD, SUB, MUL, DIV,
     61 + //a embedded function
     62 + FUNCTION
     63 +};
     64 + 
     65 + 
     66 +/**
     67 + * Expression that is x-ary expression:
     68 + * that can be a function: name( param_1, param_2, ...)
     69 + * or a boolean expression: or( param_1, param_2, ...)
     70 + * or a calculation expression: *( param_1, param_2, ... )
     71 + */
     72 +typedef struct operation_struct{
     73 + //type id of return data
     74 + enum data_type data_type;
     75 + enum operator operator;
     76 + 
     77 + /**
     78 + * Representation of operator in plain text
     79 + * that is either a function name or an operator
     80 + */
     81 + char *name;
     82 + /**
     83 + * Number of parameters
     84 + */
     85 + size_t params_size;
     86 + /**
     87 + * List of parameters, it data has type expression_t
     88 + */
     89 + link_node_t *params_list;
     90 +}operation_t;
     91 + 
     92 +/**
     93 + * An expression is either a variable, or a constant, or an operation
     94 + */
     95 +typedef struct expression_struct{
     96 + enum expression { VARIABLE, CONSTANT, OPERATION} type;
     97 + 
     98 + union{
     99 + constant_t *constant;
     100 + variable_t *variable;
     101 + operation_t *operation;
     102 + };
     103 + struct expression_struct *father;
     104 +}expression_t;
     105 + 
     106 + 
     107 + 
     108 +/**
     109 + * Get a set of variables (distinguished by "proto" and "att") of an expression.
     110 + * - Input:
     111 + * + expr
     112 + * - Output:
     113 + * + create and assign a map containing unique variables
     114 + * - Return:
     115 + * + number of unique variables
     116 + */
     117 +size_t get_unique_variables_of_expression( const expression_t *expr, mmt_map_t **variables_map, bool has_ref);
     118 + 
     119 +/**
     120 + * Parse a string to get expression
     121 + * - Input
     122 + * + string:
     123 + * + size : size of the string
     124 + * - Output
     125 + * + expr : that is a pointer points to the result
     126 + * - Return
     127 + * + O if success
     128 + * + error code if fail
     129 + * - Note:
     130 + * use mmt_free to free the expr when one does not need it anymore
     131 + */
     132 +int parse_expression( expression_t **expr, const char *string, size_t size );
     133 + 
     134 +/**
     135 + * Convert an expression to a string
     136 + * - Input
     137 + * + expr: the expression to be stringified
     138 + * - Output
     139 + * + string that is a pointer points to the result.
     140 + * - Return
     141 + * + the size of the result string
     142 + * - Note:
     143 + * use mmt_free to free the string when one does not need it anymore
     144 + */
     145 +size_t expr_stringify_constant( char **string, const constant_t *expr);
     146 +size_t expr_stringify_variable( char **string, const variable_t *var);
     147 +size_t expr_stringify_operation( char **string, const operation_t *opt );
     148 +size_t expr_stringify_expression( char **string, const expression_t *expr);
     149 + 
     150 + 
     151 +constant_t *expr_create_a_constant( enum data_type type, size_t data_size, void *data );
     152 +variable_t *expr_create_a_variable( char *proto, char *attr, uint16_t ref_index );
     153 +operation_t *expr_create_an_operation( char *name, enum operator operator );
     154 +expression_t *expr_create_an_expression( enum expression type, void *data );
     155 + 
     156 +void expr_update_data_type( expression_t *expr );
     157 +/**
     158 + * Free a constant
     159 + */
     160 +void expr_free_a_constant( constant_t *, bool free_data);
     161 +void expr_free_a_variable( variable_t *, bool free_data);
     162 +void expr_free_an_operation( operation_t *, bool free_data);
     163 +void expr_free_an_expression( expression_t *, bool free_data);
     164 + 
     165 +constant_t *evaluate_expression( const expression_t *expr, const constant_t **constants, size_t const_size );
     166 + 
     167 + 
     168 +/**
     169 + * Compare 2 variables by its "proto" and "att"
     170 + */
     171 +static inline int compare_variable_name( const void *v1, const void *v2){
     172 + variable_t *x = (variable_t *)v1, *y = (variable_t *)v2;
     173 + int d1, d2;
     174 + ASSERT( v1 != NULL && v2 != NULL, "Error: Variables are NULL" );
     175 + d1 = strcmp( x->proto, y->proto );
     176 + d2 = strcmp( x->att, y->att );
     177 + if( d1 == 0 && d2 == 0 )
     178 + return 0;
     179 + else if( d1 != 0 )
     180 + return d1;
     181 + else
     182 + return d2;
     183 +}
     184 + 
     185 +static inline int compare_variable_name_and_index( const void *v1, const void *v2){
     186 + variable_t *x = (variable_t *)v1, *y = (variable_t *)v2;
     187 + int d1, d2, d3;
     188 + ASSERT( v1 != NULL && v2 != NULL, "Error: Variables are NULL" );
     189 + d1 = strcmp( x->proto, y->proto );
     190 + d2 = strcmp( x->att, y->att );
     191 + d3 = x->ref_index - y->ref_index;
     192 + if( d1 == 0 && d2 == 0 && d3 == 0 )
     193 + return 0;
     194 + else if( d1 != 0 )
     195 + return d1;
     196 + else if( d2 != 0 )
     197 + return d2;
     198 + else
     199 + return d3;
     200 +}
     201 +#endif /* SRC_LIB_EXPRESSION_H_ */
     202 + 
Please wait...
Page is in error, reload to recover