This macro eliminates manual size/alignment management and prevents
namespace collisions by moving internal queues to file-private scope.
Signed-off-by: Cesar Vandevelde <cesar.vandevelde@gmail.com>
A downlink carrying only the FPending bit (no ACK, no FRMPayload and
no pending MAC answer) was dropped without notifying the application,
losing the queued-downlink hint. Dispatch a payload-less notification
whenever the frame carries an ACK or any flag worth reporting.
Assisted-by: claude-opus 4.8
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
FPort 0 is reserved for MAC commands; an application payload sent on port 0
would be encrypted with the network session key and parsed as MAC commands
by the server.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Per spec FPort 0 carries MAC commands only (encrypted with FNwkSIntKey)
and cannot be combined with FOpts MAC commands in the same frame: feed the
decrypted payload to the MAC command parser before any session state is
committed, reject frames mixing the two forms, and never deliver port-0
payload to the application downlink callbacks.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Queue the LinkCheckReq via the engine so it rides the next uplink; with
force_request mac_do_link_check() triggers the empty unconfirmed uplink
itself.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
MAC commands in FOpts eat into the maximum application payload for the
datarate. Subtract the pending FOpts length when validating an uplink and
in lorawan_get_payload_sizes(), which now returns distinct values for the
next payload and the no-FOpts maximum.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Walk the downlink FOpts with a CID/length table so known-but-unhandled
commands are skipped and a trailing LinkCheckAns is still reached;
unknown CIDs terminate parsing since their length is unknown. Commands are
processed only after MIC verification and replay checks.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Add the LWAN_REQ_LINK_CHECK engine request: the handler queues a
LinkCheckReq that the FOpts builder emits on the next uplink.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Use a positive sentinel for the confirmed-uplink retry loop so a real
-EAGAIN propagated from the TX/RX path cannot be mistaken for a retry
request.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
No functional changes in this commit.
mac_do_join() and mac_do_send() had grown into long monolithic functions.
Split frame building, downlink parsing and the retry loop into small
single-purpose helpers and group the per-transaction variables into
join_state / send_state structs.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Use #include <> instead of #include "" to include a header file which path
is not relative to the directory path of the file emitting the #include
directive.
This change was made running scripts/check_quoted_includes.py script
proposed in https://github.com/zephyrproject-rtos/zephyr/pull/112135
with the Linux shell command below and manually selecting the applicable
changes:
$ find subsys/lorawan/ -type f -exec \
./scripts/check_quoted_includes.py -w {} \;
Signed-off-by: Etienne Carriere <etienne.carriere@st.com>
The `thread` field is not initialized, if it's being animated via
k_work_queue_run instead of k_work_queue_start.
Signed-off-by: Michael Zimmermann <michael.zimmermann@sevenlab.de>
The MC_GROUP_SETUP handler read 29 bytes (1 id + 4 McAddr + 16 McKeyE +
4 FCountMin + 4 FCountMax) from the downlink buffer without checking
that at least 29 bytes remained after the command byte.
Assisted-by: Claude Code:claude-opus-4.8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
Two gaps in frag_transport_service_handler():
1. The FRAG_SESSION_SETUP case cast rx_buf + rx_pos to struct
frag_transport_setup_req (10 bytes) without checking remaining bytes.
2. The DATA_FRAGMENT case passed rx_buf[rx_pos] to the decoder without
confirming that len - rx_pos >= ctx.frag_size.
Add remaining-length guards before each access.
Assisted-by: Claude Code:claude-opus-4.8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
The APP_TIME handler read 5 bytes (4-byte time correction + 1-byte token)
without checking that at least 5 bytes remained in the receive buffer,
allowing a short or crafted downlink to read past the end.
Assisted-by: Claude Code:claude-opus-4.8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
DataFragment fragments are 1-indexed. A frag_counter of 0 underflows
(frag_counter - 1) in the decoder, producing a wild frame index and
flash offset. Reject it at the transport layer.
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
cast uint8_t to uint32_t before << 24 to avoid signed UB in
clock_sync_package_callback.
By undefined behavior sanitizer:
runtime error: left shift of 128 by 24 places cannot be
represented in type 'int'
Signed-off-by: Flavio Ceolin <flavio@hubblenetwork.com>
When ADR is enabled the network controls the datarate through LinkADRReq
so a user-issued lorawan_set_datarate() at that point should not have any
effect.
Applications that want to manually control the datarate must call
lorawan_enable_adr(false) first.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Make Engine Great Again (MEGA), that is route synchronous native LoRaWAN
API work through the engine thread so the engine remains the single writer
of runtime MAC state.
Replace the shared join/send result queues with caller-owned completion
state carried by each engine request. The API caller posts a request and
waits on its own semaphore, while the MAC handler stores the result and
signals that specific request. This removes result matching from shared
queues and lets join/send wait without holding api_mutex.
Move also runtime configuration updates onto the engine thread allowing us
to drop api_mutex once runtime mutations are engine-owned.
Update the public API documentation, migration guide, and native backend
version to reflect a new stricter functions ordering.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Commit 68c95f8e8f ("lorawan: native: reject unsupported downlink
FOpts") rejects any downlink that carries FOpts bytes, ahead of MIC
verification, by returning -ENOTSUP from mac_parse_downlink().
Real networks (TTN, ChirpStack, ...) typically issue a LinkADRReq within
the first downlinks after join, so the very first confirmed uplink response
carries FOpts and gets dropped.
Keep instead a non-fatal LOG_WRN after MIC verification so an operator
still sees that the network is issuing commands the stack does not yet
act on. Moving the warning past MIC also avoids spamming on spoofed or
corrupted frames.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
The delay for multicast responses was calculated in seconds but
lorawan_services_schedule_uplink() expects milliseconds.
According to RP002-1.0.3 chapter 2.3, the intended delay is 2+-1
seconds (i.e. a random value in the range [1, 3] seconds). The fix
converts to milliseconds using MSEC_PER_SEC:
uint32_t delay_ms = MSEC_PER_SEC +
(sys_rand32_get() % (2 * MSEC_PER_SEC));
The variable is also renamed from delay to delay_ms to make the unit
explicit and consistent with the fix in frag_transport.c.
Without this fix, the response is delayed by only 1-3ms instead of
1-3s, which could cause uplink collisions when multiple devices
simultaneously respond to a multicast setup command.
Signed-off-by: Dwij Patel <dwijpatel7@gmail.com>
The ans_delay for FRAG_TRANSPORT_CMD_FRAG_STATUS was calculated in
seconds but lorawan_services_schedule_uplink() expects milliseconds.
According to the LoRa Alliance Fragmented Data Block Transport spec,
the actual delay SHALL be rand() * 2^(BlockAckDelay+4) seconds.
The correct fix computes the full window in milliseconds using
MSEC_PER_SEC and proper bracket ordering to avoid operator precedence
issues with the shift operator:
ans_delay_ms = sys_rand32_get() %
((1U << (ctx.block_ack_delay + 4)) * MSEC_PER_SEC);
The variable is also renamed from ans_delay to ans_delay_ms to make
the unit explicit and help avoid similar bugs in the future.
Without this fix, the delay window for e.g. BlockAckDelay=0 is 0-16ms
instead of 0-16s, causing all devices in a multicast group to respond
nearly simultaneously and defeating the collision avoidance mechanism.
Signed-off-by: Dwij Patel <dwijpatel7@gmail.com>
Downlink MAC commands are not parsed yet.
Reject frames that carry FOpts before advancing FCntDown so the stack
does not silently consume network commands it cannot handle.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
LoRaWAN NbTrans is encoded in the 1..15 range.
Reject zero and out-of-range values so confirmed sends cannot block for
an unexpectedly large number of attempts.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Several public APIs update or consume shared MAC state while the engine
thread is processing join and send requests.
Use a single API mutex for join, send, and mutable setters so only one
public operation can touch the native LoRaWAN state at a time. Also
reject region changes after the stack has started.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Do not trust FCtrl.FOptsLen until it has been checked against the actual
frame length.
Reject frames whose FOpts area would overlap the MIC or extend past the
received buffer before verifying the MIC or updating FCntDown.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Confirmed uplink retries must retransmit the same LoRaWAN frame.
Rebuilding each attempt advanced FCntUp and changed the MIC, allowing a
missed ACK to turn a retry into a second application uplink.
Build the frame once, reuse it for confirmed retry attempts, and consume
FCntUp once after transmission.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Channel masks apply to the region channel plan initialized by
lorawan_start(). Accepting a mask before start leaves the backend without
a concrete channel table to update.
Document the ordering requirement in the public API and have the native
backend return -EPERM until the stack has been started.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
The per-session channel table size is a spec-mandated property of
the region (EU868: 16, US915/AU915: 72, CN470: 96, ...) and is
not a user preference: a region needs exactly as many slots as its
channel plan defines, no more, no less.
Replace the hardcoded #define with a hidden Kconfig in the region
subdirectory.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Refactor mac_build_data_frame() so uplink frames can carry MAC
commands in the FOpts region between FHDR and FPort, and set the
FCtrl.ADR bit when lorawan_enable_adr() has been called.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Widen the get_tx_params() region op to accept a TX power index and
compute the effective dBm from it.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Add three region-ops function pointers needed by the upcoming MAC
command framework to validate and apply parameters from LinkADRReq:
- validate_dr: is a datarate index defined in this region?
- validate_tx_power: is a TX power index defined in this region?
- apply_channel_mask: apply a ChMaskCntl + ChMask pair to the
channel table, atomically (no partial mutation on failure).
Implement them for EU868.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Add the EU868 region implementation for the native LoRaWAN backend.
Gate regions not yet implemented in the native backend behind depends on
!LORA_MODULE_BACKEND_NATIVE so they cannot be selected.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Add a native LoRaWAN 1.0.x Class A implementation that talks directly to
the LoRa radio driver, replacing the Semtech loramac-node dependency.
The backend provides:
- OTAA join (Join Request / Join Accept)
- Unconfirmed and confirmed uplinks with Class A downlink reception
- AES-128 cryptography via PSA Crypto (CMAC for MIC, ECB for payload
and join-accept encryption)
- Region operations interface for channel plans, datarate tables,
RX window parameters, and duty cycle enforcement
- Asynchronous engine thread that serializes MAC operations
- Full downlink dispatch with deferred callbacks on the system
workqueue to avoid deadlocking the engine thread
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Add a `LITTLE_ENDIAN` dependency to `LORAWAN_FRAG_TRANSPORT` due to the
use of bitfields and packed structures in decoding on-air payloads.
Signed-off-by: Jordan Yates <jordan@embeint.com>
Update the fragmented data transport parsing to always consume complete
control messages, regardless of any validation errors. Simplify the
implementation of this by defining the packets as packed structures.
Signed-off-by: Jordan Yates <jordan@embeint.com>
Replaces usage of these deprecated macros with ones that support
fixed and mapped partition compatibles. Also includes an update to
hal_espressif which also (rightly or wrongly) has zephyr specific
code in it
Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
Replace zephyr_compile_definitions_ifdef with
zephyr_library_compile_definitions_ifdef to avoid setting options globally.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Move and rename the region Kconfig symbols from LORAMAC_REGION_* to
LORAWAN_REGION_* to make them backend-agnostic.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Move the LoRaWAN emulator from the loramac-node backend directory to its
own directory because the emulator currently still depends on loramac-node
for some types and functions.
Once these dependencies are removed in a follow-up, the emulator can become
a fully standalone backend.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Remove loramac-node specific references (LORAMAC_NVM_NOTIFY_FLAG_*)
from the NVM interface documentation, making it suitable for any
LoRaWAN backend implementation.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
Refactor the LoRaWAN subsystem to support multiple backends by moving
the loramac-node specific implementation into a dedicated subdirectory.
This change prepares the subsystem for adding lora-basics-modem support
as an alternative backend.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
The LoRaWAN subsystem does not require 2KiB of system workqueue,
testing on nrf54l15 flpr (RISCV) CPU (with custom cryptography code)
shows it uses about 700 bytes of the system workqueue, therefore it
is assumed that the real requirement for 2KiB is the software
cryptography features, not the LoRaWAN stack itself, and the
cryptography parts of LoRaWAN can be replaced with alternatives
that do not need this high of a memory requirement, so the
requirement has been moved to the specific Kconfig for software
cryptography instead
Signed-off-by: Jamie McCrae <spam@helper3000.net>
Switch the selects on `LORAWAN_FRAG_TRANSPORT` to `depends on`. `select`
should only be used for simple symbols without dependencies themselves.
Mixing `depends on X` and `select X` on different symbols is the primary
cause of Kconfig dependency loops.
Signed-off-by: Jordan Yates <jordan@embeint.com>
This allows the software secure enclave to be changed to a
different implementation, which currently is not possible
Signed-off-by: Jamie McCrae <spam@helper3000.net>
Add link check support, by adding:
- link check callback in response of LinkCheckAns.
- link check request function using LinkCheckReq.
Signed-off-by: Martin Durietz <martin.durietz@gmail.com>
allow the application to decide whether to keep going with the fuota
process by setting a callback that exposes the descriptor field
whenever `FragSessionSetupReq` is sent to the device.
Signed-off-by: Kiara Navarro <sophiekovalevsky@fedoraproject.org>
Assert that the size provided to `clock_sync_serialize_device_time` is
sufficient, instead of returning an error code. The condition is already
enforced by the calling code, and returning an error code is incorrect
when the return code is unconditionally being used to increment the
buffer offset by callers.
Fixes#84720.
Signed-off-by: Jordan Yates <jordan@embeint.com>