Tutorial: Developing Tranalyzer plugins in C++
Contents
This tutorial gives you an introduction into plugin development for T2 in C++.
Why writing a plugin in C++
- Some people prefer higher level languages
- Some libraries or tools only work with C++
The one magic command for the busy people
The simplest way to create a C++ plugin is to use the t2plugin script with the -c
and --cpp
options (123 is the plugin number):
$ t2plugin -c myPluginName -n 123 --cpp
C++ plugin 'myPluginName' created
$
Differences between a C and a C++ plugin
Writing a Tranalyzer plugin in C++ is fundamentally not different from doing it in C. Therefore, this tutorial only highlights the differences or areas where special attention is required. The reader will be referred to the relevant C tutorials for all aspects which do not differ.
- Include
t2Plugin.hpp
instead oft2Plugin.h
- Prefix all callbacks with
T2_API
- Adapt the files from the build system
Include t2Plugin.hpp
instead of t2Plugin.h
The first major difference between a C and a C++ plugin is the name of the file to include in order to have access to T2 functionality:
#include "t2Plugin.hpp"
Prefix all callbacks with T2_API
When implementing a Tranalyzer callback in your C++ file, make sure to prefix it with T2_API
:
T2_API void initialize() { ... }
T2_API binary_value_t* printHeader() { ... }
T2_API void onFlowGenerated(packet_t *packet, unsigned long flowIndex) { ... }
T2_API void claimLayer2Information(packet_t *packet, unsigned long flowIndex) { ... }
T2_API void claimLayer4Information(packet_t *packet, unsigned long flowIndex) { ... }
T2_API void onFlowTerminate(unsigned long flowIndex) { ... }
T2_API void saveState(FILE *stream) { ... }
T2_API void restoreState(const char *str) { ... }
T2_API void bufferToSink(outputBuffer_t *buffer) { ... }
T2_API void pluginReport(FILE *stream) { ... }
Note that the T2_PLUGIN_INIT(...)
and T2_PLUGIN_INIT_WITH_DEPS(...)
macros already take care of the T2_API
keyword and can be used as for a C plugin.
Build systems
No matter which build system you use (autotools, cmake or meson), make sure to adapt your filenames, e.g.:
t2PSkel.c
–>t2PSkel.cpp
t2PSkel.h
–>t2PSkel.hpp
The changes are suffixed with <----
.
Autotools
Edit src/Makefile.am
as follows:
- Replace all references to
CFLAGS
withCXXFLAGS
.
$ cat src/Makefile.am
lib_LTLIBRARIES = libt2PSkel.la
libt2PSkel_la_SOURCES = \
t2PSkel.cpp <----
# ../../../utils/t2buf.c # if you uncomment this line, do not
# # forget to append a backslash above!
libt2PSkel_la_CXXFLAGS = \ <----
-I$(top_srcdir)/../../utils \
-I$(top_srcdir)/../../tranalyzer2/src
# -I$(top_srcdir)/../tcpFlags/src # tell the compiler where to find header
# # files from dependent plugins
# # !!! if you uncomment this line, do not
# # !!! forget to append a backslash above
if APPLE
libt2PSkel_la_CXXFLAGS += -D_DARWIN_C_SOURCE # OSX specific flags <----
else
libt2PSkel_la_CXXFLAGS += -D_GNU_SOURCE <----
endif
libt2PSkel_la_LDFLAGS = -shrext .so # extension for shared library
# (without this flag, would be '.dylib' on OSX)
CMake:
Edit CMakeLists.txt
as follows:
- Change
LANGUAGES
fromC
toCXX
. - Change
C_STANDARD
toCXX_STANDARD
and replace the 99 with the desired C++ standard, e.g., 11.
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
# If your plugin has only one file named src/pluginName.c,
# then this is the only line you need to change.
set(_plugin_name t2PSkel)
#set(_plugin_number 999)
project(${_plugin_name}
HOMEPAGE_URL "https://tranalyzer.com"
VERSION 0.8.11
LANGUAGES CXX <----
)
# ---------------------------------------------------------------------------- #
# Find libraries #
# ---------------------------------------------------------------------------- #
#find_package(Threads REQUIRED)
#find_package(Threads QUIET)
#find_package(PkgConfig REQUIRED)
#pkg_check_modules(MY_NAME REQUIRED module_name)
# ---------------------------------------------------------------------------- #
# Source files #
# ---------------------------------------------------------------------------- #
add_library(${_plugin_name}
MODULE
src/${_plugin_name}.cpp <----
#../../utils/t2buf.c
)
# ---------------------------------------------------------------------------- #
# C standard to use #
# ---------------------------------------------------------------------------- #
set_target_properties(${_plugin_name}
PROPERTIES
CXX_STANDARD 11 <----
#C_EXTENSIONS ON <---- you can either comment or remove
#C_STANDARD_REQUIRED ON <---- those two lines
)
# ---------------------------------------------------------------------------- #
# Include directories (-I ...) #
# ---------------------------------------------------------------------------- #
target_include_directories(${_plugin_name}
PRIVATE
../../utils
../../tranalyzer2/src
#../tcpFlags/src # tell the compiler where to find header
# files from dependent plugins
#${MY_NAME_INCLUDE_DIRS}
)
# ---------------------------------------------------------------------------- #
# Compile options #
# ---------------------------------------------------------------------------- #
target_compile_options(${_plugin_name}
PRIVATE
-Wall
-Wextra
-Wundef
)
# ---------------------------------------------------------------------------- #
# Compile definitions (-D ...) #
# ---------------------------------------------------------------------------- #
#target_compile_definitions(${_plugin_name}
# PRIVATE
# PLUGIN_NUMBER=${_plugin_number}
#)
if (APPLE)
target_compile_definitions(${_plugin_name}
PRIVATE
_DARWIN_C_SOURCE
)
set_target_properties(${_plugin_name}
PROPERTIES
LINK_FLAGS "-undefined dynamic_lookup"
)
elseif (UNIX)
target_compile_definitions(${_plugin_name}
PRIVATE
_GNU_SOURCE
)
endif()
# ---------------------------------------------------------------------------- #
# Libraries (-l ..., -L) #
# ---------------------------------------------------------------------------- #
#target_link_libraries(${_plugin_name}
# PRIVATE
# m
# ${MY_NAME_LIBRARIES}
# ${MY_NAME_LDFLAGS}
#)
# ---------------------------------------------------------------------------- #
# Installation #
# ---------------------------------------------------------------------------- #
set_target_properties(${_plugin_name}
PROPERTIES
#PREFIX ""
#OUTPUT_NAME "${_plugin_number}_${_plugin_name}"
OUTPUT_NAME "${_plugin_name}"
SUFFIX ".so"
)
Meson
Edit meson.build
as follows:
- Change all language references from
c
tocpp
. - Replace
c_std=gnu99
withcpp_std=11
(or any desired C++ standard).
$ cat meson.build
project('t2PSkel', 'cpp', <----
version: '0.8.11',
license: 'GPLv2+',
default_options: [
'warning_level=2',
'buildtype=release',
'cpp_std=c++11' <----
],
#meson_version: '>= 0.45.0',
)
plugin_name = meson.project_name()
#plugin_number = '999'
cc = meson.get_compiler('cpp') <----
os = host_machine.system()
#perl = find_program('perl')
if os == 'darwin'
add_project_arguments('-D_DARWIN_C_SOURCE', language: 'cpp') <----
elif os == 'linux'
add_project_arguments('-D_GNU_SOURCE', language: 'cpp') <----
#elif os == 'windows'
# message('You are using Windows...')
#else
# warning('OS not recognized...')
endif
libm = cc.find_library('m')
threads_dep = dependency('threads')
zlib_dep = dependency('zlib', version: '>=1.2.8', required: false)
if zlib_dep.found()
message('ZLIB >= 1.2.8 found')
else
warning('ZLIB >= 1.2.8 not found')
endif
deps = [
libm,
threads_dep,
]
inc = include_directories(
join_paths('..', '..', 'utils'),
join_paths('..', '..', 'tranalyzer2', 'src'),
#join_paths('..', 'tcpFlags', 'src'), # tell the compiler where to find header
# files from dependent plugins
)
#subdir('doc')
src = [
join_paths('src', plugin_name + '.cpp'), <----
#join_paths('..', '..', 'utils', 't2buf.c'),
]
#cmd = run_command(perl, '-nle', 'print $1 if /^#define\s+T2PSKEL_VAR1\s+(\d+).*$/', 'src/' + plugin_name + '.hpp') <----
#if cmd.returncode() != 0
# error('Could not determine value of \'T2PSKEL_VAR1\' in \'src/' + plugin_name + '.hpp\'') <----
#endif
shared_module(plugin_name,
sources: src,
dependencies: deps,
include_directories: inc,
#name_prefix: plugin_number + '_',
name_suffix: 'so',
)
Summary
Implementing a Tranalyzer plugin in C++ does not require much overhead. The main difference is the inclusion of t2Plugin.hpp
instead of t2Plugin.h
and the T2_API
keyword before every Tranalyzer callback functions.
The next tutorial will teach you how to develop a plugin in Rust
See Also
- Plugin Programming Cheatsheet
- The basics: your first flow plugin
- Adding plugin end report
- Adding plugin monitoring output
- Adding plugin packet output
- Producing summary files
- geo-whois-labeling
- All about plugin dependencies
- Plugin sinks
- Manipulating flow timeouts
- Alarm mode
- Force mode
- Pcap extraction
- Developing Tranalyzer plugins in Rust