tramex_tools/interface/parser/
parser_nas.rs

1//! Parser for NAS traces
2use super::ParsingError;
3use super::hex_extractor::extract_binary_from_lines;
4use crate::data::{AdditionalInfos, Trace};
5use crate::interface::association::TraceRelation;
6use std::str::FromStr;
7
8use crate::interface::{layer::Layer, types::Direction};
9
10use super::FileParser;
11
12#[derive(Debug, Clone)]
13/// Data structure to store the NAS message type
14pub struct NASInfos {
15    /// Direction of the message (UL/DL)
16    pub direction: Direction,
17
18    /// Message type (e.g., "Service request", "Authentication response")
19    pub message_type: String,
20}
21
22/// NAS Parser
23pub struct NASParser;
24
25impl NASParser {
26    /// Parse lines
27    fn parse_lines(lines: &[String]) -> Vec<String> {
28        lines.to_vec()
29    }
30}
31
32impl FileParser for NASParser {
33    fn parse_additional_infos(lines: &[String]) -> Result<AdditionalInfos, ParsingError> {
34        let line = &lines[0];
35        let parts: Vec<&str> = line.split_whitespace().collect();
36
37        // Example: "13:20:58.310 [NAS] UL 0048 5GMM: Service request"
38        // Parts: ["13:20:58.310", "[NAS]", "UL", "0048", "5GMM:", "Service", "request"]
39        if parts.len() < 5 {
40            return Err(ParsingError::new(
41                "Could not find enough (5) parameters for NAS".to_string(),
42                1,
43            ));
44        }
45
46        let direction_result = Direction::from_str(parts[2]);
47        let direction = match direction_result {
48            Ok(d) => d,
49            Err(_) => {
50                return Err(ParsingError::new(
51                    format!("The direction could not be parsed in the part {:?} of {}", parts[2], line),
52                    1,
53                ));
54            }
55        };
56
57        // Message type is everything after the protocol identifier (5GMM:, etc.)
58        let message_type = if parts.len() > 5 {
59            parts[5..].join(" ")
60        } else {
61            parts.get(4).unwrap_or(&"Unknown").trim_end_matches(':').to_string()
62        };
63
64        Ok(AdditionalInfos::NASInfos(NASInfos { direction, message_type }))
65    }
66
67    fn parse(lines: &[String]) -> Result<Trace, ParsingError> {
68        let additional_infos = match Self::parse_additional_infos(lines) {
69            Ok(m) => m,
70            Err(e) => {
71                return Err(e);
72            }
73        };
74
75        let text = Self::parse_lines(lines);
76
77        let binary = extract_binary_from_lines(lines);
78
79        // Parse timestamp from first line
80        let timestamp = if let Some(first_line) = lines.first() {
81            super::parse_timestamp(first_line)?
82        } else {
83            0
84        };
85
86        let trace = Trace {
87            timestamp,
88            layer: Layer::NAS,
89            additional_infos,
90            text: Some(text),
91            binary,
92            relation: TraceRelation::default(),
93        };
94        Ok(trace)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_parse_nas_line() {
104        let line = "13:20:58.310 [NAS] UL 0048 5GMM: Service request";
105        let lines = vec![line.to_string()];
106
107        let result = NASParser::parse_additional_infos(&lines);
108        assert!(result.is_ok());
109
110        match result.unwrap() {
111            AdditionalInfos::NASInfos(infos) => {
112                assert_eq!(infos.direction, Direction::UL);
113                assert_eq!(infos.message_type, "Service request");
114            }
115            _ => panic!("Expected NASInfos"),
116        }
117    }
118
119    #[test]
120    fn test_parse_nas_dl() {
121        let line = "13:20:58.310 [NAS] DL 0048 5GMM: Registration accept";
122        let lines = vec![line.to_string()];
123
124        let result = NASParser::parse_additional_infos(&lines);
125        assert!(result.is_ok());
126
127        match result.unwrap() {
128            AdditionalInfos::NASInfos(infos) => {
129                assert_eq!(infos.direction, Direction::DL);
130                assert_eq!(infos.message_type, "Registration accept");
131            }
132            _ => panic!("Expected NASInfos"),
133        }
134    }
135
136    #[test]
137    fn test_parse_nas_invalid_direction() {
138        let line = "13:20:58.310 [NAS] INVALID 0048 5GMM: Service request";
139        let lines = vec![line.to_string()];
140
141        let result = NASParser::parse_additional_infos(&lines);
142        assert!(result.is_err());
143    }
144}