pamoja_gateway/network.rs
1//! The network side of a single site: what a server does with what a gateway forwarded.
2//!
3//! A gateway hands over packets without reading them, because it holds no keys. Deciding
4//! what a packet is, admitting the device that sent it, decrypting what it carries, and
5//! working out when and where to answer is the network server's work, and this module does
6//! that much of it for one site: a [`Network`] holds the devices it admits, the sessions it
7//! has granted, and the counters it has seen, and turns an [`Rxpk`] into an [`Event`].
8//!
9//! It runs no sockets and keeps no clock. An uplink goes in as the gateway reported it, and
10//! a downlink comes back as a [`Txpk`] ready for a `PULL_RESP`, timed in the concentrator's
11//! own microseconds, so the caller owns every decision about the wire.
12//!
13//! The windows come from the published parameters rather than from habit. RP002-1.0.5
14//! section 3.3 gives the delays that are recommended for every region, TS001-1.0.4 gives the
15//! layout of the two bytes a join accept carries, and the channel a first window answers on
16//! is regional: [`Rx1Channels`] carries the two shapes the specification defines.
17//!
18//! # Examples
19//!
20//! A device joins, sends a reading, and is answered in its first receive window.
21//!
22//! ```
23//! use pamoja_gateway::network::{Event, Network, Registration};
24//! use pamoja_gateway::udp::Rxpk;
25//! use pamoja_lora::region::Region;
26//! use pamoja_lora::LinkSettings;
27//! use pamoja_lorawan::Device;
28//!
29//! let mut network = Network::new(Region::Eu868.plan(), 0x00_00_2A);
30//! let device = Device::new([1; 8], [2; 8], [3; 16]);
31//! network.register(Registration::new([1; 8], [2; 8], [3; 16]));
32//!
33//! // The gateway forwards the join request it heard.
34//! let link = LinkSettings::new(7, 125_000);
35//! let request = device.join_request(0x1234);
36//! let heard = Rxpk::new(868_100_000, link, request.as_bytes().to_vec()).with_timestamp_us(1_000);
37//! let joined = network.uplink(&heard).expect("the request verifies");
38//! assert!(matches!(joined, Event::Joined { .. }));
39//! ```
40
41use pamoja_lora::region::{ChannelBlock, ChannelPlan, OwnedChannelPlan};
42use pamoja_lora::LinkSettings;
43use pamoja_lorawan::{
44 Downlink, FrameHeader, JoinGrant, JoinRequest, LorawanError, MessageType, Session,
45};
46
47use crate::udp::{Rxpk, Txpk};
48
49/// The delay between the end of an uplink and the opening of the first receive window, in
50/// microseconds, as RP002-1.0.5 section 3.3 recommends for every region.
51pub const RECEIVE_DELAY1_US: u32 = 1_000_000;
52
53/// The delay before the second receive window, which the same table fixes at one second
54/// after the first.
55pub const RECEIVE_DELAY2_US: u32 = RECEIVE_DELAY1_US + 1_000_000;
56
57/// The delay before the first window a join accept may be sent in, in microseconds.
58pub const JOIN_ACCEPT_DELAY1_US: u32 = 5_000_000;
59
60/// The delay before the second window a join accept may be sent in, in microseconds.
61pub const JOIN_ACCEPT_DELAY2_US: u32 = 6_000_000;
62
63/// How far ahead of the counter it has seen a network will follow a device.
64///
65/// A frame further ahead than this is refused rather than accepted, which is what stops a
66/// captured frame from being replayed at a counter the device will never reach. The value
67/// is the one RP002-1.0.5 section 3.3 lists, noted there as deprecated and removed in
68/// LoRaWAN 1.0.4 and later; it is the ceiling this module applies to 1.0.x sessions.
69pub const MAX_FCNT_GAP: u32 = 16_384;
70
71/// The channel the first receive window answers on, which the region decides.
72///
73/// RP002-1.0.5 defines two shapes for the bands this carries. Most regions answer on the
74/// frequency the uplink arrived on (section 3.4.7 for EU863-870, and the same wording for
75/// EU433, AS923, KR920-923, IN865 and RU864-870). The 900 MHz plans instead answer on a run
76/// of downlink channels, choosing one by the uplink channel number: "RX1 Channel Number =
77/// Transmit Channel Number modulo NbChannel" (sections 3.5.7 and 3.8.7).
78///
79/// CN470-510 is a third shape, mapping an uplink channel number onto a published table of
80/// downlink frequencies that differs per plan type, and it is not carried here; a deployment
81/// on that band supplies its own downstream block.
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83pub enum Rx1Channels {
84 /// The window answers on the frequency the uplink arrived on.
85 SameAsUplink,
86 /// The window answers on a run of downlink channels, indexed by the uplink channel
87 /// number modulo how many the run holds.
88 Downstream(ChannelBlock),
89}
90
91impl Rx1Channels {
92 /// Returns the downstream channels US902-928 answers on.
93 ///
94 /// Eight channels of 500 kHz starting at 923.3 MHz and stepping 600 kHz to 927.5 MHz,
95 /// carrying downlink data rates DR8 to DR13, from RP002-1.0.5 section 3.5.2.
96 ///
97 /// # Returns
98 ///
99 /// The channels.
100 pub const fn us915() -> Rx1Channels {
101 Rx1Channels::Downstream(ChannelBlock::new(923_300_000, 600_000, 8, 8, 13))
102 }
103
104 /// Returns the downstream channels AU915-928 answers on, which are the same run.
105 ///
106 /// # Returns
107 ///
108 /// The channels.
109 pub const fn au915() -> Rx1Channels {
110 Rx1Channels::Downstream(ChannelBlock::new(923_300_000, 600_000, 8, 8, 13))
111 }
112}
113
114/// When and where a network answers, and at what rate.
115///
116/// The defaults are the values recommended for every region in RP002-1.0.5 section 3.3: one
117/// second to the first receive window, five to the first join window, and no offset between
118/// the uplink data rate and the downlink one.
119#[derive(Clone, Copy, Debug, PartialEq, Eq)]
120pub struct Windows {
121 receive_delay_us: u32,
122 join_delay_us: u32,
123 rx1_data_rate_offset: u8,
124 rx1_channels: Rx1Channels,
125}
126
127impl Windows {
128 /// Returns the recommended windows: one second, five seconds, no offset, answering on
129 /// the frequency the uplink arrived on.
130 ///
131 /// # Returns
132 ///
133 /// The windows.
134 pub const fn new() -> Windows {
135 Windows {
136 receive_delay_us: RECEIVE_DELAY1_US,
137 join_delay_us: JOIN_ACCEPT_DELAY1_US,
138 rx1_data_rate_offset: 0,
139 rx1_channels: Rx1Channels::SameAsUplink,
140 }
141 }
142
143 /// Sets the delay before the first receive window, in microseconds.
144 ///
145 /// # Arguments
146 ///
147 /// * `micros` - the delay.
148 ///
149 /// # Returns
150 ///
151 /// The updated windows, for chaining.
152 pub const fn with_receive_delay_us(mut self, micros: u32) -> Windows {
153 self.receive_delay_us = micros;
154 self
155 }
156
157 /// Sets the delay before the window a join accept is sent in, in microseconds.
158 ///
159 /// # Arguments
160 ///
161 /// * `micros` - the delay.
162 ///
163 /// # Returns
164 ///
165 /// The updated windows, for chaining.
166 pub const fn with_join_delay_us(mut self, micros: u32) -> Windows {
167 self.join_delay_us = micros;
168 self
169 }
170
171 /// Sets the offset between the uplink data rate and the one the first window answers at.
172 ///
173 /// # Arguments
174 ///
175 /// * `offset` - the RX1DROffset the plan allows.
176 ///
177 /// # Returns
178 ///
179 /// The updated windows, for chaining.
180 pub const fn with_rx1_data_rate_offset(mut self, offset: u8) -> Windows {
181 self.rx1_data_rate_offset = offset;
182 self
183 }
184
185 /// Sets the channels the first window answers on.
186 ///
187 /// # Arguments
188 ///
189 /// * `channels` - the regional rule.
190 ///
191 /// # Returns
192 ///
193 /// The updated windows, for chaining.
194 pub const fn with_rx1_channels(mut self, channels: Rx1Channels) -> Windows {
195 self.rx1_channels = channels;
196 self
197 }
198
199 /// Returns the delay before the first receive window, in microseconds.
200 ///
201 /// # Returns
202 ///
203 /// The delay.
204 pub const fn receive_delay_us(&self) -> u32 {
205 self.receive_delay_us
206 }
207
208 /// Returns the delay before the join accept window, in microseconds.
209 ///
210 /// # Returns
211 ///
212 /// The delay.
213 pub const fn join_delay_us(&self) -> u32 {
214 self.join_delay_us
215 }
216
217 /// Returns the offset the first window answers at.
218 ///
219 /// # Returns
220 ///
221 /// The offset.
222 pub const fn rx1_data_rate_offset(&self) -> u8 {
223 self.rx1_data_rate_offset
224 }
225
226 /// Returns the channels the first window answers on.
227 ///
228 /// # Returns
229 ///
230 /// The regional rule.
231 pub const fn rx1_channels(&self) -> Rx1Channels {
232 self.rx1_channels
233 }
234
235 /// Returns the `RXDelay` byte a join accept carries.
236 ///
237 /// TS001-1.0.4 table 44 puts the delay in the low four bits, in seconds, and states that
238 /// a zero there means one second, so a sub-second delay cannot be expressed and rounds
239 /// down to the same one second the device already assumes.
240 ///
241 /// # Returns
242 ///
243 /// The byte.
244 pub const fn rx_delay_byte(&self) -> u8 {
245 let seconds = self.receive_delay_us / 1_000_000;
246 if seconds > 15 {
247 return 15;
248 }
249 seconds as u8
250 }
251
252 /// Returns the `DLSettings` byte a join accept carries.
253 ///
254 /// TS001-1.0.4 table 55 lays it out as a reserved top bit, the RX1DROffset in bits 6 to
255 /// 4, and the second window's data rate in the low four bits.
256 ///
257 /// # Arguments
258 ///
259 /// * `rx2_data_rate` - the data rate the second window listens at, from the plan.
260 ///
261 /// # Returns
262 ///
263 /// The byte.
264 pub const fn dl_settings_byte(&self, rx2_data_rate: u8) -> u8 {
265 ((self.rx1_data_rate_offset & 0x07) << 4) | (rx2_data_rate & 0x0F)
266 }
267}
268
269impl Default for Windows {
270 fn default() -> Windows {
271 Windows::new()
272 }
273}
274
275/// A device the network admits, and the key it was provisioned with.
276#[derive(Clone, Copy)]
277pub struct Registration {
278 dev_eui: [u8; 8],
279 app_eui: [u8; 8],
280 app_key: [u8; 16],
281}
282
283impl Registration {
284 /// Registers a device by its identifiers and its root key.
285 ///
286 /// # Arguments
287 ///
288 /// * `dev_eui` - the device's identifier.
289 /// * `app_eui` - the application identifier it joins under.
290 /// * `app_key` - the root key it was provisioned with.
291 ///
292 /// # Returns
293 ///
294 /// The registration.
295 pub const fn new(dev_eui: [u8; 8], app_eui: [u8; 8], app_key: [u8; 16]) -> Registration {
296 Registration {
297 dev_eui,
298 app_eui,
299 app_key,
300 }
301 }
302
303 /// Returns the device's identifier.
304 ///
305 /// # Returns
306 ///
307 /// The DevEUI.
308 pub const fn dev_eui(&self) -> [u8; 8] {
309 self.dev_eui
310 }
311
312 /// Returns the application identifier the device joins under.
313 ///
314 /// # Returns
315 ///
316 /// The AppEUI.
317 pub const fn app_eui(&self) -> [u8; 8] {
318 self.app_eui
319 }
320}
321
322// The root key never reaches a log, a panic message, or a test failure through this type.
323impl core::fmt::Debug for Registration {
324 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
325 f.debug_struct("Registration")
326 .field("dev_eui", &self.dev_eui)
327 .field("app_eui", &self.app_eui)
328 .finish_non_exhaustive()
329 }
330}
331
332/// Where and when a downlink answers an uplink, in the concentrator's own terms.
333#[derive(Clone, Copy, Debug, PartialEq)]
334pub struct Slot {
335 /// The concentrator timestamp to transmit at, in microseconds.
336 pub timestamp_us: u32,
337 /// The frequency to transmit on, in hertz.
338 pub frequency_hz: u32,
339 /// The settings to transmit with.
340 pub link: LinkSettings,
341}
342
343/// What a forwarded packet turned out to be.
344#[derive(Clone, Debug, PartialEq)]
345pub enum Event {
346 /// A device joined, and the accept is ready to transmit.
347 Joined {
348 /// The device that joined.
349 dev_eui: [u8; 8],
350 /// The address it was granted.
351 dev_addr: u32,
352 /// The accept, timed for the join window.
353 accept: Txpk,
354 },
355 /// A session frame arrived, decrypted.
356 Data {
357 /// The address it came from.
358 dev_addr: u32,
359 /// The counter it carried, reconstructed to its full width.
360 fcnt: u32,
361 /// The port it was sent on, absent for a frame carrying only MAC options.
362 fport: Option<u8>,
363 /// What the device sent.
364 payload: Vec<u8>,
365 /// Whether the device asked to be acknowledged.
366 confirmed: bool,
367 /// Where an answer would go.
368 slot: Slot,
369 },
370 /// A data frame for an address this network has not granted, which is another
371 /// network's traffic and is not an error.
372 Foreign {
373 /// The address the frame carried.
374 dev_addr: u32,
375 },
376}
377
378/// What can go wrong admitting or reading a forwarded packet.
379#[derive(Clone, Debug, PartialEq, Eq)]
380pub enum NetworkError {
381 /// The packet was not LoRa, so it carries no LoRaWAN frame here.
382 NotLora,
383 /// The frame did not parse, verify, or decrypt.
384 Frame(LorawanError),
385 /// A join request that no registered key verifies.
386 UnknownDevice,
387 /// A frame at a counter already seen, which is a replay.
388 Replayed {
389 /// The address it claimed.
390 dev_addr: u32,
391 /// The counter it carried.
392 fcnt: u32,
393 },
394 /// A frame further ahead of the counter last seen than [`MAX_FCNT_GAP`] allows.
395 CounterGap {
396 /// The address it claimed.
397 dev_addr: u32,
398 /// The counter last accepted.
399 seen: u32,
400 /// The counter it carried.
401 carried: u32,
402 },
403 /// The plan names no data rate for the settings the packet arrived at, so the rate the
404 /// first window answers at cannot be worked out.
405 UnknownDataRate,
406 /// No first receive window exists for the packet: its frequency is not a channel the
407 /// plan defines, or the plan names no downlink rate at that offset.
408 NoWindow,
409 /// A downlink for an address this network holds no session for.
410 NoSession {
411 /// The address asked for.
412 dev_addr: u32,
413 },
414}
415
416impl core::fmt::Display for NetworkError {
417 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
418 match self {
419 NetworkError::NotLora => f.write_str("the packet was not LoRa"),
420 NetworkError::Frame(error) => write!(f, "the frame was refused: {error}"),
421 NetworkError::UnknownDevice => {
422 f.write_str("no registered key verifies the join request")
423 }
424 NetworkError::Replayed { dev_addr, fcnt } => {
425 write!(f, "frame {fcnt} from {dev_addr:#010x} was already seen")
426 }
427 NetworkError::CounterGap {
428 dev_addr,
429 seen,
430 carried,
431 } => write!(
432 f,
433 "frame {carried} from {dev_addr:#010x} runs too far ahead of {seen}"
434 ),
435 NetworkError::UnknownDataRate => {
436 f.write_str("the plan names no data rate for the settings heard")
437 }
438 NetworkError::NoWindow => f.write_str("the plan defines no first receive window"),
439 NetworkError::NoSession { dev_addr } => {
440 write!(f, "no session for {dev_addr:#010x}")
441 }
442 }
443 }
444}
445
446impl core::error::Error for NetworkError {}
447
448// A device that has joined: the session that was granted, and the counters seen since.
449struct Admitted {
450 dev_eui: [u8; 8],
451 dev_addr: u32,
452 session: Session,
453 fcnt_up: Option<u32>,
454 fcnt_down: u32,
455}
456
457/// The network side of one site.
458///
459/// It admits the devices it is told about, grants sessions, follows frame counters, and
460/// decides where an answer goes. Everything it needs about the band comes from the
461/// [`ChannelPlan`] it is built with, so the same type serves any region.
462pub struct Network {
463 plan: OwnedChannelPlan,
464 windows: Windows,
465 net_id: u32,
466 registrations: Vec<Registration>,
467 admitted: Vec<Admitted>,
468 next_dev_addr: u32,
469 next_app_nonce: u32,
470}
471
472impl Network {
473 /// Builds a network on a channel plan.
474 ///
475 /// The plan is copied into the network, so one holds its band for as long as it runs
476 /// rather than borrowing a table that lives somewhere else.
477 ///
478 /// # Arguments
479 ///
480 /// * `plan` - the band this site operates in.
481 /// * `net_id` - the network identifier granted addresses carry; only its low 24 bits
482 /// travel.
483 ///
484 /// # Returns
485 ///
486 /// The network, with the recommended windows and no devices registered.
487 pub fn new(plan: &ChannelPlan<'_>, net_id: u32) -> Network {
488 Network {
489 plan: OwnedChannelPlan::from_plan(plan),
490 windows: Windows::new(),
491 net_id,
492 registrations: Vec::new(),
493 admitted: Vec::new(),
494 next_dev_addr: 1,
495 next_app_nonce: 1,
496 }
497 }
498
499 /// Sets the windows this network answers in.
500 ///
501 /// # Arguments
502 ///
503 /// * `windows` - the delays, offset, and channels.
504 ///
505 /// # Returns
506 ///
507 /// The updated network, for chaining.
508 pub fn with_windows(mut self, windows: Windows) -> Network {
509 self.windows = windows;
510 self
511 }
512
513 /// Sets the first address this network grants; later joins take the ones after it.
514 ///
515 /// # Arguments
516 ///
517 /// * `dev_addr` - the address to grant next.
518 ///
519 /// # Returns
520 ///
521 /// The updated network, for chaining.
522 pub fn with_first_dev_addr(mut self, dev_addr: u32) -> Network {
523 self.next_dev_addr = dev_addr;
524 self
525 }
526
527 /// Admits a device, so a join request signed with its key is accepted.
528 ///
529 /// # Arguments
530 ///
531 /// * `registration` - the device and its root key.
532 pub fn register(&mut self, registration: Registration) {
533 self.registrations.push(registration);
534 }
535
536 /// Returns the windows this network answers in.
537 ///
538 /// # Returns
539 ///
540 /// The windows.
541 pub const fn windows(&self) -> Windows {
542 self.windows
543 }
544
545 /// Returns the session granted to an address, once a device has joined.
546 ///
547 /// # Arguments
548 ///
549 /// * `dev_addr` - the address.
550 ///
551 /// # Returns
552 ///
553 /// The session, or `None` if no device holds that address here.
554 pub fn session(&self, dev_addr: u32) -> Option<Session> {
555 self.admitted
556 .iter()
557 .find(|held| held.dev_addr == dev_addr)
558 .map(|held| held.session)
559 }
560
561 /// Reads a packet the gateway forwarded.
562 ///
563 /// A join request is verified against every registered key, granted an address and a
564 /// session, and answered with an accept timed for the join window. A data frame is
565 /// routed by its address, checked against the counter last seen, and decrypted. A frame
566 /// for an address this network has not granted is reported rather than refused, because
567 /// a gateway hears every network in range.
568 ///
569 /// # Arguments
570 ///
571 /// * `heard` - the packet as the gateway reported it.
572 ///
573 /// # Returns
574 ///
575 /// What the packet turned out to be.
576 ///
577 /// # Errors
578 ///
579 /// Returns [`NetworkError::NotLora`] for an FSK packet, [`NetworkError::Frame`] if the
580 /// frame does not parse or its MIC does not verify, [`NetworkError::UnknownDevice`] if
581 /// no registered key verifies a join request, [`NetworkError::Replayed`] or
582 /// [`NetworkError::CounterGap`] if the counter is not one this network will follow, and
583 /// [`NetworkError::UnknownDataRate`] or [`NetworkError::NoWindow`] if the plan does not
584 /// describe where to answer.
585 pub fn uplink(&mut self, heard: &Rxpk) -> Result<Event, NetworkError> {
586 let link = heard.modulation.link().ok_or(NetworkError::NotLora)?;
587 let header = FrameHeader::parse(&heard.payload).map_err(NetworkError::Frame)?;
588
589 match header.message_type() {
590 MessageType::JoinRequest => self.admit(heard, link),
591 MessageType::UnconfirmedUp | MessageType::ConfirmedUp => {
592 self.receive(heard, link, &header)
593 }
594 other => Err(NetworkError::Frame(LorawanError::UnsupportedMType(
595 downlink_mtype(other),
596 ))),
597 }
598 }
599
600 /// Builds a downlink for a device, encrypted with its session.
601 ///
602 /// # Arguments
603 ///
604 /// * `dev_addr` - the device to answer.
605 /// * `slot` - where and when to transmit, from the event that reported the uplink.
606 /// * `fport` - the port to answer on.
607 /// * `payload` - what to send.
608 ///
609 /// # Returns
610 ///
611 /// The packet to put in a `PULL_RESP`, with the inverted polarity a device listens for.
612 ///
613 /// # Errors
614 ///
615 /// Returns [`NetworkError::NoSession`] if no device holds that address here, or
616 /// [`NetworkError::Frame`] if the payload does not fit a single frame.
617 pub fn answer(
618 &mut self,
619 dev_addr: u32,
620 slot: Slot,
621 fport: u8,
622 payload: &[u8],
623 ) -> Result<Txpk, NetworkError> {
624 let held = self
625 .admitted
626 .iter_mut()
627 .find(|held| held.dev_addr == dev_addr)
628 .ok_or(NetworkError::NoSession { dev_addr })?;
629
630 let frame = held
631 .session
632 .encode_downlink(&Downlink::new(held.fcnt_down, fport, payload))
633 .map_err(NetworkError::Frame)?;
634 held.fcnt_down = held.fcnt_down.wrapping_add(1);
635
636 Ok(transmit(slot, frame.as_bytes().to_vec()))
637 }
638
639 // Verifies a join request against every registered key, grants a session, and answers.
640 fn admit(&mut self, heard: &Rxpk, link: LinkSettings) -> Result<Event, NetworkError> {
641 let (registration, request) = self
642 .registrations
643 .iter()
644 .find_map(|registration| {
645 let request = JoinRequest::parse(&heard.payload, ®istration.app_key).ok()?;
646 (request.dev_eui() == registration.dev_eui).then_some((*registration, request))
647 })
648 .ok_or(NetworkError::UnknownDevice)?;
649
650 let dev_addr = self.next_dev_addr;
651 let app_nonce = self.next_app_nonce;
652 let grant = JoinGrant::new(app_nonce, self.net_id, dev_addr)
653 .with_dl_settings(
654 self.windows
655 .dl_settings_byte(self.plan.with_plan(|plan| plan.rx2_data_rate)),
656 )
657 .with_rx_delay(self.windows.rx_delay_byte());
658 let accept = grant.accept(®istration.app_key, request.dev_nonce());
659 let session = grant.session(®istration.app_key, request.dev_nonce());
660
661 self.next_dev_addr = self.next_dev_addr.wrapping_add(1);
662 self.next_app_nonce = self.next_app_nonce.wrapping_add(1);
663 self.admitted
664 .retain(|held| held.dev_eui != registration.dev_eui);
665 self.admitted.push(Admitted {
666 dev_eui: registration.dev_eui,
667 dev_addr,
668 session,
669 fcnt_up: None,
670 fcnt_down: 0,
671 });
672
673 let slot = self.slot(heard, link, self.windows.join_delay_us)?;
674 Ok(Event::Joined {
675 dev_eui: registration.dev_eui,
676 dev_addr,
677 accept: transmit(slot, accept.as_bytes().to_vec()),
678 })
679 }
680
681 // Routes a data frame to its session, follows its counter, and decrypts it.
682 fn receive(
683 &mut self,
684 heard: &Rxpk,
685 link: LinkSettings,
686 header: &FrameHeader,
687 ) -> Result<Event, NetworkError> {
688 let dev_addr = header
689 .dev_addr()
690 .ok_or(NetworkError::Frame(LorawanError::MalformedFrame))?;
691 let carried = header
692 .fcnt()
693 .ok_or(NetworkError::Frame(LorawanError::MalformedFrame))?;
694 let slot = self.slot(heard, link, self.windows.receive_delay_us)?;
695
696 let Some(held) = self
697 .admitted
698 .iter_mut()
699 .find(|held| held.dev_addr == dev_addr)
700 else {
701 return Ok(Event::Foreign { dev_addr });
702 };
703
704 let fcnt = match held.fcnt_up {
705 None => u32::from(carried),
706 Some(seen) => {
707 let mut candidate = (seen & 0xFFFF_0000) | u32::from(carried);
708 // The counter already accepted, sent again.
709 if candidate == seen {
710 return Err(NetworkError::Replayed {
711 dev_addr,
712 fcnt: candidate,
713 });
714 }
715 // Only the low sixteen bits travel, so a counter below the one last seen is
716 // the device having wrapped rather than having gone backwards.
717 if candidate < seen {
718 candidate = candidate.wrapping_add(0x0001_0000);
719 }
720 if candidate - seen > MAX_FCNT_GAP {
721 return Err(NetworkError::CounterGap {
722 dev_addr,
723 seen,
724 carried: candidate,
725 });
726 }
727 candidate
728 }
729 };
730
731 let data = held
732 .session
733 .decode(&heard.payload, fcnt)
734 .map_err(NetworkError::Frame)?;
735 held.fcnt_up = Some(fcnt);
736
737 Ok(Event::Data {
738 dev_addr,
739 fcnt,
740 fport: data.fport(),
741 payload: data.payload().to_vec(),
742 confirmed: data.confirmed(),
743 slot,
744 })
745 }
746
747 // Works out where and when the first receive window opens for a packet.
748 fn slot(&self, heard: &Rxpk, link: LinkSettings, delay_us: u32) -> Result<Slot, NetworkError> {
749 let uplink_rate = self
750 .data_rate_of(link)
751 .ok_or(NetworkError::UnknownDataRate)?;
752 let settings = self
753 .plan
754 .with_plan(|plan| {
755 plan.rx1_data_rate(uplink_rate, self.windows.rx1_data_rate_offset)
756 .and_then(|downlink_rate| plan.link_settings(downlink_rate))
757 })
758 .ok_or(NetworkError::NoWindow)?;
759
760 Ok(Slot {
761 timestamp_us: heard.timestamp_us.unwrap_or(0).wrapping_add(delay_us),
762 frequency_hz: self.rx1_frequency_hz(heard.frequency_hz)?,
763 link: settings,
764 })
765 }
766
767 // Applies the region's rule for which channel the first window answers on.
768 fn rx1_frequency_hz(&self, uplink_hz: u32) -> Result<u32, NetworkError> {
769 match self.windows.rx1_channels {
770 Rx1Channels::SameAsUplink => Ok(uplink_hz),
771 Rx1Channels::Downstream(block) => {
772 let channel = self.channel_of(uplink_hz).ok_or(NetworkError::NoWindow)?;
773 block
774 .frequency_hz(channel % block.count)
775 .ok_or(NetworkError::NoWindow)
776 }
777 }
778 }
779
780 // Finds which channel of the plan a frequency is, counting through the default blocks
781 // in the order the channel numbering follows.
782 fn channel_of(&self, frequency_hz: u32) -> Option<u16> {
783 self.plan.with_plan(|plan| {
784 let mut first = 0u16;
785 for block in plan.default_channels {
786 for index in 0..block.count {
787 if block.frequency_hz(index) == Some(frequency_hz) {
788 return Some(first + index);
789 }
790 }
791 first += block.count;
792 }
793 None
794 })
795 }
796
797 // Finds the data rate number whose settings a packet arrived with. Only the spreading
798 // factor and the bandwidth name a rate; the coding rate and the frame options a radio
799 // reports alongside them do not.
800 fn data_rate_of(&self, link: LinkSettings) -> Option<u8> {
801 self.plan.with_plan(|plan| {
802 plan.uplink_data_rates
803 .iter()
804 .enumerate()
805 .find_map(|(number, rate)| {
806 let settings = rate.as_ref()?.link_settings()?;
807 (settings.spreading_factor() == link.spreading_factor()
808 && settings.bandwidth_hz() == link.bandwidth_hz())
809 .then_some(number as u8)
810 })
811 })
812 }
813}
814
815// The packet a downlink goes out as: at the window, with the polarity a device listens for.
816fn transmit(slot: Slot, payload: Vec<u8>) -> Txpk {
817 Txpk::at(slot.timestamp_us, slot.frequency_hz, slot.link, payload).with_inverted_polarity(true)
818}
819
820// The MHDR byte a message type carries, for reporting one that is not an uplink.
821const fn downlink_mtype(message_type: MessageType) -> u8 {
822 match message_type {
823 MessageType::JoinRequest => 0x00,
824 MessageType::JoinAccept => 0x20,
825 MessageType::UnconfirmedUp => 0x40,
826 MessageType::UnconfirmedDown => 0x60,
827 MessageType::ConfirmedUp => 0x80,
828 MessageType::ConfirmedDown => 0xA0,
829 }
830}
831
832#[cfg(test)]
833mod tests {
834 use super::*;
835
836 use pamoja_lora::region::Region;
837 use pamoja_lorawan::Device;
838
839 const DEV_EUI: [u8; 8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77];
840 const APP_EUI: [u8; 8] = [0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x00, 0x00, 0x01];
841 const APP_KEY: [u8; 16] = [
842 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F,
843 0x3C,
844 ];
845
846 fn site() -> Network {
847 let mut network =
848 Network::new(Region::Eu868.plan(), 0x00_00_2A).with_first_dev_addr(0x2601_0001);
849 network.register(Registration::new(DEV_EUI, APP_EUI, APP_KEY));
850 network
851 }
852
853 fn heard(frame: Vec<u8>, timestamp_us: u32) -> Rxpk {
854 Rxpk::new(868_100_000, LinkSettings::new(7, 125_000), frame).with_timestamp_us(timestamp_us)
855 }
856
857 #[test]
858 fn the_recommended_delays_are_the_published_ones() {
859 // RP002-1.0.5 section 3.3: RECEIVE_DELAY1 1s, RECEIVE_DELAY2 2s, JOIN_ACCEPT_DELAY1
860 // 5s, JOIN_ACCEPT_DELAY2 6s.
861 assert_eq!(RECEIVE_DELAY1_US, 1_000_000);
862 assert_eq!(RECEIVE_DELAY2_US, 2_000_000);
863 assert_eq!(JOIN_ACCEPT_DELAY1_US, 5_000_000);
864 assert_eq!(JOIN_ACCEPT_DELAY2_US, 6_000_000);
865 assert_eq!(MAX_FCNT_GAP, 16_384);
866 }
867
868 #[test]
869 fn the_join_accept_bytes_follow_the_specification() {
870 // TS001-1.0.4 table 55: RFU, then RX1DROffset in bits 6:4, then RX2DataRate in 3:0.
871 let windows = Windows::new().with_rx1_data_rate_offset(5);
872 assert_eq!(windows.dl_settings_byte(0), 0x50);
873 assert_eq!(windows.dl_settings_byte(0x0F), 0x5F);
874 assert_eq!(Windows::new().dl_settings_byte(8), 0x08);
875
876 // TS001-1.0.4 table 44: the delay in seconds in the low four bits, and a zero there
877 // means one second.
878 assert_eq!(Windows::new().rx_delay_byte(), 1);
879 assert_eq!(
880 Windows::new()
881 .with_receive_delay_us(5_000_000)
882 .rx_delay_byte(),
883 5
884 );
885 }
886
887 #[test]
888 fn a_registration_never_prints_its_key() {
889 let printed = format!("{:?}", Registration::new(DEV_EUI, APP_EUI, APP_KEY));
890 assert!(printed.contains("dev_eui"));
891 assert!(!printed.contains("2b") && !printed.contains("43"));
892 }
893
894 #[test]
895 fn a_device_joins_and_is_answered_in_the_join_window() {
896 let mut network = site();
897 let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
898 let request = device.join_request(0x0102);
899
900 let event = network
901 .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
902 .expect("the request verifies");
903
904 let Event::Joined {
905 dev_eui,
906 dev_addr,
907 accept,
908 } = event
909 else {
910 panic!("a join request is admitted");
911 };
912 assert_eq!(dev_eui, DEV_EUI);
913 assert_eq!(dev_addr, 0x2601_0001);
914 // Five seconds after the uplink, on the uplink frequency, with inverted polarity.
915 assert_eq!(accept.timestamp_us, Some(6_000_000));
916 assert_eq!(accept.frequency_hz, 868_100_000);
917 assert!(accept.invert_polarity);
918
919 // The device reads the accept it was sent and derives the same session.
920 let granted = device
921 .accept_join(&accept.payload, 0x0102)
922 .expect("the accept verifies");
923 assert_eq!(granted.dev_addr(), 0x2601_0001);
924 }
925
926 #[test]
927 fn an_uplink_is_decrypted_and_answered_one_second_later() {
928 let mut network = site();
929 let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
930 let request = device.join_request(0x0102);
931 let Event::Joined { accept, .. } = network
932 .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
933 .expect("the request verifies")
934 else {
935 panic!("a join request is admitted");
936 };
937 let session = device
938 .accept_join(&accept.payload, 0x0102)
939 .expect("the accept verifies")
940 .session();
941
942 let frame = session
943 .encode_uplink(&pamoja_lorawan::Uplink::new(0, 2, b"21.5"))
944 .expect("it fits one frame");
945 let event = network
946 .uplink(&heard(frame.as_bytes().to_vec(), 9_000_000))
947 .expect("the frame verifies");
948
949 let Event::Data {
950 dev_addr,
951 fcnt,
952 fport,
953 payload,
954 confirmed,
955 slot,
956 } = event
957 else {
958 panic!("a data frame is read");
959 };
960 assert_eq!(dev_addr, 0x2601_0001);
961 assert_eq!(fcnt, 0);
962 assert_eq!(fport, Some(2));
963 assert_eq!(payload, b"21.5");
964 assert!(!confirmed);
965 assert_eq!(slot.timestamp_us, 10_000_000);
966 assert_eq!(slot.frequency_hz, 868_100_000);
967 assert_eq!(slot.link.spreading_factor(), 7);
968
969 // The answer goes out in that window, and the device reads it.
970 let downlink = network
971 .answer(dev_addr, slot, 2, b"ok")
972 .expect("the session is held");
973 assert_eq!(downlink.timestamp_us, Some(10_000_000));
974 assert!(downlink.invert_polarity);
975 let read = session.decode(&downlink.payload, 0).expect("it verifies");
976 assert_eq!(read.payload(), b"ok");
977 }
978
979 #[test]
980 fn a_replayed_frame_is_refused() {
981 let mut network = site();
982 let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
983 let request = device.join_request(0x0102);
984 let Event::Joined { accept, .. } = network
985 .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
986 .expect("the request verifies")
987 else {
988 panic!("a join request is admitted");
989 };
990 let session = device
991 .accept_join(&accept.payload, 0x0102)
992 .expect("the accept verifies")
993 .session();
994
995 let first = session
996 .encode_uplink(&pamoja_lorawan::Uplink::new(1, 2, b"one"))
997 .expect("it fits one frame");
998 network
999 .uplink(&heard(first.as_bytes().to_vec(), 2_000_000))
1000 .expect("the first frame is read");
1001
1002 let error = network
1003 .uplink(&heard(first.as_bytes().to_vec(), 3_000_000))
1004 .expect_err("the same counter twice is a replay");
1005 assert!(matches!(
1006 error,
1007 NetworkError::Replayed {
1008 dev_addr: 0x2601_0001,
1009 ..
1010 }
1011 ));
1012 }
1013
1014 #[test]
1015 fn a_counter_that_wraps_carries_on_where_it_left_off() {
1016 let mut network = site();
1017 let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1018 let request = device.join_request(0x0102);
1019 let Event::Joined { accept, .. } = network
1020 .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
1021 .expect("the request verifies")
1022 else {
1023 panic!("a join request is admitted");
1024 };
1025 let session = device
1026 .accept_join(&accept.payload, 0x0102)
1027 .expect("the accept verifies")
1028 .session();
1029
1030 // The last counter the low sixteen bits can hold, then the one after it.
1031 let last = session
1032 .encode_uplink(&pamoja_lorawan::Uplink::new(0xFFFF, 2, b"last"))
1033 .expect("it fits one frame");
1034 network
1035 .uplink(&heard(last.as_bytes().to_vec(), 2_000_000))
1036 .expect("the frame is read");
1037
1038 let wrapped = session
1039 .encode_uplink(&pamoja_lorawan::Uplink::new(0x0001_0000, 2, b"next"))
1040 .expect("it fits one frame");
1041 let event = network
1042 .uplink(&heard(wrapped.as_bytes().to_vec(), 3_000_000))
1043 .expect("the wrapped frame is read");
1044
1045 let Event::Data { fcnt, payload, .. } = event else {
1046 panic!("a data frame is read");
1047 };
1048 assert_eq!(fcnt, 0x0001_0000);
1049 assert_eq!(payload, b"next");
1050 }
1051
1052 #[test]
1053 fn a_frame_for_another_network_is_reported_not_refused() {
1054 let mut network = site();
1055 let stranger = Session::new(0x1234_5678, [9; 16], [8; 16]);
1056 let frame = stranger
1057 .encode_uplink(&pamoja_lorawan::Uplink::new(0, 1, b"hello"))
1058 .expect("it fits one frame");
1059
1060 let event = network
1061 .uplink(&heard(frame.as_bytes().to_vec(), 1_000_000))
1062 .expect("a frame from elsewhere is not an error");
1063 assert_eq!(
1064 event,
1065 Event::Foreign {
1066 dev_addr: 0x1234_5678
1067 }
1068 );
1069 }
1070
1071 #[test]
1072 fn the_first_window_follows_the_region() {
1073 // RP002-1.0.5 section 3.5.7: RX1 Channel Number = Transmit Channel Number modulo
1074 // NbChannel, over the eight downlink channels of section 3.5.2.
1075 let block = match Rx1Channels::us915() {
1076 Rx1Channels::Downstream(block) => block,
1077 Rx1Channels::SameAsUplink => panic!("US902-928 answers on its own channels"),
1078 };
1079 assert_eq!(block.frequency_hz(0), Some(923_300_000));
1080 assert_eq!(block.frequency_hz(7), Some(927_500_000));
1081 assert_eq!(block.count, 8);
1082 }
1083
1084 #[test]
1085 fn an_fsk_packet_carries_no_frame() {
1086 let mut network = site();
1087 let mut packet = heard(vec![0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
1088 packet.modulation = crate::udp::Modulation::Fsk(50_000);
1089 assert_eq!(network.uplink(&packet), Err(NetworkError::NotLora));
1090 }
1091}