1use bytes::{Buf, BufMut, BytesMut};
4
5use pim_core::{FrameCodec, NodeId, PimError};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[repr(u8)]
9pub enum ControlType {
11 IpRequest = 0x01,
13 IpAssign = 0x02,
15 Goodbye = 0x03,
17 Rekey = 0x04,
19 Ping = 0x05,
21 Pong = 0x06,
23 PeerInfo = 0x07,
25 Message = 0x08,
27 MessageAck = 0x09,
29}
30
31impl ControlType {
32 pub fn from_u8(v: u8) -> Result<Self, PimError> {
34 match v {
35 0x01 => Ok(Self::IpRequest),
36 0x02 => Ok(Self::IpAssign),
37 0x03 => Ok(Self::Goodbye),
38 0x04 => Ok(Self::Rekey),
39 0x05 => Ok(Self::Ping),
40 0x06 => Ok(Self::Pong),
41 0x07 => Ok(Self::PeerInfo),
42 0x08 => Ok(Self::Message),
43 0x09 => Ok(Self::MessageAck),
44 other => Err(PimError::Protocol(format!(
45 "unknown control type: 0x{other:02x}"
46 ))),
47 }
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum ControlFrame {
56 IpRequest {
58 requester_id: NodeId,
60 },
61 IpAssign {
63 assigned_ip: [u8; 4],
65 subnet_mask: u8,
67 gateway_ip: [u8; 4],
69 lease_seconds: u32,
71 },
72 Goodbye {
74 departing_id: NodeId,
76 reason: u8,
78 },
79 Rekey,
81 Ping {
83 nonce: u64,
85 },
86 Pong {
88 nonce: u64,
90 },
91 PeerInfo {
95 x25519_pub: [u8; 32],
97 friendly_name: String,
100 },
101 Message {
108 message_id: [u8; 16],
110 timestamp_ms: u64,
112 ciphertext: Vec<u8>,
114 },
115 MessageAck {
117 message_id: [u8; 16],
119 ack_kind: u8,
121 },
122}
123
124impl FrameCodec for ControlFrame {
125 fn encode(&self, buf: &mut BytesMut) {
126 match self {
127 ControlFrame::IpRequest { requester_id } => {
128 buf.put_u8(ControlType::IpRequest as u8);
129 buf.put_slice(requester_id.as_bytes());
130 }
131 ControlFrame::IpAssign {
132 assigned_ip,
133 subnet_mask,
134 gateway_ip,
135 lease_seconds,
136 } => {
137 buf.put_u8(ControlType::IpAssign as u8);
138 buf.put_slice(assigned_ip);
139 buf.put_u8(*subnet_mask);
140 buf.put_slice(gateway_ip);
141 buf.put_u32(*lease_seconds);
142 }
143 ControlFrame::Goodbye {
144 departing_id,
145 reason,
146 } => {
147 buf.put_u8(ControlType::Goodbye as u8);
148 buf.put_slice(departing_id.as_bytes());
149 buf.put_u8(*reason);
150 }
151 ControlFrame::Rekey => {
152 buf.put_u8(ControlType::Rekey as u8);
153 }
154 ControlFrame::Ping { nonce } => {
155 buf.put_u8(ControlType::Ping as u8);
156 buf.put_u64(*nonce);
157 }
158 ControlFrame::Pong { nonce } => {
159 buf.put_u8(ControlType::Pong as u8);
160 buf.put_u64(*nonce);
161 }
162 ControlFrame::PeerInfo {
163 x25519_pub,
164 friendly_name,
165 } => {
166 buf.put_u8(ControlType::PeerInfo as u8);
167 buf.put_slice(x25519_pub);
168 let name_bytes = friendly_name.as_bytes();
169 let name_len = name_bytes.len().min(255) as u8;
170 buf.put_u8(name_len);
171 buf.put_slice(&name_bytes[..name_len as usize]);
172 }
173 ControlFrame::Message {
174 message_id,
175 timestamp_ms,
176 ciphertext,
177 } => {
178 buf.put_u8(ControlType::Message as u8);
179 buf.put_slice(message_id);
180 buf.put_u64(*timestamp_ms);
181 let ciphertext_len = ciphertext.len().min(u16::MAX as usize) as u16;
182 buf.put_u16(ciphertext_len);
183 buf.put_slice(&ciphertext[..ciphertext_len as usize]);
184 }
185 ControlFrame::MessageAck {
186 message_id,
187 ack_kind,
188 } => {
189 buf.put_u8(ControlType::MessageAck as u8);
190 buf.put_slice(message_id);
191 buf.put_u8(*ack_kind);
192 }
193 }
194 }
195
196 fn decode(buf: &mut BytesMut) -> Result<Self, PimError> {
197 if buf.is_empty() {
198 return Err(PimError::Protocol("control frame empty".into()));
199 }
200
201 let control_type = ControlType::from_u8(buf[0])?;
202
203 match control_type {
204 ControlType::IpRequest => {
205 if buf.len() < 17 {
206 return Err(PimError::Protocol("IpRequest too short".into()));
207 }
208 let mut id = [0u8; 16];
209 id.copy_from_slice(&buf[1..17]);
210 buf.advance(17);
211 Ok(ControlFrame::IpRequest {
212 requester_id: NodeId::from_bytes(id),
213 })
214 }
215 ControlType::IpAssign => {
216 if buf.len() < 14 {
217 return Err(PimError::Protocol("IpAssign too short".into()));
219 }
220 let mut assigned_ip = [0u8; 4];
221 assigned_ip.copy_from_slice(&buf[1..5]);
222 let subnet_mask = buf[5];
223 let mut gateway_ip = [0u8; 4];
224 gateway_ip.copy_from_slice(&buf[6..10]);
225 let lease_seconds = (&buf[10..14]).get_u32();
226 buf.advance(14);
227 Ok(ControlFrame::IpAssign {
228 assigned_ip,
229 subnet_mask,
230 gateway_ip,
231 lease_seconds,
232 })
233 }
234 ControlType::Goodbye => {
235 if buf.len() < 18 {
236 return Err(PimError::Protocol("Goodbye too short".into()));
238 }
239 let mut id = [0u8; 16];
240 id.copy_from_slice(&buf[1..17]);
241 let reason = buf[17];
242 buf.advance(18);
243 Ok(ControlFrame::Goodbye {
244 departing_id: NodeId::from_bytes(id),
245 reason,
246 })
247 }
248 ControlType::Rekey => {
249 buf.advance(1);
250 Ok(ControlFrame::Rekey)
251 }
252 ControlType::Ping => {
253 if buf.len() < 9 {
254 return Err(PimError::Protocol("Ping too short".into()));
255 }
256 let nonce = (&buf[1..9]).get_u64();
257 buf.advance(9);
258 Ok(ControlFrame::Ping { nonce })
259 }
260 ControlType::Pong => {
261 if buf.len() < 9 {
262 return Err(PimError::Protocol("Pong too short".into()));
263 }
264 let nonce = (&buf[1..9]).get_u64();
265 buf.advance(9);
266 Ok(ControlFrame::Pong { nonce })
267 }
268 ControlType::PeerInfo => {
269 if buf.len() < 34 {
271 return Err(PimError::Protocol("PeerInfo too short".into()));
272 }
273 let mut x25519_pub = [0u8; 32];
274 x25519_pub.copy_from_slice(&buf[1..33]);
275 let name_len = buf[33] as usize;
276 let total = 34 + name_len;
277 if buf.len() < total {
278 return Err(PimError::Protocol(format!(
279 "PeerInfo truncated: need {total}, have {}",
280 buf.len()
281 )));
282 }
283 let friendly_name = match std::str::from_utf8(&buf[34..total]) {
284 Ok(s) => s.to_owned(),
285 Err(_) => {
286 return Err(PimError::Protocol(
287 "PeerInfo friendly_name not valid UTF-8".into(),
288 ))
289 }
290 };
291 buf.advance(total);
292 Ok(ControlFrame::PeerInfo {
293 x25519_pub,
294 friendly_name,
295 })
296 }
297 ControlType::Message => {
298 if buf.len() < 27 {
300 return Err(PimError::Protocol("Message too short".into()));
301 }
302 let mut message_id = [0u8; 16];
303 message_id.copy_from_slice(&buf[1..17]);
304 let timestamp_ms = (&buf[17..25]).get_u64();
305 let ciphertext_len = (&buf[25..27]).get_u16() as usize;
306 let total = 27 + ciphertext_len;
307 if buf.len() < total {
308 return Err(PimError::Protocol(format!(
309 "Message truncated: need {total}, have {}",
310 buf.len()
311 )));
312 }
313 let ciphertext = buf[27..total].to_vec();
314 buf.advance(total);
315 Ok(ControlFrame::Message {
316 message_id,
317 timestamp_ms,
318 ciphertext,
319 })
320 }
321 ControlType::MessageAck => {
322 if buf.len() < 18 {
324 return Err(PimError::Protocol("MessageAck too short".into()));
325 }
326 let mut message_id = [0u8; 16];
327 message_id.copy_from_slice(&buf[1..17]);
328 let ack_kind = buf[17];
329 buf.advance(18);
330 Ok(ControlFrame::MessageAck {
331 message_id,
332 ack_kind,
333 })
334 }
335 }
336 }
337}
338
339#[cfg(test)]
340mod tests;