leodos_protocols/network/isl/gossip/
packet.rs1use bon::bon;
2use zerocopy::FromBytes;
3use zerocopy::Immutable;
4use zerocopy::IntoBytes;
5use zerocopy::KnownLayout;
6use zerocopy::Unaligned;
7use zerocopy::network_endian::U16;
8
9use crate::network::cfe::tc::Telecommand;
10use crate::network::cfe::tc::TelecommandError;
11use crate::network::cfe::tc::TelecommandSecondaryHeader;
12use crate::network::isl::address::Address;
13use crate::network::isl::address::RawAddress;
14use crate::network::spp::Apid;
15use crate::network::spp::PrimaryHeader;
16use crate::network::spp::SequenceCount;
17use crate::utils::checksum_u8;
18use crate::utils::validate_checksum_u8;
19
20#[repr(C)]
50#[derive(FromBytes, IntoBytes, KnownLayout, Unaligned, Immutable)]
51pub struct IslGossipTelecommand {
52 pub primary: PrimaryHeader,
54 pub secondary: TelecommandSecondaryHeader,
56 pub gossip_header: IslGossipHeader,
58 pub payload: [u8],
60}
61
62impl core::fmt::Debug for IslGossipTelecommand {
63 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64 f.debug_struct("GossipTelecommand")
65 .field("primary", &self.primary)
66 .field("secondary", &self.secondary)
67 .field("gossip_header", &self.gossip_header)
68 .field("payload_len", &self.payload.len())
69 .finish()
70 }
71}
72
73#[derive(
75 Clone, Copy, Debug, PartialEq, Eq, IntoBytes, FromBytes, Immutable, KnownLayout, Unaligned,
76)]
77#[repr(transparent)]
78pub struct Epoch(pub U16);
79
80#[repr(C, packed)]
82#[derive(FromBytes, IntoBytes, KnownLayout, Unaligned, Immutable, Copy, Clone, Debug)]
83pub struct IslGossipHeader {
84 origin: RawAddress,
85 predecessor: RawAddress,
86 pub(crate) service_area_min: u8,
87 pub(crate) service_area_max: u8,
88 epoch: Epoch,
89}
90
91#[derive(Debug, Copy, Clone, Eq, PartialEq, thiserror::Error)]
93pub enum GossipMessageError {
94 #[error("CFE Telecommand error: {0}")]
96 Cfe(#[from] TelecommandError),
97 #[error("Payload too small to contain a valid gossip header")]
99 PayloadTooSmall,
100 #[error(
102 "Payload too large: maximum allowed is {max} bytes, but {provided} bytes were provided"
103 )]
104 PayloadTooLarge {
105 max: usize,
107 provided: usize,
109 },
110}
111
112impl IslGossipHeader {
113 pub fn origin(&self) -> Address {
115 self.origin.parse()
116 }
117
118 pub fn set_origin(&mut self, addr: Address) {
120 self.origin = RawAddress::from(addr);
121 }
122
123 pub fn predecessor(&self) -> Address {
125 self.predecessor.parse()
126 }
127
128 pub fn set_predecessor(&mut self, addr: Address) {
130 self.predecessor = RawAddress::from(addr);
131 }
132
133 pub fn epoch(&self) -> Epoch {
135 self.epoch
136 }
137
138 pub fn set_epoch(&mut self, epoch: Epoch) {
140 self.epoch = epoch;
141 }
142
143 pub fn service_area_min(&self) -> u8 {
146 self.service_area_min
147 }
148
149 pub fn set_service_area_min(&mut self, area: u8) {
151 self.service_area_min = area;
152 }
153
154 pub fn service_area_max(&self) -> u8 {
157 self.service_area_max
158 }
159
160 pub fn set_service_area_max(&mut self, area: u8) {
162 self.service_area_max = area;
163 }
164}
165
166#[bon]
167impl IslGossipTelecommand {
168 #[builder]
170 pub fn new<'a>(
171 buffer: &'a mut [u8],
172 apid: Apid,
173 function_code: u8,
174 origin: Address,
175 predecessor: Address,
176 service_area_min: u8,
177 service_area_max: u8,
178 epoch: Epoch,
179 payload_len: usize,
180 ) -> Result<&'a mut Self, GossipMessageError> {
181 if payload_len > u16::MAX as usize {
182 return Err(GossipMessageError::PayloadTooLarge {
183 max: u16::MAX as usize,
184 provided: payload_len,
185 });
186 }
187
188 let tc_payload_len = size_of::<IslGossipHeader>() + payload_len;
189 let tc = Telecommand::builder()
190 .buffer(buffer)
191 .apid(apid)
192 .sequence_count(SequenceCount::new())
193 .function_code(function_code)
194 .payload_len(tc_payload_len)
195 .build()
196 .map_err(GossipMessageError::Cfe)?;
197
198 let buffer = tc.as_mut_bytes();
199 let provided_len = buffer.len();
200 let gossip_tc = Self::mut_from_bytes_with_elems(buffer, payload_len).map_err(|_| {
201 GossipMessageError::Cfe(TelecommandError::BufferTooSmall {
202 required_len: size_of::<PrimaryHeader>()
203 + size_of::<TelecommandSecondaryHeader>()
204 + tc_payload_len,
205 provided_len,
206 })
207 })?;
208
209 gossip_tc.gossip_header.set_origin(origin);
210 gossip_tc.gossip_header.set_predecessor(predecessor);
211 gossip_tc.gossip_header.service_area_min = service_area_min;
212 gossip_tc.gossip_header.service_area_max = service_area_max;
213 gossip_tc.gossip_header.set_epoch(epoch);
214
215 gossip_tc.set_cfe_checksum();
216
217 Ok(gossip_tc)
218 }
219
220 pub fn from_telecommand(tc: &Telecommand) -> Result<&Self, GossipMessageError> {
222 if tc.payload().len() < size_of::<IslGossipHeader>() {
223 return Err(GossipMessageError::PayloadTooSmall);
224 }
225 Ok(Self::ref_from_bytes(tc.as_bytes()).unwrap())
226 }
227
228 pub fn set_cfe_checksum(&mut self) {
230 self.secondary.set_checksum(0);
231 self.secondary.set_checksum(checksum_u8(self.as_bytes()));
232 }
233
234 pub fn validate_cfe_checksum(&self) -> bool {
236 validate_checksum_u8(self.as_bytes())
237 }
238
239 pub fn data_len(&self) -> usize {
241 self.payload.len()
242 }
243}