Skip to main content

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: by default the plan decides it, and [`Rx1Channels`] can override that.
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::{Eui, Rxpk};
25//! use pamoja_lora::region::Region;
26//! use pamoja_lora::LinkSettings;
27//! use pamoja_lorawan::{Device, Uplink};
28//!
29//! // What a soil probe was provisioned with: its own EUI from its label, the EUI of the
30//! // application it joins, and its root key.
31//! let dev_eui = Eui::from_hex("70b3d57ed0001234").expect("the probe's EUI").bytes();
32//! let join_eui = Eui::from_hex("70b3d57ed0000000").expect("the application's EUI").bytes();
33//! let app_key = [7; 16];
34//!
35//! // A private network that admits it.
36//! let mut network = Network::new(Region::Eu868.plan(), 0x00_00_2A);
37//! network.register(Registration::new(dev_eui, join_eui, app_key));
38//!
39//! // The gateway forwards the probe's join request, and the network grants it a session.
40//! let probe = Device::new(dev_eui, join_eui, app_key);
41//! let link = LinkSettings::new(7, 125_000);
42//! let request = probe.join_request(1);
43//! let heard = Rxpk::new(868_100_000, link, request.as_bytes().to_vec()).with_timestamp_us(1_000);
44//! let Ok(Event::Joined { dev_addr, accept, .. }) = network.uplink(&heard) else {
45//!     panic!("the join request verifies");
46//! };
47//!
48//! // The probe reads the accept and holds the session the network granted.
49//! let session = probe
50//!     .accept_join(&accept.payload, 1)
51//!     .expect("the accept verifies")
52//!     .session();
53//! assert_eq!(session.dev_addr(), dev_addr);
54//!
55//! // Its first reading arrives decrypted, and the answer goes out in the first receive
56//! // window, a second after the uplink ended.
57//! let frame = session
58//!     .encode_uplink(&Uplink::new(0, 2, b"21.5"))
59//!     .expect("an uplink");
60//! let heard = Rxpk::new(868_100_000, link, frame.as_bytes().to_vec()).with_timestamp_us(9_000_000);
61//! let Ok(Event::Data { payload, slot, .. }) = network.uplink(&heard) else {
62//!     panic!("the reading verifies");
63//! };
64//! assert_eq!(payload, b"21.5");
65//! let reply = network
66//!     .answer(dev_addr, slot, 2, b"ok")
67//!     .expect("the probe holds a session");
68//! assert_eq!(reply.timestamp_us, Some(10_000_000));
69//! ```
70
71use pamoja_lora::region::{ChannelBlock, ChannelPlan, OwnedChannelPlan};
72use pamoja_lora::LinkSettings;
73use pamoja_lorawan::mac::{encode_all, MacCommand, MacCommands, FOPTS_MAX, MAX_COMMAND};
74use pamoja_lorawan::relay::{ForwardedUplink, UplinkMetadata, LA_FPORT_RELAY};
75use pamoja_lorawan::{
76    Direction, Downlink, FrameHeader, JoinGrant, JoinRequest, LorawanError, MessageType, Session,
77    Version,
78};
79
80use crate::udp::{Rxpk, Txpk};
81
82// The delays a window opens after and the counter gap a session follows are the ones every
83// region shares, kept with the rest of the link layer so a device and this network read the
84// same values.
85pub use pamoja_lorawan::defaults::{
86    JOIN_ACCEPT_DELAY1_US, JOIN_ACCEPT_DELAY2_US, MAX_FCNT_GAP, RECEIVE_DELAY1_US,
87    RECEIVE_DELAY2_US,
88};
89
90/// The channel the first receive window answers on, which the region decides.
91///
92/// Most regions answer on the frequency the uplink arrived on (RP002-1.0.5 section 3.4.7 for
93/// EU863-870, and the same wording for EU433, AS923, KR920-923, IN865 and RU864-870). The
94/// fixed plans instead answer on numbered downlink channels, choosing one by the uplink
95/// channel number: "RX1 Channel Number = Transmit Channel Number modulo NbChannel" (sections
96/// 3.5.7 and 3.8.7, and section 3.9.7 for CN470-510). The channel plan carries which, so
97/// [`Rx1Channels::Plan`] is the default, and the other two override it.
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
99pub enum Rx1Channels {
100    /// The window answers where the network's channel plan says: on the uplink's frequency in
101    /// a dynamic plan, and on the downlink channel a fixed plan numbers for the uplink
102    /// channel. A join on a CN470-510 common join channel is answered on the frequency
103    /// RP002-1.0.5 table 49 gives it.
104    Plan,
105    /// The window answers on the frequency the uplink arrived on.
106    SameAsUplink,
107    /// The window answers on a run of downlink channels, indexed by the uplink channel
108    /// number modulo how many the run holds.
109    Downstream(ChannelBlock),
110}
111
112impl Rx1Channels {
113    /// Returns the downstream channels US902-928 answers on.
114    ///
115    /// Eight channels of 500 kHz starting at 923.3 MHz and stepping 600 kHz to 927.5 MHz,
116    /// carrying downlink data rates DR8 to DR13, from RP002-1.0.5 section 3.5.2.
117    ///
118    /// # Returns
119    ///
120    /// The channels.
121    pub const fn us915() -> Rx1Channels {
122        Rx1Channels::Downstream(ChannelBlock::new(923_300_000, 600_000, 8, 8, 13))
123    }
124
125    /// Returns the downstream channels AU915-928 answers on, which are the same run.
126    ///
127    /// # Returns
128    ///
129    /// The channels.
130    pub const fn au915() -> Rx1Channels {
131        Rx1Channels::Downstream(ChannelBlock::new(923_300_000, 600_000, 8, 8, 13))
132    }
133}
134
135/// When and where a network answers, and at what rate.
136///
137/// The defaults are the values recommended for every region in RP002-1.0.5 section 3.3: one
138/// second to the first receive window, five to the first join window, and no offset between
139/// the uplink data rate and the downlink one.
140#[derive(Clone, Copy, Debug, PartialEq, Eq)]
141pub struct Windows {
142    receive_delay_us: u32,
143    join_delay_us: u32,
144    rx1_data_rate_offset: u8,
145    rx1_channels: Rx1Channels,
146}
147
148impl Windows {
149    /// Returns the recommended windows: one second, five seconds, no offset, answering where
150    /// the channel plan says.
151    ///
152    /// # Returns
153    ///
154    /// The windows.
155    pub const fn new() -> Windows {
156        Windows {
157            receive_delay_us: RECEIVE_DELAY1_US,
158            join_delay_us: JOIN_ACCEPT_DELAY1_US,
159            rx1_data_rate_offset: 0,
160            rx1_channels: Rx1Channels::Plan,
161        }
162    }
163
164    /// Sets the delay before the first receive window, in microseconds.
165    ///
166    /// # Arguments
167    ///
168    /// * `micros` - the delay.
169    ///
170    /// # Returns
171    ///
172    /// The updated windows, for chaining.
173    pub const fn with_receive_delay_us(mut self, micros: u32) -> Windows {
174        self.receive_delay_us = micros;
175        self
176    }
177
178    /// Sets the delay before the window a join accept is sent in, in microseconds.
179    ///
180    /// # Arguments
181    ///
182    /// * `micros` - the delay.
183    ///
184    /// # Returns
185    ///
186    /// The updated windows, for chaining.
187    pub const fn with_join_delay_us(mut self, micros: u32) -> Windows {
188        self.join_delay_us = micros;
189        self
190    }
191
192    /// Sets the offset between the uplink data rate and the one the first window answers at.
193    ///
194    /// # Arguments
195    ///
196    /// * `offset` - the RX1DROffset the plan allows.
197    ///
198    /// # Returns
199    ///
200    /// The updated windows, for chaining.
201    pub const fn with_rx1_data_rate_offset(mut self, offset: u8) -> Windows {
202        self.rx1_data_rate_offset = offset;
203        self
204    }
205
206    /// Sets the channels the first window answers on.
207    ///
208    /// # Arguments
209    ///
210    /// * `channels` - the regional rule.
211    ///
212    /// # Returns
213    ///
214    /// The updated windows, for chaining.
215    pub const fn with_rx1_channels(mut self, channels: Rx1Channels) -> Windows {
216        self.rx1_channels = channels;
217        self
218    }
219
220    /// Returns the delay before the first receive window, in microseconds.
221    ///
222    /// # Returns
223    ///
224    /// The delay.
225    pub const fn receive_delay_us(&self) -> u32 {
226        self.receive_delay_us
227    }
228
229    /// Returns the delay before the join accept window, in microseconds.
230    ///
231    /// # Returns
232    ///
233    /// The delay.
234    pub const fn join_delay_us(&self) -> u32 {
235        self.join_delay_us
236    }
237
238    /// Returns the offset the first window answers at.
239    ///
240    /// # Returns
241    ///
242    /// The offset.
243    pub const fn rx1_data_rate_offset(&self) -> u8 {
244        self.rx1_data_rate_offset
245    }
246
247    /// Returns the channels the first window answers on.
248    ///
249    /// # Returns
250    ///
251    /// The regional rule.
252    pub const fn rx1_channels(&self) -> Rx1Channels {
253        self.rx1_channels
254    }
255
256    /// Returns the `RXDelay` byte a join accept carries.
257    ///
258    /// TS001-1.0.4 table 44 puts the delay in the low four bits, in seconds, and states that
259    /// a zero there means one second, so a sub-second delay cannot be expressed and rounds
260    /// down to the same one second the device already assumes.
261    ///
262    /// # Returns
263    ///
264    /// The byte.
265    pub const fn rx_delay_byte(&self) -> u8 {
266        let seconds = self.receive_delay_us / 1_000_000;
267        if seconds > 15 {
268            return 15;
269        }
270        seconds as u8
271    }
272
273    /// Returns the `DLSettings` byte a join accept carries.
274    ///
275    /// TS001-1.0.4 table 55 lays it out as a reserved top bit, the RX1DROffset in bits 6 to
276    /// 4, and the second window's data rate in the low four bits.
277    ///
278    /// # Arguments
279    ///
280    /// * `rx2_data_rate` - the data rate the second window listens at, from the plan.
281    ///
282    /// # Returns
283    ///
284    /// The byte.
285    pub const fn dl_settings_byte(&self, rx2_data_rate: u8) -> u8 {
286        ((self.rx1_data_rate_offset & 0x07) << 4) | (rx2_data_rate & 0x0F)
287    }
288}
289
290impl Default for Windows {
291    fn default() -> Windows {
292        Windows::new()
293    }
294}
295
296/// A device the network admits, the key it was provisioned with, and the revision of the
297/// link layer it follows.
298#[derive(Clone, Copy)]
299pub struct Registration {
300    dev_eui: [u8; 8],
301    app_eui: [u8; 8],
302    app_key: [u8; 16],
303    version: Version,
304}
305
306impl Registration {
307    /// Registers a device by its identifiers and its root key, as one that follows
308    /// TS001-1.0.4.
309    ///
310    /// # Arguments
311    ///
312    /// * `dev_eui` - the device's identifier.
313    /// * `app_eui` - the application identifier it joins under.
314    /// * `app_key` - the root key it was provisioned with.
315    ///
316    /// # Returns
317    ///
318    /// The registration.
319    pub const fn new(dev_eui: [u8; 8], app_eui: [u8; 8], app_key: [u8; 16]) -> Registration {
320        Registration {
321            dev_eui,
322            app_eui,
323            app_key,
324            version: Version::V1_0_4,
325        }
326    }
327
328    /// Sets the revision of the link layer the device follows.
329    ///
330    /// The network holds a device to its own revision's rules. The one that differs here is
331    /// the uplink counter: LoRaWAN 1.0.3 section 4.3.1.5 refuses a frame [`MAX_FCNT_GAP`] or
332    /// more ahead of the last one accepted, and TS001-1.0.4 asks only that the counter go
333    /// up.
334    ///
335    /// # Arguments
336    ///
337    /// * `version` - the revision the device was built to.
338    ///
339    /// # Returns
340    ///
341    /// The updated registration, for chaining.
342    pub const fn with_version(mut self, version: Version) -> Registration {
343        self.version = version;
344        self
345    }
346
347    /// Returns the revision of the link layer the device follows.
348    ///
349    /// # Returns
350    ///
351    /// The version.
352    pub const fn version(&self) -> Version {
353        self.version
354    }
355
356    /// Returns the device's identifier.
357    ///
358    /// # Returns
359    ///
360    /// The DevEUI.
361    pub const fn dev_eui(&self) -> [u8; 8] {
362        self.dev_eui
363    }
364
365    /// Returns the application identifier the device joins under.
366    ///
367    /// # Returns
368    ///
369    /// The AppEUI.
370    pub const fn app_eui(&self) -> [u8; 8] {
371        self.app_eui
372    }
373}
374
375// The root key never reaches a log, a panic message, or a test failure through this type.
376impl core::fmt::Debug for Registration {
377    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
378        f.debug_struct("Registration")
379            .field("dev_eui", &self.dev_eui)
380            .field("app_eui", &self.app_eui)
381            .field("version", &self.version)
382            .finish_non_exhaustive()
383    }
384}
385
386/// What a relay added to an uplink it forwarded, TS011-1.0.1 section 9.1.
387#[derive(Clone, Copy, Debug, PartialEq, Eq)]
388pub struct Relayed {
389    /// The relay's own address.
390    pub relay: u32,
391    /// What it heard of the uplink.
392    pub metadata: UplinkMetadata,
393    /// The frequency the uplink arrived on, in hertz.
394    pub frequency_hz: u32,
395}
396
397/// A device a relay heard and could not verify, TS011-1.0.1 section 10.7.
398#[derive(Clone, Copy, Debug, PartialEq, Eq)]
399pub struct Notice {
400    /// The relay that heard it.
401    pub relay: u32,
402    /// The address the wake-on-radio frame named.
403    pub dev_addr: u32,
404    /// The frame's signal strength in dBm.
405    pub rssi_dbm: i16,
406    /// Its signal-to-noise ratio in dB.
407    pub snr_db: i8,
408}
409
410/// Where and when a downlink answers an uplink, in the concentrator's own terms.
411#[derive(Clone, Copy, Debug, PartialEq)]
412pub struct Slot {
413    /// The concentrator timestamp to transmit at, in microseconds.
414    pub timestamp_us: u32,
415    /// The frequency to transmit on, in hertz.
416    pub frequency_hz: u32,
417    /// The settings to transmit with.
418    pub link: LinkSettings,
419}
420
421/// What a forwarded packet turned out to be.
422#[derive(Clone, Debug, PartialEq)]
423pub enum Event {
424    /// A device joined, and the accept is ready to transmit.
425    Joined {
426        /// The device that joined.
427        dev_eui: [u8; 8],
428        /// The address it was granted.
429        dev_addr: u32,
430        /// The accept, timed for the join window.
431        accept: Txpk,
432    },
433    /// A session frame arrived, decrypted.
434    Data {
435        /// The address it came from.
436        dev_addr: u32,
437        /// The counter it carried, reconstructed to its full width.
438        fcnt: u32,
439        /// The port it was sent on, absent for a frame carrying only MAC options.
440        fport: Option<u8>,
441        /// What the device sent.
442        payload: Vec<u8>,
443        /// Whether the device asked to be acknowledged.
444        confirmed: bool,
445        /// Where an answer would go.
446        slot: Slot,
447        /// The relay that forwarded it, and what it heard, for an uplink that came that
448        /// way. [`answer`](Network::answer) sends the reply back through the same relay.
449        relay: Option<Relayed>,
450    },
451    /// A data frame for an address this network has not granted, which is another
452    /// network's traffic and is not an error.
453    Foreign {
454        /// The address the frame carried.
455        dev_addr: u32,
456    },
457}
458
459/// What can go wrong admitting or reading a forwarded packet.
460#[derive(Clone, Debug, PartialEq, Eq)]
461pub enum NetworkError {
462    /// A relay forwarded something that is not an uplink it could have heard.
463    Relayed(LorawanError),
464    /// The packet was not LoRa, so it carries no LoRaWAN frame here.
465    NotLora,
466    /// The frame did not parse, verify, or decrypt.
467    Frame(LorawanError),
468    /// A join request that no registered key verifies.
469    UnknownDevice,
470    /// A frame at a counter already seen, which is a replay.
471    Replayed {
472        /// The address it claimed.
473        dev_addr: u32,
474        /// The counter it carried.
475        fcnt: u32,
476    },
477    /// A frame from a LoRaWAN 1.0.3 device [`MAX_FCNT_GAP`] or more ahead of the counter
478    /// last seen, which LoRaWAN 1.0.3 section 4.3.1.5 does not follow.
479    CounterGap {
480        /// The address it claimed.
481        dev_addr: u32,
482        /// The counter last accepted.
483        seen: u32,
484        /// The counter it carried.
485        carried: u32,
486    },
487    /// A frame whose counter would run past 32 bits, which a session cannot carry: the
488    /// device has to join again.
489    CounterExhausted {
490        /// The address it claimed.
491        dev_addr: u32,
492    },
493    /// The plan names no data rate for the settings the packet arrived at, so the rate the
494    /// first window answers at cannot be worked out.
495    UnknownDataRate,
496    /// No first receive window exists for the packet: its frequency is not a channel the
497    /// plan defines, or the plan names no downlink rate at that offset.
498    NoWindow,
499    /// A downlink for an address this network holds no session for.
500    NoSession {
501        /// The address asked for.
502        dev_addr: u32,
503    },
504}
505
506impl core::fmt::Display for NetworkError {
507    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
508        match self {
509            NetworkError::NotLora => f.write_str("the packet was not LoRa"),
510            NetworkError::Frame(error) => write!(f, "the frame was refused: {error}"),
511            NetworkError::Relayed(error) => {
512                write!(f, "the frame a relay forwarded was refused: {error}")
513            }
514            NetworkError::UnknownDevice => {
515                f.write_str("no registered key verifies the join request")
516            }
517            NetworkError::Replayed { dev_addr, fcnt } => {
518                write!(f, "frame {fcnt} from {dev_addr:#010x} was already seen")
519            }
520            NetworkError::CounterGap {
521                dev_addr,
522                seen,
523                carried,
524            } => write!(
525                f,
526                "frame {carried} from {dev_addr:#010x} runs too far ahead of {seen}"
527            ),
528            NetworkError::CounterExhausted { dev_addr } => write!(
529                f,
530                "the frame counter of {dev_addr:#010x} has run out; the device must join again"
531            ),
532            NetworkError::UnknownDataRate => {
533                f.write_str("the plan names no data rate for the settings heard")
534            }
535            NetworkError::NoWindow => f.write_str("the plan defines no first receive window"),
536            NetworkError::NoSession { dev_addr } => {
537                write!(f, "no session for {dev_addr:#010x}")
538            }
539        }
540    }
541}
542
543impl core::error::Error for NetworkError {}
544
545// A run of common join channels and where a join on each is answered, kept apart from the
546// plan because an owned copy of a plan does not carry the plans a join selects between.
547struct JoinAccepts {
548    channels: ChannelBlock,
549    start_hz: u32,
550    step_hz: u32,
551}
552
553// A device that has joined: the session that was granted, and the counters seen since.
554struct Admitted {
555    dev_eui: [u8; 8],
556    dev_addr: u32,
557    version: Version,
558    session: Session,
559    fcnt_up: Option<u32>,
560    fcnt_down: u32,
561    via_relay: Option<u32>,
562}
563
564impl Admitted {
565    // Works the full uplink counter out from the 16 bits a frame carries. Only the low half
566    // travels, so a value below the one last seen is the device having wrapped past a
567    // multiple of 65536 rather than having gone backwards.
568    fn uplink_counter(&self, carried: u16) -> Result<u32, NetworkError> {
569        let dev_addr = self.dev_addr;
570        let Some(seen) = self.fcnt_up else {
571            return Ok(u32::from(carried));
572        };
573        let mut candidate = (seen & 0xFFFF_0000) | u32::from(carried);
574        if candidate == seen {
575            return Err(NetworkError::Replayed {
576                dev_addr,
577                fcnt: candidate,
578            });
579        }
580        if candidate < seen {
581            candidate = candidate
582                .checked_add(0x0001_0000)
583                .ok_or(NetworkError::CounterExhausted { dev_addr })?;
584        }
585        if self.version == Version::V1_0_3 && candidate - seen >= MAX_FCNT_GAP {
586            return Err(NetworkError::CounterGap {
587                dev_addr,
588                seen,
589                carried: candidate,
590            });
591        }
592        Ok(candidate)
593    }
594}
595
596/// The network side of one site.
597///
598/// It admits the devices it is told about, grants sessions, follows frame counters, and
599/// decides where an answer goes. Everything it needs about the band comes from the
600/// [`ChannelPlan`] it is built with, so the same type serves any region.
601pub struct Network {
602    plan: OwnedChannelPlan,
603    join_accepts: Vec<JoinAccepts>,
604    windows: Windows,
605    net_id: u32,
606    registrations: Vec<Registration>,
607    admitted: Vec<Admitted>,
608    next_dev_addr: u32,
609    next_app_nonce: u32,
610    notices: Vec<Notice>,
611}
612
613impl Network {
614    /// Builds a network on a channel plan.
615    ///
616    /// The plan is copied into the network, so one holds its band for as long as it runs
617    /// rather than borrowing a table that lives somewhere else.
618    ///
619    /// # Arguments
620    ///
621    /// * `plan` - the band this site operates in.
622    /// * `net_id` - the network identifier granted addresses carry; only its low 24 bits
623    ///   travel.
624    ///
625    /// # Returns
626    ///
627    /// The network, with the recommended windows and no devices registered.
628    pub fn new(plan: &ChannelPlan<'_>, net_id: u32) -> Network {
629        Network {
630            plan: OwnedChannelPlan::from_plan(plan),
631            join_accepts: plan
632                .join_plans
633                .iter()
634                .map(|run| JoinAccepts {
635                    channels: run.channels,
636                    start_hz: run.accept_start_hz,
637                    step_hz: run.accept_step_hz,
638                })
639                .collect(),
640            windows: Windows::new(),
641            net_id,
642            registrations: Vec::new(),
643            admitted: Vec::new(),
644            next_dev_addr: 1,
645            next_app_nonce: 1,
646            notices: Vec::new(),
647        }
648    }
649
650    /// Sets the windows this network answers in.
651    ///
652    /// # Arguments
653    ///
654    /// * `windows` - the delays, offset, and channels.
655    ///
656    /// # Returns
657    ///
658    /// The updated network, for chaining.
659    pub fn with_windows(mut self, windows: Windows) -> Network {
660        self.windows = windows;
661        self
662    }
663
664    /// Sets the first address this network grants; later joins take the ones after it.
665    ///
666    /// # Arguments
667    ///
668    /// * `dev_addr` - the address to grant next.
669    ///
670    /// # Returns
671    ///
672    /// The updated network, for chaining.
673    pub fn with_first_dev_addr(mut self, dev_addr: u32) -> Network {
674        self.next_dev_addr = dev_addr;
675        self
676    }
677
678    /// Admits a device, so a join request signed with its key is accepted.
679    ///
680    /// # Arguments
681    ///
682    /// * `registration` - the device and its root key.
683    pub fn register(&mut self, registration: Registration) {
684        self.registrations.push(registration);
685    }
686
687    /// Returns the windows this network answers in.
688    ///
689    /// # Returns
690    ///
691    /// The windows.
692    pub const fn windows(&self) -> Windows {
693        self.windows
694    }
695
696    /// Returns the session granted to an address, once a device has joined.
697    ///
698    /// # Arguments
699    ///
700    /// * `dev_addr` - the address.
701    ///
702    /// # Returns
703    ///
704    /// The session, or `None` if no device holds that address here.
705    pub fn session(&self, dev_addr: u32) -> Option<Session> {
706        self.admitted
707            .iter()
708            .find(|held| held.dev_addr == dev_addr)
709            .map(|held| held.session)
710    }
711
712    /// Reads a packet the gateway forwarded.
713    ///
714    /// A join request is verified against every registered key, granted an address and a
715    /// session, and answered with an accept timed for the join window. A data frame is
716    /// routed by its address, checked against the counter last seen, and decrypted. A frame
717    /// for an address this network has not granted is reported rather than refused, because
718    /// a gateway hears every network in range.
719    ///
720    /// # Arguments
721    ///
722    /// * `heard` - the packet as the gateway reported it.
723    ///
724    /// # Returns
725    ///
726    /// What the packet turned out to be.
727    ///
728    /// # Errors
729    ///
730    /// Returns [`NetworkError::NotLora`] for an FSK packet, [`NetworkError::Frame`] if the
731    /// frame does not parse or its MIC does not verify, [`NetworkError::UnknownDevice`] if
732    /// no registered key verifies a join request, [`NetworkError::Replayed`] or
733    /// [`NetworkError::CounterGap`] if the counter is not one this network will follow, and
734    /// [`NetworkError::UnknownDataRate`] or [`NetworkError::NoWindow`] if the plan does not
735    /// describe where to answer.
736    pub fn uplink(&mut self, heard: &Rxpk) -> Result<Event, NetworkError> {
737        let link = heard.modulation.link().ok_or(NetworkError::NotLora)?;
738        let header = FrameHeader::parse(&heard.payload).map_err(NetworkError::Frame)?;
739
740        match header.message_type() {
741            MessageType::JoinRequest => self.admit(heard, link),
742            MessageType::UnconfirmedUp | MessageType::ConfirmedUp => {
743                self.receive(heard, link, &header)
744            }
745            other => Err(NetworkError::Frame(LorawanError::UnsupportedMType(
746                downlink_mtype(other),
747            ))),
748        }
749    }
750
751    /// Builds a downlink for a device, encrypted with its session.
752    ///
753    /// # Arguments
754    ///
755    /// * `dev_addr` - the device to answer.
756    /// * `slot` - where and when to transmit, from the event that reported the uplink.
757    /// * `fport` - the port to answer on.
758    /// * `payload` - what to send.
759    ///
760    /// # Returns
761    ///
762    /// The packet to put in a `PULL_RESP`, with the inverted polarity a device listens for.
763    ///
764    /// # Errors
765    ///
766    /// Returns [`NetworkError::NoSession`] if no device holds that address here, or
767    /// [`NetworkError::Frame`] if the payload does not fit a single frame.
768    pub fn answer(
769        &mut self,
770        dev_addr: u32,
771        slot: Slot,
772        fport: u8,
773        payload: &[u8],
774    ) -> Result<Txpk, NetworkError> {
775        let held = self
776            .admitted
777            .iter_mut()
778            .find(|held| held.dev_addr == dev_addr)
779            .ok_or(NetworkError::NoSession { dev_addr })?;
780
781        let frame = held
782            .session
783            .encode_downlink(&Downlink::new(held.fcnt_down, fport, payload))
784            .map_err(NetworkError::Frame)?;
785        held.fcnt_down = held.fcnt_down.wrapping_add(1);
786        let relay = held.via_relay;
787
788        let frame = match relay {
789            Some(relay) => self.wrap_for_relay(relay, frame.as_bytes())?,
790            None => frame.as_bytes().to_vec(),
791        };
792        Ok(transmit(slot, frame))
793    }
794
795    /// Builds a downlink carrying MAC commands, which is how a network configures a relay,
796    /// TS011-1.0.1 chapter 10.
797    ///
798    /// The commands ride in the frame options where they fit, and in a frame of their own on
799    /// port 0 where they do not.
800    ///
801    /// # Arguments
802    ///
803    /// * `dev_addr` - the device to send to, a relay for the relay commands.
804    /// * `slot` - where and when to transmit, from the event that reported its uplink.
805    /// * `commands` - the commands, in the order they should be read.
806    ///
807    /// # Returns
808    ///
809    /// The packet to put in a `PULL_RESP`.
810    ///
811    /// # Errors
812    ///
813    /// Returns [`NetworkError::NoSession`] if no device holds that address here, and
814    /// [`NetworkError::Frame`] if the commands do not fit one frame.
815    pub fn command(
816        &mut self,
817        dev_addr: u32,
818        slot: Slot,
819        commands: &[MacCommand],
820    ) -> Result<Txpk, NetworkError> {
821        let mut bytes = vec![0u8; commands.len() * MAX_COMMAND];
822        let len = encode_all(commands, &mut bytes).map_err(NetworkError::Frame)?;
823        let held = self
824            .admitted
825            .iter_mut()
826            .find(|held| held.dev_addr == dev_addr)
827            .ok_or(NetworkError::NoSession { dev_addr })?;
828
829        let downlink = if len <= FOPTS_MAX {
830            Downlink::empty(held.fcnt_down).with_fopts(&bytes[..len])
831        } else {
832            Downlink::new(held.fcnt_down, 0, &bytes[..len])
833        };
834        let frame = held
835            .session
836            .encode_downlink(&downlink)
837            .map_err(NetworkError::Frame)?;
838        held.fcnt_down = held.fcnt_down.wrapping_add(1);
839        Ok(transmit(slot, frame.as_bytes().to_vec()))
840    }
841
842    /// Builds the command that tells a relay to trust a device, with the key that lets it
843    /// verify the device's wake-on-radio frames, TS011-1.0.1 section 10.4.
844    ///
845    /// # Arguments
846    ///
847    /// * `dev_addr` - the end device to trust.
848    /// * `index` - which of the relay's sixteen entries to put it in.
849    /// * `reload_rate` - how many of its uplinks the relay forwards an hour, 63 for no
850    ///   limit.
851    /// * `bucket_size` - the coded bucket size multiplier, table 55.
852    ///
853    /// # Returns
854    ///
855    /// The `UpdateUplinkListReq` to send to the relay with [`command`](Network::command).
856    ///
857    /// # Errors
858    ///
859    /// Returns [`NetworkError::NoSession`] if no device holds that address here.
860    pub fn trust_command(
861        &self,
862        dev_addr: u32,
863        index: u8,
864        reload_rate: u8,
865        bucket_size: u8,
866    ) -> Result<MacCommand, NetworkError> {
867        let held = self
868            .admitted
869            .iter()
870            .find(|held| held.dev_addr == dev_addr)
871            .ok_or(NetworkError::NoSession { dev_addr })?;
872        Ok(MacCommand::UpdateUplinkListReq {
873            index,
874            reload_rate,
875            bucket_size,
876            dev_addr,
877            wfcnt: 0,
878            root_wor_s_key: held.session.root_wor_s_key(),
879        })
880    }
881
882    /// Takes the devices relays have reported hearing and could not verify, TS011-1.0.1
883    /// section 10.7.
884    ///
885    /// A relay carries these in the MAC commands of its own uplinks, alongside whatever
886    /// else that uplink was for, so they wait here until they are read.
887    ///
888    /// # Returns
889    ///
890    /// Every notice since the last call, in the order they arrived.
891    pub fn notices(&mut self) -> Vec<Notice> {
892        core::mem::take(&mut self.notices)
893    }
894
895    /// Counts the notices waiting to be read, without taking them.
896    ///
897    /// # Returns
898    ///
899    /// How many [`Notice`]s the next call to [`Network::notices`] would return.
900    #[must_use]
901    pub fn notice_count(&self) -> usize {
902        self.notices.len()
903    }
904
905    // Verifies a join request against every registered key, grants a session, and answers.
906    fn admit(&mut self, heard: &Rxpk, link: LinkSettings) -> Result<Event, NetworkError> {
907        self.admit_frame(
908            &heard.payload,
909            self.slot(heard, link, self.windows.join_delay_us)?,
910            Some(heard.frequency_hz),
911            None,
912        )
913    }
914
915    // The same, for a frame a relay forwarded, which is answered through that relay in its
916    // own window rather than in the device's.
917    fn admit_frame(
918        &mut self,
919        payload: &[u8],
920        mut slot: Slot,
921        accept_hz: Option<u32>,
922        relay: Option<u32>,
923    ) -> Result<Event, NetworkError> {
924        let (registration, request) = self
925            .registrations
926            .iter()
927            .find_map(|registration| {
928                let request = JoinRequest::parse(payload, &registration.app_key).ok()?;
929                (request.dev_eui() == registration.dev_eui).then_some((*registration, request))
930            })
931            .ok_or(NetworkError::UnknownDevice)?;
932
933        let dev_addr = self.next_dev_addr;
934        let app_nonce = self.next_app_nonce;
935        let grant = JoinGrant::new(app_nonce, self.net_id, dev_addr)
936            .with_dl_settings(
937                self.windows
938                    .dl_settings_byte(self.plan.with_plan(|plan| plan.rx2_data_rate)),
939            )
940            .with_rx_delay(self.windows.rx_delay_byte());
941        let accept = grant.accept(&registration.app_key, request.dev_nonce());
942        let session = grant.session(&registration.app_key, request.dev_nonce());
943
944        self.next_dev_addr = self.next_dev_addr.wrapping_add(1);
945        self.next_app_nonce = self.next_app_nonce.wrapping_add(1);
946        self.admitted
947            .retain(|held| held.dev_eui != registration.dev_eui);
948        self.admitted.push(Admitted {
949            dev_eui: registration.dev_eui,
950            dev_addr,
951            version: registration.version,
952            session,
953            fcnt_up: None,
954            fcnt_down: 0,
955            via_relay: relay,
956        });
957
958        if let Some(uplink_hz) = accept_hz {
959            if self.windows.rx1_channels == Rx1Channels::Plan {
960                if let Some(hz) = self.join_accept_hz(uplink_hz) {
961                    slot.frequency_hz = hz;
962                }
963            }
964        }
965        let accept = match relay {
966            Some(relay) => self.wrap_for_relay(relay, accept.as_bytes())?,
967            None => accept.as_bytes().to_vec(),
968        };
969        Ok(Event::Joined {
970            dev_eui: registration.dev_eui,
971            dev_addr,
972            accept: transmit(slot, accept),
973        })
974    }
975
976    // Routes a data frame to its session, follows its counter, and decrypts it.
977    fn receive(
978        &mut self,
979        heard: &Rxpk,
980        link: LinkSettings,
981        header: &FrameHeader,
982    ) -> Result<Event, NetworkError> {
983        let dev_addr = header
984            .dev_addr()
985            .ok_or(NetworkError::Frame(LorawanError::MalformedFrame))?;
986        let carried = header
987            .fcnt()
988            .ok_or(NetworkError::Frame(LorawanError::MalformedFrame))?;
989        let slot = self.slot(heard, link, self.windows.receive_delay_us)?;
990
991        let Some(held) = self
992            .admitted
993            .iter_mut()
994            .find(|held| held.dev_addr == dev_addr)
995        else {
996            return Ok(Event::Foreign { dev_addr });
997        };
998
999        let fcnt = held.uplink_counter(carried)?;
1000
1001        let data = held
1002            .session
1003            .decode(&heard.payload, fcnt)
1004            .map_err(NetworkError::Frame)?;
1005        held.fcnt_up = Some(fcnt);
1006        held.via_relay = None;
1007
1008        // A relay tells the network about devices it could not verify in the MAC commands
1009        // of its own uplinks, TS011-1.0.1 section 10.7.
1010        let commands = if data.fport() == Some(0) {
1011            data.payload()
1012        } else {
1013            data.fopts()
1014        };
1015        for command in MacCommands::new(Direction::Uplink, commands).flatten() {
1016            if let MacCommand::NotifyNewEndDeviceReq {
1017                dev_addr: heard_addr,
1018                rssi_dbm,
1019                snr_db,
1020            } = command
1021            {
1022                self.notices.push(Notice {
1023                    relay: dev_addr,
1024                    dev_addr: heard_addr,
1025                    rssi_dbm,
1026                    snr_db,
1027                });
1028            }
1029        }
1030
1031        if data.fport() == Some(LA_FPORT_RELAY) {
1032            return self.forwarded(dev_addr, data.payload(), slot);
1033        }
1034
1035        Ok(Event::Data {
1036            dev_addr,
1037            fcnt,
1038            fport: data.fport(),
1039            payload: data.payload().to_vec(),
1040            confirmed: data.confirmed(),
1041            slot,
1042            relay: None,
1043        })
1044    }
1045
1046    // Reads an uplink a relay forwarded, and treats it as if the device itself had sent it,
1047    // TS011-1.0.1 section 9.1.
1048    fn forwarded(&mut self, relay: u32, payload: &[u8], slot: Slot) -> Result<Event, NetworkError> {
1049        let forwarded = ForwardedUplink::parse(payload).map_err(NetworkError::Relayed)?;
1050        let relayed = Relayed {
1051            relay,
1052            metadata: forwarded.metadata,
1053            frequency_hz: forwarded.frequency_hz,
1054        };
1055        let header = FrameHeader::parse(forwarded.phy_payload).map_err(NetworkError::Relayed)?;
1056        match header.message_type() {
1057            MessageType::JoinRequest => {
1058                self.admit_frame(forwarded.phy_payload, slot, None, Some(relay))
1059            }
1060            MessageType::UnconfirmedUp | MessageType::ConfirmedUp => {
1061                let dev_addr = header
1062                    .dev_addr()
1063                    .ok_or(NetworkError::Relayed(LorawanError::MalformedFrame))?;
1064                let carried = header
1065                    .fcnt()
1066                    .ok_or(NetworkError::Relayed(LorawanError::MalformedFrame))?;
1067                let Some(held) = self
1068                    .admitted
1069                    .iter_mut()
1070                    .find(|held| held.dev_addr == dev_addr)
1071                else {
1072                    return Ok(Event::Foreign { dev_addr });
1073                };
1074                let fcnt = held.uplink_counter(carried)?;
1075                let data = held
1076                    .session
1077                    .decode(forwarded.phy_payload, fcnt)
1078                    .map_err(NetworkError::Relayed)?;
1079                held.fcnt_up = Some(fcnt);
1080                held.via_relay = Some(relay);
1081                Ok(Event::Data {
1082                    dev_addr,
1083                    fcnt,
1084                    fport: data.fport(),
1085                    payload: data.payload().to_vec(),
1086                    confirmed: data.confirmed(),
1087                    slot,
1088                    relay: Some(relayed),
1089                })
1090            }
1091            other => Err(NetworkError::Relayed(LorawanError::UnsupportedMType(
1092                downlink_mtype(other),
1093            ))),
1094        }
1095    }
1096
1097    // Wraps a frame for an end device in the downlink its relay forwards it in, section 9.2.
1098    fn wrap_for_relay(&mut self, relay: u32, frame: &[u8]) -> Result<Vec<u8>, NetworkError> {
1099        let held = self
1100            .admitted
1101            .iter_mut()
1102            .find(|held| held.dev_addr == relay)
1103            .ok_or(NetworkError::NoSession { dev_addr: relay })?;
1104        let wrapped = held
1105            .session
1106            .encode_downlink(&Downlink::new(held.fcnt_down, LA_FPORT_RELAY, frame))
1107            .map_err(NetworkError::Frame)?;
1108        held.fcnt_down = held.fcnt_down.wrapping_add(1);
1109        Ok(wrapped.as_bytes().to_vec())
1110    }
1111
1112    // Works out where and when the first receive window opens for a packet.
1113    fn slot(&self, heard: &Rxpk, link: LinkSettings, delay_us: u32) -> Result<Slot, NetworkError> {
1114        let uplink_rate = self
1115            .data_rate_of(link)
1116            .ok_or(NetworkError::UnknownDataRate)?;
1117        let settings = self
1118            .plan
1119            .with_plan(|plan| {
1120                plan.rx1_data_rate(uplink_rate, self.windows.rx1_data_rate_offset)
1121                    .and_then(|downlink_rate| plan.downlink_data_rate(downlink_rate))
1122                    .and_then(|rate| rate.link_settings())
1123            })
1124            .ok_or(NetworkError::NoWindow)?;
1125
1126        Ok(Slot {
1127            timestamp_us: heard.timestamp_us.unwrap_or(0).wrapping_add(delay_us),
1128            frequency_hz: self.rx1_frequency_hz(heard.frequency_hz)?,
1129            link: settings,
1130        })
1131    }
1132
1133    // Applies the region's rule for which channel the first window answers on.
1134    fn rx1_frequency_hz(&self, uplink_hz: u32) -> Result<u32, NetworkError> {
1135        match self.windows.rx1_channels {
1136            Rx1Channels::Plan => {
1137                if self.plan.with_plan(|plan| plan.downlink_channel_count()) == 0 {
1138                    return Ok(uplink_hz);
1139                }
1140                let channel = self.channel_of(uplink_hz).ok_or(NetworkError::NoWindow)?;
1141                self.plan
1142                    .with_plan(|plan| plan.rx1_frequency_hz(channel, uplink_hz))
1143                    .ok_or(NetworkError::NoWindow)
1144            }
1145            Rx1Channels::SameAsUplink => Ok(uplink_hz),
1146            Rx1Channels::Downstream(block) => {
1147                let channel = self.channel_of(uplink_hz).ok_or(NetworkError::NoWindow)?;
1148                block
1149                    .frequency_hz(channel % block.count)
1150                    .ok_or(NetworkError::NoWindow)
1151            }
1152        }
1153    }
1154
1155    // Finds where a join on a common join channel is answered, for a plan that has them.
1156    fn join_accept_hz(&self, uplink_hz: u32) -> Option<u32> {
1157        self.join_accepts.iter().find_map(|run| {
1158            (0..run.channels.count)
1159                .find(|offset| run.channels.frequency_hz(*offset) == Some(uplink_hz))
1160                .map(|offset| run.start_hz + run.step_hz * u32::from(offset))
1161        })
1162    }
1163
1164    // Finds which channel of the plan a frequency is, counting through the default blocks
1165    // in the order the channel numbering follows.
1166    fn channel_of(&self, frequency_hz: u32) -> Option<u16> {
1167        self.plan.with_plan(|plan| {
1168            let mut first = 0u16;
1169            for block in plan.default_channels {
1170                for index in 0..block.count {
1171                    if block.frequency_hz(index) == Some(frequency_hz) {
1172                        return Some(first + index);
1173                    }
1174                }
1175                first += block.count;
1176            }
1177            None
1178        })
1179    }
1180
1181    // Finds the data rate number whose settings a packet arrived with. Only the spreading
1182    // factor and the bandwidth name a rate; the coding rate and the frame options a radio
1183    // reports alongside them do not.
1184    fn data_rate_of(&self, link: LinkSettings) -> Option<u8> {
1185        self.plan.with_plan(|plan| {
1186            plan.uplink_data_rates
1187                .iter()
1188                .enumerate()
1189                .find_map(|(number, rate)| {
1190                    let settings = rate.as_ref()?.link_settings()?;
1191                    (settings.spreading_factor() == link.spreading_factor()
1192                        && settings.bandwidth_hz() == link.bandwidth_hz())
1193                    .then_some(number as u8)
1194                })
1195        })
1196    }
1197}
1198
1199// The packet a downlink goes out as: at the window, with the polarity a device listens for.
1200fn transmit(slot: Slot, payload: Vec<u8>) -> Txpk {
1201    Txpk::at(slot.timestamp_us, slot.frequency_hz, slot.link, payload).with_inverted_polarity(true)
1202}
1203
1204// The MHDR byte a message type carries, for reporting one that is not an uplink.
1205const fn downlink_mtype(message_type: MessageType) -> u8 {
1206    match message_type {
1207        MessageType::JoinRequest => 0x00,
1208        MessageType::JoinAccept => 0x20,
1209        MessageType::UnconfirmedUp => 0x40,
1210        MessageType::UnconfirmedDown => 0x60,
1211        MessageType::ConfirmedUp => 0x80,
1212        MessageType::ConfirmedDown => 0xA0,
1213    }
1214}
1215
1216#[cfg(test)]
1217mod tests {
1218    use super::*;
1219
1220    use pamoja_lora::region::{Cn470Plan, Region};
1221    use pamoja_lorawan::{Device, Uplink};
1222
1223    const DEV_EUI: [u8; 8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77];
1224    const APP_EUI: [u8; 8] = [0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x00, 0x00, 0x01];
1225    const APP_KEY: [u8; 16] = [
1226        0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F,
1227        0x3C,
1228    ];
1229
1230    fn site() -> Network {
1231        let mut network =
1232            Network::new(Region::Eu868.plan(), 0x00_00_2A).with_first_dev_addr(0x2601_0001);
1233        network.register(Registration::new(DEV_EUI, APP_EUI, APP_KEY));
1234        network
1235    }
1236
1237    fn heard(frame: Vec<u8>, timestamp_us: u32) -> Rxpk {
1238        Rxpk::new(868_100_000, LinkSettings::new(7, 125_000), frame).with_timestamp_us(timestamp_us)
1239    }
1240
1241    #[test]
1242    fn the_recommended_delays_are_the_published_ones() {
1243        // RP002-1.0.5 section 3.3: RECEIVE_DELAY1 1s, RECEIVE_DELAY2 2s, JOIN_ACCEPT_DELAY1
1244        // 5s, JOIN_ACCEPT_DELAY2 6s.
1245        assert_eq!(RECEIVE_DELAY1_US, 1_000_000);
1246        assert_eq!(RECEIVE_DELAY2_US, 2_000_000);
1247        assert_eq!(JOIN_ACCEPT_DELAY1_US, 5_000_000);
1248        assert_eq!(JOIN_ACCEPT_DELAY2_US, 6_000_000);
1249        assert_eq!(MAX_FCNT_GAP, 16_384);
1250    }
1251
1252    #[test]
1253    fn the_join_accept_bytes_follow_the_specification() {
1254        // TS001-1.0.4 table 55: RFU, then RX1DROffset in bits 6:4, then RX2DataRate in 3:0.
1255        let windows = Windows::new().with_rx1_data_rate_offset(5);
1256        assert_eq!(windows.dl_settings_byte(0), 0x50);
1257        assert_eq!(windows.dl_settings_byte(0x0F), 0x5F);
1258        assert_eq!(Windows::new().dl_settings_byte(8), 0x08);
1259
1260        // TS001-1.0.4 table 44: the delay in seconds in the low four bits, and a zero there
1261        // means one second.
1262        assert_eq!(Windows::new().rx_delay_byte(), 1);
1263        assert_eq!(
1264            Windows::new()
1265                .with_receive_delay_us(5_000_000)
1266                .rx_delay_byte(),
1267            5
1268        );
1269    }
1270
1271    #[test]
1272    fn a_registration_never_prints_its_key() {
1273        let printed = format!("{:?}", Registration::new(DEV_EUI, APP_EUI, APP_KEY));
1274        assert!(printed.contains("dev_eui"));
1275        assert!(!printed.contains("2b") && !printed.contains("43"));
1276    }
1277
1278    #[test]
1279    fn a_device_joins_and_is_answered_in_the_join_window() {
1280        let mut network = site();
1281        let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1282        let request = device.join_request(0x0102);
1283
1284        let event = network
1285            .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
1286            .expect("the request verifies");
1287
1288        let Event::Joined {
1289            dev_eui,
1290            dev_addr,
1291            accept,
1292        } = event
1293        else {
1294            panic!("a join request is admitted");
1295        };
1296        assert_eq!(dev_eui, DEV_EUI);
1297        assert_eq!(dev_addr, 0x2601_0001);
1298        // Five seconds after the uplink, on the uplink frequency, with inverted polarity.
1299        assert_eq!(accept.timestamp_us, Some(6_000_000));
1300        assert_eq!(accept.frequency_hz, 868_100_000);
1301        assert!(accept.invert_polarity);
1302
1303        // The device reads the accept it was sent and derives the same session.
1304        let granted = device
1305            .accept_join(&accept.payload, 0x0102)
1306            .expect("the accept verifies");
1307        assert_eq!(granted.dev_addr(), 0x2601_0001);
1308    }
1309
1310    #[test]
1311    fn an_uplink_is_decrypted_and_answered_one_second_later() {
1312        let mut network = site();
1313        let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1314        let request = device.join_request(0x0102);
1315        let Event::Joined { accept, .. } = network
1316            .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
1317            .expect("the request verifies")
1318        else {
1319            panic!("a join request is admitted");
1320        };
1321        let session = device
1322            .accept_join(&accept.payload, 0x0102)
1323            .expect("the accept verifies")
1324            .session();
1325
1326        let frame = session
1327            .encode_uplink(&pamoja_lorawan::Uplink::new(0, 2, b"21.5"))
1328            .expect("it fits one frame");
1329        let event = network
1330            .uplink(&heard(frame.as_bytes().to_vec(), 9_000_000))
1331            .expect("the frame verifies");
1332
1333        let Event::Data {
1334            dev_addr,
1335            fcnt,
1336            fport,
1337            payload,
1338            confirmed,
1339            slot,
1340            ..
1341        } = event
1342        else {
1343            panic!("a data frame is read");
1344        };
1345        assert_eq!(dev_addr, 0x2601_0001);
1346        assert_eq!(fcnt, 0);
1347        assert_eq!(fport, Some(2));
1348        assert_eq!(payload, b"21.5");
1349        assert!(!confirmed);
1350        assert_eq!(slot.timestamp_us, 10_000_000);
1351        assert_eq!(slot.frequency_hz, 868_100_000);
1352        assert_eq!(slot.link.spreading_factor(), 7);
1353
1354        // The answer goes out in that window, and the device reads it.
1355        let downlink = network
1356            .answer(dev_addr, slot, 2, b"ok")
1357            .expect("the session is held");
1358        assert_eq!(downlink.timestamp_us, Some(10_000_000));
1359        assert!(downlink.invert_polarity);
1360        let read = session.decode(&downlink.payload, 0).expect("it verifies");
1361        assert_eq!(read.payload(), b"ok");
1362    }
1363
1364    #[test]
1365    fn a_replayed_frame_is_refused() {
1366        let mut network = site();
1367        let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1368        let request = device.join_request(0x0102);
1369        let Event::Joined { accept, .. } = network
1370            .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
1371            .expect("the request verifies")
1372        else {
1373            panic!("a join request is admitted");
1374        };
1375        let session = device
1376            .accept_join(&accept.payload, 0x0102)
1377            .expect("the accept verifies")
1378            .session();
1379
1380        let first = session
1381            .encode_uplink(&pamoja_lorawan::Uplink::new(1, 2, b"one"))
1382            .expect("it fits one frame");
1383        network
1384            .uplink(&heard(first.as_bytes().to_vec(), 2_000_000))
1385            .expect("the first frame is read");
1386
1387        let error = network
1388            .uplink(&heard(first.as_bytes().to_vec(), 3_000_000))
1389            .expect_err("the same counter twice is a replay");
1390        assert!(matches!(
1391            error,
1392            NetworkError::Replayed {
1393                dev_addr: 0x2601_0001,
1394                ..
1395            }
1396        ));
1397    }
1398
1399    // LoRaWAN 1.0.3 section 4.3.1.5: the receiver follows a counter that has gone up by less
1400    // than MAX_FCNT_GAP, so one exactly that far ahead is refused, as the device refuses it.
1401    // TS001-1.0.4 section 4.3.1.5 asks only that the counter have gone up, so a 1.0.4 device
1402    // is followed however far it jumps.
1403    #[test]
1404    fn a_counter_a_whole_gap_ahead_is_refused_from_a_1_0_3_device_alone() {
1405        for version in [Version::V1_0_3, Version::V1_0_4] {
1406            let mut network =
1407                Network::new(Region::Eu868.plan(), 0x00_00_2A).with_first_dev_addr(0x2601_0001);
1408            network.register(Registration::new(DEV_EUI, APP_EUI, APP_KEY).with_version(version));
1409            let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1410            let request = device.join_request(0x0102);
1411            let Event::Joined { accept, .. } = network
1412                .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
1413                .expect("the request verifies")
1414            else {
1415                panic!("a join request is admitted");
1416            };
1417            let session = device
1418                .accept_join(&accept.payload, 0x0102)
1419                .expect("the accept verifies")
1420                .session();
1421            let frame = |fcnt: u32| {
1422                session
1423                    .encode_uplink(&pamoja_lorawan::Uplink::new(fcnt, 2, b"level"))
1424                    .expect("it fits one frame")
1425                    .as_bytes()
1426                    .to_vec()
1427            };
1428
1429            network
1430                .uplink(&heard(frame(1), 2_000_000))
1431                .expect("the first frame is read");
1432            let jumped = network.uplink(&heard(frame(1 + MAX_FCNT_GAP), 3_000_000));
1433            match version {
1434                Version::V1_0_3 => {
1435                    assert!(
1436                        matches!(
1437                            jumped,
1438                            Err(NetworkError::CounterGap { seen: 1, carried, .. }) if carried == 1 + MAX_FCNT_GAP
1439                        ),
1440                        "{jumped:?}"
1441                    );
1442                    let Event::Data { fcnt, .. } = network
1443                        .uplink(&heard(frame(MAX_FCNT_GAP), 4_000_000))
1444                        .expect("one short of the gap is followed")
1445                    else {
1446                        panic!("a data frame is read");
1447                    };
1448                    assert_eq!(fcnt, MAX_FCNT_GAP);
1449                }
1450                Version::V1_0_4 => {
1451                    let Ok(Event::Data { fcnt, .. }) = jumped else {
1452                        panic!("a 1.0.4 counter is followed across the gap: {jumped:?}");
1453                    };
1454                    assert_eq!(fcnt, 1 + MAX_FCNT_GAP);
1455                }
1456            }
1457        }
1458    }
1459
1460    #[test]
1461    fn a_registration_follows_1_0_4_unless_told_otherwise() {
1462        let registration = Registration::new(DEV_EUI, APP_EUI, APP_KEY);
1463        assert_eq!(registration.version(), Version::V1_0_4);
1464        assert_eq!(
1465            registration.with_version(Version::V1_0_3).version(),
1466            Version::V1_0_3
1467        );
1468    }
1469
1470    #[test]
1471    fn a_counter_that_would_pass_32_bits_ends_the_session() {
1472        let mut network = site();
1473        let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1474        let request = device.join_request(0x0102);
1475        let Event::Joined { accept, .. } = network
1476            .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
1477            .expect("the request verifies")
1478        else {
1479            panic!("a join request is admitted");
1480        };
1481        let session = device
1482            .accept_join(&accept.payload, 0x0102)
1483            .expect("the accept verifies")
1484            .session();
1485        network.admitted[0].fcnt_up = Some(0xFFFF_FFF0);
1486        let frame = session
1487            .encode_uplink(&pamoja_lorawan::Uplink::new(0x0005, 2, b"level"))
1488            .expect("it fits one frame");
1489        let error = network
1490            .uplink(&heard(frame.as_bytes().to_vec(), 2_000_000))
1491            .expect_err("no counter follows 0xFFFFFFF0 with 0x0005 in its low half");
1492        assert_eq!(
1493            error,
1494            NetworkError::CounterExhausted {
1495                dev_addr: 0x2601_0001
1496            }
1497        );
1498        assert!(error.to_string().contains("must join again"));
1499    }
1500
1501    #[test]
1502    fn a_counter_that_wraps_carries_on_where_it_left_off() {
1503        let mut network = site();
1504        let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1505        let request = device.join_request(0x0102);
1506        let Event::Joined { accept, .. } = network
1507            .uplink(&heard(request.as_bytes().to_vec(), 1_000_000))
1508            .expect("the request verifies")
1509        else {
1510            panic!("a join request is admitted");
1511        };
1512        let session = device
1513            .accept_join(&accept.payload, 0x0102)
1514            .expect("the accept verifies")
1515            .session();
1516
1517        // The last counter the low sixteen bits can hold, then the one after it.
1518        let last = session
1519            .encode_uplink(&pamoja_lorawan::Uplink::new(0xFFFF, 2, b"last"))
1520            .expect("it fits one frame");
1521        network
1522            .uplink(&heard(last.as_bytes().to_vec(), 2_000_000))
1523            .expect("the frame is read");
1524
1525        let wrapped = session
1526            .encode_uplink(&pamoja_lorawan::Uplink::new(0x0001_0000, 2, b"next"))
1527            .expect("it fits one frame");
1528        let event = network
1529            .uplink(&heard(wrapped.as_bytes().to_vec(), 3_000_000))
1530            .expect("the wrapped frame is read");
1531
1532        let Event::Data { fcnt, payload, .. } = event else {
1533            panic!("a data frame is read");
1534        };
1535        assert_eq!(fcnt, 0x0001_0000);
1536        assert_eq!(payload, b"next");
1537    }
1538
1539    #[test]
1540    fn a_frame_for_another_network_is_reported_not_refused() {
1541        let mut network = site();
1542        let stranger = Session::new(0x1234_5678, [9; 16], [8; 16]);
1543        let frame = stranger
1544            .encode_uplink(&pamoja_lorawan::Uplink::new(0, 1, b"hello"))
1545            .expect("it fits one frame");
1546
1547        let event = network
1548            .uplink(&heard(frame.as_bytes().to_vec(), 1_000_000))
1549            .expect("a frame from elsewhere is not an error");
1550        assert_eq!(
1551            event,
1552            Event::Foreign {
1553                dev_addr: 0x1234_5678
1554            }
1555        );
1556    }
1557
1558    #[test]
1559    fn the_first_window_follows_the_region() {
1560        // RP002-1.0.5 section 3.5.7: RX1 Channel Number = Transmit Channel Number modulo
1561        // NbChannel, over the eight downlink channels of section 3.5.2.
1562        let block = match Rx1Channels::us915() {
1563            Rx1Channels::Downstream(block) => block,
1564            other => panic!("US902-928 answers on its own channels, not {other:?}"),
1565        };
1566        assert_eq!(block.frequency_hz(0), Some(923_300_000));
1567        assert_eq!(block.frequency_hz(7), Some(927_500_000));
1568        assert_eq!(block.count, 8);
1569    }
1570
1571    /// Admits the test device on a plan and returns the accept it was sent.
1572    fn joined_on(network: &mut Network, frequency_hz: u32, link: LinkSettings) -> Txpk {
1573        network.register(Registration::new(DEV_EUI, APP_EUI, APP_KEY));
1574        let request = Device::new(DEV_EUI, APP_EUI, APP_KEY).join_request(0x0102);
1575        let heard =
1576            Rxpk::new(frequency_hz, link, request.as_bytes().to_vec()).with_timestamp_us(1_000_000);
1577        match network.uplink(&heard).expect("the request verifies") {
1578            Event::Joined { accept, .. } => accept,
1579            other => panic!("a join request is admitted, not {other:?}"),
1580        }
1581    }
1582
1583    #[test]
1584    fn a_fixed_plan_is_answered_on_its_downlink_channels_at_its_downlink_rates() {
1585        // RP002-1.0.5 sections 3.5.2 and 3.5.7, and tables 20 and 26: a join on channel 0 at
1586        // DR0 is answered on 923.3 MHz at DR10, SF10 on 500 kHz, and an uplink on channel 65
1587        // at DR4 on 923.9 MHz at DR13, SF7 on 500 kHz.
1588        let mut network = Network::new(Region::Us915.plan(), 0x00_00_2A);
1589        let accept = joined_on(&mut network, 902_300_000, LinkSettings::new(10, 125_000));
1590        assert_eq!(accept.frequency_hz, 923_300_000);
1591        let crate::udp::Modulation::Lora(link) = accept.modulation else {
1592            panic!("a LoRa accept");
1593        };
1594        assert_eq!(
1595            (link.spreading_factor(), link.bandwidth_hz()),
1596            (10, 500_000)
1597        );
1598
1599        let session = Device::new(DEV_EUI, APP_EUI, APP_KEY)
1600            .accept_join(&accept.payload, 0x0102)
1601            .expect("the accept verifies")
1602            .session();
1603        let frame = session
1604            .encode_uplink(&pamoja_lorawan::Uplink::new(0, 2, b"21.5"))
1605            .expect("it fits one frame");
1606        let heard = Rxpk::new(
1607            904_600_000,
1608            LinkSettings::new(8, 500_000),
1609            frame.as_bytes().to_vec(),
1610        )
1611        .with_timestamp_us(9_000_000);
1612        let Event::Data { slot, .. } = network.uplink(&heard).expect("the frame verifies") else {
1613            panic!("a data frame is read");
1614        };
1615        assert_eq!(slot.frequency_hz, 923_900_000);
1616        assert_eq!(
1617            (slot.link.spreading_factor(), slot.link.bandwidth_hz()),
1618            (7, 500_000)
1619        );
1620    }
1621
1622    #[test]
1623    fn a_chinese_join_is_answered_where_its_common_join_channel_says() {
1624        // RP002-1.0.5 table 49: common join channel 11, 472.3 MHz, is answered on 492.5 MHz,
1625        // and channel 3, 475.7 MHz, on 489.3 MHz.
1626        let mut network = Network::new(Cn470Plan::Antenna26MhzA.plan(), 0x00_00_2A);
1627        let accept = joined_on(&mut network, 472_300_000, LinkSettings::new(9, 125_000));
1628        assert_eq!(accept.frequency_hz, 492_500_000);
1629
1630        let mut network = Network::new(Region::Cn470.plan(), 0x00_00_2A);
1631        let accept = joined_on(&mut network, 475_700_000, LinkSettings::new(9, 125_000));
1632        assert_eq!(accept.frequency_hz, 489_300_000);
1633    }
1634
1635    #[test]
1636    fn a_window_override_still_wins_over_the_plan() {
1637        let mut network = Network::new(Region::Us915.plan(), 0x00_00_2A)
1638            .with_windows(Windows::new().with_rx1_channels(Rx1Channels::SameAsUplink));
1639        let accept = joined_on(&mut network, 902_300_000, LinkSettings::new(10, 125_000));
1640        assert_eq!(accept.frequency_hz, 902_300_000);
1641    }
1642
1643    #[test]
1644    fn an_fsk_packet_carries_no_frame() {
1645        let mut network = site();
1646        let mut packet = heard(vec![0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
1647        packet.modulation = crate::udp::Modulation::Fsk(50_000);
1648        assert_eq!(network.uplink(&packet), Err(NetworkError::NotLora));
1649    }
1650
1651    const RELAY_EUI: [u8; 8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x88];
1652    const RELAY_KEY: [u8; 16] = [0x5A; 16];
1653
1654    /// A network with a relay and an end device registered, the relay joined and holding a
1655    /// session, and its address.
1656    fn site_with_relay() -> (Network, Session, u32) {
1657        let mut network = site();
1658        network.register(Registration::new(RELAY_EUI, APP_EUI, RELAY_KEY));
1659        let relay = Device::new(RELAY_EUI, APP_EUI, RELAY_KEY);
1660        let request = relay.join_request(0x0101);
1661        let Event::Joined { dev_addr, .. } = network
1662            .uplink(&heard(request.as_bytes().to_vec(), 1_000))
1663            .expect("the relay joins")
1664        else {
1665            panic!("the relay joined");
1666        };
1667        let session = network.session(dev_addr).expect("a session");
1668        (network, session, dev_addr)
1669    }
1670
1671    /// The uplink a relay sends to forward a frame it heard.
1672    fn forwarded(session: &Session, fcnt: u32, phy_payload: &[u8]) -> Vec<u8> {
1673        let uplink = ForwardedUplink {
1674            metadata: UplinkMetadata {
1675                wor_channel: pamoja_lorawan::relay::WorChannel::Default,
1676                rssi_dbm: -92,
1677                snr_db: 5,
1678                data_rate: 5,
1679            },
1680            frequency_hz: 868_300_000,
1681            phy_payload,
1682        };
1683        let mut payload = vec![0u8; phy_payload.len() + 16];
1684        let len = uplink.encode(&mut payload).expect("it encodes");
1685        session
1686            .encode_uplink(&Uplink::new(fcnt, LA_FPORT_RELAY, &payload[..len]))
1687            .expect("the relay's uplink encodes")
1688            .as_bytes()
1689            .to_vec()
1690    }
1691
1692    #[test]
1693    fn a_join_a_relay_forwards_is_admitted_and_its_accept_goes_back_the_same_way() {
1694        let (mut network, relay_session, relay_addr) = site_with_relay();
1695        let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1696        let request = device.join_request(0x1234);
1697
1698        let event = network
1699            .uplink(&heard(
1700                forwarded(&relay_session, 0, request.as_bytes()),
1701                5_000,
1702            ))
1703            .expect("the forwarded request verifies");
1704        let Event::Joined {
1705            dev_eui,
1706            dev_addr,
1707            accept,
1708        } = event
1709        else {
1710            panic!("the device joined: {event:?}");
1711        };
1712        assert_eq!(dev_eui, DEV_EUI);
1713        assert_ne!(dev_addr, relay_addr);
1714
1715        // The accept is addressed to the relay, on the port a relay forwards on, and
1716        // carries the device's own accept for it to send on.
1717        let to_relay = relay_session
1718            .decode(&accept.payload, 0)
1719            .expect("the relay's downlink decodes");
1720        assert_eq!(to_relay.fport(), Some(LA_FPORT_RELAY));
1721        let session = device
1722            .accept_join(to_relay.payload(), 0x1234)
1723            .expect("the device's own accept")
1724            .session();
1725        assert_eq!(session.dev_addr(), dev_addr);
1726    }
1727
1728    #[test]
1729    fn an_uplink_a_relay_forwards_carries_what_it_heard_and_is_answered_through_it() {
1730        let (mut network, relay_session, _) = site_with_relay();
1731        let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1732        let request = device.join_request(0x1234);
1733        let Event::Joined { dev_addr, .. } = network
1734            .uplink(&heard(
1735                forwarded(&relay_session, 0, request.as_bytes()),
1736                5_000,
1737            ))
1738            .expect("the device joins")
1739        else {
1740            panic!("the device joined");
1741        };
1742        let session = network.session(dev_addr).expect("the granted session");
1743
1744        let reading = session
1745            .encode_uplink(&Uplink::new(0, 2, b"21.5"))
1746            .expect("the reading encodes");
1747        let event = network
1748            .uplink(&heard(
1749                forwarded(&relay_session, 1, reading.as_bytes()),
1750                9_000,
1751            ))
1752            .expect("the forwarded reading verifies");
1753        let Event::Data {
1754            dev_addr: from,
1755            payload,
1756            relay,
1757            slot,
1758            ..
1759        } = event
1760        else {
1761            panic!("a reading came through: {event:?}");
1762        };
1763        assert_eq!(from, dev_addr);
1764        assert_eq!(payload, b"21.5");
1765        let relayed = relay.expect("it came through a relay");
1766        assert_eq!(relayed.metadata.rssi_dbm, -92);
1767        assert_eq!(relayed.metadata.snr_db, 5);
1768        assert_eq!(relayed.frequency_hz, 868_300_000);
1769
1770        // The answer goes to the relay, which sends it on.
1771        let answer = network
1772            .answer(dev_addr, slot, 2, b"ok")
1773            .expect("an answer for the device");
1774        // The accept took the relay's first downlink, so this is its second.
1775        let to_relay = relay_session
1776            .decode(&answer.payload, 1)
1777            .expect("the relay's downlink decodes");
1778        assert_eq!(to_relay.fport(), Some(LA_FPORT_RELAY));
1779        let inside = session
1780            .decode(to_relay.payload(), 0)
1781            .expect("the device's own downlink");
1782        assert_eq!(inside.payload(), b"ok");
1783    }
1784
1785    #[test]
1786    fn a_relay_reports_the_devices_it_cannot_verify_and_is_told_to_trust_them() {
1787        let (mut network, relay_session, relay_addr) = site_with_relay();
1788        let device = Device::new(DEV_EUI, APP_EUI, APP_KEY);
1789        let request = device.join_request(0x1234);
1790        let Event::Joined { dev_addr, .. } = network
1791            .uplink(&heard(
1792                forwarded(&relay_session, 0, request.as_bytes()),
1793                5_000,
1794            ))
1795            .expect("the device joins")
1796        else {
1797            panic!("the device joined");
1798        };
1799
1800        // The relay carries a notice in the frame options of its own uplink.
1801        let mut fopts = [0u8; MAX_COMMAND];
1802        let len = encode_all(
1803            &[MacCommand::NotifyNewEndDeviceReq {
1804                dev_addr: 0x2601_9999,
1805                rssi_dbm: -100,
1806                snr_db: 3,
1807            }],
1808            &mut fopts,
1809        )
1810        .expect("the notice encodes");
1811        let uplink = relay_session
1812            .encode_uplink(&Uplink::empty(1).with_fopts(&fopts[..len]))
1813            .expect("the relay's uplink encodes");
1814        let event = network
1815            .uplink(&heard(uplink.as_bytes().to_vec(), 11_000))
1816            .expect("the uplink verifies");
1817        let slot = match event {
1818            Event::Data { slot, .. } => slot,
1819            other => panic!("the relay sent an uplink: {other:?}"),
1820        };
1821        assert_eq!(
1822            network.notices(),
1823            vec![Notice {
1824                relay: relay_addr,
1825                dev_addr: 0x2601_9999,
1826                rssi_dbm: -100,
1827                snr_db: 3,
1828            }]
1829        );
1830        assert!(network.notices().is_empty(), "each notice is read once");
1831
1832        // The network tells the relay to trust the device, with the key that lets it check
1833        // the device's wake-on-radio frames.
1834        let command = network
1835            .trust_command(dev_addr, 0, 63, 0)
1836            .expect("the device has a session");
1837        let MacCommand::UpdateUplinkListReq {
1838            root_wor_s_key,
1839            dev_addr: trusted,
1840            ..
1841        } = command
1842        else {
1843            panic!("an UpdateUplinkListReq: {command:?}");
1844        };
1845        assert_eq!(trusted, dev_addr);
1846        assert_eq!(
1847            root_wor_s_key,
1848            network
1849                .session(dev_addr)
1850                .expect("a session")
1851                .root_wor_s_key(),
1852        );
1853
1854        let txpk = network
1855            .command(relay_addr, slot, &[command])
1856            .expect("a downlink for the relay");
1857        let to_relay = relay_session
1858            .decode(&txpk.payload, 1)
1859            .expect("the relay's downlink decodes");
1860        let carried: Vec<_> = MacCommands::new(Direction::Downlink, to_relay.payload())
1861            .flatten()
1862            .collect();
1863        assert_eq!(carried, vec![command], "a long command takes port 0");
1864        assert_eq!(to_relay.fport(), Some(0));
1865    }
1866
1867    #[test]
1868    fn a_relay_that_forwards_nonsense_is_refused_without_taking_the_network_down() {
1869        let (mut network, relay_session, _) = site_with_relay();
1870        let uplink = relay_session
1871            .encode_uplink(&Uplink::new(0, LA_FPORT_RELAY, &[0x01, 0x02]))
1872            .expect("the relay's uplink encodes");
1873        assert!(matches!(
1874            network.uplink(&heard(uplink.as_bytes().to_vec(), 3_000)),
1875            Err(NetworkError::Relayed(_))
1876        ));
1877
1878        let stranger = Session::new(0x2601_5555, [0x11; 16], [0x22; 16]);
1879        let frame = stranger
1880            .encode_uplink(&Uplink::new(0, 1, b"hi"))
1881            .expect("it encodes");
1882        assert_eq!(
1883            network.uplink(&heard(
1884                forwarded(&relay_session, 1, frame.as_bytes()),
1885                4_000
1886            )),
1887            Ok(Event::Foreign {
1888                dev_addr: 0x2601_5555
1889            }),
1890            "a device this network never granted",
1891        );
1892    }
1893}