smb: use lru for ssn2tree; rename

Turn the map mapping the smb session key to smb tree into a lru cache,
limited to 1024 by default.

Add `app-layer.protocols.smb.max-tree-cache-size` option to control the
limit.

Ticket: #5672.
pull/12094/head
Victor Julien 9 months ago committed by Victor Julien
parent ce44d38ca8
commit 0f23557ef7

@ -69,11 +69,11 @@ impl SMBState {
#[cfg(feature = "debug")]
pub fn _debug_state_stats(&self) {
SCLogDebug!("ssn2vec_map {} guid2name_cache {} read_offset_cache {} ssn2tree_map {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}",
SCLogDebug!("ssn2vec_map {} guid2name_cache {} read_offset_cache {} ssn2tree_cache {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}",
self.ssn2vec_map.len(),
self.guid2name_cache.len(),
self.read_offset_cache.len(),
self.ssn2tree_map.len(),
self.ssn2tree_cache.len(),
self.ssnguid2vec_map.len(),
self.file_ts_guid.len(),
self.file_tc_guid.len(),

@ -86,6 +86,8 @@ pub static mut SMB_CFG_MAX_WRITE_QUEUE_CNT: u32 = 64;
pub static mut SMB_CFG_MAX_GUID_CACHE_SIZE: usize = 1024;
/// SMBState::read_offset_cache
pub static mut SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE: usize = 128;
/// For SMBState::ssn2tree_cache
pub static mut SMB_CFG_MAX_TREE_CACHE_SIZE: usize = 512;
static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN;
@ -707,8 +709,8 @@ pub struct SMBState<> {
/// map ssn key to read offset
pub read_offset_cache: LruCache<SMBCommonHdr, SMBFileGUIDOffset>,
pub ssn2tree_map: HashMap<SMBCommonHdr, SMBTree>,
/// Map session key to SMBTree
pub ssn2tree_cache: LruCache<SMBCommonHdr, SMBTree>,
// store partial data records that are transferred in multiple
// requests for DCERPC.
@ -784,7 +786,7 @@ impl SMBState {
ssn2vec_map:HashMap::new(),
guid2name_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_GUID_CACHE_SIZE }).unwrap()),
read_offset_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE }).unwrap()),
ssn2tree_map:HashMap::new(),
ssn2tree_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_TREE_CACHE_SIZE }).unwrap()),
ssnguid2vec_map:HashMap::new(),
skip_ts:0,
skip_tc:0,
@ -1302,7 +1304,7 @@ impl SMBState {
// if complete.
let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
r.ssn_id as u64, r.tree_id as u32, 0);
let is_pipe = match self.ssn2tree_map.get(&tree_key) {
let is_pipe = match self.ssn2tree_cache.get(&tree_key) {
Some(n) => n.is_pipe,
None => false,
};
@ -1638,7 +1640,7 @@ impl SMBState {
if r.command == SMB1_COMMAND_READ_ANDX {
let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
r.ssn_id as u64, r.tree_id as u32, 0);
let is_pipe = match self.ssn2tree_map.get(&tree_key) {
let is_pipe = match self.ssn2tree_cache.get(&tree_key) {
Some(n) => n.is_pipe,
None => false,
};
@ -2469,6 +2471,18 @@ pub unsafe extern "C" fn rs_smb_register_parser() {
SCLogError!("Invalid max-read-offset-cache-size value");
}
}
let retval = conf_get("app-layer.protocols.smb.max-tree-cache-size");
if let Some(val) = retval {
if let Ok(v) = val.parse::<usize>() {
if v > 0 {
SMB_CFG_MAX_TREE_CACHE_SIZE = v;
} else {
SCLogError!("Invalid max-tree-cache-size value");
}
} else {
SCLogError!("Invalid max-tree-cache-size value");
}
}
SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}",
SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE);
SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}",

@ -527,7 +527,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and
},
SMB1_COMMAND_TREE_DISCONNECT => {
let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
state.ssn2tree_map.remove(&tree_key);
state.ssn2tree_cache.pop(&tree_key);
false
},
SMB1_COMMAND_CLOSE => {
@ -702,7 +702,7 @@ fn smb1_response_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, an
if found {
let tree = SMBTree::new(share_name.to_vec(), is_pipe);
let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
state.ssn2tree_map.insert(tree_key, tree);
state.ssn2tree_cache.put(tree_key, tree);
}
found
},
@ -716,7 +716,7 @@ fn smb1_response_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, an
// normally removed when processing request,
// but in case we missed that try again here
let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
state.ssn2tree_map.remove(&tree_key);
state.ssn2tree_cache.pop(&tree_key);
false
},
SMB1_COMMAND_NT_CREATE_ANDX => {
@ -977,7 +977,7 @@ pub fn smb1_write_request_record(state: &mut SMBState, r: &SmbRecord, andx_offse
};
if !found {
let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
let (share_name, is_pipe) = match state.ssn2tree_map.get(&tree_key) {
let (share_name, is_pipe) = match state.ssn2tree_cache.get(&tree_key) {
Some(n) => (n.name.to_vec(), n.is_pipe),
None => (Vec::new(), false),
};
@ -1050,7 +1050,7 @@ pub fn smb1_read_response_record(state: &mut SMBState, r: &SmbRecord, andx_offse
SCLogDebug!("SMBv1 READ: FID {:?} offset {}", file_fid, offset);
let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
let (is_pipe, share_name) = match state.ssn2tree_map.get(&tree_key) {
let (is_pipe, share_name) = match state.ssn2tree_cache.get(&tree_key) {
Some(n) => (n.is_pipe, n.name.to_vec()),
_ => { (false, Vec::new()) },
};

@ -189,7 +189,7 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
SCLogDebug!("existing file tx? {}", found);
if !found {
let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) {
let (share_name, mut is_pipe) = match state.ssn2tree_cache.get(&tree_key) {
Some(n) => (n.name.to_vec(), n.is_pipe),
_ => { (Vec::new(), false) },
};
@ -208,7 +208,7 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
SCLogDebug!("SMBv2/READ: looks like dcerpc");
// insert fake tree to assist in follow up lookups
let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
state.ssn2tree_map.insert(tree_key, tree);
state.ssn2tree_cache.put(tree_key, tree);
if !is_dcerpc {
_ = state.guid2name_cache.put(file_guid.to_vec(), b"suricata::dcerpc".to_vec());
}
@ -332,7 +332,7 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
};
if !found {
let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) {
let (share_name, mut is_pipe) = match state.ssn2tree_cache.get(&tree_key) {
Some(n) => { (n.name.to_vec(), n.is_pipe) },
_ => { (Vec::new(), false) },
};
@ -352,7 +352,7 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
SCLogDebug!("SMBv2/WRITE: looks like we have dcerpc");
let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
state.ssn2tree_map.insert(tree_key, tree);
state.ssn2tree_cache.put(tree_key, tree);
if !is_dcerpc {
_ = state.guid2name_cache.put(file_guid.to_vec(),
b"suricata::dcerpc".to_vec());
@ -484,7 +484,7 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
},
SMB2_COMMAND_TREE_DISCONNECT => {
let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
state.ssn2tree_map.remove(&tree_key);
state.ssn2tree_cache.pop(&tree_key);
false
}
SMB2_COMMAND_NEGOTIATE_PROTOCOL => {
@ -728,7 +728,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
// normally removed when processing request,
// but in case we missed that try again here
let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
state.ssn2tree_map.remove(&tree_key);
state.ssn2tree_cache.pop(&tree_key);
false
}
SMB2_COMMAND_TREE_CONNECT => {
@ -756,7 +756,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
if found {
let tree = SMBTree::new(share_name.to_vec(), is_pipe);
let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
state.ssn2tree_map.insert(tree_key, tree);
state.ssn2tree_cache.put(tree_key, tree);
}
true
} else {

Loading…
Cancel
Save