]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
app-layer: add direction to transaction creation where needed
authorJason Ish <jason.ish@oisf.net>
Wed, 15 Mar 2023 22:23:49 +0000 (16:23 -0600)
committerVictor Julien <vjulien@oisf.net>
Fri, 31 Mar 2023 17:30:07 +0000 (19:30 +0200)
Build on Eric's but set the direction on transaction creation when
needed. I think this makes it a little more clear, and easier to
document when creating single direction transactions.

This also somewhat abstracts the inner-workings of a directional
transaction from the implementation.

Ticket: #4759

14 files changed:
rust/src/applayer.rs
rust/src/bittorrent_dht/bittorrent_dht.rs
rust/src/bittorrent_dht/parser.rs
rust/src/core.rs
rust/src/dns/dns.rs
rust/src/ike/ike.rs
rust/src/ike/ikev1.rs
rust/src/ike/ikev2.rs
rust/src/krb/krb5.rs
rust/src/mqtt/detect.rs
rust/src/mqtt/mqtt.rs
rust/src/ntp/ntp.rs
rust/src/quic/quic.rs
rust/src/snmp/snmp.rs

index fd189c68656ff920d24bfe932d978cb27c968f84..155d5701d00b96b9e582d7767c1c86958d245c03 100644 (file)
@@ -148,6 +148,8 @@ impl Drop for AppLayerTxData {
 }
 
 impl AppLayerTxData {
+    /// Create new AppLayerTxData for a transaction that covers both
+    /// directions.
     pub fn new() -> Self {
         Self {
             config: AppLayerTxConfig::new(),
@@ -163,9 +165,33 @@ impl AppLayerTxData {
             events: std::ptr::null_mut(),
         }
     }
+
+    /// Create new AppLayerTxData for a transaction in a single
+    /// direction.
+    pub fn for_direction(direction: Direction) -> Self {
+        let (detect_flags_ts, detect_flags_tc) = match direction {
+            Direction::ToServer => (0, APP_LAYER_TX_SKIP_INSPECT_FLAG),
+            Direction::ToClient => (APP_LAYER_TX_SKIP_INSPECT_FLAG, 0),
+        };
+        Self {
+            config: AppLayerTxConfig::new(),
+            logged: LoggerFlags::new(),
+            files_opened: 0,
+            files_logged: 0,
+            files_stored: 0,
+            file_flags: 0,
+            file_tx: 0,
+            detect_flags_ts,
+            detect_flags_tc,
+            de_state: std::ptr::null_mut(),
+            events: std::ptr::null_mut(),
+        }
+    }
+
     pub fn init_files_opened(&mut self) {
         self.files_opened = 1;
     }
+
     pub fn incr_files_opened(&mut self) {
         self.files_opened += 1;
     }
@@ -180,14 +206,6 @@ impl AppLayerTxData {
             self.file_flags |= state_flags;
         }
     }
-
-    pub fn set_inspect_direction(&mut self, direction: Direction) {
-        if direction == Direction::ToClient {
-            self.detect_flags_ts |= APP_LAYER_TX_SKIP_INSPECT_FLAG;
-        } else {
-            self.detect_flags_tc |= APP_LAYER_TX_SKIP_INSPECT_FLAG;
-        }
-    }
 }
 
 #[macro_export]
index a6a35b2a72b3c673909f17ad63acd0596a892c34..9b36e2eaba6b36ae47b4c12a5468a131b692ebd7 100644 (file)
@@ -19,7 +19,7 @@ use crate::applayer::{self, *};
 use crate::bittorrent_dht::parser::{
     parse_bittorrent_dht_packet, BitTorrentDHTError, BitTorrentDHTRequest, BitTorrentDHTResponse,
 };
-use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN, IPPROTO_UDP};
+use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN, IPPROTO_UDP, Direction};
 use std::ffi::CString;
 use std::os::raw::c_char;
 
@@ -46,8 +46,11 @@ pub struct BitTorrentDHTTransaction {
 }
 
 impl BitTorrentDHTTransaction {
-    pub fn new() -> Self {
-        Self::default()
+    pub fn new(direction: Direction) -> Self {
+       Self {
+           tx_data: AppLayerTxData::for_direction(direction),
+           ..Default::default()
+       }
     }
 
     /// Set an event on the transaction
@@ -77,11 +80,10 @@ impl BitTorrentDHTState {
         self.transactions.iter().find(|&tx| tx.tx_id == tx_id + 1)
     }
 
-    fn new_tx(&mut self, _direction: crate::core::Direction) -> BitTorrentDHTTransaction {
-        let mut tx = BitTorrentDHTTransaction::default();
+    fn new_tx(&mut self, direction: Direction) -> BitTorrentDHTTransaction {
+        let mut tx = BitTorrentDHTTransaction::new(direction);
         self.tx_id += 1;
         tx.tx_id = self.tx_id;
-        tx.tx_data.set_inspect_direction(_direction);
         return tx;
     }
 
index ffc31ac52b07667b3b50953ff03feb95db515c15..545a1ad53774694a2d88ac86a87862cd125053c3 100644 (file)
@@ -446,6 +446,7 @@ pub fn parse_bittorrent_dht_packet(
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::core::Direction;
     use test_case::test_case;
 
     #[test_case(
@@ -576,7 +577,7 @@ mod tests {
         expected_error: Option<BitTorrentDHTError>, expected_transaction_id: Vec<u8>,
         expected_client_version: Option<Vec<u8>>,
     ) {
-        let mut tx = BitTorrentDHTTransaction::new();
+        let mut tx = BitTorrentDHTTransaction::new(Direction::ToServer);
         parse_bittorrent_dht_packet(encoded, &mut tx).unwrap();
         assert_eq!(request_type, tx.request_type);
         assert_eq!(expected_request, tx.request);
@@ -637,7 +638,7 @@ mod tests {
         "test parse bittorrent dht packet err 10"
     )]
     fn test_parse_bittorrent_dht_packet_err(encoded: &[u8], expected_error: &str) {
-        let mut tx = BitTorrentDHTTransaction::new();
+        let mut tx = BitTorrentDHTTransaction::new(Direction::ToServer);
         let err = parse_bittorrent_dht_packet(encoded, &mut tx).unwrap_err();
         assert_eq!(expected_error, err.to_string());
     }
index 3f892bf528da14d35c7f5debbef95564dff41c95..6ad9004ea611ee93e9e0812f4650841986a56615 100644 (file)
@@ -49,6 +49,18 @@ pub enum Direction {
     ToClient = 0x08,
 }
 
+impl Direction {
+    /// Return true if the direction is to server.
+    pub fn is_to_server(&self) -> bool {
+       matches!(self, Self::ToServer)
+    }
+
+    /// Return true if the direction is to client.
+    pub fn is_to_client(&self) -> bool {
+       matches!(self, Self::ToClient)
+    }
+}
+
 impl Default for Direction {
     fn default() -> Self { Direction::ToServer }
 }
@@ -315,3 +327,17 @@ impl Flow {
         unsafe { (FlowGetSourcePort(self), FlowGetDestinationPort(self)) }
     }
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_direction() {
+       assert!(Direction::ToServer.is_to_server());
+       assert!(!Direction::ToServer.is_to_client());
+
+       assert!(Direction::ToClient.is_to_client());
+       assert!(!Direction::ToClient.is_to_server());
+    }
+}
index 9bc4f6681b751a592b05999d3fb01f9c8f100621..bfd57dc7077552bb0a9d90417c4bee3dfb85cba7 100644 (file)
@@ -249,8 +249,11 @@ impl Transaction for DNSTransaction {
 }
 
 impl DNSTransaction {
-    pub fn new() -> Self {
-        Default::default()
+    pub fn new(direction: Direction) -> Self {
+       Self {
+           tx_data: AppLayerTxData::for_direction(direction),
+            ..Default::default()
+       }
     }
 
     /// Get the DNS transactions ID (not the internal tracking ID).
@@ -336,8 +339,8 @@ impl DNSState {
         Default::default()
     }
 
-    pub fn new_tx(&mut self) -> DNSTransaction {
-        let mut tx = DNSTransaction::new();
+    pub fn new_tx(&mut self, direction: Direction) -> DNSTransaction {
+        let mut tx = DNSTransaction::new(direction);
         self.tx_id += 1;
         tx.id = self.tx_id;
         return tx;
@@ -410,9 +413,8 @@ impl DNSState {
                 let z_flag = request.header.flags & 0x0040 != 0;
                 let opcode = ((request.header.flags >> 11) & 0xf) as u8;
 
-                let mut tx = self.new_tx();
+                let mut tx = self.new_tx(Direction::ToServer);
                 tx.request = Some(request);
-                tx.tx_data.set_inspect_direction(Direction::ToServer);
                 self.transactions.push_back(tx);
 
                 if z_flag {
@@ -484,14 +486,13 @@ impl DNSState {
                 let z_flag = response.header.flags & 0x0040 != 0;
                 let opcode = ((response.header.flags >> 11) & 0xf) as u8;
 
-                let mut tx = self.new_tx();
+                let mut tx = self.new_tx(Direction::ToClient);
                 if let Some(ref mut config) = &mut self.config {
                     if let Some(config) = config.remove(&response.header.tx_id) {
                         tx.tx_data.config = config;
                     }
                 }
                 tx.response = Some(response);
-                tx.tx_data.set_inspect_direction(Direction::ToClient);
                 self.transactions.push_back(tx);
 
                 if z_flag {
index 997348053e1428dcb61a96ed0b89a795d85b9192..eff74a893494274504241fbfb2497d9ec51e0f4f 100644 (file)
@@ -123,8 +123,12 @@ impl Transaction for IKETransaction {
 }
 
 impl IKETransaction {
-    pub fn new() -> Self {
-        Default::default()
+    pub fn new(direction: Direction) -> Self {
+       Self {
+           direction,
+           tx_data: applayer::AppLayerTxData::for_direction(direction),
+           ..Default::default()
+       }
     }
 
     /// Set an event.
@@ -170,8 +174,8 @@ impl IKEState {
         self.transactions.iter_mut().find(|tx| tx.tx_id == tx_id + 1)
     }
 
-    pub fn new_tx(&mut self) -> IKETransaction {
-        let mut tx = IKETransaction::new();
+    pub fn new_tx(&mut self, direction: Direction) -> IKETransaction {
+        let mut tx = IKETransaction::new(direction);
         self.tx_id += 1;
         tx.tx_id = self.tx_id;
         return tx;
index 18d586ac4ecc65b20e80d1eaaee4c85053998f89..1e79c293cdea2abec876fd0b2c4e553582124a5b 100644 (file)
@@ -74,11 +74,9 @@ pub struct Ikev1Container {
 pub fn handle_ikev1(
     state: &mut IKEState, current: &[u8], isakmp_header: IsakmpHeader, direction: Direction,
 ) -> AppLayerResult {
-    let mut tx = state.new_tx();
+    let mut tx = state.new_tx(direction);
 
     tx.ike_version = 1;
-    tx.direction = direction;
-    tx.tx_data.set_inspect_direction(direction);
     tx.hdr.spi_initiator = format!("{:016x}", isakmp_header.init_spi);
     tx.hdr.spi_responder = format!("{:016x}", isakmp_header.resp_spi);
     tx.hdr.maj_ver = isakmp_header.maj_ver;
index 6e3640d25791beed234b0cac30ffa15df78d888f..672fbbaccd791d6a7008ae69f41789f4c4978ed7 100644 (file)
@@ -113,11 +113,10 @@ pub fn handle_ikev2(
         length: isakmp_header.length,
     };
 
-    let mut tx = state.new_tx();
+    let mut tx = state.new_tx(direction);
     tx.ike_version = 2;
     // use init_spi as transaction identifier
     // tx.xid = hdr.init_spi; todo is this used somewhere?
-    tx.tx_data.set_inspect_direction(direction);
     tx.hdr.ikev2_header = hdr.clone();
     tx.hdr.spi_initiator = format!("{:016x}", isakmp_header.init_spi);
     tx.hdr.spi_responder = format!("{:016x}", isakmp_header.resp_spi);
index d20bd7db8266dd69b67ce39c87e5066aa78fe9b4..3c12e9297eb1c0579b4244ea04a4e3275e4e9292 100644 (file)
@@ -125,7 +125,7 @@ impl KRB5State {
     /// Parse a Kerberos request message
     ///
     /// Returns 0 in case of success, or -1 on error
-    fn parse(&mut self, i: &[u8], _direction: Direction) -> i32 {
+    fn parse(&mut self, i: &[u8], direction: Direction) -> i32 {
         match der_read_element_header(i) {
             Ok((_rem,hdr)) => {
                 // Kerberos messages start with an APPLICATION header
@@ -137,7 +137,7 @@ impl KRB5State {
                     11 => {
                         let res = krb5_parser::parse_as_rep(i);
                         if let Ok((_,kdc_rep)) = res {
-                            let mut tx = self.new_tx();
+                            let mut tx = self.new_tx(direction);
                             tx.msg_type = MessageType::KRB_AS_REP;
                             tx.cname = Some(kdc_rep.cname);
                             tx.realm = Some(kdc_rep.crealm);
@@ -157,7 +157,7 @@ impl KRB5State {
                     13 => {
                         let res = krb5_parser::parse_tgs_rep(i);
                         if let Ok((_,kdc_rep)) = res {
-                            let mut tx = self.new_tx();
+                            let mut tx = self.new_tx(direction);
                             tx.msg_type = MessageType::KRB_TGS_REP;
                             tx.cname = Some(kdc_rep.cname);
                             tx.realm = Some(kdc_rep.crealm);
@@ -180,7 +180,7 @@ impl KRB5State {
                     30 => {
                         let res = krb5_parser::parse_krb_error(i);
                         if let Ok((_,error)) = res {
-                            let mut tx = self.new_tx();
+                            let mut tx = self.new_tx(direction);
                             tx.msg_type = MessageType(self.req_id as u32);
                             tx.cname = error.cname;
                             tx.realm = error.crealm;
@@ -213,9 +213,9 @@ impl KRB5State {
         self.transactions.clear();
     }
 
-    fn new_tx(&mut self) -> KRB5Transaction {
+    fn new_tx(&mut self, direction: Direction) -> KRB5Transaction {
         self.tx_id += 1;
-        KRB5Transaction::new(self.tx_id)
+        KRB5Transaction::new(direction, self.tx_id)
     }
 
     fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&KRB5Transaction> {
@@ -239,8 +239,8 @@ impl KRB5State {
 }
 
 impl KRB5Transaction {
-    pub fn new(id: u64) -> KRB5Transaction {
-        let mut krbtx = KRB5Transaction{
+    pub fn new(direction: Direction, id: u64) -> KRB5Transaction {
+        let krbtx = KRB5Transaction{
             msg_type: MessageType(0),
             cname: None,
             realm: None,
@@ -249,9 +249,8 @@ impl KRB5Transaction {
             ticket_etype: None,
             error_code: None,
             id,
-            tx_data: applayer::AppLayerTxData::new(),
+            tx_data: applayer::AppLayerTxData::for_direction(direction),
         };
-        krbtx.tx_data.set_inspect_direction(Direction::ToClient);
         return krbtx;
     }
 }
index ba0f6409a56e0a82239a5c76593aa7dd65b5627c..b47a84f74409a9b35bc0b2ff6bd3011ceda2c254 100644 (file)
@@ -393,6 +393,7 @@ mod test {
     use crate::mqtt::mqtt::MQTTTransaction;
     use crate::mqtt::mqtt_message::*;
     use crate::mqtt::parser::FixedHeader;
+    use crate::core::Direction;
     use std;
 
     #[test]
@@ -410,7 +411,7 @@ mod test {
                 topics: vec!["foo".to_string(), "baar".to_string()],
                 properties: None,
             }),
-        });
+        }, Direction::ToServer);
         t.msg.push(MQTTMessage {
             header: FixedHeader {
                 message_type: MQTTTypeCode::UNSUBSCRIBE,
@@ -471,7 +472,7 @@ mod test {
                 ],
                 properties: None,
             }),
-        });
+        }, Direction::ToServer);
         t.msg.push(MQTTMessage {
             header: FixedHeader {
                 message_type: MQTTTypeCode::SUBSCRIBE,
index f81cfc2604ba7a235e900a0c213d05da339654a2..8ec2ad3558c511777919bb0d66231049f627c054 100644 (file)
@@ -70,22 +70,22 @@ pub struct MQTTTransaction {
 }
 
 impl MQTTTransaction {
-    pub fn new(msg: MQTTMessage) -> MQTTTransaction {
-        let mut m = MQTTTransaction::new_empty();
+    pub fn new(msg: MQTTMessage, direction: Direction) -> MQTTTransaction {
+        let mut m = MQTTTransaction::new_empty(direction);
         m.msg.push(msg);
         return m;
     }
 
-    pub fn new_empty() -> MQTTTransaction {
+    pub fn new_empty(direction: Direction) -> MQTTTransaction {
         return MQTTTransaction {
             tx_id: 0,
             pkt_id: None,
             complete: false,
             logged: LoggerFlags::new(),
             msg: Vec::new(),
-            toclient: false,
-            toserver: false,
-            tx_data: applayer::AppLayerTxData::new(),
+            toclient: direction.is_to_client(),
+            toserver: direction.is_to_server(),
+            tx_data: applayer::AppLayerTxData::for_direction(direction),
         };
     }
 }
@@ -175,16 +175,14 @@ impl MQTTState {
     }
 
     fn new_tx(&mut self, msg: MQTTMessage, toclient: bool) -> MQTTTransaction {
-        let mut tx = MQTTTransaction::new(msg);
+       let direction = if toclient {
+           Direction::ToClient
+       } else {
+           Direction::ToServer
+       };
+        let mut tx = MQTTTransaction::new(msg, direction);
         self.tx_id += 1;
         tx.tx_id = self.tx_id;
-        if toclient {
-            tx.toclient = true;
-            tx.tx_data.set_inspect_direction(Direction::ToClient);
-        } else {
-            tx.toserver = true;
-            tx.tx_data.set_inspect_direction(Direction::ToServer);
-        }
         if self.transactions.len() > unsafe { MQTT_MAX_TX } {
             let mut index = self.tx_index_completed;
             for tx_old in &mut self.transactions.range_mut(self.tx_index_completed..) {
@@ -579,7 +577,7 @@ impl MQTTState {
     }
 
     fn set_event_notx(&mut self, event: MQTTEvent, toclient: bool) {
-        let mut tx = MQTTTransaction::new_empty();
+        let mut tx = MQTTTransaction::new_empty(if toclient { Direction::ToClient } else { Direction::ToServer });
         self.tx_id += 1;
         tx.tx_id = self.tx_id;
         if toclient {
index db8c9897988e892039a67c3fe7e25b4387d12379..2b01a0d6c4340afe3757ba6f1a35866217d2b48e 100644 (file)
@@ -86,7 +86,7 @@ impl NTPState {
     /// Parse an NTP request message
     ///
     /// Returns 0 if successful, or -1 on error
-    fn parse(&mut self, i: &[u8], _direction: u8) -> i32 {
+    fn parse(&mut self, i: &[u8], direction: Direction) -> i32 {
         match parse_ntp(i) {
             Ok((_,ref msg)) => {
                 // SCLogDebug!("parse_ntp: {:?}",msg);
@@ -95,7 +95,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);
+                    let mut tx = self.new_tx(direction);
                     // use the reference id as identifier
                     tx.xid = ref_id;
                     self.transactions.push(tx);
@@ -121,15 +121,9 @@ impl NTPState {
         self.transactions.clear();
     }
 
-    fn new_tx(&mut self, _direction: u8) -> NTPTransaction {
+    fn new_tx(&mut self, direction: Direction) -> NTPTransaction {
         self.tx_id += 1;
-        let mut tx = NTPTransaction::new(self.tx_id);
-        if _direction == 0 {
-            tx.tx_data.set_inspect_direction(Direction::ToServer);
-        } else {
-            tx.tx_data.set_inspect_direction(Direction::ToClient);
-        }
-        tx
+        NTPTransaction::new(direction, self.tx_id)
     }
 
     pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&NTPTransaction> {
@@ -154,11 +148,11 @@ impl NTPState {
 }
 
 impl NTPTransaction {
-    pub fn new(id: u64) -> NTPTransaction {
+    pub fn new(direction: Direction, id: u64) -> NTPTransaction {
         NTPTransaction {
             xid: 0,
             id,
-            tx_data: applayer::AppLayerTxData::new(),
+            tx_data: applayer::AppLayerTxData::for_direction(direction),
         }
     }
 }
@@ -187,7 +181,7 @@ pub unsafe extern "C" fn rs_ntp_parse_request(_flow: *const core::Flow,
                                        _data: *const std::os::raw::c_void,
                                        ) -> AppLayerResult {
     let state = cast_pointer!(state,NTPState);
-    if state.parse(stream_slice.as_slice(), 0) < 0 {
+    if state.parse(stream_slice.as_slice(), Direction::ToServer) < 0 {
         return AppLayerResult::err();
     }
     AppLayerResult::ok()
@@ -201,7 +195,7 @@ pub unsafe extern "C" fn rs_ntp_parse_response(_flow: *const core::Flow,
                                        _data: *const std::os::raw::c_void,
                                        ) -> AppLayerResult {
     let state = cast_pointer!(state,NTPState);
-    if state.parse(stream_slice.as_slice(), 1) < 0 {
+    if state.parse(stream_slice.as_slice(), Direction::ToClient) < 0 {
         return AppLayerResult::err();
     }
     AppLayerResult::ok()
@@ -324,7 +318,7 @@ pub unsafe extern "C" fn rs_register_ntp_parser() {
 
 #[cfg(test)]
 mod tests {
-    use super::NTPState;
+    use super::*;
 
     #[test]
     fn test_ntp_parse_request_valid() {
@@ -339,6 +333,6 @@ mod tests {
         ];
 
         let mut state = NTPState::new();
-        assert_eq!(0, state.parse(REQ, 0));
+        assert_eq!(0, state.parse(REQ, Direction::ToServer));
     }
 }
index d4ba7ed7d5196a2f7c65b879ebea1f7127a9162e..dd8909b15e986c47ceca02e1a7459200f64abf5c 100644 (file)
@@ -57,8 +57,9 @@ impl QuicTransaction {
         header: QuicHeader, data: QuicData, sni: Option<Vec<u8>>, ua: Option<Vec<u8>>,
         extv: Vec<QuicTlsExtension>, ja3: Option<String>, client: bool,
     ) -> Self {
+       let direction = if client { Direction::ToServer } else { Direction::ToClient };
         let cyu = Cyu::generate(&header, &data.frames);
-        let mut ntx = QuicTransaction {
+        QuicTransaction {
             tx_id: 0,
             header,
             cyu,
@@ -67,17 +68,12 @@ impl QuicTransaction {
             extv,
             ja3,
             client,
-            tx_data: AppLayerTxData::new(),
-        };
-        if client {
-            ntx.tx_data.set_inspect_direction(Direction::ToServer);
-        } else {
-            ntx.tx_data.set_inspect_direction(Direction::ToClient);
+            tx_data: AppLayerTxData::for_direction(direction),
         }
-        return ntx;
     }
 
     fn new_empty(client: bool, header: QuicHeader) -> Self {
+       let direction = if client { Direction::ToServer } else { Direction::ToClient };
         QuicTransaction {
             tx_id: 0,
             header,
@@ -87,7 +83,7 @@ impl QuicTransaction {
             extv: Vec::new(),
             ja3: None,
             client,
-            tx_data: AppLayerTxData::new(),
+            tx_data: AppLayerTxData::for_direction(direction),
         }
     }
 }
@@ -141,11 +137,6 @@ impl QuicState {
         let mut tx = QuicTransaction::new(header, data, sni, ua, extb, ja3, client);
         self.max_tx_id += 1;
         tx.tx_id = self.max_tx_id;
-        if client {
-            tx.tx_data.set_inspect_direction(Direction::ToServer);
-        } else {
-            tx.tx_data.set_inspect_direction(Direction::ToClient);
-        }
         self.transactions.push_back(tx);
     }
 
index 634df2027ad185a41b7d5c7dfe43aeb529b113ed..037efe9e3df05a731ca85ae2c6ac3278327d2114 100644 (file)
@@ -204,11 +204,9 @@ impl<'a> SNMPState<'a> {
         self.transactions.clear();
     }
 
-    fn new_tx(&mut self, _direction: Direction) -> SNMPTransaction<'a> {
+    fn new_tx(&mut self, direction: Direction) -> SNMPTransaction<'a> {
         self.tx_id += 1;
-        let mut tx = SNMPTransaction::new(self.version, self.tx_id);
-        tx.tx_data.set_inspect_direction(_direction);
-        tx
+        SNMPTransaction::new(direction, self.version, self.tx_id)
     }
 
     fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SNMPTransaction> {
@@ -237,7 +235,7 @@ impl<'a> SNMPState<'a> {
 }
 
 impl<'a> SNMPTransaction<'a> {
-    pub fn new(version: u32, id: u64) -> SNMPTransaction<'a> {
+    pub fn new(direction: Direction, version: u32, id: u64) -> SNMPTransaction<'a> {
         SNMPTransaction {
             version,
             info: None,
@@ -245,7 +243,7 @@ impl<'a> SNMPTransaction<'a> {
             usm: None,
             encrypted: false,
             id,
-            tx_data: applayer::AppLayerTxData::new(),
+            tx_data: applayer::AppLayerTxData::for_direction(direction),
         }
     }
 }