1use smallvec::SmallVec;
4
5use super::ethernet::ethertype;
6use super::{FieldValue, ParseContext, ParseResult, Protocol};
7use crate::schema::{DataKind, FieldDescriptor};
8
9#[derive(Debug, Clone, Copy)]
11pub struct VlanProtocol;
12
13impl Protocol for VlanProtocol {
14 fn name(&self) -> &'static str {
15 "vlan"
16 }
17
18 fn display_name(&self) -> &'static str {
19 "802.1Q VLAN"
20 }
21
22 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
23 match context.hint("ethertype") {
25 Some(etype) if etype == ethertype::VLAN as u64 => Some(100),
26 Some(etype) if etype == ethertype::QINQ as u64 => Some(100),
27 _ => None,
28 }
29 }
30
31 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
32 if data.len() < 4 {
36 return ParseResult::error("VLAN tag too short".to_string(), data);
37 }
38
39 let mut fields = SmallVec::new();
40
41 let tci = u16::from_be_bytes([data[0], data[1]]);
43
44 let priority = (tci >> 13) & 0x07;
46 fields.push(("priority", FieldValue::UInt8(priority as u8)));
47
48 let dei = (tci >> 12) & 0x01;
50 fields.push(("dei", FieldValue::Bool(dei != 0)));
51
52 let vlan_id = tci & 0x0FFF;
54 fields.push(("vlan_id", FieldValue::UInt16(vlan_id)));
55
56 let inner_ethertype = u16::from_be_bytes([data[2], data[3]]);
58 fields.push(("inner_ethertype", FieldValue::UInt16(inner_ethertype)));
59
60 let mut child_hints = SmallVec::new();
62 child_hints.push(("ethertype", inner_ethertype as u64));
63 child_hints.push(("vlan_id", vlan_id as u64));
64
65 ParseResult::success(fields, &data[4..], child_hints)
67 }
68
69 fn schema_fields(&self) -> Vec<FieldDescriptor> {
70 vec![
71 FieldDescriptor::new("vlan.vlan_id", DataKind::UInt16).set_nullable(true),
72 FieldDescriptor::new("vlan.priority", DataKind::UInt8).set_nullable(true),
73 FieldDescriptor::new("vlan.dei", DataKind::Bool).set_nullable(true),
74 FieldDescriptor::new("vlan.inner_ethertype", DataKind::UInt16).set_nullable(true),
75 ]
76 }
77
78 fn child_protocols(&self) -> &[&'static str] {
79 &["ipv4", "ipv6", "arp", "vlan"]
80 }
81
82 fn dependencies(&self) -> &'static [&'static str] {
83 &["ethernet", "vlan"] }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 fn create_vlan_tag(vlan_id: u16, priority: u8, dei: bool, inner_ethertype: u16) -> Vec<u8> {
93 let mut tag = Vec::with_capacity(4);
94
95 let tci = ((priority as u16 & 0x07) << 13) | ((dei as u16) << 12) | (vlan_id & 0x0FFF);
97
98 tag.extend_from_slice(&tci.to_be_bytes());
99 tag.extend_from_slice(&inner_ethertype.to_be_bytes());
100
101 tag
102 }
103
104 #[test]
105 fn test_parse_vlan_basic() {
106 let tag = create_vlan_tag(100, 0, false, ethertype::IPV4);
108
109 let parser = VlanProtocol;
110 let mut context = ParseContext::new(1);
111 context.insert_hint("ethertype", ethertype::VLAN as u64);
112 context.parent_protocol = Some("ethernet");
113
114 let result = parser.parse(&tag, &context);
115
116 assert!(result.is_ok());
117 assert_eq!(result.get("vlan_id"), Some(&FieldValue::UInt16(100)));
118 assert_eq!(result.get("priority"), Some(&FieldValue::UInt8(0)));
119 assert_eq!(result.get("dei"), Some(&FieldValue::Bool(false)));
120 assert_eq!(
121 result.get("inner_ethertype"),
122 Some(&FieldValue::UInt16(ethertype::IPV4))
123 );
124 }
125
126 #[test]
127 fn test_parse_vlan_with_priority() {
128 let tag = create_vlan_tag(200, 5, true, ethertype::IPV6);
130
131 let parser = VlanProtocol;
132 let mut context = ParseContext::new(1);
133 context.insert_hint("ethertype", ethertype::VLAN as u64);
134 context.parent_protocol = Some("ethernet");
135
136 let result = parser.parse(&tag, &context);
137
138 assert!(result.is_ok());
139 assert_eq!(result.get("vlan_id"), Some(&FieldValue::UInt16(200)));
140 assert_eq!(result.get("priority"), Some(&FieldValue::UInt8(5)));
141 assert_eq!(result.get("dei"), Some(&FieldValue::Bool(true)));
142 assert_eq!(
143 result.get("inner_ethertype"),
144 Some(&FieldValue::UInt16(ethertype::IPV6))
145 );
146 }
147
148 #[test]
149 fn test_parse_vlan_max_id() {
150 let tag = create_vlan_tag(4095, 7, true, ethertype::IPV4);
152
153 let parser = VlanProtocol;
154 let mut context = ParseContext::new(1);
155 context.insert_hint("ethertype", ethertype::VLAN as u64);
156
157 let result = parser.parse(&tag, &context);
158
159 assert!(result.is_ok());
160 assert_eq!(result.get("vlan_id"), Some(&FieldValue::UInt16(4095)));
161 assert_eq!(result.get("priority"), Some(&FieldValue::UInt8(7)));
162 }
163
164 #[test]
165 fn test_can_parse_vlan() {
166 let parser = VlanProtocol;
167
168 let ctx1 = ParseContext::new(1);
170 assert!(parser.can_parse(&ctx1).is_none());
171
172 let mut ctx2 = ParseContext::new(1);
174 ctx2.insert_hint("ethertype", ethertype::VLAN as u64);
175 assert!(parser.can_parse(&ctx2).is_some());
176
177 let mut ctx3 = ParseContext::new(1);
179 ctx3.insert_hint("ethertype", ethertype::QINQ as u64);
180 assert!(parser.can_parse(&ctx3).is_some());
181
182 let mut ctx4 = ParseContext::new(1);
184 ctx4.insert_hint("ethertype", ethertype::IPV4 as u64);
185 assert!(parser.can_parse(&ctx4).is_none());
186 }
187
188 #[test]
189 fn test_parse_vlan_too_short() {
190 let short_tag = [0x00, 0x64]; let parser = VlanProtocol;
193 let mut context = ParseContext::new(1);
194 context.insert_hint("ethertype", ethertype::VLAN as u64);
195
196 let result = parser.parse(&short_tag, &context);
197
198 assert!(!result.is_ok());
199 assert!(result.error.is_some());
200 }
201
202 #[test]
203 fn test_vlan_child_hints() {
204 let tag = create_vlan_tag(42, 3, false, ethertype::IPV4);
206
207 let parser = VlanProtocol;
208 let mut context = ParseContext::new(1);
209 context.insert_hint("ethertype", ethertype::VLAN as u64);
210
211 let result = parser.parse(&tag, &context);
212
213 assert!(result.is_ok());
214 assert_eq!(result.hint("ethertype"), Some(ethertype::IPV4 as u64));
215 assert_eq!(result.hint("vlan_id"), Some(42u64));
216 }
217
218 #[test]
219 fn test_vlan_with_payload() {
220 let mut data = create_vlan_tag(100, 0, false, ethertype::IPV4);
221 data.extend_from_slice(&[0x45, 0x00, 0x00, 0x28]);
223
224 let parser = VlanProtocol;
225 let mut context = ParseContext::new(1);
226 context.insert_hint("ethertype", ethertype::VLAN as u64);
227
228 let result = parser.parse(&data, &context);
229
230 assert!(result.is_ok());
231 assert_eq!(result.remaining.len(), 4); assert_eq!(result.remaining[0], 0x45); }
234
235 #[test]
236 fn test_vlan_schema_fields() {
237 let parser = VlanProtocol;
238 let fields = parser.schema_fields();
239
240 assert!(!fields.is_empty());
241
242 let field_names: Vec<&str> = fields.iter().map(|f| f.name).collect();
243 assert!(field_names.contains(&"vlan.vlan_id"));
244 assert!(field_names.contains(&"vlan.priority"));
245 assert!(field_names.contains(&"vlan.dei"));
246 assert!(field_names.contains(&"vlan.inner_ethertype"));
247 }
248}