Baby Bro, Part 3: Containers and Loops

Bro has four main container types, which I'm going to cover in somewhat nontraditional order:
  • tables
  • sets
  • vectors
  • records
Tables
A table is a collection of indexed key-value pairs: the same idea is referred to as a dictionary, associative array, or hash table in other languages. Here's a simple example that pairs letters with their place in the alphabet:


1
2
3
4
5
event bro_init()
{
local letters = table([1] = "a", [2] = "b", [3] = "c");
print letters;
}

Running it, we get this:

jswan@so12a:~/bro$ bro tables.bro
{
[3] = c,
[1] = a,
[2] = b
}


 Note that the output isn't in the same order as the script; in Bro, like in most other languages, hash tables are unordered.

Iterating over a table with a "for" loop returns the key, again like other languages:


1
2
3
4
5
6
7
8
9
event bro_init()
{
local letters = table([1] = "a", [2] = "b", [3] = "c");

for (key in letters)
{
print letters[key];
}
}

And the output:

jswan@so12a:~/bro$ bro tables.bro
c
b
a


Because we printed only the value associated with the key, we never see the key in the output.

Sets
It's common in programming to need a data type that allows one to make a collection of distinct objects, without containing multiple instances of identical objects. This is the mathematical notion of a set. Bro has a native set type. Consider an example where you have a large list of IP addresses that you got from some other source: an intel feed, a firewall log, a web server log, etc. You want to get a unique set of addresses that have appeared, but you don't care how many times they appeared. This is the perfect use for a set:


 1
2
3
4
5
6
7
8
9
10
11
12
13
event bro_init()
{
local a1 = 1.1.1.1;
local a2 = 2.2.2.2;
local a3 = 3.3.3.3;
local a4 = [fe80::abcd:1];
local a5 = 2.2.2.2;
local a6 = 2.2.2.2;
local unique = set(a1,a2,a3,a4,a5,a6);
print unique;
}

And the output:

jswan@so12a:~/bro$ bro sets.bro
{
1.1.1.1,
3.3.3.3,
2.2.2.2,
fe80::abcd:1
}


Note that 2.2.2.2 appears only once in the set, despite having been included three times as different variables.

Vectors
A vector is Bro's version of a one dimensional array. Having spent most of my recent programming time in Python, I was surprised to find that Bro vectors work like hash tables for iteration: when you loop over a vector, you get the index into the vector rather than the object itself. Here's an example:


1
2
3
4
5
6
7
8
event bro_init()
{
local animals = vector("cat","dog","dinosaur","rat");
for (animal in animals)
{
print animal;
}
}

The output:
jswan@so12a:~/bro$ bro vectors.bro
0
1
2
3


If you want to iterate a vector and get the object, you have to specify the index:

1
2
3
4
5
6
7
8
event bro_init()
{
local animals = vector("cat","dog","dinosaur","rat");
for (index in animals)
{
print animals[index];
}
}

jswan@so12a:~/bro$ bro vectors.bro
cat
dog
dinosaur
rat


Bro's vectors work pretty much exactly like a table with "counts" as keys (a count is yet another native Bro type that we haven't discussed yet; it's the same as an integer but it's always unsigned; it can't be a negative). In fact, some of the earlier Bro documentation doesn't even show vectors as valid types, so I wonder if they are actually implemented internally as tables.

Records
The last Bro container type is the record, which is sort of the meat and potatoes of Bro's wonderful logging tools. Records are discussed in detail in most of the other beginner-Bro material out there, so I'm not going to cover them here.

This will probably be the last "Baby Bro" post that I do with just the raw language features demonstrated inside the bro_init() event. Any further Bro posts will probably be using Bro in its intended context. Hope this was helpful!

Published: January 27 2013

  • category:
  • tags: