Spicy conditional parsing variables not set evt file issue

I have some parsing in Spicy that is conditional for example the following with a num_ips and then up to 4,

num_ips: uint8;
ip1:     addr &ipv4;
ip2:     addr &ipv4 if (self.num_ips > 1);
ip3:     addr &ipv4 if (self.num_ips > 2);
ip4:     addr &ipv4 if (self.num_ips > 3);

The parsing works in Spicy, and based on the num_ips the ip variables are either set or not set. I am having an issue getting it outputting in the zeek log file. I have everything set up as optional in Zeek, but I think it isn’t getting there because it has a problem in the .evt file (I get no error that I see, I’m guessing).

I tried to make some conditional statements in the evt file which would only send the variables that are set to Zeek, but then the Zeek function gets type mismatches because even though the vars are optional it doesn’t know which ones I’m sending.

on parser::getdata if (self.num_ips == 1) -> event parser::output_log($conn, self.data, self.ip1)

Is there a way to tell Zeek from the evt file which variables I’m sending? ip1=self.ip1 … this gave me an error.

I’m looking for a way not to have a whole bunch of different things in the evt file, and also not have a bunch of Zeek functions one for each value of num_ips.

I can confirm that everything works as it should because if I set in Spicy the not_set variables to values before it finishes, everything works successfully and I get the values I want. I just don’t want to have all the rest of the values that weren’t present and I just set with dummy values.

Is there a way to tell Zeek from the evt file which variables I’m sending? ip1=self.ip1 … this gave me an error.

One way to communicate that would be to invoke different Zeek-side events, e.g.,

on parser::getdata if (self.num_ips == 1) -> event parser::output_log_ip1($conn, self.data, self.ip1);

That being said, a simpler way might be to directly export[1] your getdata type (assuming this is the parser with num_ips) as a Zeek type and pass values directly to Zeek, assuming it does not contain lots of unneeded data[2]. This could look like this:

# EVT file
export parser::getdata; # Make `getdata` available as a Zeek type.
on parser::getdata -> event parser::output_log($conn, self.data, self)

The export statement will create a Zeek type with the fields of your Spicy type. All fields are automatically &optional on the Zeek side (parser fields in Spicy are always implicitly optional since parsing starts out with them unset). Your Zeek-side event handler can then react dynamically, depending on whether fields are set or not, e.g.,

event parser::output_log( $ conn, self.data, p: parser::getdata )
        {
        if ( p?$ip1 )
                print "print ip1 was set";
        }

Even more, if your parser really only consisted of the snippet you posted above you could instead also parse your IPs as a vector, and then pass the vector of IPs to Zeek (or export the full unit like described above), e.g.,

type IPs = unit {
    num_ips: uint8;
    ips: (addr &ipv4)[self.num_ips];
};

  1. Automatic exporting of types is only fully fleshed out with zeek-6.2. Some support is available in earlier versions, but worst case you might have to create a Spicy-side tuple which can convert to a Zeek-side record you defined. ↩︎

  2. Exporting unneeded data would introduce additional work (CPU load) for no benefit. ↩︎

Thanks. My parser is not just the snipet I included, there are some other things being parsed so a vector probably wouldn’t work in my case.

I attempted to do the export, and then I get an error in the zeek code that p doesn’t contain ip1, I’m not sure why.

I ended up doing the conditionals in the evt file. I had done this before, but I was trying to use the same output_log zeek function, instead of writing different ones for each case. I was expecting since the variables are optional in zeek there would be a way to define which variables are being sent in and just use those. That didn’t seem to be the case.

I just wrote 12 different output_log zeek functions and 12 conditional evt lines and everything works. If I had 1000 different variations though I would have had to find a different way.