Documentation
¶
Overview ¶
Package lorawan decodes LoRaWAN PHYPayload frames — the MAC-layer packet format used by LoRaWAN 1.0.x and 1.1 networks. Pure offline parser; no transport, no hardware.
Wrap-vs-native judgement: LoRaWAN is a fully open specification (LoRa Alliance LoRaWAN 1.0.x / 1.1 specifications). The walker is bit-level decoding over a ~12-300 byte frame with a documented MAC-header byte and a FHDR / FPort / FRMPayload split. Wrapping a FAP for this would require an SD-card install + a firmware-fork dependency for a pure parser. Native delivers offline analysis — operators paste a captured PHYPayload (from a Flipper LoRa sub-board, a CatSniffer, or any LoRa SDR) and inspect every MAC-layer field without an antenna attached.
Pairs with the bruce_lora_scan capability (which gets a device-side LoRa scan running) and with future LoRaWAN containerbridge integrations — this Spec is the offline- analyst entry point.
What this package covers:
- PHYPayload split: MHDR + MACPayload + 4-byte MIC
- MHDR decode: MType (Join Request / Accept, Confirmed / Unconfirmed Data Up / Down, Rejoin, Proprietary) + Major
- Data-frame MACPayload walk: FHDR (DevAddr little-endian, FCtrl bitfield, FCnt, FOpts MAC commands), FPort, FRMPayload (surfaced as hex; encrypted under AppSKey)
- FCtrl bitfield decode with uplink vs downlink interpretation (uplink: ADR / ADRACKReq / ACK / ClassB / FOptsLen; downlink: ADR / RFU / ACK / FPending / FOptsLen)
- Join Request decode: JoinEUI + DevEUI + DevNonce (all little-endian on the wire)
- Join Accept decode: AppNonce + NetID + DevAddr + DLSettings
- RxDelay + optional CFList (encrypted under AppKey; we surface structure when called with the decrypted form)
What this package does NOT cover (deliberately out of scope):
- AES-CMAC MIC validation (needs NwkSKey / NwkSEncKey)
- FRMPayload decryption (needs AppSKey)
- Join Accept decryption (needs AppKey — the Join Accept decoder walks the cleartext-structure form; operators decrypt with their own AppKey before passing)
- PHY-layer decode (chirp / spreading-factor / CR — those are the SDR's job, not the MAC-layer parser's)
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type DataPayload ¶
type DataPayload struct {
FHDR FHDR `json:"fhdr"`
// FPort: 1 byte. nil when FRMPayload is empty.
FPort *int `json:"f_port,omitempty"`
// FRMPayloadHex is the encrypted application payload as hex.
// Empty when no FRMPayload is present. We do not decrypt
// (needs AppSKey / NwkSEncKey out-of-band).
FRMPayloadHex string `json:"frm_payload_hex,omitempty"`
}
DataPayload is the structured view of a data-frame MACPayload (Unconfirmed/Confirmed Data Up/Down).
type FCtrl ¶
type FCtrl struct {
Raw int `json:"raw"`
ADR bool `json:"adr"`
// ADRACKReq is set on uplinks only.
ADRACKReq bool `json:"adr_ack_req,omitempty"`
ACK bool `json:"ack"`
// ClassB is set on uplinks only; FPending on downlinks only.
ClassB bool `json:"class_b,omitempty"`
FPending bool `json:"f_pending,omitempty"`
FOptsLen int `json:"f_opts_len"`
}
FCtrl is the decoded 1-byte FCtrl field of an FHDR. Bit interpretations differ between uplink and downlink frames.
type FHDR ¶
type FHDR struct {
// DevAddr is the 32-bit device address. Stored on the wire
// little-endian; we render the big-endian hex form here so
// it matches the form network servers / chirpstack use.
DevAddrHex string `json:"dev_addr_hex"`
DevAddr uint32 `json:"dev_addr"`
FCtrl FCtrl `json:"f_ctrl"`
FCnt int `json:"f_cnt"`
// FOptsHex is the optional MAC-command field (up to 15
// bytes). Surfaced as hex; we don't dissect MAC commands
// here (that's a follow-on Spec when a caller materialises).
FOptsHex string `json:"f_opts_hex,omitempty"`
}
FHDR is the Frame Header — DevAddr + FCtrl + FCnt + FOpts.
type JoinAccept ¶
type JoinAccept struct {
// AppNonce is the 3-byte network-supplied nonce.
AppNonceHex string `json:"app_nonce_hex"`
// NetID is the 3-byte network identifier.
NetIDHex string `json:"net_id_hex"`
// DevAddr is the 4-byte device address assigned to the
// device for this session.
DevAddrHex string `json:"dev_addr_hex"`
DevAddr uint32 `json:"dev_addr"`
// DLSettings is the downlink-settings byte (RX1 DR offset +
// RX2 data rate).
DLSettings int `json:"dl_settings"`
// RxDelay is the receive delay byte (0-15 seconds).
RxDelay int `json:"rx_delay"`
// CFListHex is the optional 16-byte channel-frequency list.
// Empty when absent.
CFListHex string `json:"cf_list_hex,omitempty"`
}
JoinAccept is the structured view of a (decrypted) Join Accept MACPayload. Network servers encrypt Join Accept with AppKey using AES-128-ECB before transmit; operators bring the decrypted bytes before calling this decoder.
type JoinRequest ¶
type JoinRequest struct {
// JoinEUI is the 8-byte EUI-64 of the Join Server (LoRaWAN
// 1.0.x called this AppEUI). Wire form is little-endian; we
// render the big-endian hex here so it matches the form
// printed on device labels.
JoinEUIHex string `json:"join_eui_hex"`
// DevEUI is the 8-byte EUI-64 of the device. Same little-
// endian-on-wire convention.
DevEUIHex string `json:"dev_eui_hex"`
// DevNonce is the 2-byte little-endian device-supplied nonce.
DevNonce int `json:"dev_nonce"`
}
JoinRequest is the structured view of a Join Request MACPayload.
type MHDR ¶
type MHDR struct {
Raw int `json:"raw"`
MType int `json:"mtype"`
Name string `json:"mtype_name"`
Major int `json:"major"`
Uplink bool `json:"uplink"`
}
MHDR is the 1-byte MAC header.
type MType ¶
type MType int
MType is the 3-bit Message Type field at the top of the MHDR.
type PHYPayload ¶
type PHYPayload struct {
// MHDR is the parsed MAC header.
MHDR MHDR `json:"mhdr"`
// MIC is the 4-byte Message Integrity Code at the frame end.
MICHex string `json:"mic_hex"`
// Data is set for the data-frame MTypes (2-5).
Data *DataPayload `json:"data,omitempty"`
// JoinRequest is set when MType == 0.
JoinRequest *JoinRequest `json:"join_request,omitempty"`
// JoinAccept is set when MType == 1 (assumes operator has
// decrypted the body).
JoinAccept *JoinAccept `json:"join_accept,omitempty"`
// RejoinRequestHex is set when MType == 6 (Rejoin Request).
// We surface the raw bytes — Rejoin Type byte then variable
// fields per LoRaWAN 1.1 §6.2.4.
RejoinRequestHex string `json:"rejoin_request_hex,omitempty"`
// ProprietaryHex is set when MType == 7.
ProprietaryHex string `json:"proprietary_hex,omitempty"`
// PayloadHex is the raw PHYPayload for callers that want to
// re-render or audit.
PayloadHex string `json:"payload_hex"`
}
PHYPayload is the top-level decoded frame.
func Decode ¶
func Decode(hexBlob string) (PHYPayload, error)
Decode parses a hex-encoded LoRaWAN PHYPayload frame. Tolerates ':' / '-' / '_' / whitespace separators.
func DecodeBytes ¶
func DecodeBytes(b []byte) (PHYPayload, error)
DecodeBytes is the byte-slice variant of Decode.