1#![allow(dead_code)]
7
8use alloc::{string::String, vec::Vec};
9
10use spin::Mutex;
11
12use crate::sync::once_lock::OnceLock;
13
14pub const TPID_8021Q: u16 = 0x8100;
16
17const ETH_HEADER_MIN: usize = 14;
19
20const VLAN_TAG_SIZE: usize = 4;
22
23const VLAN_ID_MAX: u16 = 4094;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub struct VlanTag {
35 pub tpid: u16,
37 pub tci: u16,
39}
40
41impl VlanTag {
42 pub fn new(vid: u16, pcp: u8, dei: bool) -> Self {
48 let tci = ((pcp as u16 & 0x07) << 13) | (if dei { 1u16 << 12 } else { 0 }) | (vid & 0x0FFF);
49 Self {
50 tpid: TPID_8021Q,
51 tci,
52 }
53 }
54
55 pub fn vid(&self) -> u16 {
57 self.tci & 0x0FFF
58 }
59
60 pub fn pcp(&self) -> u8 {
62 ((self.tci >> 13) & 0x07) as u8
63 }
64
65 pub fn dei(&self) -> bool {
67 (self.tci >> 12) & 0x01 != 0
68 }
69
70 pub fn to_bytes(&self) -> [u8; VLAN_TAG_SIZE] {
72 let tpid = self.tpid.to_be_bytes();
73 let tci = self.tci.to_be_bytes();
74 [tpid[0], tpid[1], tci[0], tci[1]]
75 }
76
77 pub fn from_bytes(bytes: &[u8; VLAN_TAG_SIZE]) -> Self {
79 let tpid = u16::from_be_bytes([bytes[0], bytes[1]]);
80 let tci = u16::from_be_bytes([bytes[2], bytes[3]]);
81 Self { tpid, tci }
82 }
83}
84
85pub fn has_vlan_tag(frame: &[u8]) -> bool {
91 if frame.len() < ETH_HEADER_MIN {
92 return false;
93 }
94 let ethertype = u16::from_be_bytes([frame[12], frame[13]]);
95 ethertype == TPID_8021Q
96}
97
98pub fn insert_tag(frame: &[u8], tag: VlanTag) -> Vec<u8> {
102 if frame.len() < ETH_HEADER_MIN {
103 return frame.to_vec();
104 }
105 let mut out = Vec::with_capacity(frame.len() + VLAN_TAG_SIZE);
106 out.extend_from_slice(&frame[..12]);
108 out.extend_from_slice(&tag.to_bytes());
110 out.extend_from_slice(&frame[12..]);
112 out
113}
114
115pub fn strip_tag(frame: &[u8]) -> (Option<VlanTag>, Vec<u8>) {
120 if !has_vlan_tag(frame) || frame.len() < ETH_HEADER_MIN + VLAN_TAG_SIZE {
121 return (None, frame.to_vec());
122 }
123 let tag_bytes: [u8; VLAN_TAG_SIZE] = [frame[12], frame[13], frame[14], frame[15]];
124 let tag = VlanTag::from_bytes(&tag_bytes);
125
126 let mut out = Vec::with_capacity(frame.len() - VLAN_TAG_SIZE);
127 out.extend_from_slice(&frame[..12]);
129 out.extend_from_slice(&frame[16..]);
131 (Some(tag), out)
132}
133
134#[derive(Debug, Clone, PartialEq, Eq)]
138pub enum VlanMode {
139 Access(u16),
142 Trunk(Vec<u16>),
145}
146
147#[derive(Debug, Clone)]
149pub struct VlanInterface {
150 pub parent_device: String,
152 pub vid: u16,
154 pub mode: VlanMode,
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
162pub enum VlanError {
163 InvalidVid(u16),
165 AlreadyExists,
167 NotFound,
169}
170
171#[derive(Debug)]
175pub struct VlanManager {
176 interfaces: Vec<VlanInterface>,
177}
178
179impl VlanManager {
180 pub fn new() -> Self {
182 Self {
183 interfaces: Vec::new(),
184 }
185 }
186
187 pub fn create_vlan(&mut self, parent: &str, vid: u16, mode: VlanMode) -> Result<(), VlanError> {
189 if vid == 0 || vid > VLAN_ID_MAX {
190 return Err(VlanError::InvalidVid(vid));
191 }
192 let exists = self
194 .interfaces
195 .iter()
196 .any(|i| i.parent_device == parent && i.vid == vid);
197 if exists {
198 return Err(VlanError::AlreadyExists);
199 }
200 self.interfaces.push(VlanInterface {
201 parent_device: String::from(parent),
202 vid,
203 mode,
204 });
205 Ok(())
206 }
207
208 pub fn delete_vlan(&mut self, parent: &str, vid: u16) -> Result<(), VlanError> {
210 let pos = self
211 .interfaces
212 .iter()
213 .position(|i| i.parent_device == parent && i.vid == vid);
214 match pos {
215 Some(idx) => {
216 self.interfaces.remove(idx);
217 Ok(())
218 }
219 None => Err(VlanError::NotFound),
220 }
221 }
222
223 pub fn list_vlans(&self) -> Vec<VlanInterface> {
225 self.interfaces.clone()
226 }
227
228 pub fn process_ingress(&self, parent: &str, frame: &[u8]) -> Option<(u16, Vec<u8>)> {
233 let matching: Vec<&VlanInterface> = self
234 .interfaces
235 .iter()
236 .filter(|i| i.parent_device == parent)
237 .collect();
238
239 if matching.is_empty() {
240 return None;
241 }
242
243 if has_vlan_tag(frame) {
244 let (tag_opt, inner) = strip_tag(frame);
246 let tag = tag_opt?;
247 let vid = tag.vid();
248
249 for iface in &matching {
250 match &iface.mode {
251 VlanMode::Access(access_vid) => {
252 if vid == *access_vid {
253 return Some((vid, inner));
254 }
255 }
256 VlanMode::Trunk(allowed) => {
257 if allowed.contains(&vid) {
258 return Some((vid, inner));
259 }
260 }
261 }
262 }
263 None
264 } else {
265 for iface in &matching {
267 if let VlanMode::Access(access_vid) = &iface.mode {
268 return Some((*access_vid, frame.to_vec()));
269 }
270 }
271 None
272 }
273 }
274
275 pub fn process_egress(&self, parent: &str, vid: u16, frame: &[u8]) -> Vec<u8> {
280 let iface = self
281 .interfaces
282 .iter()
283 .find(|i| i.parent_device == parent && i.vid == vid);
284
285 match iface {
286 Some(i) => match &i.mode {
287 VlanMode::Access(_) => {
288 frame.to_vec()
290 }
291 VlanMode::Trunk(_) => {
292 let tag = VlanTag::new(vid, 0, false);
294 insert_tag(frame, tag)
295 }
296 },
297 None => {
298 frame.to_vec()
300 }
301 }
302 }
303}
304
305impl Default for VlanManager {
306 fn default() -> Self {
307 Self::new()
308 }
309}
310
311static VLAN_MANAGER: OnceLock<Mutex<VlanManager>> = OnceLock::new();
314
315pub fn init() {
317 let _ = VLAN_MANAGER.set(Mutex::new(VlanManager::new()));
318}
319
320pub fn with_manager<R, F: FnOnce(&mut VlanManager) -> R>(f: F) -> Option<R> {
322 VLAN_MANAGER.get().map(|m| {
323 let mut guard = m.lock();
324 f(&mut guard)
325 })
326}
327
328#[cfg(test)]
331mod tests {
332 #[allow(unused_imports)]
333 use alloc::vec;
334
335 use super::*;
336
337 #[test]
338 fn test_vlan_tag_new() {
339 let tag = VlanTag::new(100, 5, true);
340 assert_eq!(tag.vid(), 100);
341 assert_eq!(tag.pcp(), 5);
342 assert!(tag.dei());
343 assert_eq!(tag.tpid, TPID_8021Q);
344 }
345
346 #[test]
347 fn test_vlan_tag_fields_zero() {
348 let tag = VlanTag::new(0, 0, false);
349 assert_eq!(tag.vid(), 0);
350 assert_eq!(tag.pcp(), 0);
351 assert!(!tag.dei());
352 }
353
354 #[test]
355 fn test_vlan_tag_max_vid() {
356 let tag = VlanTag::new(4095, 7, true);
357 assert_eq!(tag.vid(), 4095);
358 assert_eq!(tag.pcp(), 7);
359 assert!(tag.dei());
360 }
361
362 #[test]
363 fn test_vlan_tag_roundtrip_bytes() {
364 let original = VlanTag::new(42, 3, false);
365 let bytes = original.to_bytes();
366 let parsed = VlanTag::from_bytes(&bytes);
367 assert_eq!(original, parsed);
368 }
369
370 #[test]
371 fn test_has_vlan_tag_true() {
372 let mut frame = vec![0u8; 14];
374 frame[12] = 0x81;
375 frame[13] = 0x00;
376 assert!(has_vlan_tag(&frame));
377 }
378
379 #[test]
380 fn test_has_vlan_tag_false() {
381 let mut frame = vec![0u8; 14];
383 frame[12] = 0x08;
384 frame[13] = 0x00;
385 assert!(!has_vlan_tag(&frame));
386 }
387
388 #[test]
389 fn test_has_vlan_tag_short_frame() {
390 let frame = vec![0u8; 10];
391 assert!(!has_vlan_tag(&frame));
392 }
393
394 #[test]
395 fn test_insert_and_strip_tag() {
396 let mut frame = vec![0xAA; 6]; frame.extend_from_slice(&[0xBB; 6]); frame.extend_from_slice(&[0x08, 0x00]); frame.extend_from_slice(&[1, 2, 3, 4]); let tag = VlanTag::new(200, 2, false);
403 let tagged = insert_tag(&frame, tag);
404
405 assert_eq!(tagged.len(), frame.len() + 4);
407 assert!(has_vlan_tag(&tagged));
408
409 let (stripped_tag, inner) = strip_tag(&tagged);
411 assert_eq!(stripped_tag, Some(tag));
412 assert_eq!(inner, frame);
413 }
414
415 #[test]
416 fn test_strip_untagged_frame() {
417 let frame = vec![0u8; 20];
418 let (tag, inner) = strip_tag(&frame);
419 assert!(tag.is_none());
420 assert_eq!(inner, frame);
421 }
422
423 #[test]
424 fn test_manager_create_and_list() {
425 let mut mgr = VlanManager::new();
426 mgr.create_vlan("eth0", 10, VlanMode::Access(10)).unwrap();
427 mgr.create_vlan("eth0", 20, VlanMode::Trunk(vec![20, 30]))
428 .unwrap();
429
430 let vlans = mgr.list_vlans();
431 assert_eq!(vlans.len(), 2);
432 assert_eq!(vlans[0].vid, 10);
433 assert_eq!(vlans[1].vid, 20);
434 }
435
436 #[test]
437 fn test_manager_duplicate_error() {
438 let mut mgr = VlanManager::new();
439 mgr.create_vlan("eth0", 10, VlanMode::Access(10)).unwrap();
440 let err = mgr
441 .create_vlan("eth0", 10, VlanMode::Access(10))
442 .unwrap_err();
443 assert_eq!(err, VlanError::AlreadyExists);
444 }
445
446 #[test]
447 fn test_manager_invalid_vid() {
448 let mut mgr = VlanManager::new();
449 assert_eq!(
450 mgr.create_vlan("eth0", 0, VlanMode::Access(0)),
451 Err(VlanError::InvalidVid(0))
452 );
453 assert_eq!(
454 mgr.create_vlan("eth0", 4095, VlanMode::Access(4095)),
455 Err(VlanError::InvalidVid(4095))
456 );
457 }
458
459 #[test]
460 fn test_manager_delete() {
461 let mut mgr = VlanManager::new();
462 mgr.create_vlan("eth0", 10, VlanMode::Access(10)).unwrap();
463 mgr.delete_vlan("eth0", 10).unwrap();
464 assert!(mgr.list_vlans().is_empty());
465 assert_eq!(mgr.delete_vlan("eth0", 10), Err(VlanError::NotFound));
466 }
467
468 #[test]
469 fn test_ingress_access_untagged() {
470 let mut mgr = VlanManager::new();
471 mgr.create_vlan("eth0", 10, VlanMode::Access(10)).unwrap();
472
473 let mut frame = vec![0u8; 18]; frame[12] = 0x08;
476 frame[13] = 0x00;
477 let result = mgr.process_ingress("eth0", &frame);
478 assert!(result.is_some());
479 let (vid, inner) = result.unwrap();
480 assert_eq!(vid, 10);
481 assert_eq!(inner, frame);
482 }
483
484 #[test]
485 fn test_ingress_trunk_tagged() {
486 let mut mgr = VlanManager::new();
487 mgr.create_vlan("eth0", 20, VlanMode::Trunk(vec![20, 30]))
488 .unwrap();
489
490 let mut frame = vec![0u8; 18];
492 frame[12] = 0x08;
493 frame[13] = 0x00;
494 let tag = VlanTag::new(20, 0, false);
495 let tagged = insert_tag(&frame, tag);
496
497 let result = mgr.process_ingress("eth0", &tagged);
498 assert!(result.is_some());
499 let (vid, inner) = result.unwrap();
500 assert_eq!(vid, 20);
501 assert_eq!(inner, frame);
502 }
503
504 #[test]
505 fn test_ingress_trunk_disallowed_vid() {
506 let mut mgr = VlanManager::new();
507 mgr.create_vlan("eth0", 20, VlanMode::Trunk(vec![20, 30]))
508 .unwrap();
509
510 let mut frame = vec![0u8; 18];
512 frame[12] = 0x08;
513 frame[13] = 0x00;
514 let tag = VlanTag::new(99, 0, false);
515 let tagged = insert_tag(&frame, tag);
516
517 let result = mgr.process_ingress("eth0", &tagged);
518 assert!(result.is_none());
519 }
520
521 #[test]
522 fn test_egress_access_untagged() {
523 let mut mgr = VlanManager::new();
524 mgr.create_vlan("eth0", 10, VlanMode::Access(10)).unwrap();
525
526 let frame = vec![0u8; 18];
527 let out = mgr.process_egress("eth0", 10, &frame);
528 assert_eq!(out, frame);
530 }
531
532 #[test]
533 fn test_egress_trunk_tagged() {
534 let mut mgr = VlanManager::new();
535 mgr.create_vlan("eth0", 20, VlanMode::Trunk(vec![20, 30]))
536 .unwrap();
537
538 let mut frame = vec![0u8; 18];
539 frame[12] = 0x08;
540 frame[13] = 0x00;
541 let out = mgr.process_egress("eth0", 20, &frame);
542 assert!(has_vlan_tag(&out));
544 assert_eq!(out.len(), frame.len() + 4);
545 }
546}