1#![forbid(unsafe_code)]
16
17use anyhow::{self as ah, format_err as err, Context as _};
18use letmein_conf::ConfigChecksum;
19use letmein_proto::{ResourceId, UserId};
20use std::{
21 future::Future,
22 net::{IpAddr, Ipv4Addr},
23};
24use tokio::io::ErrorKind;
25
26#[cfg(any(target_os = "linux", target_os = "android"))]
27use tokio::net::UnixStream;
28
29#[cfg(target_os = "windows")]
30use tokio::net::windows::named_pipe::{NamedPipeClient, NamedPipeServer};
31
32pub const SOCK_FILE: &str = "letmeinfwd.sock";
34
35#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
37#[repr(u16)]
38pub enum FirewallOperation {
39 #[default]
41 Nack,
42 Ack,
44 Install,
46 Revoke,
48}
49
50impl TryFrom<u16> for FirewallOperation {
51 type Error = ah::Error;
52
53 fn try_from(value: u16) -> Result<Self, Self::Error> {
54 const OPERATION_NACK: u16 = FirewallOperation::Nack as u16;
55 const OPERATION_ACK: u16 = FirewallOperation::Ack as u16;
56 const OPERATION_INSTALL: u16 = FirewallOperation::Install as u16;
57 const OPERATION_REVOKE: u16 = FirewallOperation::Revoke as u16;
58 match value {
59 OPERATION_NACK => Ok(Self::Nack),
60 OPERATION_ACK => Ok(Self::Ack),
61 OPERATION_INSTALL => Ok(Self::Install),
62 OPERATION_REVOKE => Ok(Self::Revoke),
63 _ => Err(err!("Invalid FirewallMessage/Operation value")),
64 }
65 }
66}
67
68impl From<FirewallOperation> for u16 {
69 fn from(operation: FirewallOperation) -> u16 {
70 operation as _
71 }
72}
73
74#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
76#[repr(u16)]
77pub enum AddrType {
78 #[default]
80 Ipv6,
81 Ipv4,
83}
84
85impl TryFrom<u16> for AddrType {
86 type Error = ah::Error;
87
88 fn try_from(value: u16) -> Result<Self, Self::Error> {
89 const ADDRTYPE_IPV6: u16 = AddrType::Ipv6 as u16;
90 const ADDRTYPE_IPV4: u16 = AddrType::Ipv4 as u16;
91 match value {
92 ADDRTYPE_IPV6 => Ok(Self::Ipv6),
93 ADDRTYPE_IPV4 => Ok(Self::Ipv4),
94 _ => Err(err!("Invalid FirewallMessage/AddrType value")),
95 }
96 }
97}
98
99impl From<AddrType> for u16 {
100 fn from(addr_type: AddrType) -> u16 {
101 addr_type as _
102 }
103}
104
105const ADDR_SIZE: usize = 16;
107
108const CONF_CS_SIZE: usize = ConfigChecksum::SIZE;
110
111const FWMSG_SIZE: usize = 2 + 4 + 4 + 2 + ADDR_SIZE + CONF_CS_SIZE;
113
114const FWMSG_OFFS_OPERATION: usize = 0;
116
117const FWMSG_OFFS_USER: usize = 2;
119
120const FWMSG_OFFS_RESOURCE: usize = 6;
122
123const FWMSG_OFFS_ADDR_TYPE: usize = 10;
125
126const FWMSG_OFFS_ADDR: usize = 12;
128
129const FWMSG_OFFS_CONF_CS: usize = 28;
131
132#[derive(PartialEq, Eq, Debug, Default)]
134pub struct FirewallMessage {
135 operation: FirewallOperation,
136 user: UserId,
137 resource: ResourceId,
138 addr_type: AddrType,
139 addr: [u8; ADDR_SIZE],
140 conf_cs: ConfigChecksum,
141}
142
143fn addr_to_octets(addr: IpAddr) -> (AddrType, [u8; ADDR_SIZE]) {
145 match addr {
146 IpAddr::V4(addr) => {
147 let o = addr.octets();
148 (
149 AddrType::Ipv4,
150 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, o[0], o[1], o[2], o[3]],
151 )
152 }
153 IpAddr::V6(addr) => (AddrType::Ipv6, addr.octets()),
154 }
155}
156
157fn octets_to_addr(addr_type: AddrType, addr: &[u8; ADDR_SIZE]) -> IpAddr {
159 match addr_type {
160 AddrType::Ipv4 => Ipv4Addr::new(addr[12], addr[13], addr[14], addr[15]).into(),
161 AddrType::Ipv6 => (*addr).into(),
162 }
163}
164
165impl FirewallMessage {
166 pub fn new_install(
168 user: UserId,
169 resource: ResourceId,
170 addr: IpAddr,
171 conf_cs: &ConfigChecksum,
172 ) -> Self {
173 let (addr_type, addr) = addr_to_octets(addr);
174 Self {
175 operation: FirewallOperation::Install,
176 user,
177 resource,
178 addr_type,
179 addr,
180 conf_cs: conf_cs.clone(),
181 }
182 }
183
184 pub fn new_revoke(
186 user: UserId,
187 resource: ResourceId,
188 addr: IpAddr,
189 conf_cs: &ConfigChecksum,
190 ) -> Self {
191 let (addr_type, addr) = addr_to_octets(addr);
192 Self {
193 operation: FirewallOperation::Revoke,
194 user,
195 resource,
196 addr_type,
197 addr,
198 conf_cs: conf_cs.clone(),
199 }
200 }
201
202 pub fn new_ack() -> Self {
204 Self {
205 operation: FirewallOperation::Ack,
206 ..Default::default()
207 }
208 }
209
210 pub fn new_nack() -> Self {
212 Self {
213 operation: FirewallOperation::Nack,
214 ..Default::default()
215 }
216 }
217
218 pub fn operation(&self) -> FirewallOperation {
220 self.operation
221 }
222
223 pub fn user(&self) -> UserId {
225 self.user
226 }
227
228 pub fn resource(&self) -> ResourceId {
230 self.resource
231 }
232
233 pub fn addr(&self) -> Option<IpAddr> {
235 match self.operation {
236 FirewallOperation::Install | FirewallOperation::Revoke => {
237 Some(octets_to_addr(self.addr_type, &self.addr))
238 }
239 FirewallOperation::Ack | FirewallOperation::Nack => None,
240 }
241 }
242
243 pub fn conf_checksum(&self) -> Option<&ConfigChecksum> {
245 match self.operation {
246 FirewallOperation::Install | FirewallOperation::Revoke => Some(&self.conf_cs),
247 FirewallOperation::Ack | FirewallOperation::Nack => None,
248 }
249 }
250
251 pub fn msg_serialize(&self) -> ah::Result<[u8; FWMSG_SIZE]> {
253 #[inline]
257 fn serialize_u16(buf: &mut [u8], value: u16) {
258 buf[0..2].copy_from_slice(&value.to_be_bytes());
259 }
260
261 #[inline]
262 fn serialize_u32(buf: &mut [u8], value: u32) {
263 buf[0..4].copy_from_slice(&value.to_be_bytes());
264 }
265
266 let mut buf = [0; FWMSG_SIZE];
267 serialize_u16(&mut buf[FWMSG_OFFS_OPERATION..], self.operation.into());
268 serialize_u32(&mut buf[FWMSG_OFFS_USER..], self.user.into());
269 serialize_u32(&mut buf[FWMSG_OFFS_RESOURCE..], self.resource.into());
270 serialize_u16(&mut buf[FWMSG_OFFS_ADDR_TYPE..], self.addr_type.into());
271 buf[FWMSG_OFFS_ADDR..FWMSG_OFFS_ADDR + ADDR_SIZE].copy_from_slice(&self.addr);
272 buf[FWMSG_OFFS_CONF_CS..FWMSG_OFFS_CONF_CS + CONF_CS_SIZE]
273 .copy_from_slice(self.conf_cs.as_bytes());
274
275 Ok(buf)
276 }
277
278 pub fn try_msg_deserialize(buf: &[u8]) -> ah::Result<Self> {
280 if buf.len() != FWMSG_SIZE {
281 return Err(err!("Deserialize: Raw message size mismatch."));
282 }
283
284 #[inline]
288 fn deserialize_u16(buf: &[u8]) -> ah::Result<u16> {
289 Ok(u16::from_be_bytes(buf[0..2].try_into()?))
290 }
291
292 #[inline]
293 fn deserialize_u32(buf: &[u8]) -> ah::Result<u32> {
294 Ok(u32::from_be_bytes(buf[0..4].try_into()?))
295 }
296
297 let operation = deserialize_u16(&buf[FWMSG_OFFS_OPERATION..])?;
298 let user = deserialize_u32(&buf[FWMSG_OFFS_USER..])?;
299 let resource = deserialize_u32(&buf[FWMSG_OFFS_RESOURCE..])?;
300 let addr_type = deserialize_u16(&buf[FWMSG_OFFS_ADDR_TYPE..])?;
301 let addr = &buf[FWMSG_OFFS_ADDR..FWMSG_OFFS_ADDR + ADDR_SIZE];
302 let conf_cs = &buf[FWMSG_OFFS_CONF_CS..FWMSG_OFFS_CONF_CS + CONF_CS_SIZE];
303
304 Ok(Self {
305 operation: operation.try_into()?,
306 resource: resource.into(),
307 user: user.into(),
308 addr_type: addr_type.try_into()?,
309 addr: addr.try_into()?,
310 conf_cs: conf_cs.try_into()?,
311 })
312 }
313
314 pub async fn send(&self, stream: &mut impl Stream) -> ah::Result<()> {
316 let txbuf = self.msg_serialize()?;
317 let mut txcount = 0;
318 loop {
319 stream.writable().await.context("Socket polling (tx)")?;
320 match stream.try_write(&txbuf[txcount..]) {
321 Ok(n) => {
322 txcount += n;
323 assert!(txcount <= txbuf.len());
324 if txcount == txbuf.len() {
325 return Ok(());
326 }
327 }
328 Err(e) if e.kind() == ErrorKind::WouldBlock => (),
329 Err(e) => {
330 return Err(err!("Socket write: {e}"));
331 }
332 }
333 }
334 }
335
336 pub async fn recv(stream: &mut impl Stream) -> ah::Result<Option<Self>> {
338 let mut rxbuf = [0; FWMSG_SIZE];
339 let mut rxcount = 0;
340 loop {
341 stream.readable().await.context("Socket polling (rx)")?;
342 match stream.try_read(&mut rxbuf[rxcount..]) {
343 Ok(n) => {
344 if n == 0 {
345 return Ok(None);
346 }
347 rxcount += n;
348 assert!(rxcount <= FWMSG_SIZE);
349 if rxcount == FWMSG_SIZE {
350 return Ok(Some(Self::try_msg_deserialize(&rxbuf)?));
351 }
352 }
353 Err(e) if e.kind() == ErrorKind::WouldBlock => (),
354 Err(e) => {
355 return Err(err!("Socket read: {e}"));
356 }
357 }
358 }
359 }
360}
361
362pub trait Stream {
364 fn readable(&self) -> impl Future<Output = std::io::Result<()>> + Send;
365 fn try_read(&self, buf: &mut [u8]) -> std::io::Result<usize>;
366 fn writable(&self) -> impl Future<Output = std::io::Result<()>> + Send;
367 fn try_write(&self, buf: &[u8]) -> std::io::Result<usize>;
368}
369
370macro_rules! impl_stream_for {
371 ($ty:ty) => {
372 impl Stream for $ty {
373 fn readable(&self) -> impl Future<Output = std::io::Result<()>> + Send {
374 self.readable()
375 }
376 fn try_read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
377 self.try_read(buf)
378 }
379 fn writable(&self) -> impl Future<Output = std::io::Result<()>> + Send {
380 self.writable()
381 }
382 fn try_write(&self, buf: &[u8]) -> std::io::Result<usize> {
383 self.try_write(buf)
384 }
385 }
386 };
387}
388
389#[cfg(any(target_os = "linux", target_os = "android"))]
390impl_stream_for!(UnixStream);
391#[cfg(target_os = "windows")]
392impl_stream_for!(NamedPipeClient);
393#[cfg(target_os = "windows")]
394impl_stream_for!(NamedPipeServer);
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399
400 fn check_ser_de(msg: &FirewallMessage) {
401 let bytes = msg.msg_serialize().unwrap();
404 let msg_de = FirewallMessage::try_msg_deserialize(&bytes).unwrap();
405 assert_eq!(*msg, msg_de);
406 }
407
408 #[test]
409 fn test_msg_install_v6() {
410 let msg = FirewallMessage::new_install(
411 0x66773322.into(),
412 0xAABB9988.into(),
413 "::1".parse().unwrap(),
414 &ConfigChecksum::calculate(b"foo"),
415 );
416 assert_eq!(msg.operation(), FirewallOperation::Install);
417 assert_eq!(msg.addr(), Some("::1".parse().unwrap()));
418 check_ser_de(&msg);
419
420 let msg = FirewallMessage::new_install(
421 0x66773322.into(),
422 0xAABB9988.into(),
423 "0102:0304:0506:0708:090A:0B0C:0D0E:0F10".parse().unwrap(),
424 &ConfigChecksum::calculate(b"foo"),
425 );
426 let bytes = msg.msg_serialize().unwrap();
427 assert_eq!(
428 bytes,
429 [
430 0x00, 0x02, 0x66, 0x77, 0x33, 0x22, 0xAA, 0xBB, 0x99, 0x88, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0xA9, 0x14, 0x20, 0xEB, 0x27, 0x9B, 0xD1, 0x96, 0x15, 0x04, 0x9D, 0x00, 0xD6, 0x07, 0x07, 0x35, 0xAF, 0xF1, 0xE9, 0x28, 0xC1, 0xBF, 0x9C, 0x65, 0x3F, 0x29, 0x22, 0x33, 0x11, 0xDD, 0x4C, 0xAA, ]
441 );
442
443 let msg = FirewallMessage::new_install(
444 0x66773322.into(),
445 0xAABB9988.into(),
446 "0102:0304:0506:0708:090A:0B0C:0D0E:0F10".parse().unwrap(),
447 &ConfigChecksum::calculate(b"foo"),
448 );
449 let bytes = msg.msg_serialize().unwrap();
450 assert_eq!(
451 bytes,
452 [
453 0x00, 0x02, 0x66, 0x77, 0x33, 0x22, 0xAA, 0xBB, 0x99, 0x88, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0xA9, 0x14, 0x20, 0xEB, 0x27, 0x9B, 0xD1, 0x96, 0x15, 0x04, 0x9D, 0x00, 0xD6, 0x07, 0x07, 0x35, 0xAF, 0xF1, 0xE9, 0x28, 0xC1, 0xBF, 0x9C, 0x65, 0x3F, 0x29, 0x22, 0x33, 0x11, 0xDD, 0x4C, 0xAA, ]
464 );
465
466 let msg = FirewallMessage::new_install(
467 0x66773322.into(),
468 0xAABB9988.into(),
469 "0102:0304:0506:0708:090A:0B0C:0D0E:0F10".parse().unwrap(),
470 &ConfigChecksum::calculate(b"bar"),
471 );
472 let bytes = msg.msg_serialize().unwrap();
473 assert_eq!(
474 bytes,
475 [
476 0x00, 0x02, 0x66, 0x77, 0x33, 0x22, 0xAA, 0xBB, 0x99, 0x88, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0xFB, 0x58, 0xE5, 0x1D, 0x34, 0xB5, 0x6B, 0x33, 0x78, 0xF6, 0xEC, 0x2D, 0x4A, 0x54, 0x96, 0xC2, 0xAF, 0x00, 0xF2, 0x75, 0x02, 0x00, 0x0F, 0xD5, 0x98, 0xED, 0x31, 0x09, 0x73, 0x68, 0x50, 0x79, ]
487 );
488 }
489
490 #[test]
491 fn test_msg_install_v4() {
492 let msg = FirewallMessage::new_install(
493 0x66773322.into(),
494 0xAABB9988.into(),
495 "1.2.3.4".parse().unwrap(),
496 &ConfigChecksum::calculate(b"foo"),
497 );
498 assert_eq!(msg.operation(), FirewallOperation::Install);
499 assert_eq!(msg.addr(), Some("1.2.3.4".parse().unwrap()));
500 check_ser_de(&msg);
501
502 let bytes = msg.msg_serialize().unwrap();
503 assert_eq!(
504 bytes,
505 [
506 0x00, 0x02, 0x66, 0x77, 0x33, 0x22, 0xAA, 0xBB, 0x99, 0x88, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0xA9, 0x14, 0x20, 0xEB, 0x27, 0x9B, 0xD1, 0x96, 0x15, 0x04, 0x9D, 0x00, 0xD6, 0x07, 0x07, 0x35, 0xAF, 0xF1, 0xE9, 0x28, 0xC1, 0xBF, 0x9C, 0x65, 0x3F, 0x29, 0x22, 0x33, 0x11, 0xDD, 0x4C, 0xAA, ]
517 );
518 }
519
520 #[test]
521 fn test_msg_revoke_v6() {
522 let msg = FirewallMessage::new_revoke(
523 0x66773322.into(),
524 0xAABB9988.into(),
525 "0102:0304:0506:0708:090A:0B0C:0D0E:0F10".parse().unwrap(),
526 &ConfigChecksum::calculate(b"foo"),
527 );
528 assert_eq!(msg.operation(), FirewallOperation::Revoke);
529 assert_eq!(
530 msg.addr(),
531 Some("0102:0304:0506:0708:090A:0B0C:0D0E:0F10".parse().unwrap())
532 );
533 check_ser_de(&msg);
534
535 let bytes = msg.msg_serialize().unwrap();
536 assert_eq!(
537 bytes,
538 [
539 0x00, 0x03, 0x66, 0x77, 0x33, 0x22, 0xAA, 0xBB, 0x99, 0x88, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0xA9, 0x14, 0x20, 0xEB, 0x27, 0x9B, 0xD1, 0x96, 0x15, 0x04, 0x9D, 0x00, 0xD6, 0x07, 0x07, 0x35, 0xAF, 0xF1, 0xE9, 0x28, 0xC1, 0xBF, 0x9C, 0x65, 0x3F, 0x29, 0x22, 0x33, 0x11, 0xDD, 0x4C, 0xAA, ]
550 );
551 }
552
553 #[test]
554 fn test_msg_revoke_v4() {
555 let msg = FirewallMessage::new_revoke(
556 0x66773322.into(),
557 0xAABB9988.into(),
558 "1.2.3.4".parse().unwrap(),
559 &ConfigChecksum::calculate(b"foo"),
560 );
561 assert_eq!(msg.operation(), FirewallOperation::Revoke);
562 assert_eq!(msg.addr(), Some("1.2.3.4".parse().unwrap()));
563 check_ser_de(&msg);
564
565 let bytes = msg.msg_serialize().unwrap();
566 assert_eq!(
567 bytes,
568 [
569 0x00, 0x03, 0x66, 0x77, 0x33, 0x22, 0xAA, 0xBB, 0x99, 0x88, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0xA9, 0x14, 0x20, 0xEB, 0x27, 0x9B, 0xD1, 0x96, 0x15, 0x04, 0x9D, 0x00, 0xD6, 0x07, 0x07, 0x35, 0xAF, 0xF1, 0xE9, 0x28, 0xC1, 0xBF, 0x9C, 0x65, 0x3F, 0x29, 0x22, 0x33, 0x11, 0xDD, 0x4C, 0xAA, ]
580 );
581 }
582
583 #[test]
584 fn test_msg_ack() {
585 let msg = FirewallMessage::new_ack();
586 assert_eq!(msg.operation(), FirewallOperation::Ack);
587 assert_eq!(msg.addr(), None);
588 check_ser_de(&msg);
589
590 let bytes = msg.msg_serialize().unwrap();
591 assert_eq!(
592 bytes,
593 [
594 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]
605 );
606 }
607
608 #[test]
609 fn test_msg_nack() {
610 let msg = FirewallMessage::new_nack();
611 assert_eq!(msg.operation(), FirewallOperation::Nack);
612 assert_eq!(msg.addr(), None);
613 check_ser_de(&msg);
614
615 let bytes = msg.msg_serialize().unwrap();
616 assert_eq!(
617 bytes,
618 [
619 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]
630 );
631 }
632}
633
634