Bro currently supports only a modest group of statements, which we have so far found sufficient. Along with C-style if and return and expression evaluation, other statements are: print a list of expressions to a file (stdout by default); log a list of expressions; add an element to a set; delete an element from a set or a table; and event, which generates a new event.
In particular, the language does not support looping using a for-style construct. We are wary of loops in event handlers because they can lead to arbitrarily large processing delays, which in turn could lead to packet filter drops. We wanted to see whether we could still adequately express security policies in Bro without resorting to loops; if so, then we have some confidence that every event is handled quickly. So far, this experiment has been successful. Looping is still possible via recursion (either functions calling themselves, or event handlers generating their own events), but we have not found a need to resort to it.
Like in C, we can group sets of statements into blocks by enclosing them within {}'s. Function definitions look like:
function endpoint_id(h: addr, p: port): string { if ( p in port_names ) return fmt("%s/%s", h, port_names[p]); else return fmt("%s/%d", h, p); }Event handler definitions look the same except that function is replaced by event and they cannot specify a return type. See Appendix A for an example.
Functions are invoked the usual way, as expressions specified by the function's name followed by its arguments enclosed within parentheses. Events are generated in a similar fashion, except using the keyword event before the handler's name and argument list. Since events do not return values (they can't, since they are processed asynchronously), event generation is a statement in Bro and not an expression.
Bro also allows ``global'' statements that are not part of a function or event handler definition. These are executed after parsing the full script, and can of course invoke functions or generate events. The event engine also generates events during different phases of its operation: bro_init when it is about to begin operation, bro_done when it is about to terminate, and bro_signal when it receives a Unix signal.
One difference between defining functions and defining event handlers is that Bro allows multiple, different definitions for a given event handler. Whenever an event is generated, each instance of a handler is invoked in turn (in the order they appear in the script). So, for example, different (conceptual) modules can each define bro_init handlers to take care of their initialization. We find this considerably simplifies the task of creating modular sets of event handlers, but we anticipate requiring greater control in the future over the exact order in which Bro invokes multiple handlers.