From 31b967b089d3d4ad62bf334b655e9cf68eb7ad10 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Wed, 15 Apr 2026 10:31:41 -0600 Subject: [PATCH] ntp: add transaction logging Adds basic NTP transaction logging for the current supported message types. Includes small cleanups around reference ID. Ticket: #8425 --- doc/userguide/partials/eve-log.yaml | 2 ++ etc/schema.json | 10 ++++++++ rust/src/ntp/log.rs | 34 ++++++++++++++++++++++++++ rust/src/ntp/mod.rs | 3 ++- rust/src/ntp/ntp.rs | 38 +++++++++++++++++------------ suricata.yaml.in | 2 ++ 6 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 rust/src/ntp/log.rs diff --git a/doc/userguide/partials/eve-log.yaml b/doc/userguide/partials/eve-log.yaml index 08e9237e0c..025908cce7 100644 --- a/doc/userguide/partials/eve-log.yaml +++ b/doc/userguide/partials/eve-log.yaml @@ -240,6 +240,8 @@ outputs: - ssh - arp: enabled: no + - ntp: + enabled: no - snmp - rfb - sip diff --git a/etc/schema.json b/etc/schema.json index 08c1529866..88626b6e6a 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -4467,6 +4467,16 @@ } } }, + "ntp": { + "type": "object", + "additionalProperties": false, + "properties": { + "reference_id": { + "type": "integer", + "description": "Identifies specific server or reference clock" + } + } + }, "packet": { "type": "string" }, diff --git a/rust/src/ntp/log.rs b/rust/src/ntp/log.rs new file mode 100644 index 0000000000..9226f514e8 --- /dev/null +++ b/rust/src/ntp/log.rs @@ -0,0 +1,34 @@ +/* Copyright (C) 2026 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use super::ntp::NTPTransaction; +use crate::jsonbuilder::{JsonBuilder, JsonError}; + +fn log(jb: &mut JsonBuilder, tx: &NTPTransaction) -> Result<(), JsonError> { + jb.open_object("ntp")?; + jb.set_uint("reference_id", tx.reference_id)?; + jb.close()?; + Ok(()) +} + +pub(super) unsafe extern "C" fn ntp_log_json( + tx: *const std::os::raw::c_void, jb: *mut std::os::raw::c_void, +) -> bool { + let tx = cast_pointer!(tx, NTPTransaction); + let jb = cast_pointer!(jb, JsonBuilder); + log(jb, tx).is_ok() +} diff --git a/rust/src/ntp/mod.rs b/rust/src/ntp/mod.rs index 30ff834304..33bf3e558c 100644 --- a/rust/src/ntp/mod.rs +++ b/rust/src/ntp/mod.rs @@ -15,8 +15,9 @@ * 02110-1301, USA. */ -//! NTP application layer and parser module. +//! NTP application layer, parser and logger module. // written by Pierre Chifflier +pub mod log; pub mod ntp; diff --git a/rust/src/ntp/ntp.rs b/rust/src/ntp/ntp.rs index 0a3575fe18..2ac66acfbe 100644 --- a/rust/src/ntp/ntp.rs +++ b/rust/src/ntp/ntp.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2017-2021 Open Information Security Foundation +/* Copyright (C) 2017-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -19,6 +19,7 @@ extern crate ntp_parser; use self::ntp_parser::*; +use super::log::ntp_log_json; use crate::applayer::{self, *}; use crate::core; use crate::core::{ALPROTO_FAILED, ALPROTO_UNKNOWN}; @@ -29,8 +30,10 @@ use std::ffi::CString; use nom7::Err; use suricata_sys::sys::{ - AppLayerParserState, AppProto, SCAppLayerParserConfParserEnabled, - SCAppLayerProtoDetectConfProtoDetectionEnabled, + AppLayerParserState, AppProto, EveJsonTxLoggerRegistrationData, + SCAppLayerParserConfParserEnabled, SCAppLayerParserRegisterLogger, + SCAppLayerProtoDetectConfProtoDetectionEnabled, SCOutputEvePreRegisterLogger, + SCOutputJsonLogDirection, }; #[derive(AppLayerEvent)] @@ -58,7 +61,7 @@ pub struct NTPState { #[derive(Debug, Default)] pub struct NTPTransaction { /// The NTP reference ID - pub xid: u32, + pub reference_id: u32, /// The internal transaction id id: u64, @@ -101,9 +104,7 @@ impl NTPState { NtpPacket::V4(pkt) => (pkt.mode, pkt.ref_id), }; if mode == NtpMode::SymmetricActive || mode == NtpMode::Client { - let mut tx = self.new_tx(direction); - // use the reference id as identifier - tx.xid = ref_id; + let tx = self.new_tx(direction, ref_id); self.transactions.push(tx); } 0 @@ -127,9 +128,9 @@ impl NTPState { self.transactions.clear(); } - fn new_tx(&mut self, direction: Direction) -> NTPTransaction { + fn new_tx(&mut self, direction: Direction, ref_id: u32) -> NTPTransaction { self.tx_id += 1; - NTPTransaction::new(direction, self.tx_id) + NTPTransaction::new(direction, self.tx_id, ref_id) } pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&NTPTransaction> { @@ -154,9 +155,9 @@ impl NTPState { } impl NTPTransaction { - pub fn new(direction: Direction, id: u64) -> NTPTransaction { + pub fn new(direction: Direction, id: u64, reference_id: u32) -> NTPTransaction { NTPTransaction { - xid: 0, + reference_id, id, tx_data: applayer::AppLayerTxData::for_direction(direction), } @@ -295,12 +296,19 @@ pub unsafe extern "C" fn SCRegisterNtpParser() { let ip_proto_str = CString::new("udp").unwrap(); if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { - let alproto = applayer_register_protocol_detection(&parser, 1); - // store the allocated ID for the probe function - ALPROTO_NTP = alproto; + ALPROTO_NTP = applayer_register_protocol_detection(&parser, 1); + let reg_data = EveJsonTxLoggerRegistrationData { + confname: b"eve-log.ntp\0".as_ptr() as *const std::os::raw::c_char, + logname: b"JsonNTPLog\0".as_ptr() as *const std::os::raw::c_char, + alproto: ALPROTO_NTP, + dir: SCOutputJsonLogDirection::LOG_DIR_PACKET as u8, + LogTx: Some(ntp_log_json), + }; + SCOutputEvePreRegisterLogger(reg_data); if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { - let _ = AppLayerRegisterParser(&parser, alproto); + let _ = AppLayerRegisterParser(&parser, ALPROTO_NTP); } + SCAppLayerParserRegisterLogger(core::IPPROTO_UDP, ALPROTO_NTP); } else { SCLogDebug!("Protocol detector and parser disabled for NTP."); } diff --git a/suricata.yaml.in b/suricata.yaml.in index 87a49fa13f..859c443903 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -328,6 +328,8 @@ outputs: - ftp - rdp - nfs + #- ntp: + # enabled: no - smb: # restrict to only certain types in the following list #types: [file, tree_connect, negotiate, dcerpc, create,