]> code.octet-stream.net Git - m17rt/blob - m17core/src/protocol.rs
Partial unit tests for stream frame handling by TNC
[m17rt] / m17core / src / protocol.rs
1 use crate::address::Address;
2
3 pub(crate) const LSF_SYNC: [i8; 8] = [1, 1, 1, 1, -1, -1, 1, -1];
4 pub(crate) const BERT_SYNC: [i8; 8] = [-1, 1, -1, -1, 1, 1, 1, 1];
5 pub(crate) const STREAM_SYNC: [i8; 8] = [-1, -1, -1, -1, 1, 1, -1, 1];
6 pub(crate) const PACKET_SYNC: [i8; 8] = [1, -1, 1, 1, -1, -1, -1, -1];
7
8 #[derive(Debug, Clone, PartialEq, Eq)]
9 pub enum Mode {
10 Packet,
11 Stream,
12 }
13 #[derive(Debug, Clone, PartialEq, Eq)]
14 pub enum DataType {
15 Reserved,
16 Data,
17 Voice,
18 VoiceAndData,
19 }
20 #[derive(Debug, Clone, PartialEq, Eq)]
21 pub enum EncryptionType {
22 None,
23 Scrambler,
24 Aes,
25 Other,
26 }
27
28 #[derive(Debug, Clone, PartialEq, Eq)]
29 pub enum Frame {
30 Lsf(LsfFrame),
31 Stream(StreamFrame),
32 Packet(PacketFrame),
33 // BERT
34 }
35
36 #[derive(Debug, Clone, PartialEq, Eq)]
37 pub enum PacketType {
38 /// RAW
39 Raw,
40 /// AX.25
41 Ax25,
42 /// APRS
43 Aprs,
44 /// 6LoWPAN
45 SixLowPan,
46 /// IPv4
47 Ipv4,
48 /// SMS
49 Sms,
50 /// Winlink
51 Winlink,
52 /// Custom identifier
53 Other(char),
54 }
55
56 impl PacketType {
57 pub fn from_proto(buf: &[u8]) -> Option<(Self, usize)> {
58 buf.utf8_chunks()
59 .next()
60 .and_then(|chunk| chunk.valid().chars().next())
61 .map(|c| match c as u32 {
62 0x00 => (PacketType::Raw, 1),
63 0x01 => (PacketType::Ax25, 1),
64 0x02 => (PacketType::Aprs, 1),
65 0x03 => (PacketType::SixLowPan, 1),
66 0x04 => (PacketType::Ipv4, 1),
67 0x05 => (PacketType::Sms, 1),
68 0x06 => (PacketType::Winlink, 1),
69 _ => (PacketType::Other(c), c.len_utf8()),
70 })
71 }
72
73 pub fn as_proto(&self) -> ([u8; 4], usize) {
74 match self {
75 PacketType::Raw => ([0, 0, 0, 0], 1),
76 PacketType::Ax25 => ([1, 0, 0, 0], 1),
77 PacketType::Aprs => ([2, 0, 0, 0], 1),
78 PacketType::SixLowPan => ([3, 0, 0, 0], 1),
79 PacketType::Ipv4 => ([4, 0, 0, 0], 1),
80 PacketType::Sms => ([5, 0, 0, 0], 1),
81 PacketType::Winlink => ([6, 0, 0, 0], 1),
82 PacketType::Other(c) => {
83 let mut buf = [0u8; 4];
84 let s = c.encode_utf8(&mut buf);
85 let len = s.len();
86 (buf, len)
87 }
88 }
89 }
90 }
91
92 #[derive(Debug, Clone, PartialEq, Eq)]
93 pub struct LsfFrame(pub [u8; 30]);
94
95 impl LsfFrame {
96 pub fn crc(&self) -> u16 {
97 crate::crc::m17_crc(&self.0)
98 }
99
100 pub fn destination(&self) -> Address {
101 crate::address::decode_address((&self.0[0..6]).try_into().unwrap())
102 }
103
104 pub fn source(&self) -> Address {
105 crate::address::decode_address((&self.0[6..12]).try_into().unwrap())
106 }
107
108 pub fn mode(&self) -> Mode {
109 if self.0[12] & 0x01 > 0 {
110 Mode::Stream
111 } else {
112 Mode::Packet
113 }
114 }
115
116 pub fn data_type(&self) -> DataType {
117 match (self.0[12] >> 1) & 0x03 {
118 0b00 => DataType::Reserved,
119 0b01 => DataType::Data,
120 0b10 => DataType::Voice,
121 0b11 => DataType::VoiceAndData,
122 _ => unreachable!(),
123 }
124 }
125
126 pub fn encryption_type(&self) -> EncryptionType {
127 match (self.0[12] >> 3) & 0x03 {
128 0b00 => EncryptionType::None,
129 0b01 => EncryptionType::Scrambler,
130 0b10 => EncryptionType::Aes,
131 0b11 => EncryptionType::Other,
132 _ => unreachable!(),
133 }
134 }
135
136 // TODO: encryption sub-type
137
138 pub fn channel_access_number(&self) -> u8 {
139 (self.0[12] >> 7) & 0x0f
140 }
141
142 pub fn meta(&self) -> [u8; 14] {
143 self.0[14..28].try_into().unwrap()
144 }
145 }
146
147 #[derive(Debug, Clone, PartialEq, Eq)]
148 pub struct StreamFrame {
149 /// Which LICH segment is given in this frame, from 0 to 5 inclusive
150 pub lich_idx: u8,
151 /// Decoded LICH segment
152 pub lich_part: [u8; 5],
153 /// Which frame in the transmission this is, starting from 0
154 pub frame_number: u16,
155 /// Is this the last frame in the transmission?
156 pub end_of_stream: bool,
157 /// Raw application data in this frame
158 pub stream_data: [u8; 16],
159 }
160
161 #[derive(Debug, Clone, PartialEq, Eq)]
162 pub struct PacketFrame {
163 /// Application packet payload (chunk)
164 pub payload: [u8; 25],
165
166 /// Frame counter, which provides different information depending on whether this is the last frame or not.
167 pub counter: PacketFrameCounter,
168 }
169
170 #[derive(Debug, Clone, PartialEq, Eq)]
171 pub enum PacketFrameCounter {
172 /// Any packet frame that comes after the LSF and is not the final frame.
173 Frame {
174 /// Which frame this is in the superframe, from 0 to 31 inclusive.
175 ///
176 /// If a 33rd frame exists (index 32), it will be a `FinalFrame` instead.
177 ///
178 /// All 25 bytes of of `payload` are filled and valid.
179 index: usize,
180 },
181 /// The final frame in the packet superframe.
182 FinalFrame {
183 /// The number of bytes in `payload` that are filled.
184 payload_len: usize,
185 },
186 }
187
188 pub struct LichCollection([Option<[u8; 5]>; 6]);
189
190 impl LichCollection {
191 pub fn new() -> Self {
192 Self([None; 6])
193 }
194
195 pub fn valid_segments(&self) -> usize {
196 self.0.iter().filter(|s| s.is_some()).count()
197 }
198
199 pub fn set_segment(&mut self, counter: u8, part: [u8; 5]) {
200 self.0[counter as usize] = Some(part);
201 }
202
203 pub fn try_assemble(&self) -> Option<[u8; 30]> {
204 let mut out = [0u8; 30];
205 for (i, segment) in self.0.iter().enumerate() {
206 let Some(segment) = segment else {
207 return None;
208 };
209 for (j, seg_val) in segment.iter().enumerate() {
210 out[i * 5 + j] = *seg_val;
211 }
212 }
213 Some(out)
214 }
215 }
216
217 impl Default for LichCollection {
218 fn default() -> Self {
219 Self::new()
220 }
221 }