1use thiserror::Error;
3
4#[derive(Debug, PartialEq, Eq)]
7pub enum EtherTalkPhase2Type {
8 Aarp,
9 Ddp,
10}
11
12#[derive(Debug)]
13pub struct EtherTalkPhase2Frame {
14 pub dst_mac: [u8; 6],
15 pub src_mac: [u8; 6],
16 pub len: u16,
17 pub protocol: EtherTalkPhase2Type,
18}
19
20#[derive(Error, Debug)]
21pub enum EtherTalkError {
22 #[error("invalid size - expected frame to be at least 20 bytes, but found {found:?}")]
23 InvalidSize { found: usize },
24 #[error("not a SNAP frame")]
25 NotSNAP,
26 #[error("unknown OUI+protocol ID")]
27 UnknownHeader,
28}
29
30impl EtherTalkPhase2Frame {
31 pub const LLC_LEN: usize = 8;
32 const FRAME_LEN: usize = 22;
33 const SNAP_MARKER: [u8; 3] = [0xAA, 0xAA, 0x03];
34 const AARP_OUI: [u8; 5] = [0x00, 0x00, 0x00, 0x80, 0xF3];
35 const DDP_OUI: [u8; 5] = [0x08, 0x00, 0x07, 0x80, 0x9B];
36 const DST_MAC_OFF: usize = 0;
37 const SRC_MAC_OFF: usize = 6;
38 const MAC_LEN: usize = 6;
39 const LEN_OFF: usize = 12;
40 const SNAP_OFF: usize = 14;
41 const OUI_OFF: usize = 17;
42
43 pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, EtherTalkError> {
44 assert!(buf.len() >= Self::FRAME_LEN);
45
46 buf[Self::DST_MAC_OFF..(Self::DST_MAC_OFF + self.dst_mac.len())]
47 .copy_from_slice(&self.dst_mac);
48 buf[Self::SRC_MAC_OFF..(Self::SRC_MAC_OFF + self.src_mac.len())]
49 .copy_from_slice(&self.src_mac);
50 buf[Self::LEN_OFF..(Self::LEN_OFF + 2)].copy_from_slice(&u16::to_be_bytes(self.len));
51
52 buf[Self::SNAP_OFF..(Self::SNAP_OFF + Self::SNAP_MARKER.len())]
54 .copy_from_slice(&Self::SNAP_MARKER);
55
56 match self.protocol {
57 EtherTalkPhase2Type::Aarp => {
58 buf[Self::OUI_OFF..(Self::OUI_OFF + Self::AARP_OUI.len())]
59 .copy_from_slice(&Self::AARP_OUI);
60 Ok(Self::FRAME_LEN)
61 }
62 EtherTalkPhase2Type::Ddp => {
63 buf[Self::OUI_OFF..(Self::OUI_OFF + Self::DDP_OUI.len())]
64 .copy_from_slice(&Self::DDP_OUI);
65 Ok(Self::FRAME_LEN)
66 }
67 }
68 }
69
70 pub const fn len() -> usize {
71 Self::FRAME_LEN
72 }
73
74 pub fn parse(buf: &[u8]) -> Result<Self, EtherTalkError> {
75 use EtherTalkError::*;
76
77 if buf.len() < Self::FRAME_LEN {
78 return Err(InvalidSize { found: buf.len() });
79 } else if buf[Self::SNAP_OFF..(Self::SNAP_OFF + Self::SNAP_MARKER.len())]
80 != Self::SNAP_MARKER
81 {
82 return Err(NotSNAP);
83 }
84
85 let dst_mac = &buf[Self::DST_MAC_OFF..(Self::DST_MAC_OFF + Self::MAC_LEN)];
86 let src_mac = &buf[Self::SRC_MAC_OFF..(Self::SRC_MAC_OFF + Self::MAC_LEN)];
87
88 if buf[Self::OUI_OFF..(Self::OUI_OFF + Self::AARP_OUI.len())] == Self::AARP_OUI {
89 return Ok(Self {
90 dst_mac: *dst_mac.as_array().unwrap(),
91 src_mac: *src_mac.as_array().unwrap(),
92 len: 10,
93 protocol: EtherTalkPhase2Type::Aarp,
94 });
95 } else if buf[Self::OUI_OFF..(Self::OUI_OFF + Self::DDP_OUI.len())] == Self::DDP_OUI {
96 return Ok(Self {
97 dst_mac: *dst_mac.as_array().unwrap(),
98 src_mac: *src_mac.as_array().unwrap(),
99 len: 10,
100 protocol: EtherTalkPhase2Type::Ddp,
101 });
102 }
103
104 Err(UnknownHeader)
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use assert_hex::assert_eq_hex;
112
113 #[test]
114 fn test_parse_ethertalk_aarp() {
115 let test_data: &[u8] = &[
116 0x00, 0x0c, 0x29, 0x0d, 0x56, 0xe3, 0x00, 0x0c, 0x29, 0x0d, 0x56, 0xe4, 0x00, 0x04,
117 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x80, 0xf3, 0x00, 0x01, 0x80, 0x9b, 0x06, 0x04,
118 0x00, 0x03, 0x00, 0x0c, 0x29, 0x0d, 0x56, 0xe3, 0x00, 0xff, 0x1e, 0xf8, 0x00, 0x00,
119 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1e, 0xf8,
120 ];
121 let dst_mac: [u8; 6] = [0x00, 0x0c, 0x29, 0x0d, 0x56, 0xe3];
122 let src_mac: [u8; 6] = [0x00, 0x0c, 0x29, 0x0d, 0x56, 0xe4];
123
124 let packet = EtherTalkPhase2Frame::parse(test_data).expect("failed to parse");
125
126 assert_eq_hex!(
127 packet.dst_mac,
128 dst_mac,
129 "Destination MAC did not match expected"
130 );
131 assert_eq_hex!(packet.src_mac, src_mac, "Source MAC did not match expected");
132
133 match packet.protocol {
134 EtherTalkPhase2Type::Aarp => {}
135 _ => panic!("parsed as wrong type"),
136 };
137 }
138
139 #[test]
140 fn test_parse_ethertalk_ddp() {
141 let test_data: &[u8] = &[
142 0x00, 0x0c, 0x29, 0x0d, 0x56, 0xe3, 0x00, 0x0c, 0x29, 0x0d, 0x56, 0xe4, 0x00, 0x04,
143 0xaa, 0xaa, 0x03, 0x08, 0x00, 0x07, 0x80, 0x9b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00,
144 0xff, 0x1e, 0xff, 0xf8, 0x06, 0x06, 0x06, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 ];
146
147 let packet = EtherTalkPhase2Frame::parse(test_data).expect("failed to parse");
148
149 if EtherTalkPhase2Type::Ddp != packet.protocol {
150 panic!("parsed as wrong type");
151 }
152 }
153}