Add pcap_file option to supervised nodes.

This allows to start Supervised nodes with a pcap_file argument
rather than interface.

This is based on changes from @J-Gras.
This commit is contained in:
Jan Grashoefer 2022-07-08 17:31:20 +02:00 committed by Arne Welzel
parent 859ecc7b8b
commit 1882307cf3
7 changed files with 169 additions and 0 deletions

View file

@ -27,6 +27,9 @@ export {
## The interface name from which the node will read/analyze packets. ## The interface name from which the node will read/analyze packets.
## Typically used by worker nodes. ## Typically used by worker nodes.
interface: string &optional; interface: string &optional;
## The PCAP file name from which the node will read/analyze packets.
## Typically used by worker nodes.
pcap_file: string &optional;
}; };
## Configuration options that influence behavior of a supervised Zeek node. ## Configuration options that influence behavior of a supervised Zeek node.
@ -36,6 +39,8 @@ export {
name: string; name: string;
## The interface name from which the node will read/analyze packets. ## The interface name from which the node will read/analyze packets.
interface: string &optional; interface: string &optional;
## The PCAP file name from which the node will read/analyze packets.
pcap_file: string &optional;
## The working directory that the node should use. ## The working directory that the node should use.
directory: string &optional; directory: string &optional;
## The filename/path to which the node's stdout will be redirected. ## The filename/path to which the node's stdout will be redirected.

View file

@ -1233,6 +1233,11 @@ Supervisor::NodeConfig Supervisor::NodeConfig::FromRecord(const RecordVal* node)
if ( iface_val ) if ( iface_val )
rval.interface = iface_val->AsString()->CheckString(); rval.interface = iface_val->AsString()->CheckString();
const auto& pcap_file_val = node->GetField("pcap_file");
if ( pcap_file_val )
rval.pcap_file = pcap_file_val->AsString()->CheckString();
const auto& directory_val = node->GetField("directory"); const auto& directory_val = node->GetField("directory");
if ( directory_val ) if ( directory_val )
@ -1326,6 +1331,11 @@ Supervisor::NodeConfig Supervisor::NodeConfig::FromRecord(const RecordVal* node)
if ( iface ) if ( iface )
ep.interface = iface->AsStringVal()->ToStdString(); ep.interface = iface->AsStringVal()->ToStdString();
const auto& pcap_file = rv->GetField("pcap_file");
if ( pcap_file )
ep.pcap_file = pcap_file->AsStringVal()->ToStdString();
rval.cluster.emplace(name, std::move(ep)); rval.cluster.emplace(name, std::move(ep));
} }
@ -1342,6 +1352,9 @@ Supervisor::NodeConfig Supervisor::NodeConfig::FromJSON(std::string_view json)
if ( auto it = j.FindMember("interface"); it != j.MemberEnd() ) if ( auto it = j.FindMember("interface"); it != j.MemberEnd() )
rval.interface = it->value.GetString(); rval.interface = it->value.GetString();
if ( auto it = j.FindMember("pcap_file"); it != j.MemberEnd() )
rval.pcap_file = it->value.GetString();
if ( auto it = j.FindMember("directory"); it != j.MemberEnd() ) if ( auto it = j.FindMember("directory"); it != j.MemberEnd() )
rval.directory = it->value.GetString(); rval.directory = it->value.GetString();
@ -1402,6 +1415,9 @@ Supervisor::NodeConfig Supervisor::NodeConfig::FromJSON(std::string_view json)
if ( auto it = val.FindMember("interface"); it != val.MemberEnd() ) if ( auto it = val.FindMember("interface"); it != val.MemberEnd() )
ep.interface = it->value.GetString(); ep.interface = it->value.GetString();
if ( auto it = val.FindMember("pcap_file"); it != val.MemberEnd() )
ep.pcap_file = it->value.GetString();
rval.cluster.emplace(key, std::move(ep)); rval.cluster.emplace(key, std::move(ep));
} }
@ -1423,6 +1439,9 @@ RecordValPtr Supervisor::NodeConfig::ToRecord() const
if ( interface ) if ( interface )
rval->AssignField("interface", *interface); rval->AssignField("interface", *interface);
if ( pcap_file )
rval->AssignField("pcap_file", *pcap_file);
if ( directory ) if ( directory )
rval->AssignField("directory", *directory); rval->AssignField("directory", *directory);
@ -1499,6 +1518,9 @@ RecordValPtr Supervisor::NodeConfig::ToRecord() const
if ( ep.interface ) if ( ep.interface )
val->AssignField("interface", *ep.interface); val->AssignField("interface", *ep.interface);
if ( ep.pcap_file )
val->AssignField("pcap_file", *ep.pcap_file);
cluster_val->Assign(std::move(key), std::move(val)); cluster_val->Assign(std::move(key), std::move(val));
} }
@ -1666,6 +1688,9 @@ void SupervisedNode::Init(Options* options) const
if ( config.interface ) if ( config.interface )
options->interface = *config.interface; options->interface = *config.interface;
if ( config.pcap_file )
options->pcap_file = *config.pcap_file;
auto& stl = options->scripts_to_load; auto& stl = options->scripts_to_load;
stl.insert(stl.begin(), config.addl_base_scripts.begin(), config.addl_base_scripts.end()); stl.insert(stl.begin(), config.addl_base_scripts.begin(), config.addl_base_scripts.end());
@ -1732,6 +1757,9 @@ std::string Supervisor::Create(const Supervisor::NodeConfig& node)
if ( nodes.find(node.name) != nodes.end() ) if ( nodes.find(node.name) != nodes.end() )
return util::fmt("node with name '%s' already exists", node.name.data()); return util::fmt("node with name '%s' already exists", node.name.data());
if ( node.interface.has_value() && node.pcap_file.has_value() )
return util::fmt("node with name '%s' has interface and pcap_file set", node.name.data());
if ( node.directory ) if ( node.directory )
{ {
auto res = util::detail::ensure_intermediate_dirs(node.directory->data()); auto res = util::detail::ensure_intermediate_dirs(node.directory->data());

View file

@ -138,6 +138,11 @@ public:
* Typically used by worker nodes. * Typically used by worker nodes.
*/ */
std::optional<std::string> interface; std::optional<std::string> interface;
/**
* The PCAP file name from which the node read/analyze packets.
* Typically used by worker nodes.
*/
std::optional<std::string> pcap_file;
}; };
/** /**
@ -199,6 +204,10 @@ public:
* The interface name from which the node should read/analyze packets. * The interface name from which the node should read/analyze packets.
*/ */
std::optional<std::string> interface; std::optional<std::string> interface;
/**
* The PCAP file name from which the node should read/analyze packets.
*/
std::optional<std::string> pcap_file;
/** /**
* The working directory that should be used by the node. * The working directory that should be used by the node.
*/ */

View file

@ -0,0 +1,35 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
1300475167.096535 CHhAvVGS1DHFjwGM9 141.142.220.202 224.0.0.251 D dns
1300475167.097012 ClEkJM2Vm5giqnMf4h fe80::217:f2ff:fed7:cf65 ff02::fb D dns
1300475167.099816 C4J4Th3PJpwUYZZ6gc 141.142.220.50 224.0.0.251 D dns
1300475168.853899 CmES5u32sYpV7JYN 141.142.220.118 141.142.2.2 Dd dns
1300475168.854378 CP5puj4I8PtEU4qzYg 141.142.220.118 141.142.2.2 Dd dns
1300475168.854837 C37jN32gN3y3AZzyf6 141.142.220.118 141.142.2.2 Dd dns
1300475168.857956 C0LAHyvtKSQHyJxIl 141.142.220.118 141.142.2.2 Dd dns
1300475168.858306 CFLRIC3zaTU1loLGxh 141.142.220.118 141.142.2.2 Dd dns
1300475168.858713 C9rXSW3KSpTYvPrlI1 141.142.220.118 141.142.2.2 Dd dns
1300475168.891644 C9mvWx3ezztgzcexV7 141.142.220.118 141.142.2.2 Dd dns
1300475168.892037 CNnMIj2QSd84NKf7U3 141.142.220.118 141.142.2.2 Dd dns
1300475168.892414 C7fIlMZDuRiqjpYbb 141.142.220.118 141.142.2.2 Dd dns
1300475168.893988 CpmdRlaUoJLN3uIRa 141.142.220.118 141.142.2.2 Dd dns
1300475168.894422 C1Xkzz2MaGtLrc1Tla 141.142.220.118 141.142.2.2 Dd dns
1300475168.894787 CqlVyW1YwZ15RhTBc4 141.142.220.118 141.142.2.2 Dd dns
1300475168.901749 CBA8792iHmnhPLksKa 141.142.220.118 141.142.2.2 Dd dns
1300475168.902195 CGLPPc35OzDQij1XX8 141.142.220.118 141.142.2.2 Dd dns
1300475169.899438 Cipfzj1BEnhejw8cGf 141.142.220.44 224.0.0.251 D dns
1300475170.862384 CV5WJ42jPYbNW9JNWf 141.142.220.226 141.142.220.255 D dns
1300475171.675372 CPhDKt12KQPUVbQz06 fe80::3074:17d5:2052:c324 ff02::1:3 D dns
1300475171.677081 CAnFrb2Cvxr5T7quOc 141.142.220.226 224.0.0.252 D dns
1300475173.116749 C8rquZ3DjgNW06JGLl fe80::3074:17d5:2052:c324 ff02::1:3 D dns
1300475173.117362 CzrZOtXqhwwndQva3 141.142.220.226 224.0.0.252 D dns
1300475173.153679 CaGCc13FffXe6RkQl9 141.142.220.238 141.142.220.255 D dns
1300475168.652003 CtPZjS20MLrsMUOJi2 141.142.220.118 208.80.152.2 DdA -
1300475168.902635 CiyBAq1bBLNaTiTAc 141.142.220.118 208.80.152.2 ShADad http
1300475168.855305 C3eiCBGOLw3VtHfOj 141.142.220.118 208.80.152.3 ShADad http
1300475168.855330 CwjjYJ2WqgTbAqiHl6 141.142.220.118 208.80.152.3 ShADad http
1300475168.859163 Ck51lg1bScffFj34Ri 141.142.220.118 208.80.152.3 ShADad http
1300475168.892913 CykQaM33ztNt0csB9a 141.142.220.118 208.80.152.3 ShADad http
1300475168.892936 CtxTCR2Yer0FR1tIBg 141.142.220.118 208.80.152.3 ShADad http
1300475168.895267 CLNN1k2QMum1aexUK7 141.142.220.118 208.80.152.3 ShADad http
1300475168.724007 CUM0KZ3MLUfNB0cl11 141.142.220.118 208.80.152.118 ShADad http
1300475169.780331 CFSwNi4CNGxcuffo49 141.142.220.235 173.192.163.128 ^h -

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
is_supervisor, T
PASS (got error), node with name 'grault' has interface and pcap_file set

View file

@ -0,0 +1,70 @@
# @TEST-DOC: Test support for pcap_file on Supervisor::ClusterEndpoint and Supervisor::NodeConfig
#
# @TEST-PORT: MANAGER_PORT
# @TEST-PORT: WORKER_PORT
# @TEST-EXEC: btest-bg-run zeek zeek -j %INPUT
# @TEST-EXEC: btest-bg-wait 45
# @TEST-EXEC: mv zeek/worker/conn.log zeek/worker/conn.log.orig
# @TEST-EXEC: zeek-cut ts uid id.orig_h id.resp_h history service < zeek/worker/conn.log.orig > zeek/worker/conn.log
# @TEST-EXEC: TEST_DIFF_CANONIFIER= btest-diff zeek/worker/conn.log
redef Log::default_rotation_interval = 0sec;
@if ( Supervisor::is_supervisor() )
redef SupervisorControl::enable_listen = T;
event zeek_init()
{
local cluster: table[string] of Supervisor::ClusterEndpoint;
cluster["manager"] = [$role=Supervisor::MANAGER, $host=127.0.0.1,
$p=to_port(getenv("MANAGER_PORT"))];
cluster["worker"] = [$role=Supervisor::WORKER, $host=127.0.0.1,
$p=to_port(getenv("WORKER_PORT")),
$pcap_file=(getenv("TRACES") + "/wikipedia.trace")];
for ( n, ep in cluster )
{
local sn = Supervisor::NodeConfig($name = n);
sn$cluster = cluster;
sn$directory = n;
sn$stdout_file = "stdout";
sn$stderr_file = "stderr";
if ( ep?$pcap_file )
sn$pcap_file = ep$pcap_file;
local res = Supervisor::create(sn);
if ( res != "" )
print fmt("failed to create node %s: %s", n, res);
}
}
global ready_for_shutdown = F;
# Immediately terminate the supervisor once we get a report about the worker
# starting for a second time.
event Supervisor::node_status(node: string, pid: count)
{
if ( node != "worker" )
return;
if ( ready_for_shutdown )
terminate();
ready_for_shutdown = T;
}
@else
redef Log::enable_local_logging = T;
redef Log::enable_remote_logging = F;
# Even though we run with a pcap_file, we will not terminate
# once fully read, trigger terminate() directly.
event Pcap::file_done(path: string)
{
terminate();
}
@endif

View file

@ -0,0 +1,19 @@
# @TEST-DOC: Creating a node with inteface and pcap_file fails.
# @TEST-EXEC: zeek -j -b %INPUT >out
# @TEST-EXEC: btest-diff out
# Providing interface and pcap_file is an error.
event zeek_init()
{
print "is_supervisor", Supervisor::is_supervisor();
local sn = Supervisor::NodeConfig(
$name="grault",
$interface="lo",
$pcap_file="/dev/null",
);
local res = Supervisor::create(sn);
print res != "" ? "PASS (got error)" : " FAIL (no error)", res;
terminate();
}