diff --git a/scripts/base/init-bare.zeek b/scripts/base/init-bare.zeek index b69880db55..847c2f21e8 100644 --- a/scripts/base/init-bare.zeek +++ b/scripts/base/init-bare.zeek @@ -161,6 +161,32 @@ type PacketSource: record { netmask: count; }; + +## If a packet source does not yield packets for this amount of time, +## it is considered idle. When a packet source is found to be idle, +## Zeek will update network_time to current time in order for timer expiration +## to function. A packet source queueing up packets and not yielding them for +## longer than this interval without yielding any packets will provoke +## not-very-well-defined timer behavior. +## +## On Zeek workers with low packet rates, timer expiration may be delayed +## by this many milliseconds after the last packet has been received. +const packet_source_inactivity_timeout = 100msec &redef; + +## Whether Zeek will forward network_time to the current time upon +## observing an idle packet source (or no configured packet source). +## +## Only set this to *F* if you really know what you're doing. Setting this to +## *F* on non-worker systems causes :zeek:see:`network_time` to be stuck +## at 0.0 and timer expiration will be non-functional. +## +## The main purpose of this option is to yield control over network time +## to plugins or scripts via broker or other non-timer events. +## +## .. zeek:see:: network_time set_network_time packet_source_inactivity_timeout +## +const allow_network_time_forward = T &redef; + ## A connection's transport-layer protocol. Note that Zeek uses the term ## "connection" broadly, using flow semantics for ICMP and UDP. type transport_proto: enum { diff --git a/src/RunState.cc b/src/RunState.cc index 3188f4b63c..19cc97c0de 100644 --- a/src/RunState.cc +++ b/src/RunState.cc @@ -137,6 +137,44 @@ void update_network_time(double new_network_time) PLUGIN_HOOK_VOID(HOOK_UPDATE_NETWORK_TIME, HookUpdateNetworkTime(new_network_time)); } +// Logic to decide when updating network_time is acceptable: +static bool should_forward_network_time() + { + // In pseudo_realtime mode, always update time once + // we've dispatched and processed the first packet. + // run_state::detail::first_timestamp is currently set + // in PktSrc::ExtractNextPacketInternal() + if ( pseudo_realtime != 0.0 && run_state::detail::first_timestamp != 0.0 ) + return true; + + if ( iosource::PktSrc* ps = iosource_mgr->GetPktSrc() ) + { + // Offline packet sources always control network time + // unless we're running pseudo_realtime, see above. + if ( ! ps->IsLive() ) + return false; + + if ( ! ps->HasBeenIdleFor(BifConst::packet_source_inactivity_timeout) ) + return false; + } + + // We determined that we don't have a packet source, or it is idle. + // Unless it has been disabled, network_time will now be moved forward. + return BifConst::allow_network_time_forward; + } + +static void forward_network_time_if_applicable() + { + if ( ! should_forward_network_time() ) + return; + + double now = util::current_time(true); + if ( now > network_time ) + update_network_time(now); + + return; + } + void init_run(const std::optional& interface, const std::optional& pcap_input_file, const std::optional& pcap_output_file, bool do_watchdog) @@ -319,21 +357,21 @@ void run_loop() // date on timers and events. Because we only // have timers as sources, going to sleep here // doesn't risk blocking on other inputs. - update_network_time(util::current_time()); + // + // TBD: Is this actually still relevant given that the TimerMgr + // is an IO source now? It'll be processed once its + // GetNextTimeout() yields 0 and before that there's nothing + // to expire anyway. + forward_network_time_if_applicable(); expire_timers(); + + // Prevent another forward_network_time_if_applicable() below + // even if time wasn't actually updated. + time_updated = true; } - // Ensure that the time gets updated every pass if we're reading live. - // This is necessary for e.g. packet sources that don't have a selectable - // file descriptor. They'll always be ready on a very short timeout, but - // won't necessarily have a packet to process. In these case, sometimes - // the time won't get updated for a long time and timers don't function - // correctly. - if ( (! time_updated && reading_live) ) - { - update_network_time(util::current_time()); - expire_timers(); - } + if ( ! time_updated ) + forward_network_time_if_applicable(); event_mgr.Drain(); diff --git a/src/const.bif b/src/const.bif index 4d793d82c1..55d5222f47 100644 --- a/src/const.bif +++ b/src/const.bif @@ -2,6 +2,8 @@ ##! internally. Documentation and default values for the scripting-layer ##! variables themselves are found in :doc:`/scripts/base/init-bare.zeek`. +const packet_source_inactivity_timeout: interval; +const allow_network_time_forward: bool; const ignore_keep_alive_rexmit: bool; const skip_http_data: bool; const use_conn_size_analyzer: bool; diff --git a/src/zeek-setup.cc b/src/zeek-setup.cc index 1ba3e4270b..98e87cfcbf 100644 --- a/src/zeek-setup.cc +++ b/src/zeek-setup.cc @@ -1036,7 +1036,8 @@ SetupResult setup(int argc, char** argv, Options* zopts) segment_logger = profiling_logger; } - if ( ! run_state::reading_live && ! run_state::reading_traces ) + if ( ! run_state::reading_live && ! run_state::reading_traces && + id::find_const("allow_network_time_forward")->AsBool() ) // Set up network_time to track real-time, since // we don't have any other source for it. run_state::detail::update_network_time(util::current_time());