In some scenarios it is helpful to use custom C/C++ functions (e.g., external libraries) from the Zeek policy layer. Builtin Functions (BiFs) can help in such situations, and Zeek’s bifcl tool makes it easy to build the necessary stubs.
Contents
BiFs provide a convenient way of interaction between the C/C++ event engine and the script policy layer by making C/C++ functions easily available to the policy layer and making policy types, consts, and variables available to the C/C++ layer. (The same principles of C/C++ <—> policy interaction also apply for writing new analyzers. )
There is an example putting everything together, that might also give additional insight: example.html
BiFs are in .bif files in Zeek’s source distribution, they are then compiled into C/C++ and Zeek policy code using the custom bifcl tool. The C/C++ is included (by #include) in the approriate C/C++ files and then compiled into the core. I.e., Zeek must be recompiled if BiFs are changed. You can find the generated bifcl output files in the build/src directory.
While every BiF-file in general supports the full BiF language, the way the generated files are included in C/C++ files limits the places consts, events, functions, etc. should be defined. However, you could add a new BiF-file and add the appropriate includes to the C/C++ files.
Zeek variables and constants are represented in the C/C++ layer as pointers to class Val objects. Actually, it is a class hierarchy, with class Val being the ancestor of all derived Val classes. E.g., a count is represented by a Val*, while an enum is represented as an EnumVal. See also Val.h and Val.cc in the Zeek distribution for more information.
Complex, user defined types (enums, records, sets, etc.) are represented in the C/C++ layer as pointers to class BroType. Again, a class hierarchy is used with BroType as the base class. E.g., user defined records are represented instances of class RecordType.
Zeek event handlers are represented as class EventHandlerPtr. The boolean value of an EventHandlerPtr (e.g., in an if) is true if there is at least one event handler defined for this event.
BiF supports namespaces for the policy layer (modules) as well as the C/C++ layer (namespaces). To use a namespace in a BiF file, use the module <MODULENAME> syntax we know from Zeek policy scripts. In addition, you can use fully qualified identifiers, i.e., <MODULENAME>::<IDENTIFIER>.
You can use the special module name GLOBAL to switch back to the global default namespace.
We first describe the BiF syntax. There is also a full example at the end demonstrating (almost all) of the BiF features and how to use them.
By convention (see above), constants that you want to have easily accessible in the C/C++ layer are declared in const.bif. You still need to define these constants in bro.init. Adding them to the BiF files merely makes them easily available in C/C++. If a const is declared in a BiF but not defined, it will result in a runtime error.
Syntax:
const <IDENTIFIER> : <BROTYPE>;
<IDENTIFIER> can include a module/namespace specification and must match a define const (e.g., in bro.init).
<BROTYPE> can be any Zeek type, either basic types (count, interval, etc.) or user defined types (records).
Examples:
const XYZ::the_time_diff : interval; const constdummy: string;
Some Zeek types are converted into basic C/C++ types while others are available as Val*. See bif_type.def in the source directory for a list of these mappings.
Constants are available in the BifConst namespace in C/C++. If a const is part of a Zeek module, it is available in BifConst::<MODULENAME> namespace. For the above examples:
double BifConst::XYZ::the_time_diff StringVal* BifConst::constdummy
By convention user-defined types (enum, record, vector, table, and set) that should be easily accessible in C/C++ are declared, but not defined, in type.bif. You still need to define these types in bro.init. However, there is one exception: enums can be defined in a BiF as well.
Enums are supported in .bif and .bro scripts. An enum in a BiF will become available in the event engine and the policy layer.
Example:
module FOO; enum status %{ s1 = 1, s2 = 2, s3 = 3, %}
Enums are available in C/C++ in the BifTypePtr::Enum namespace as an EnumType*. If the enum is in a Zeek module, the C/C++ namespace will be BifTypePtr::Enum::<MODULENAME>. In addition, you have access to a C/C++ enum:
namespace BifEnum { namespace FOO { enum status { s1 = 1, s2 = 2, s3 = 3, }; }} namespace BifTypePtr { namespace Enum { namespace FOO { extern EnumType * status; }}}
You can declare record, set, table, and vector user-defined Zeek types in a BiF.
Syntax:
type <IDENTIFIER> : <TYPE_CATEGORY> ;
Where <TYPE_CATEGORY> is one of record, set, table, or vector. <IDENTIFIER> is the name of the type you used when defining the type in bro.init:
type category | namespace | C type |
---|---|---|
record | BifTypePtr::Record | RecordType* |
set | BifTypePtr::Set | SetType* |
table | BifTypePtr::Table | TableType* |
vector | BifTypePtr::Vector | VectorType* |
If a type is in a Zeek module, the module name is used as an additional C/C++ namespace, e.g., BifTypePtr::Set::<MODULENAME>.
TODO
TODO
There is an example putting everything together: example.html
© 2014 The Bro Project.