]> code.octet-stream.net Git - m17rt/blobdiff - m17core/src/reflector.rs
Setters for reflector packets
[m17rt] / m17core / src / reflector.rs
index 64f61ece441f06134bcfae9c986db905f2148a63..657d8644cde46479f7ba5de7031cd63015873698 100644 (file)
@@ -1,14 +1,61 @@
 // Based on https://github.com/n7tae/mrefd/blob/master/Packet-Description.md
 // and the main M17 specification
 
 // Based on https://github.com/n7tae/mrefd/blob/master/Packet-Description.md
 // and the main M17 specification
 
+use crate::address::Address;
 use crate::protocol::LsfFrame;
 
 use crate::protocol::LsfFrame;
 
+macro_rules! define_message {
+    ($t:tt, $sz:tt, $min_sz:tt, $magic:tt) => {
+        pub struct $t(pub [u8; $sz], pub usize);
+
+        impl $t {
+            pub fn new() -> Self {
+                let mut bytes = [0u8; $sz];
+                bytes[0..4].copy_from_slice($magic);
+                Self(bytes, $sz)
+            }
+
+            #[allow(clippy::double_comparisons)] // occurs in some macro invocations
+            #[allow(clippy::manual_range_contains)] // way more readable, good grief
+            pub fn from_bytes(b: &[u8]) -> Option<Self> {
+                let len = b.len();
+                if len > $sz || len < $min_sz {
+                    return None;
+                }
+                let mut s = Self([0; $sz], len);
+                s.0[0..len].copy_from_slice(b);
+                if !s.verify_integrity() {
+                    return None;
+                }
+                Some(s)
+            }
+
+            pub fn as_bytes(&self) -> &[u8] {
+                &self.0[0..self.1]
+            }
+        }
+
+        impl Default for $t {
+            fn default() -> Self {
+                Self::new()
+            }
+        }
+    };
+}
+
 macro_rules! impl_stream_id {
     ($t:ty, $from:tt) => {
         impl $t {
             pub fn stream_id(&self) -> u16 {
                 u16::from_be_bytes([self.0[$from], self.0[$from + 1]])
             }
 macro_rules! impl_stream_id {
     ($t:ty, $from:tt) => {
         impl $t {
             pub fn stream_id(&self) -> u16 {
                 u16::from_be_bytes([self.0[$from], self.0[$from + 1]])
             }
+
+            pub fn set_stream_id(&mut self, id: u16) {
+                let bytes = id.to_be_bytes();
+                self.0[$from] = bytes[0];
+                self.0[$from + 1] = bytes[1];
+                self.recalculate_crc();
+            }
         }
     };
 }
         }
     };
 }
@@ -22,6 +69,11 @@ macro_rules! impl_link_setup {
                 frame.recalculate_crc();
                 frame
             }
                 frame.recalculate_crc();
                 frame
             }
+
+            pub fn set_link_setup_frame(&mut self, lsf: &LsfFrame) {
+                self.0[$from..($from + 28)].copy_from_slice(&lsf.0[0..28]);
+                self.recalculate_crc();
+            }
         }
     };
 }
         }
     };
 }
@@ -34,6 +86,12 @@ macro_rules! impl_link_setup_frame {
                 frame.0[..].copy_from_slice(&self.0[$from..($from + 30)]);
                 frame
             }
                 frame.0[..].copy_from_slice(&self.0[$from..($from + 30)]);
                 frame
             }
+
+            pub fn set_link_setup_frame(&mut self, lsf: &LsfFrame) {
+                debug_assert_eq!(lsf.check_crc(), 0);
+                self.0[$from..($from + 30)].copy_from_slice(&lsf.0);
+                self.recalculate_crc();
+            }
         }
     };
 }
         }
     };
 }
@@ -50,6 +108,22 @@ macro_rules! impl_frame_number {
                 let frame_num = u16::from_be_bytes([self.0[$from], self.0[$from + 1]]);
                 (frame_num & 0x8000) > 0
             }
                 let frame_num = u16::from_be_bytes([self.0[$from], self.0[$from + 1]]);
                 (frame_num & 0x8000) > 0
             }
+
+            pub fn set_frame_number(&mut self, number: u16) {
+                let existing_eos = u16::from_be_bytes([self.0[$from], self.0[$from + 1]]) & 0x8000;
+                let new = (existing_eos | (number & 0x7fff)).to_be_bytes();
+                self.0[$from] = new[0];
+                self.0[$from + 1] = new[1];
+                self.recalculate_crc();
+            }
+
+            pub fn set_end_of_stream(&mut self, eos: bool) {
+                let existing_fn = u16::from_be_bytes([self.0[$from], self.0[$from + 1]]) & 0x7fff;
+                let new = (existing_fn | (if eos { 0x8000 } else { 0 })).to_be_bytes();
+                self.0[$from] = new[0];
+                self.0[$from + 1] = new[1];
+                self.recalculate_crc();
+            }
         }
     };
 }
         }
     };
 }
@@ -60,6 +134,11 @@ macro_rules! impl_payload {
             pub fn payload(&self) -> &[u8] {
                 &self.0[$from..$to]
             }
             pub fn payload(&self) -> &[u8] {
                 &self.0[$from..$to]
             }
+
+            pub fn set_payload(&mut self, bytes: &[u8]) {
+                self.0[$from..$to].copy_from_slice(bytes);
+                self.recalculate_crc();
+            }
         }
     };
 }
         }
     };
 }
@@ -70,6 +149,17 @@ macro_rules! impl_modules {
             pub fn modules(&self) -> ModulesIterator {
                 ModulesIterator::new(&self.0[$from..$to])
             }
             pub fn modules(&self) -> ModulesIterator {
                 ModulesIterator::new(&self.0[$from..$to])
             }
+
+            pub fn set_modules(&mut self, list: &str) {
+                debug_assert!(list.len() < 27);
+                let mut idx = $from;
+                for m in list.chars() {
+                    self.0[idx] = m as u8;
+                    idx += 1;
+                }
+                self.0[idx] = 0;
+                self.recalculate_crc();
+            }
         }
     };
 }
         }
     };
 }
@@ -80,6 +170,11 @@ macro_rules! impl_module {
             pub fn module(&self) -> char {
                 self.0[$at] as char
             }
             pub fn module(&self) -> char {
                 self.0[$at] as char
             }
+
+            pub fn set_module(&mut self, m: char) {
+                self.0[$at] = m as u8;
+                self.recalculate_crc();
+            }
         }
     };
 }
         }
     };
 }
@@ -87,9 +182,15 @@ macro_rules! impl_module {
 macro_rules! impl_address {
     ($t:ty, $from:tt) => {
         impl $t {
 macro_rules! impl_address {
     ($t:ty, $from:tt) => {
         impl $t {
-            pub fn address(&self) -> crate::address::Address {
+            pub fn address(&self) -> Address {
                 crate::address::decode_address(self.0[$from..($from + 6)].try_into().unwrap())
             }
                 crate::address::decode_address(self.0[$from..($from + 6)].try_into().unwrap())
             }
+
+            pub fn set_address(&mut self, address: Address) {
+                let encoded = crate::address::encode_address(&address);
+                self.0[$from..($from + 6)].copy_from_slice(&encoded);
+                self.recalculate_crc();
+            }
         }
     };
 }
         }
     };
 }
@@ -100,6 +201,14 @@ macro_rules! impl_trailing_crc_verify {
             pub fn verify_integrity(&self) -> bool {
                 crate::crc::m17_crc(&self.0) == 0
             }
             pub fn verify_integrity(&self) -> bool {
                 crate::crc::m17_crc(&self.0) == 0
             }
+
+            pub fn recalculate_crc(&mut self) {
+                let len = self.0.len();
+                let start_crc = crate::crc::m17_crc(&self.0[0..(len - 2)]).to_be_bytes();
+                self.0[len - 2] = start_crc[0];
+                self.0[len - 1] = start_crc[1];
+                debug_assert!(self.verify_integrity());
+            }
         }
     };
 }
         }
     };
 }
@@ -110,6 +219,25 @@ macro_rules! impl_internal_crc {
             pub fn verify_integrity(&self) -> bool {
                 crate::crc::m17_crc(&self.0[$from..$to]) == 0
             }
             pub fn verify_integrity(&self) -> bool {
                 crate::crc::m17_crc(&self.0[$from..$to]) == 0
             }
+
+            pub fn recalculate_crc(&mut self) {
+                // assume the last two bytes of the range are the CRC
+                let start_crc = crate::crc::m17_crc(&self.0[$from..($to - 2)]).to_be_bytes();
+                self.0[$to - 2] = start_crc[0];
+                self.0[$to - 1] = start_crc[1];
+                debug_assert!(self.verify_integrity());
+            }
+        }
+    };
+}
+
+macro_rules! no_crc {
+    ($t:ty) => {
+        impl $t {
+            pub fn verify_integrity(&self) -> bool {
+                true
+            }
+            pub fn recalculate_crc(&mut self) {}
         }
     };
 }
         }
     };
 }
@@ -120,6 +248,11 @@ macro_rules! impl_is_relayed {
             pub fn is_relayed(&self) -> bool {
                 self.0[self.0.len() - 1] != 0
             }
             pub fn is_relayed(&self) -> bool {
                 self.0[self.0.len() - 1] != 0
             }
+
+            pub fn set_relayed(&mut self, relayed: bool) {
+                self.0[self.0.len() - 1] = if relayed { 1 } else { 0 };
+                self.recalculate_crc();
+            }
         }
     };
 }
         }
     };
 }
@@ -139,10 +272,10 @@ impl Iterator for ModulesIterator<'_> {
     type Item = char;
 
     fn next(&mut self) -> Option<Self::Item> {
     type Item = char;
 
     fn next(&mut self) -> Option<Self::Item> {
-        if self.modules[self.idx] == 0 {
-            return None;
-        }
         if self.idx < self.modules.len() {
         if self.idx < self.modules.len() {
+            if self.modules[self.idx] == 0 {
+                return None;
+            }
             self.idx += 1;
             return Some(self.modules[self.idx - 1] as char);
         }
             self.idx += 1;
             return Some(self.modules[self.idx - 1] as char);
         }
@@ -165,7 +298,7 @@ pub const MAGIC_PONG: &[u8] = b"PONG";
 /// Messages sent from a station/client to a reflector
 #[allow(clippy::large_enum_variant)]
 pub enum ClientMessage {
 /// Messages sent from a station/client to a reflector
 #[allow(clippy::large_enum_variant)]
 pub enum ClientMessage {
-    VoiceFull(VoiceFull),
+    Voice(Voice),
     VoiceHeader(VoiceHeader),
     VoiceData(VoiceData),
     Packet(Packet),
     VoiceHeader(VoiceHeader),
     VoiceData(VoiceData),
     Packet(Packet),
@@ -175,10 +308,29 @@ pub enum ClientMessage {
     Disconnect(Disconnect),
 }
 
     Disconnect(Disconnect),
 }
 
+impl ClientMessage {
+    pub fn parse(bytes: &[u8]) -> Option<Self> {
+        if bytes.len() < 4 {
+            return None;
+        }
+        match &bytes[0..4] {
+            MAGIC_VOICE => Some(Self::Voice(Voice::from_bytes(bytes)?)),
+            MAGIC_VOICE_HEADER => Some(Self::VoiceHeader(VoiceHeader::from_bytes(bytes)?)),
+            MAGIC_VOICE_DATA => Some(Self::VoiceData(VoiceData::from_bytes(bytes)?)),
+            MAGIC_PACKET => Some(Self::Packet(Packet::from_bytes(bytes)?)),
+            MAGIC_PONG => Some(Self::Pong(Pong::from_bytes(bytes)?)),
+            MAGIC_CONNECT => Some(Self::Connect(Connect::from_bytes(bytes)?)),
+            MAGIC_LISTEN => Some(Self::Listen(Listen::from_bytes(bytes)?)),
+            MAGIC_DISCONNECT => Some(Self::Disconnect(Disconnect::from_bytes(bytes)?)),
+            _ => None,
+        }
+    }
+}
+
 /// Messages sent from a reflector to a station/client
 #[allow(clippy::large_enum_variant)]
 pub enum ServerMessage {
 /// Messages sent from a reflector to a station/client
 #[allow(clippy::large_enum_variant)]
 pub enum ServerMessage {
-    VoiceFull(VoiceFull),
+    Voice(Voice),
     VoiceHeader(VoiceHeader),
     VoiceData(VoiceData),
     Packet(Packet),
     VoiceHeader(VoiceHeader),
     VoiceData(VoiceData),
     Packet(Packet),
@@ -189,6 +341,30 @@ pub enum ServerMessage {
     ConnectNack(ConnectNack),
 }
 
     ConnectNack(ConnectNack),
 }
 
+impl ServerMessage {
+    pub fn parse(bytes: &[u8]) -> Option<Self> {
+        if bytes.len() < 4 {
+            return None;
+        }
+        match &bytes[0..4] {
+            MAGIC_VOICE => Some(Self::Voice(Voice::from_bytes(bytes)?)),
+            MAGIC_VOICE_HEADER => Some(Self::VoiceHeader(VoiceHeader::from_bytes(bytes)?)),
+            MAGIC_VOICE_DATA => Some(Self::VoiceData(VoiceData::from_bytes(bytes)?)),
+            MAGIC_PACKET => Some(Self::Packet(Packet::from_bytes(bytes)?)),
+            MAGIC_PING => Some(Self::Ping(Ping::from_bytes(bytes)?)),
+            MAGIC_DISCONNECT if bytes.len() == 4 => Some(Self::DisconnectAcknowledge(
+                DisconnectAcknowledge::from_bytes(bytes)?,
+            )),
+            MAGIC_DISCONNECT => Some(Self::ForceDisconnect(ForceDisconnect::from_bytes(bytes)?)),
+            MAGIC_ACKNOWLEDGE => Some(Self::ConnectAcknowledge(ConnectAcknowledge::from_bytes(
+                bytes,
+            )?)),
+            MAGIC_NACK => Some(Self::ConnectNack(ConnectNack::from_bytes(bytes)?)),
+            _ => None,
+        }
+    }
+}
+
 /// Messages sent and received between reflectors
 #[allow(clippy::large_enum_variant)]
 pub enum InterlinkMessage {
 /// Messages sent and received between reflectors
 #[allow(clippy::large_enum_variant)]
 pub enum InterlinkMessage {
@@ -203,30 +379,64 @@ pub enum InterlinkMessage {
     DisconnectInterlink(DisconnectInterlink),
 }
 
     DisconnectInterlink(DisconnectInterlink),
 }
 
-pub struct VoiceFull(pub [u8; 54]);
-impl_stream_id!(VoiceFull, 4);
-impl_link_setup!(VoiceFull, 6);
-impl_frame_number!(VoiceFull, 34);
-impl_payload!(VoiceFull, 36, 52);
-impl_trailing_crc_verify!(VoiceFull);
+impl InterlinkMessage {
+    pub fn parse(bytes: &[u8]) -> Option<Self> {
+        if bytes.len() < 4 {
+            return None;
+        }
+        match &bytes[0..4] {
+            MAGIC_VOICE => Some(Self::VoiceInterlink(VoiceInterlink::from_bytes(bytes)?)),
+            MAGIC_VOICE_HEADER => Some(Self::VoiceHeaderInterlink(
+                VoiceHeaderInterlink::from_bytes(bytes)?,
+            )),
+            MAGIC_VOICE_DATA => Some(Self::VoiceDataInterlink(VoiceDataInterlink::from_bytes(
+                bytes,
+            )?)),
+            MAGIC_PACKET => Some(Self::PacketInterlink(PacketInterlink::from_bytes(bytes)?)),
+            MAGIC_PING => Some(Self::Ping(Ping::from_bytes(bytes)?)),
+            MAGIC_CONNECT => Some(Self::ConnectInterlink(ConnectInterlink::from_bytes(bytes)?)),
+            MAGIC_ACKNOWLEDGE => Some(Self::ConnectInterlinkAcknowledge(
+                ConnectInterlinkAcknowledge::from_bytes(bytes)?,
+            )),
+            MAGIC_NACK => Some(Self::ConnectNack(ConnectNack::from_bytes(bytes)?)),
+            MAGIC_DISCONNECT => Some(Self::DisconnectInterlink(DisconnectInterlink::from_bytes(
+                bytes,
+            )?)),
+            _ => None,
+        }
+    }
+}
+
+define_message!(Voice, 54, 54, MAGIC_VOICE);
+impl_stream_id!(Voice, 4);
+impl_link_setup!(Voice, 6);
+impl_frame_number!(Voice, 34);
+impl_payload!(Voice, 36, 52);
+impl_trailing_crc_verify!(Voice);
 
 
-pub struct VoiceHeader(pub [u8; 36]);
+define_message!(VoiceHeader, 36, 36, MAGIC_VOICE_HEADER);
 impl_stream_id!(VoiceHeader, 4);
 impl_link_setup!(VoiceHeader, 6);
 impl_trailing_crc_verify!(VoiceHeader);
 
 impl_stream_id!(VoiceHeader, 4);
 impl_link_setup!(VoiceHeader, 6);
 impl_trailing_crc_verify!(VoiceHeader);
 
-pub struct VoiceData(pub [u8; 26]);
+define_message!(VoiceData, 26, 26, MAGIC_VOICE_DATA);
 impl_stream_id!(VoiceData, 4);
 impl_frame_number!(VoiceData, 6);
 impl_payload!(VoiceData, 8, 24);
 impl_trailing_crc_verify!(VoiceData);
 
 impl_stream_id!(VoiceData, 4);
 impl_frame_number!(VoiceData, 6);
 impl_payload!(VoiceData, 8, 24);
 impl_trailing_crc_verify!(VoiceData);
 
-pub struct Packet(pub [u8; 859]);
+define_message!(Packet, 859, 38, MAGIC_PACKET);
 impl_link_setup_frame!(Packet, 4);
 
 impl Packet {
     pub fn payload(&self) -> &[u8] {
 impl_link_setup_frame!(Packet, 4);
 
 impl Packet {
     pub fn payload(&self) -> &[u8] {
-        &self.0[34..]
+        &self.0[34..self.1]
+    }
+
+    pub fn set_payload(&mut self, bytes: &[u8]) {
+        let end = 34 + bytes.len();
+        self.0[34..end].copy_from_slice(bytes);
+        self.1 = end;
     }
 
     pub fn verify_integrity(&self) -> bool {
     }
 
     pub fn verify_integrity(&self) -> bool {
@@ -234,35 +444,48 @@ impl Packet {
             && self.payload().len() >= 4
             && crate::crc::m17_crc(self.payload()) == 0
     }
             && self.payload().len() >= 4
             && crate::crc::m17_crc(self.payload()) == 0
     }
+
+    pub fn recalculate_crc(&mut self) {
+        // LSF and payload should be confirmed valid before construction
+    }
 }
 
 }
 
-pub struct Pong(pub [u8; 10]);
+define_message!(Pong, 10, 10, MAGIC_PONG);
 impl_address!(Pong, 4);
 impl_address!(Pong, 4);
+no_crc!(Pong);
 
 
-pub struct Connect(pub [u8; 11]);
+define_message!(Connect, 11, 11, MAGIC_CONNECT);
 impl_address!(Connect, 4);
 impl_module!(Connect, 10);
 impl_address!(Connect, 4);
 impl_module!(Connect, 10);
+no_crc!(Connect);
 
 
-pub struct Listen(pub [u8; 11]);
+define_message!(Listen, 11, 11, MAGIC_LISTEN);
 impl_address!(Listen, 4);
 impl_module!(Listen, 10);
 impl_address!(Listen, 4);
 impl_module!(Listen, 10);
+no_crc!(Listen);
 
 
-pub struct Disconnect(pub [u8; 10]);
+define_message!(Disconnect, 10, 10, MAGIC_DISCONNECT);
 impl_address!(Disconnect, 4);
 impl_address!(Disconnect, 4);
+no_crc!(Disconnect);
 
 
-pub struct Ping(pub [u8; 10]);
+define_message!(Ping, 10, 10, MAGIC_PING);
 impl_address!(Ping, 4);
 impl_address!(Ping, 4);
+no_crc!(Ping);
 
 
-pub struct DisconnectAcknowledge(pub [u8; 4]);
+define_message!(DisconnectAcknowledge, 4, 4, MAGIC_DISCONNECT);
+no_crc!(DisconnectAcknowledge);
 
 
-pub struct ForceDisconnect(pub [u8; 10]);
+define_message!(ForceDisconnect, 10, 10, MAGIC_DISCONNECT);
 impl_address!(ForceDisconnect, 4);
 impl_address!(ForceDisconnect, 4);
+no_crc!(ForceDisconnect);
 
 
-pub struct ConnectAcknowledge(pub [u8; 4]);
+define_message!(ConnectAcknowledge, 4, 4, MAGIC_ACKNOWLEDGE);
+no_crc!(ConnectAcknowledge);
 
 
-pub struct ConnectNack(pub [u8; 4]);
+define_message!(ConnectNack, 4, 4, MAGIC_NACK);
+no_crc!(ConnectNack);
 
 
-pub struct VoiceInterlink(pub [u8; 55]);
+define_message!(VoiceInterlink, 55, 55, MAGIC_VOICE);
 impl_stream_id!(VoiceInterlink, 4);
 impl_link_setup!(VoiceInterlink, 6);
 impl_frame_number!(VoiceInterlink, 34);
 impl_stream_id!(VoiceInterlink, 4);
 impl_link_setup!(VoiceInterlink, 6);
 impl_frame_number!(VoiceInterlink, 34);
@@ -270,26 +493,34 @@ impl_payload!(VoiceInterlink, 36, 52);
 impl_internal_crc!(VoiceInterlink, 0, 54);
 impl_is_relayed!(VoiceInterlink);
 
 impl_internal_crc!(VoiceInterlink, 0, 54);
 impl_is_relayed!(VoiceInterlink);
 
-pub struct VoiceHeaderInterlink(pub [u8; 37]);
+define_message!(VoiceHeaderInterlink, 37, 37, MAGIC_VOICE_HEADER);
 impl_stream_id!(VoiceHeaderInterlink, 4);
 impl_link_setup!(VoiceHeaderInterlink, 6);
 impl_internal_crc!(VoiceHeaderInterlink, 0, 36);
 impl_is_relayed!(VoiceHeaderInterlink);
 
 impl_stream_id!(VoiceHeaderInterlink, 4);
 impl_link_setup!(VoiceHeaderInterlink, 6);
 impl_internal_crc!(VoiceHeaderInterlink, 0, 36);
 impl_is_relayed!(VoiceHeaderInterlink);
 
-pub struct VoiceDataInterlink(pub [u8; 27]);
+define_message!(VoiceDataInterlink, 27, 27, MAGIC_VOICE_DATA);
 impl_stream_id!(VoiceDataInterlink, 4);
 impl_frame_number!(VoiceDataInterlink, 6);
 impl_payload!(VoiceDataInterlink, 8, 24);
 impl_internal_crc!(VoiceDataInterlink, 0, 24);
 impl_is_relayed!(VoiceDataInterlink);
 
 impl_stream_id!(VoiceDataInterlink, 4);
 impl_frame_number!(VoiceDataInterlink, 6);
 impl_payload!(VoiceDataInterlink, 8, 24);
 impl_internal_crc!(VoiceDataInterlink, 0, 24);
 impl_is_relayed!(VoiceDataInterlink);
 
-pub struct PacketInterlink(pub [u8; 860]);
+define_message!(PacketInterlink, 860, 39, MAGIC_PACKET);
 impl_link_setup_frame!(PacketInterlink, 4);
 impl_is_relayed!(PacketInterlink);
 
 impl PacketInterlink {
     pub fn payload(&self) -> &[u8] {
 impl_link_setup_frame!(PacketInterlink, 4);
 impl_is_relayed!(PacketInterlink);
 
 impl PacketInterlink {
     pub fn payload(&self) -> &[u8] {
-        &self.0[34..(self.0.len() - 1)]
+        &self.0[34..(self.1 - 1)]
+    }
+
+    pub fn set_payload(&mut self, bytes: &[u8]) {
+        let is_relayed = self.is_relayed();
+        let end = 34 + bytes.len();
+        self.0[34..end].copy_from_slice(bytes);
+        self.1 = end + 1;
+        self.set_relayed(is_relayed);
     }
 
     pub fn verify_integrity(&self) -> bool {
     }
 
     pub fn verify_integrity(&self) -> bool {
@@ -297,15 +528,22 @@ impl PacketInterlink {
             && self.payload().len() >= 4
             && crate::crc::m17_crc(self.payload()) == 0
     }
             && self.payload().len() >= 4
             && crate::crc::m17_crc(self.payload()) == 0
     }
+
+    pub fn recalculate_crc(&mut self) {
+        // LSF and payload should be confirmed valid before construction
+    }
 }
 
 }
 
-pub struct ConnectInterlink(pub [u8; 37]);
+define_message!(ConnectInterlink, 37, 37, MAGIC_CONNECT);
 impl_address!(ConnectInterlink, 4);
 impl_modules!(ConnectInterlink, 10, 37);
 impl_address!(ConnectInterlink, 4);
 impl_modules!(ConnectInterlink, 10, 37);
+no_crc!(ConnectInterlink);
 
 
-pub struct ConnectInterlinkAcknowledge(pub [u8; 37]);
+define_message!(ConnectInterlinkAcknowledge, 37, 37, MAGIC_ACKNOWLEDGE);
 impl_address!(ConnectInterlinkAcknowledge, 4);
 impl_modules!(ConnectInterlinkAcknowledge, 10, 37);
 impl_address!(ConnectInterlinkAcknowledge, 4);
 impl_modules!(ConnectInterlinkAcknowledge, 10, 37);
+no_crc!(ConnectInterlinkAcknowledge);
 
 
-pub struct DisconnectInterlink(pub [u8; 10]);
+define_message!(DisconnectInterlink, 10, 10, MAGIC_DISCONNECT);
 impl_address!(DisconnectInterlink, 4);
 impl_address!(DisconnectInterlink, 4);
+no_crc!(DisconnectInterlink);