1use std::fmt::{self, Formatter};
79
80use zerocopy::byteorder::{BigEndian, U16};
81use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
82
83use crate::packet::protocol::EtherProto;
84use crate::packet::{HeaderParser, PacketHeader};
85
86pub const PBB_ITAG_ETHERTYPE: u16 = 0x88E7;
88
89pub const PBB_BTAG_ETHERTYPE: u16 = 0x88A8;
91
92pub const PBB_MAX_ISID: u32 = 0xFFFFFF;
94
95#[inline]
97pub fn is_pbb_itag_ethertype(ethertype: u16) -> bool {
98 ethertype == PBB_ITAG_ETHERTYPE
99}
100
101#[inline]
103pub fn is_pbb_btag_ethertype(ethertype: u16) -> bool {
104 ethertype == PBB_BTAG_ETHERTYPE
105}
106
107#[repr(C, packed)]
130#[derive(FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, KnownLayout, Immutable)]
131pub struct PbbITag {
132 ethertype: U16<BigEndian>,
133 tci_isid_high: U16<BigEndian>,
134 isid_low: U16<BigEndian>,
135}
136
137impl PbbITag {
138 pub const ETHERTYPE: u16 = PBB_ITAG_ETHERTYPE;
140
141 pub const HEADER_LEN: usize = 6;
143
144 const IPCP_MASK: u16 = 0xE000;
146 const IPCP_SHIFT: u32 = 13;
147 const DEI_MASK: u16 = 0x1000;
148 const UCA_MASK: u16 = 0x0800;
149 const RESERVED_MASK: u16 = 0x0700;
150 const ISID_HIGH_MASK: u16 = 0x00FF;
151
152 #[allow(unused)]
153 const NAME: &'static str = "PBB-I-Tag";
154
155 #[inline]
157 pub fn ethertype(&self) -> u16 {
158 self.ethertype.get()
159 }
160
161 #[inline]
165 pub fn ipcp(&self) -> u8 {
166 ((self.tci_isid_high.get() & Self::IPCP_MASK) >> Self::IPCP_SHIFT) as u8
167 }
168
169 #[inline]
173 pub fn dei(&self) -> bool {
174 (self.tci_isid_high.get() & Self::DEI_MASK) != 0
175 }
176
177 #[inline]
181 pub fn uca(&self) -> bool {
182 (self.tci_isid_high.get() & Self::UCA_MASK) != 0
183 }
184
185 #[inline]
187 pub fn reserved(&self) -> u8 {
188 ((self.tci_isid_high.get() & Self::RESERVED_MASK) >> 8) as u8
189 }
190
191 #[inline]
195 pub fn isid(&self) -> u32 {
196 let high = (self.tci_isid_high.get() & Self::ISID_HIGH_MASK) as u32;
197 let low = self.isid_low.get() as u32;
198 (high << 16) | low
199 }
200
201 #[inline]
203 fn is_valid(&self) -> bool {
204 self.ethertype.get() == Self::ETHERTYPE
205 }
206
207 pub fn flags_string(&self) -> String {
209 let mut flags = Vec::new();
210 if self.dei() {
211 flags.push("DEI");
212 }
213 if self.uca() {
214 flags.push("UCA");
215 }
216 if flags.is_empty() {
217 "none".to_string()
218 } else {
219 flags.join("|")
220 }
221 }
222}
223
224impl PacketHeader for PbbITag {
225 const NAME: &'static str = "PBB-I-Tag";
226
227 type InnerType = EtherProto;
228
229 #[inline]
230 fn inner_type(&self) -> Self::InnerType {
231 EtherProto::TEB
233 }
234
235 #[inline]
236 fn total_len(&self, _buf: &[u8]) -> usize {
237 Self::HEADER_LEN
238 }
239
240 #[inline]
241 fn is_valid(&self) -> bool {
242 PbbITag::is_valid(self)
243 }
244}
245
246impl HeaderParser for PbbITag {
247 type Output<'a> = &'a PbbITag;
248
249 #[inline]
250 fn into_view<'a>(header: &'a Self, _raw_options: &'a [u8]) -> Self::Output<'a> {
251 header
252 }
253}
254
255impl fmt::Display for PbbITag {
256 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
257 write!(
258 f,
259 "PBB I-Tag I-SID={} I-PCP={} flags=[{}]",
260 self.isid(),
261 self.ipcp(),
262 self.flags_string()
263 )
264 }
265}
266
267#[repr(C, packed)]
281#[derive(FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, KnownLayout, Immutable)]
282pub struct PbbBTag {
283 ethertype: U16<BigEndian>,
284 tci: U16<BigEndian>,
285}
286
287impl PbbBTag {
288 pub const ETHERTYPE: u16 = PBB_BTAG_ETHERTYPE;
290
291 pub const HEADER_LEN: usize = 4;
293
294 const PCP_MASK: u16 = 0xE000;
296 const PCP_SHIFT: u32 = 13;
297 const DEI_MASK: u16 = 0x1000;
298 const VID_MASK: u16 = 0x0FFF;
299
300 #[allow(unused)]
301 const NAME: &'static str = "PBB-B-Tag";
302
303 #[inline]
305 pub fn ethertype(&self) -> u16 {
306 self.ethertype.get()
307 }
308
309 #[inline]
311 pub fn pcp(&self) -> u8 {
312 ((self.tci.get() & Self::PCP_MASK) >> Self::PCP_SHIFT) as u8
313 }
314
315 #[inline]
317 pub fn dei(&self) -> bool {
318 (self.tci.get() & Self::DEI_MASK) != 0
319 }
320
321 #[inline]
323 pub fn bvid(&self) -> u16 {
324 self.tci.get() & Self::VID_MASK
325 }
326
327 #[inline]
329 pub fn tci(&self) -> u16 {
330 self.tci.get()
331 }
332
333 #[inline]
335 fn is_valid(&self) -> bool {
336 self.ethertype.get() == Self::ETHERTYPE
337 }
338}
339
340impl PacketHeader for PbbBTag {
341 const NAME: &'static str = "PBB-B-Tag";
342
343 type InnerType = EtherProto;
344
345 #[inline]
346 fn inner_type(&self) -> Self::InnerType {
347 EtherProto::VLAN_8021AH
349 }
350
351 #[inline]
352 fn total_len(&self, _buf: &[u8]) -> usize {
353 Self::HEADER_LEN
354 }
355
356 #[inline]
357 fn is_valid(&self) -> bool {
358 PbbBTag::is_valid(self)
359 }
360}
361
362impl HeaderParser for PbbBTag {
363 type Output<'a> = &'a PbbBTag;
364
365 #[inline]
366 fn into_view<'a>(header: &'a Self, _raw_options: &'a [u8]) -> Self::Output<'a> {
367 header
368 }
369}
370
371impl fmt::Display for PbbBTag {
372 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
373 write!(
374 f,
375 "PBB B-Tag B-VID={} PCP={}{}",
376 self.bvid(),
377 self.pcp(),
378 if self.dei() { " DEI" } else { "" }
379 )
380 }
381}
382
383#[derive(Debug, Clone)]
389pub struct PbbHeader<'a> {
390 pub btag: Option<&'a PbbBTag>,
392 pub itag: &'a PbbITag,
394}
395
396impl<'a> PbbHeader<'a> {
397 #[inline]
399 pub fn isid(&self) -> u32 {
400 self.itag.isid()
401 }
402
403 #[inline]
405 pub fn bvid(&self) -> Option<u16> {
406 self.btag.map(|b| b.bvid())
407 }
408
409 #[inline]
411 pub fn ipcp(&self) -> u8 {
412 self.itag.ipcp()
413 }
414
415 #[inline]
417 pub fn dei(&self) -> bool {
418 self.itag.dei()
419 }
420
421 #[inline]
423 pub fn uca(&self) -> bool {
424 self.itag.uca()
425 }
426
427 #[inline]
429 pub fn header_len(&self) -> usize {
430 let btag_len = if self.btag.is_some() {
431 PbbBTag::HEADER_LEN
432 } else {
433 0
434 };
435 btag_len + PbbITag::HEADER_LEN
436 }
437
438 pub fn parse(buf: &'a [u8]) -> Result<(Self, &'a [u8]), crate::packet::PacketHeaderError> {
444 use crate::packet::PacketHeaderError;
445
446 if buf.len() < 2 {
447 return Err(PacketHeaderError::TooShort("PBB"));
448 }
449
450 let first_ethertype = u16::from_be_bytes([buf[0], buf[1]]);
451
452 if first_ethertype == PBB_BTAG_ETHERTYPE {
454 if buf.len() < PbbBTag::HEADER_LEN {
456 return Err(PacketHeaderError::TooShort("PBB-B-Tag"));
457 }
458
459 let btag = zerocopy::Ref::<_, PbbBTag>::from_prefix(buf)
460 .map_err(|_| PacketHeaderError::TooShort("PBB-B-Tag"))?;
461 let btag = zerocopy::Ref::into_ref(btag.0);
462
463 if !btag.is_valid() {
464 return Err(PacketHeaderError::Invalid("PBB-B-Tag"));
465 }
466
467 let itag_buf = &buf[PbbBTag::HEADER_LEN..];
469 if itag_buf.len() < PbbITag::HEADER_LEN {
470 return Err(PacketHeaderError::TooShort("PBB-I-Tag"));
471 }
472
473 let itag = zerocopy::Ref::<_, PbbITag>::from_prefix(itag_buf)
474 .map_err(|_| PacketHeaderError::TooShort("PBB-I-Tag"))?;
475 let itag = zerocopy::Ref::into_ref(itag.0);
476
477 if !itag.is_valid() {
478 return Err(PacketHeaderError::Invalid("PBB-I-Tag"));
479 }
480
481 let payload = &itag_buf[PbbITag::HEADER_LEN..];
482
483 Ok((
484 PbbHeader {
485 btag: Some(btag),
486 itag,
487 },
488 payload,
489 ))
490 } else if first_ethertype == PBB_ITAG_ETHERTYPE {
491 if buf.len() < PbbITag::HEADER_LEN {
493 return Err(PacketHeaderError::TooShort("PBB-I-Tag"));
494 }
495
496 let itag = zerocopy::Ref::<_, PbbITag>::from_prefix(buf)
497 .map_err(|_| PacketHeaderError::TooShort("PBB-I-Tag"))?;
498 let itag = zerocopy::Ref::into_ref(itag.0);
499
500 if !itag.is_valid() {
501 return Err(PacketHeaderError::Invalid("PBB-I-Tag"));
502 }
503
504 let payload = &buf[PbbITag::HEADER_LEN..];
505
506 Ok((PbbHeader { btag: None, itag }, payload))
507 } else {
508 Err(PacketHeaderError::Invalid("PBB: unknown EtherType"))
509 }
510 }
511}
512
513impl fmt::Display for PbbHeader<'_> {
514 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
515 if let Some(btag) = self.btag {
516 write!(f, "{} -> {}", btag, self.itag)
517 } else {
518 write!(f, "{}", self.itag)
519 }
520 }
521}
522
523#[derive(Debug, Clone, Copy, PartialEq, Eq)]
528pub enum PbbTeEspType {
529 Working,
531 Protection,
533 Unknown(u8),
535}
536
537impl From<u8> for PbbTeEspType {
538 fn from(value: u8) -> Self {
539 match value {
540 0 => PbbTeEspType::Working,
541 1 => PbbTeEspType::Protection,
542 v => PbbTeEspType::Unknown(v),
543 }
544 }
545}
546
547impl fmt::Display for PbbTeEspType {
548 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
549 match self {
550 PbbTeEspType::Working => write!(f, "Working"),
551 PbbTeEspType::Protection => write!(f, "Protection"),
552 PbbTeEspType::Unknown(v) => write!(f, "Unknown({})", v),
553 }
554 }
555}
556
557#[cfg(test)]
558mod tests {
559 use super::*;
560 use crate::packet::HeaderParser;
561
562 #[test]
563 fn test_pbb_itag_header_size() {
564 assert_eq!(std::mem::size_of::<PbbITag>(), 6);
565 assert_eq!(PbbITag::HEADER_LEN, 6);
566 }
567
568 #[test]
569 fn test_pbb_btag_header_size() {
570 assert_eq!(std::mem::size_of::<PbbBTag>(), 4);
571 assert_eq!(PbbBTag::HEADER_LEN, 4);
572 }
573
574 #[test]
575 fn test_pbb_itag_basic() {
576 let packet = vec![
578 0x88, 0xE7, 0x00, 0x00, 0x00, 0x64, 0xFF, 0xFF,
582 ];
583
584 let (itag, payload) = PbbITag::from_bytes(&packet).unwrap();
585 assert_eq!(itag.ethertype(), PBB_ITAG_ETHERTYPE);
586 assert_eq!(itag.isid(), 100);
587 assert_eq!(itag.ipcp(), 0);
588 assert!(!itag.dei());
589 assert!(!itag.uca());
590 assert_eq!(payload.len(), 2);
591 }
592
593 #[test]
594 fn test_pbb_itag_with_priority() {
595 let packet = vec![
597 0x88, 0xE7, 0xB0, 0x12, 0x34, 0x56, ];
601
602 let (itag, _) = PbbITag::from_bytes(&packet).unwrap();
603 assert_eq!(itag.ipcp(), 5);
604 assert!(itag.dei());
605 assert!(!itag.uca());
606 assert_eq!(itag.isid(), 0x123456);
607 }
608
609 #[test]
610 fn test_pbb_itag_with_uca() {
611 let packet = vec![
613 0x88, 0xE7, 0x08, 0x00, 0x00, 0x01, ];
617
618 let (itag, _) = PbbITag::from_bytes(&packet).unwrap();
619 assert!(itag.uca());
620 assert_eq!(itag.isid(), 1);
621 }
622
623 #[test]
624 fn test_pbb_itag_max_isid() {
625 let packet = vec![
627 0x88, 0xE7, 0x00, 0xFF, 0xFF, 0xFF, ];
631
632 let (itag, _) = PbbITag::from_bytes(&packet).unwrap();
633 assert_eq!(itag.isid(), PBB_MAX_ISID);
634 }
635
636 #[test]
637 fn test_pbb_itag_invalid_ethertype() {
638 let packet = vec![
639 0x08, 0x00, 0x00, 0x00, 0x00, 0x64,
641 ];
642
643 let result = PbbITag::from_bytes(&packet);
644 assert!(result.is_err());
645 }
646
647 #[test]
648 fn test_pbb_itag_too_short() {
649 let packet = vec![0x88, 0xE7, 0x00, 0x00];
650 let result = PbbITag::from_bytes(&packet);
651 assert!(result.is_err());
652 }
653
654 #[test]
655 fn test_pbb_itag_display() {
656 let packet = vec![0x88, 0xE7, 0xA0, 0x00, 0x00, 0x64];
657
658 let (itag, _) = PbbITag::from_bytes(&packet).unwrap();
659 let display = format!("{}", itag);
660 assert!(display.contains("PBB I-Tag"));
661 assert!(display.contains("I-SID=100"));
662 assert!(display.contains("I-PCP=5"));
663 }
664
665 #[test]
666 fn test_pbb_itag_flags_string() {
667 let packet = vec![0x88, 0xE7, 0x18, 0x00, 0x00, 0x01];
669
670 let (itag, _) = PbbITag::from_bytes(&packet).unwrap();
671 let flags = itag.flags_string();
672 assert!(flags.contains("DEI"));
673 assert!(flags.contains("UCA"));
674
675 let packet2 = vec![0x88, 0xE7, 0x00, 0x00, 0x00, 0x01];
677 let (itag2, _) = PbbITag::from_bytes(&packet2).unwrap();
678 assert_eq!(itag2.flags_string(), "none");
679 }
680
681 #[test]
682 fn test_pbb_btag_basic() {
683 let packet = vec![
685 0x88, 0xA8, 0x00, 0x64, 0x88, 0xE7,
689 ];
690
691 let (btag, payload) = PbbBTag::from_bytes(&packet).unwrap();
692 assert_eq!(btag.ethertype(), PBB_BTAG_ETHERTYPE);
693 assert_eq!(btag.bvid(), 100);
694 assert_eq!(btag.pcp(), 0);
695 assert!(!btag.dei());
696 assert_eq!(payload.len(), 2);
697 }
698
699 #[test]
700 fn test_pbb_btag_with_priority() {
701 let packet = vec![
703 0x88, 0xA8, 0xFF, 0xFF, ];
706
707 let (btag, _) = PbbBTag::from_bytes(&packet).unwrap();
708 assert_eq!(btag.pcp(), 7);
709 assert!(btag.dei());
710 assert_eq!(btag.bvid(), 4095);
711 }
712
713 #[test]
714 fn test_pbb_btag_display() {
715 let packet = vec![0x88, 0xA8, 0xA0, 0x64];
716
717 let (btag, _) = PbbBTag::from_bytes(&packet).unwrap();
718 let display = format!("{}", btag);
719 assert!(display.contains("PBB B-Tag"));
720 assert!(display.contains("B-VID=100"));
721 assert!(display.contains("PCP=5"));
722 }
723
724 #[test]
725 fn test_pbb_header_parse_with_btag() {
726 let packet = vec![
728 0x88, 0xA8, 0xA0, 0x64, 0x88, 0xE7, 0x60, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
736 ];
737
738 let (header, payload) = PbbHeader::parse(&packet).unwrap();
739 assert!(header.btag.is_some());
740 assert_eq!(header.bvid(), Some(100));
741 assert_eq!(header.isid(), 256);
742 assert_eq!(header.ipcp(), 3);
743 assert_eq!(header.header_len(), 10);
744 assert_eq!(payload.len(), 4);
745 }
746
747 #[test]
748 fn test_pbb_header_parse_itag_only() {
749 let packet = vec![
751 0x88, 0xE7, 0x00, 0x00, 0x00, 0x64, 0xFF, 0xFF,
755 ];
756
757 let (header, payload) = PbbHeader::parse(&packet).unwrap();
758 assert!(header.btag.is_none());
759 assert_eq!(header.bvid(), None);
760 assert_eq!(header.isid(), 100);
761 assert_eq!(header.header_len(), 6);
762 assert_eq!(payload.len(), 2);
763 }
764
765 #[test]
766 fn test_pbb_header_parse_invalid() {
767 let packet = vec![0x08, 0x00, 0x00, 0x00, 0x00, 0x00];
769 let result = PbbHeader::parse(&packet);
770 assert!(result.is_err());
771 }
772
773 #[test]
774 fn test_pbb_header_display() {
775 let packet = vec![
776 0x88, 0xA8, 0x00, 0x64, 0x88, 0xE7, 0x00, 0x00, 0x01, 0x00, ];
779
780 let (header, _) = PbbHeader::parse(&packet).unwrap();
781 let display = format!("{}", header);
782 assert!(display.contains("B-Tag"));
783 assert!(display.contains("I-Tag"));
784 assert!(display.contains("->"));
785 }
786
787 #[test]
788 fn test_pbb_ethertype_helpers() {
789 assert!(is_pbb_itag_ethertype(0x88E7));
790 assert!(!is_pbb_itag_ethertype(0x88A8));
791
792 assert!(is_pbb_btag_ethertype(0x88A8));
793 assert!(!is_pbb_btag_ethertype(0x88E7));
794 }
795
796 #[test]
797 fn test_pbb_inner_type() {
798 let packet = vec![0x88, 0xE7, 0x00, 0x00, 0x00, 0x01];
799 let (itag, _) = PbbITag::from_bytes(&packet).unwrap();
800 assert_eq!(itag.inner_type(), EtherProto::TEB);
801
802 let packet2 = vec![0x88, 0xA8, 0x00, 0x01];
803 let (btag, _) = PbbBTag::from_bytes(&packet2).unwrap();
804 assert_eq!(btag.inner_type(), EtherProto::VLAN_8021AH);
805 }
806
807 #[test]
808 fn test_pbb_te_esp_type() {
809 assert_eq!(PbbTeEspType::from(0), PbbTeEspType::Working);
810 assert_eq!(PbbTeEspType::from(1), PbbTeEspType::Protection);
811 assert_eq!(PbbTeEspType::from(2), PbbTeEspType::Unknown(2));
812
813 assert_eq!(format!("{}", PbbTeEspType::Working), "Working");
814 assert_eq!(format!("{}", PbbTeEspType::Protection), "Protection");
815 assert_eq!(format!("{}", PbbTeEspType::Unknown(5)), "Unknown(5)");
816 }
817
818 #[test]
819 fn test_pbb_real_world_scenario() {
820 let mut packet = Vec::new();
823
824 packet.extend_from_slice(&[0x88, 0xA8]);
826 packet.extend_from_slice(&[0x83, 0xE8]); packet.extend_from_slice(&[0x88, 0xE7]);
830 packet.extend_from_slice(&[0xC0, 0x00]); packet.extend_from_slice(&[0xC3, 0x50]); packet.extend_from_slice(&[
835 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x00, ]);
839
840 let (header, payload) = PbbHeader::parse(&packet).unwrap();
841 assert_eq!(header.bvid(), Some(1000));
842 assert_eq!(header.isid(), 50000);
843 assert_eq!(header.btag.unwrap().pcp(), 4);
844 assert_eq!(header.ipcp(), 6);
845 assert_eq!(payload.len(), 14); }
847
848 #[test]
849 fn test_pbb_itag_all_priorities() {
850 for pcp in 0..8u8 {
851 let tci_high = (pcp as u16) << 13;
853 let packet = vec![
854 0x88,
855 0xE7,
856 (tci_high >> 8) as u8,
857 (tci_high & 0xFF) as u8,
858 0x00,
859 0x01,
860 ];
861
862 let (itag, _) = PbbITag::from_bytes(&packet).unwrap();
863 assert_eq!(itag.ipcp(), pcp);
864 }
865 }
866
867 #[test]
868 fn test_pbb_btag_all_priorities() {
869 for pcp in 0..8u8 {
870 let tci = (pcp as u16) << 13;
871 let packet = vec![0x88, 0xA8, (tci >> 8) as u8, tci as u8];
872
873 let (btag, _) = PbbBTag::from_bytes(&packet).unwrap();
874 assert_eq!(btag.pcp(), pcp);
875 }
876 }
877}