From 1882307cf3a6ddefcd045858413327ad6d4eca3c Mon Sep 17 00:00:00 2001 From: Jan Grashoefer Date: Fri, 8 Jul 2022 17:31:20 +0200 Subject: [PATCH] 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. --- scripts/base/frameworks/supervisor/api.zeek | 5 ++ src/supervisor/Supervisor.cc | 28 ++++++++ src/supervisor/Supervisor.h | 9 +++ .../zeek.worker.conn.log | 35 ++++++++++ .../out | 3 + .../btest/supervisor/config-cluster-pcap.zeek | 70 +++++++++++++++++++ .../create-interface-pcap-file-error.zeek | 19 +++++ 7 files changed, 169 insertions(+) create mode 100644 testing/btest/Baseline/supervisor.config-cluster-pcap/zeek.worker.conn.log create mode 100644 testing/btest/Baseline/supervisor.create-interface-pcap-file-error/out create mode 100644 testing/btest/supervisor/config-cluster-pcap.zeek create mode 100644 testing/btest/supervisor/create-interface-pcap-file-error.zeek diff --git a/scripts/base/frameworks/supervisor/api.zeek b/scripts/base/frameworks/supervisor/api.zeek index cc9f3fb004..0475d305cc 100644 --- a/scripts/base/frameworks/supervisor/api.zeek +++ b/scripts/base/frameworks/supervisor/api.zeek @@ -27,6 +27,9 @@ export { ## The interface name from which the node will read/analyze packets. ## Typically used by worker nodes. 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. @@ -36,6 +39,8 @@ export { name: string; ## The interface name from which the node will read/analyze packets. 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. directory: string &optional; ## The filename/path to which the node's stdout will be redirected. diff --git a/src/supervisor/Supervisor.cc b/src/supervisor/Supervisor.cc index e74da48ddd..9ee98e0b1b 100644 --- a/src/supervisor/Supervisor.cc +++ b/src/supervisor/Supervisor.cc @@ -1233,6 +1233,11 @@ Supervisor::NodeConfig Supervisor::NodeConfig::FromRecord(const RecordVal* node) if ( iface_val ) 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"); if ( directory_val ) @@ -1326,6 +1331,11 @@ Supervisor::NodeConfig Supervisor::NodeConfig::FromRecord(const RecordVal* node) if ( iface ) 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)); } @@ -1342,6 +1352,9 @@ Supervisor::NodeConfig Supervisor::NodeConfig::FromJSON(std::string_view json) if ( auto it = j.FindMember("interface"); it != j.MemberEnd() ) 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() ) 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() ) 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)); } @@ -1423,6 +1439,9 @@ RecordValPtr Supervisor::NodeConfig::ToRecord() const if ( interface ) rval->AssignField("interface", *interface); + if ( pcap_file ) + rval->AssignField("pcap_file", *pcap_file); + if ( directory ) rval->AssignField("directory", *directory); @@ -1499,6 +1518,9 @@ RecordValPtr Supervisor::NodeConfig::ToRecord() const if ( 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)); } @@ -1666,6 +1688,9 @@ void SupervisedNode::Init(Options* options) const if ( config.interface ) options->interface = *config.interface; + if ( config.pcap_file ) + options->pcap_file = *config.pcap_file; + auto& stl = options->scripts_to_load; 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() ) 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 ) { auto res = util::detail::ensure_intermediate_dirs(node.directory->data()); diff --git a/src/supervisor/Supervisor.h b/src/supervisor/Supervisor.h index 3d883c7617..5147ecbea1 100644 --- a/src/supervisor/Supervisor.h +++ b/src/supervisor/Supervisor.h @@ -138,6 +138,11 @@ public: * Typically used by worker nodes. */ std::optional interface; + /** + * The PCAP file name from which the node read/analyze packets. + * Typically used by worker nodes. + */ + std::optional pcap_file; }; /** @@ -199,6 +204,10 @@ public: * The interface name from which the node should read/analyze packets. */ std::optional interface; + /** + * The PCAP file name from which the node should read/analyze packets. + */ + std::optional pcap_file; /** * The working directory that should be used by the node. */ diff --git a/testing/btest/Baseline/supervisor.config-cluster-pcap/zeek.worker.conn.log b/testing/btest/Baseline/supervisor.config-cluster-pcap/zeek.worker.conn.log new file mode 100644 index 0000000000..9e72352fcc --- /dev/null +++ b/testing/btest/Baseline/supervisor.config-cluster-pcap/zeek.worker.conn.log @@ -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 - diff --git a/testing/btest/Baseline/supervisor.create-interface-pcap-file-error/out b/testing/btest/Baseline/supervisor.create-interface-pcap-file-error/out new file mode 100644 index 0000000000..8cf8e4f01f --- /dev/null +++ b/testing/btest/Baseline/supervisor.create-interface-pcap-file-error/out @@ -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 diff --git a/testing/btest/supervisor/config-cluster-pcap.zeek b/testing/btest/supervisor/config-cluster-pcap.zeek new file mode 100644 index 0000000000..a373d05702 --- /dev/null +++ b/testing/btest/supervisor/config-cluster-pcap.zeek @@ -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 diff --git a/testing/btest/supervisor/create-interface-pcap-file-error.zeek b/testing/btest/supervisor/create-interface-pcap-file-error.zeek new file mode 100644 index 0000000000..c94b587c8e --- /dev/null +++ b/testing/btest/supervisor/create-interface-pcap-file-error.zeek @@ -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(); + }