LoRaWAN 1.1 Features added (Some LoRaPhy impl missing still + some TODOs in code)

- MLME confirm handling refactored
- Rejoin handling missing
- new CF_LIST mechanism missing (+resets involved)
- NVM handling missing

Fixed automerge issue
feature-lorawan-1-1
Antti Kauppila 2018-08-23 17:29:11 +03:00
parent 8dc15ee6e1
commit 0d3283e3a0
13 changed files with 1215 additions and 228 deletions

View File

@ -72,6 +72,12 @@ LoRaWANStack::LoRaWANStack()
_ctrl_flags(IDLE_FLAG),
_app_port(INVALID_PORT),
_link_check_requested(false),
_reset_ind_requested(false),
_rekey_ind_needed(false),
_rekey_ind_counter(0),
_device_mode_ind_needed(false),
_device_mode_ind_ongoing(false),
_new_class_type(CLASS_A),
_automatic_uplink_ongoing(false),
_queue(NULL)
{
@ -295,6 +301,31 @@ int16_t LoRaWANStack::handle_tx(const uint8_t port, const uint8_t *data,
if (!null_allowed && !data) {
return LORAWAN_STATUS_PARAMETER_INVALID;
} else if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) {
return LORAWAN_STATUS_NOT_INITIALIZED;
}
// ResetInd is only used for ABP devices after connect, until ResetConf is received
if (_reset_ind_requested) {
set_reset_indication();
} else if (_rekey_ind_needed) {
if (_rekey_ind_counter < _loramac.get_current_adr_ack_limit()) {
set_rekey_indication();
_rekey_ind_counter++;
} else {
//TODO: Check if something else is needed also (reset settings?)
_rekey_ind_needed = false;
send_event_to_application(JOIN_FAILURE);
_device_current_state = DEVICE_STATE_IDLE;
}
} else if (_link_check_requested) {
// add a link check request with normal data, until the application
// explicitly removes it.
_loramac.setup_link_check_request();
}
if (_device_mode_ind_needed) {
set_device_mode_indication();
}
if (!_lw_session.active) {
@ -430,6 +461,21 @@ lorawan_status_t LoRaWANStack::set_link_check_request()
return LORAWAN_STATUS_OK;
}
void LoRaWANStack::set_reset_indication()
{
_loramac.setup_reset_indication();
}
void LoRaWANStack::set_rekey_indication()
{
_loramac.setup_rekey_indication();
}
void LoRaWANStack::set_device_mode_indication()
{
_loramac.setup_device_mode_indication(_new_class_type);
}
void LoRaWANStack::remove_link_check_request()
{
_link_check_requested = false;
@ -453,8 +499,18 @@ lorawan_status_t LoRaWANStack::set_device_class(const device_class_t &device_cla
if (device_class == CLASS_B) {
return LORAWAN_STATUS_UNSUPPORTED;
}
_loramac.set_device_class(device_class,
mbed::callback(this, &LoRaWANStack::post_process_tx_no_reception));
// Only change the class when needed
if (_loramac.get_device_class() != device_class) {
if (_loramac.get_server_type() == LW1_1) {
_new_class_type = device_class;
set_device_mode_indication();
_device_mode_ind_needed = true;
_device_mode_ind_ongoing = true;
} else {
_loramac.set_device_class(device_class, mbed::callback(this, &LoRaWANStack::post_process_tx_no_reception));
}
}
return LORAWAN_STATUS_OK;
}
@ -565,7 +621,9 @@ void LoRaWANStack::process_transmission_timeout()
_loramac.on_radio_tx_timeout();
_ctrl_flags &= ~TX_DONE_FLAG;
if (_device_current_state == DEVICE_STATE_JOINING) {
mlme_confirm_handler();
_device_current_state = DEVICE_STATE_IDLE;
tr_error("Joining abandoned: Radio failed to transmit");
send_event_to_application(TX_TIMEOUT);
} else {
state_controller(DEVICE_STATE_STATUS_CHECK);
}
@ -576,6 +634,15 @@ void LoRaWANStack::process_transmission_timeout()
void LoRaWANStack::process_transmission(void)
{
tr_debug("Transmission completed");
_loramac.on_radio_tx_done(_tx_timestamp);
if (_loramac.get_server_type() == LW1_1 && _device_mode_ind_ongoing) {
_device_mode_ind_ongoing = false;
_loramac.set_device_class(device_class_t(_new_class_type), mbed::callback(this, &LoRaWANStack::post_process_tx_no_reception));
send_event_to_application(CLASS_CHANGED);
}
make_tx_metadata_available();
if (_device_current_state == DEVICE_STATE_JOINING) {
_device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT;
@ -701,7 +768,7 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size
_ctrl_flags &= ~TX_DONE_FLAG;
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
_loramac.on_radio_rx_done(payload, size, rssi, snr);
_loramac.on_radio_rx_done(payload, size, rssi, snr, callback(this, &LoRaWANStack::mlme_confirm_handler));
if (_loramac.get_mlme_confirmation()->pending) {
_loramac.post_process_mlme_request();
@ -712,6 +779,7 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size
return;
}
}
make_rx_metadata_available();
if (!_loramac.nwk_joined()) {
core_util_atomic_flag_clear(&_rx_payload_in_use);
@ -900,6 +968,7 @@ lorawan_status_t LoRaWANStack::handle_connect(bool is_otaa)
_lw_session.downlink_counter = 0;
_lw_session.uplink_counter = 0;
_ctrl_flags |= USING_OTAA_FLAG;
//We cannot set _rekey_ind_needed here, because server might not support LW1.1
} else {
// If current state is SHUTDOWN, device may be trying to re-establish
// communication. In case of ABP specification is meddled about frame counters.
@ -910,6 +979,11 @@ lorawan_status_t LoRaWANStack::handle_connect(bool is_otaa)
//_lw_session.downlink_counter; //Get from NVM
//_lw_session.uplink_counter; //Get from NVM
if (MBED_CONF_LORA_VERSION == LORAWAN_VERSION_1_1) {
_reset_ind_requested = true;
//TODO: Switch back to default MAC and radio parameters, but leave counters untouched
}
tr_debug("Initiating ABP");
tr_debug("Frame Counters. UpCnt=%lu, DownCnt=%lu",
_lw_session.uplink_counter, _lw_session.downlink_counter);
@ -939,27 +1013,37 @@ void LoRaWANStack::mlme_indication_handler()
tr_error("Unknown MLME Indication type.");
}
void LoRaWANStack::mlme_confirm_handler()
void LoRaWANStack::mlme_confirm_handler(loramac_mlme_confirm_t& mlme_confirm)
{
if (_loramac.get_mlme_confirmation()->req_type == MLME_LINK_CHECK) {
if (_loramac.get_mlme_confirmation()->status
== LORAMAC_EVENT_INFO_STATUS_OK) {
if (mlme_confirm.type == MLME_LINK_CHECK) {
if (mlme_confirm.status == LORAMAC_EVENT_INFO_STATUS_OK) {
if (_callbacks.link_check_resp) {
const int ret = _queue->call(
_callbacks.link_check_resp,
_loramac.get_mlme_confirmation()->demod_margin,
_loramac.get_mlme_confirmation()->nb_gateways);
const int ret = _queue->call(_callbacks.link_check_resp,
mlme_confirm.demod_margin,
mlme_confirm.nb_gateways);
MBED_ASSERT(ret != 0);
(void) ret;
(void)ret;
}
}
}
if (_loramac.get_mlme_confirmation()->req_type == MLME_JOIN) {
switch (_loramac.get_mlme_confirmation()->status) {
} else if (mlme_confirm.type == MLME_RESET) {
_reset_ind_requested = false;
} else if (mlme_confirm.type == MLME_REKEY) {
_rekey_ind_needed = false;
_rekey_ind_counter = 0;
} else if (mlme_confirm.type == MLME_DEVICE_MODE) {
_device_mode_ind_needed = false;
if (_loramac.get_device_class() == mlme_confirm.classType) {
send_event_to_application(SERVER_ACCEPTED_CLASS_IN_USE);
} else {
send_event_to_application(SERVER_DOES_NOT_SUPPORT_CLASS_IN_USE);
}
} else if (mlme_confirm.type == MLME_JOIN_ACCEPT) {
switch (mlme_confirm.status) {
case LORAMAC_EVENT_INFO_STATUS_OK:
if (_loramac.get_server_type() == LW1_1) {
_rekey_ind_needed = true;
_rekey_ind_counter = 0;
}
state_controller(DEVICE_STATE_CONNECTED);
break;
@ -969,19 +1053,13 @@ void LoRaWANStack::mlme_confirm_handler()
tr_error("Joining abandoned: CRYPTO_ERROR");
send_event_to_application(CRYPTO_ERROR);
break;
case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT:
// fatal error
_device_current_state = DEVICE_STATE_IDLE;
tr_error("Joining abandoned: Radio failed to transmit");
send_event_to_application(TX_TIMEOUT);
break;
default:
// non-fatal, retry if possible
_device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT;
state_controller(DEVICE_STATE_JOINING);
}
} else if (mlme_confirm.type == MLME_FORCE_REJOIN) {
//TODO: handle this
}
}

View File

@ -405,6 +405,27 @@ private:
*/
lorawan_status_t state_controller(device_states_t new_state);
/**
* Send Reset indication (only in ABP & LW1.1)
* LoRaWAN 1.1 specification mandates ABP device to send
* ResetInd MAC command until ResetConf is received.
*
*/
void set_reset_indication();
/**
* Send Rekey indication (only in OTAA & LW1.1)
*
*/
void set_rekey_indication();
/**
* Send Device mode indication (only in OTAA & LW1.1)
*
*/
void set_device_mode_indication();
/**
* Helpers for state controller
*/
@ -426,7 +447,7 @@ private:
/**
* Handles an MLME confirmation
*/
void mlme_confirm_handler(void);
void mlme_confirm_handler(loramac_mlme_confirm_t& mlme_confirm);
/**
* Handles an MCPS confirmation
@ -507,6 +528,12 @@ private:
uint32_t _ctrl_flags;
uint8_t _app_port;
bool _link_check_requested;
bool _reset_ind_requested;
bool _rekey_ind_needed;
uint8_t _rekey_ind_counter;
bool _device_mode_ind_needed;
bool _device_mode_ind_ongoing;
uint8_t _new_class_type;
bool _automatic_uplink_ongoing;
core_util_atomic_flag _rx_payload_in_use;
uint8_t _rx_payload[LORAMAC_PHY_MAXPAYLOAD];

View File

@ -90,8 +90,13 @@ LoRaMac::LoRaMac()
_params.max_ack_timeout_retries = 1;
_params.ack_timeout_retry_counter = 1;
_params.join_request_type = JOIN_REQUEST;
//TODO: RJcount1 must be stored to NVM!
_params.RJcount0 = 0;
_params.RJcount1 = 0;
reset_mcps_confirmation();
reset_mlme_confirmation();
reset_mcps_indication();
}
@ -123,11 +128,6 @@ const loramac_mlme_indication_t *LoRaMac::get_mlme_indication() const
return &_mlme_indication;
}
void LoRaMac::post_process_mlme_request()
{
_mlme_confirmation.pending = false;
}
void LoRaMac::post_process_mcps_req()
{
_params.is_last_tx_join_request = false;
@ -178,61 +178,115 @@ rx_slot_t LoRaMac::get_current_slot(void)
/**
* This part handles incoming frames in response to Radio RX Interrupt
*/
void LoRaMac::handle_join_accept_frame(const uint8_t *payload, uint16_t size)
loramac_event_info_status_t LoRaMac::handle_join_accept_frame(const uint8_t *payload, uint16_t size)
{
uint32_t mic = 0;
uint32_t mic_rx = 0;
server_type_t stype = LW1_0_2;
_mlme_confirmation.nb_retries = _params.join_request_trial_counter;
//Store server type to local so that invalid join accept of rejoin request won't affect the orig. type.
if ( (((_params.rx_buffer[11] >> 7) & 0x01) == 1) && MBED_CONF_LORA_VERSION == LORAWAN_VERSION_1_1) {
stype = LW1_1;
} else {
stype = LW1_0_2;
//Server does not support LW 1.1 so we need to unset JS keys
memcpy(_params.keys.js_intkey, _params.keys.nwk_key, sizeof(_params.keys.nwk_skey));
memcpy(_params.keys.js_enckey, _params.keys.nwk_key, sizeof(_params.keys.nwk_skey));
}
uint8_t *decrypt_key = NULL;
uint8_t *mic_key = _params.keys.js_intkey; //in case of LW1.0.2 js_intkey == nwk_key == app_key
if (_params.join_request_type == JOIN_REQUEST) {
decrypt_key = _params.keys.nwk_key;
} else {
decrypt_key = _params.keys.js_enckey;
}
//Store server type to local so that invalid join accept of rejoin request won't affect the orig. type.
if ( (((_params.rx_buffer[11] >> 7) & 0x01) == 1) && MBED_CONF_LORA_VERSION == LORAWAN_VERSION_1_1) {
stype = LW1_1;
} else {
stype = LW1_0_2;
//Server does not support LW 1.1 so we need to unset JS keys
memcpy(_params.keys.js_intkey, _params.keys.nwk_key, sizeof(_params.keys.nwk_skey));
memcpy(_params.keys.js_enckey, _params.keys.nwk_key, sizeof(_params.keys.nwk_skey));
}
if (0 != _lora_crypto.decrypt_join_frame(payload + 1, size - 1,
_params.keys.app_key, APPKEY_KEY_LENGTH,
decrypt_key, APPKEY_KEY_LENGTH,
_params.rx_buffer + 1)) {
_mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL;
return;
return LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL;
}
_params.rx_buffer[0] = payload[0];
uint8_t payload_start = 1;
uint8_t mic_start = 0;
uint8_t args_size = 0;
uint8_t args[16];
if (stype == LW1_0_2) {
_params.rx_buffer[0] = payload[0];
mic_start = size - LORAMAC_MFR_LEN;
memcpy(args, _params.rx_buffer + 1, 6);
memcpy(args + 6, (uint8_t *) &_params.dev_nonce, 2);
args_size = 8;
} else {
//MIC calculation needs more params, so we move the payload a bit
_params.rx_buffer[0] = (uint8_t)_params.join_request_type;
memcpy(_params.rx_buffer + 11, _params.rx_buffer, size - LORAMAC_MFR_LEN);
memcpy(_params.rx_buffer + 1, _params.keys.app_eui, 8);
memcpy(_params.rx_buffer + 9, (uint8_t *) &_params.dev_nonce, 2);
mic_start = size - LORAMAC_MFR_LEN + 11;
payload_start += 11;
memcpy(args, _params.rx_buffer + payload_start, 3);
memcpy(args + 3, _params.keys.app_eui, 8);
memcpy(args + 3 + 8, (uint8_t *) &_params.dev_nonce, 2);
args_size = 13;
}
if (_lora_crypto.compute_join_frame_mic(_params.rx_buffer,
size - LORAMAC_MFR_LEN,
_params.keys.app_key,
mic_start,
mic_key,
APPKEY_KEY_LENGTH,
&mic) != 0) {
_mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL;
return;
return LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL;
}
mic_rx |= (uint32_t) _params.rx_buffer[size - LORAMAC_MFR_LEN];
mic_rx |= ((uint32_t) _params.rx_buffer[size - LORAMAC_MFR_LEN + 1] << 8);
mic_rx |= ((uint32_t) _params.rx_buffer[size - LORAMAC_MFR_LEN + 2] << 16);
mic_rx |= ((uint32_t) _params.rx_buffer[size - LORAMAC_MFR_LEN + 3] << 24);
mic_rx |= (uint32_t) _params.rx_buffer[mic_start];
mic_rx |= ((uint32_t) _params.rx_buffer[mic_start + 1] << 8);
mic_rx |= ((uint32_t) _params.rx_buffer[mic_start + 2] << 16);
mic_rx |= ((uint32_t) _params.rx_buffer[mic_start + 3] << 24);
if (mic_rx == mic) {
_lora_time.stop(_params.timers.rx_window2_timer);
if (_lora_crypto.compute_skeys_for_join_frame(_params.keys.app_key,
_params.server_type = stype;
if (_lora_crypto.compute_skeys_for_join_frame(_params.keys.nwk_key,
APPKEY_KEY_LENGTH,
_params.rx_buffer + 1,
_params.dev_nonce,
_params.keys.app_key,
APPKEY_KEY_LENGTH,
args, args_size,
_params.keys.nwk_skey,
_params.keys.app_skey) != 0) {
_mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL;
return;
_params.keys.app_skey,
_params.keys.snwk_sintkey,
_params.keys.nwk_senckey,
_params.server_type) != 0) {
return LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL;
}
_params.net_id = (uint32_t) _params.rx_buffer[4];
_params.net_id |= ((uint32_t) _params.rx_buffer[5] << 8);
_params.net_id |= ((uint32_t) _params.rx_buffer[6] << 16);
_params.net_id = (uint32_t) _params.rx_buffer[payload_start + 3];
_params.net_id |= ((uint32_t) _params.rx_buffer[payload_start + 4] << 8);
_params.net_id |= ((uint32_t) _params.rx_buffer[payload_start + 5] << 16);
_params.dev_addr = (uint32_t) _params.rx_buffer[7];
_params.dev_addr |= ((uint32_t) _params.rx_buffer[8] << 8);
_params.dev_addr |= ((uint32_t) _params.rx_buffer[9] << 16);
_params.dev_addr |= ((uint32_t) _params.rx_buffer[10] << 24);
_params.dev_addr = (uint32_t) _params.rx_buffer[payload_start + 6];
_params.dev_addr |= ((uint32_t) _params.rx_buffer[payload_start + 7] << 8);
_params.dev_addr |= ((uint32_t) _params.rx_buffer[payload_start + 8] << 16);
_params.dev_addr |= ((uint32_t) _params.rx_buffer[payload_start + 9] << 24);
_params.sys_params.rx1_dr_offset = (_params.rx_buffer[11] >> 4) & 0x07;
_params.sys_params.rx2_channel.datarate = _params.rx_buffer[11] & 0x0F;
_params.sys_params.rx1_dr_offset = (_params.rx_buffer[payload_start + 10] >> 4) & 0x07;
_params.sys_params.rx2_channel.datarate = _params.rx_buffer[payload_start + 10] & 0x0F;
_params.sys_params.recv_delay1 = (_params.rx_buffer[12] & 0x0F);
_params.sys_params.recv_delay1 = (_params.rx_buffer[payload_start + 11] & 0x0F);
if (_params.sys_params.recv_delay1 == 0) {
_params.sys_params.recv_delay1 = 1;
@ -241,19 +295,22 @@ void LoRaMac::handle_join_accept_frame(const uint8_t *payload, uint16_t size)
_params.sys_params.recv_delay1 *= 1000;
_params.sys_params.recv_delay2 = _params.sys_params.recv_delay1 + 1000;
// Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC
_lora_phy->apply_cf_list(&_params.rx_buffer[13], size - 17);
// Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC (== 17)
//TODO: join request type is needed here also! See LW1.1 lines 1711 -> 1719 (Reset or not)
// LW1.1 CF_LIST's 16th byte is CFListType!
_lora_phy->apply_cf_list(&_params.rx_buffer[payload_start + 12], size - 17);
_mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK;
_is_nwk_joined = true;
// Node joined successfully
_params.ul_frame_counter = 0;
_params.ul_nb_rep_counter = 0;
_params.adr_ack_counter = 0;
_params.RJcount0 = 0;
} else {
_mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
return LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
}
return LORAMAC_EVENT_INFO_STATUS_OK;
}
void LoRaMac::check_frame_size(uint16_t size)
@ -269,13 +326,14 @@ void LoRaMac::check_frame_size(uint16_t size)
bool LoRaMac::message_integrity_check(const uint8_t *const payload,
const uint16_t size,
uint8_t *const ptr_pos,
uint8_t *const ptr_pos, uint16_t confFCnt,
uint32_t address,
uint32_t *downlink_counter,
const uint8_t *nwk_skey)
{
uint32_t mic = 0;
uint32_t mic_rx = 0;
uint32_t args = confFCnt;
uint16_t sequence_counter = 0;
uint16_t sequence_counter_prev = 0;
@ -299,10 +357,9 @@ bool LoRaMac::message_integrity_check(const uint8_t *const payload,
return false;
}
// sizeof nws_skey must be the same as _params.keys.nwk_skey,
// sizeof nwk_skey must be the same as _params.keys.nwk_skey,
_lora_crypto.compute_mic(payload, size - LORAMAC_MFR_LEN,
nwk_skey,
sizeof(_params.keys.nwk_skey) * 8,
nwk_skey, sizeof(_params.keys.nwk_skey) * 8, args,
address, DOWN_LINK, *downlink_counter, &mic);
if (mic_rx != mic) {
@ -321,7 +378,8 @@ void LoRaMac::extract_data_and_mac_commands(const uint8_t *payload,
uint32_t address,
uint32_t downlink_counter,
int16_t rssi,
int8_t snr)
int8_t snr,
Callback<void(loramac_mlme_confirm_t&)> confirm_handler)
{
uint8_t frame_len = 0;
uint8_t payload_start_index = 8 + fopts_len;
@ -346,8 +404,8 @@ void LoRaMac::extract_data_and_mac_commands(const uint8_t *payload,
}
if (_mac_commands.process_mac_commands(_params.rx_buffer, 0, frame_len,
snr, _mlme_confirmation,
_params.sys_params, *_lora_phy)
snr, _params.sys_params, *_lora_phy,
confirm_handler)
!= LORAWAN_STATUS_OK) {
_mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR;
return;
@ -368,23 +426,8 @@ void LoRaMac::extract_data_and_mac_commands(const uint8_t *payload,
return;
}
// normal unicast/multicast port handling
if (fopts_len > 0) {
// Decode Options field MAC commands. Omit the fPort.
if (_mac_commands.process_mac_commands(payload, 8,
payload_start_index - 1,
snr,
_mlme_confirmation,
_params.sys_params,
*_lora_phy) != LORAWAN_STATUS_OK) {
_mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR;
return;
}
if (_mac_commands.has_sticky_mac_cmd()) {
set_mlme_schedule_ul_indication();
_mac_commands.clear_sticky_mac_cmd();
}
if(!extract_mac_commands_only(payload, snr, fopts_len, confirm_handler)) {
return;
}
// sizeof app_skey must be the same as _params.keys.app_skey
@ -404,18 +447,34 @@ void LoRaMac::extract_data_and_mac_commands(const uint8_t *payload,
}
}
void LoRaMac::extract_mac_commands_only(const uint8_t *payload,
bool LoRaMac::extract_mac_commands_only(const uint8_t *payload,
int8_t snr,
uint8_t fopts_len)
uint8_t fopts_len,
Callback<void(loramac_mlme_confirm_t&)> confirm_handler)
{
uint8_t payload_start_index = 8 + fopts_len;
if (fopts_len > 0) {
if (_mac_commands.process_mac_commands(payload, 8, payload_start_index,
snr, _mlme_confirmation,
_params.sys_params, *_lora_phy)
uint8_t buffer[15];
if (_params.server_type == LW1_1) {
if (0 != _lora_crypto.encrypt_payload(payload + 8, fopts_len,
_params.keys.nwk_senckey, sizeof(_params.keys.nwk_senckey) * 8,
_params.dev_addr, DOWN_LINK,
_params.dl_frame_counter,
buffer)) {
_mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL;
return false;
}
} else {
memcpy(buffer, payload + 8, fopts_len);
}
if (_mac_commands.process_mac_commands(buffer, 0, fopts_len,
snr, _params.sys_params,
*_lora_phy, confirm_handler)
!= LORAWAN_STATUS_OK) {
_mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR;
return;
return false;
}
if (_mac_commands.has_sticky_mac_cmd()) {
@ -423,6 +482,7 @@ void LoRaMac::extract_mac_commands_only(const uint8_t *payload,
_mac_commands.clear_sticky_mac_cmd();
}
}
return true;
}
void LoRaMac::handle_data_frame(const uint8_t *const payload,
@ -430,7 +490,8 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
uint8_t ptr_pos,
uint8_t msg_type,
int16_t rssi,
int8_t snr)
int8_t snr,
Callback<void(loramac_mlme_confirm_t&)> confirm_handler)
{
check_frame_size(size);
@ -441,13 +502,19 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
uint32_t downlink_counter = 0;
uint8_t app_payload_start_index = 0;
uint8_t *nwk_skey = _params.keys.nwk_skey;
uint8_t *mic_key = _params.keys.nwk_skey;
uint8_t *app_skey = _params.keys.app_skey;
uint16_t fport = 0;
uint16_t confFCnt = 0;
address = payload[ptr_pos++];
address |= ((uint32_t) payload[ptr_pos++] << 8);
address |= ((uint32_t) payload[ptr_pos++] << 16);
address |= ((uint32_t) payload[ptr_pos++] << 24);
fctrl.value = payload[ptr_pos++];
fport = payload[7 + fctrl.bits.fopts_len];
if (address != _params.dev_addr) {
// check if Multicast is destined for us
cur_multicast_params = _params.multicast_channels;
@ -456,6 +523,7 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
if (address == cur_multicast_params->address) {
is_multicast = true;
nwk_skey = cur_multicast_params->nwk_skey;
mic_key = cur_multicast_params->nwk_skey;
app_skey = cur_multicast_params->app_skey;
downlink_counter = cur_multicast_params->dl_frame_counter;
break;
@ -473,16 +541,29 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
} else {
is_multicast = false;
nwk_skey = _params.keys.nwk_skey;
mic_key = _params.keys.snwk_sintkey;
app_skey = _params.keys.app_skey;
downlink_counter = _params.dl_frame_counter;
}
fctrl.value = payload[ptr_pos++];
app_payload_start_index = 8 + fctrl.bits.fopts_len;
if (_params.server_type == LW1_1) {
if (_params.is_node_ack_requested && fctrl.bits.ack) {
confFCnt = _mcps_confirmation.ul_frame_counter;
}
if (!is_multicast) {
if (fport != 0) {
downlink_counter = _params.app_dl_frame_counter;
} else {
nwk_skey = _params.keys.nwk_senckey;
}
}
}
//perform MIC check
if (!message_integrity_check(payload, size, &ptr_pos, address,
&downlink_counter, nwk_skey)) {
if (!message_integrity_check(payload, size, &ptr_pos, confFCnt, address,
&downlink_counter, mic_key)) {
tr_error("MIC failed");
_mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL;
_mcps_indication.pending = false;
@ -526,6 +607,7 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
} else {
if (msg_type == FRAME_TYPE_DATA_CONFIRMED_DOWN) {
_params.is_srv_ack_requested = true;
_params.counterForAck = downlink_counter;
_mcps_indication.type = MCPS_CONFIRMED;
if ((_params.dl_frame_counter == downlink_counter)
@ -580,9 +662,10 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
if (frame_len > 0) {
extract_data_and_mac_commands(payload, size, fctrl.bits.fopts_len,
nwk_skey, app_skey, address,
downlink_counter, rssi, snr);
downlink_counter, rssi, snr,
confirm_handler);
} else {
extract_mac_commands_only(payload, snr, fctrl.bits.fopts_len);
extract_mac_commands_only(payload, snr, fctrl.bits.fopts_len, confirm_handler);
}
// Handle proprietary messages.
@ -595,7 +678,7 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
_mcps_indication.buffer_size = size - ptr_pos;
}
// only stop act timer, if the ack is actually recieved
// only stop ack timer, if the ack is actually received
if (_mcps_confirmation.ack_received) {
_lora_time.stop(_params.timers.ack_timeout_timer);
}
@ -673,7 +756,8 @@ void LoRaMac::on_radio_tx_done(lorawan_time_t timestamp)
}
void LoRaMac::on_radio_rx_done(const uint8_t *const payload, uint16_t size,
int16_t rssi, int8_t snr)
int16_t rssi, int8_t snr,
Callback<void(loramac_mlme_confirm_t&)> confirm_handler)
{
_demod_ongoing = false;
if (_device_class == CLASS_C && !_continuous_rx2_window_open) {
@ -685,6 +769,7 @@ void LoRaMac::on_radio_rx_done(const uint8_t *const payload, uint16_t size,
}
loramac_mhdr_t mac_hdr;
loramac_mlme_confirm_t mlme;
uint8_t pos = 0;
mac_hdr.value = payload[pos++];
@ -693,11 +778,14 @@ void LoRaMac::on_radio_rx_done(const uint8_t *const payload, uint16_t size,
case FRAME_TYPE_JOIN_ACCEPT:
if (nwk_joined()) {
_mlme_confirmation.pending = false;
//TODO: this might need more logic as with rejoin this will happen more often
_params.RJcount0 = 0;
return;
} else {
handle_join_accept_frame(payload, size);
_mlme_confirmation.pending = true;
loramac_event_info_status_t ret = handle_join_accept_frame(payload, size);
mlme.type = MLME_JOIN_ACCEPT;
mlme.status = ret;
confirm_handler(mlme);
}
break;
@ -706,7 +794,7 @@ void LoRaMac::on_radio_rx_done(const uint8_t *const payload, uint16_t size,
case FRAME_TYPE_DATA_CONFIRMED_DOWN:
case FRAME_TYPE_PROPRIETARY:
handle_data_frame(payload, size, pos, mac_hdr.bits.mtype, rssi, snr);
handle_data_frame(payload, size, pos, mac_hdr.bits.mtype, rssi, snr, confirm_handler);
break;
@ -823,15 +911,20 @@ lorawan_status_t LoRaMac::send_join_request()
_lora_phy->get_alternate_DR(_params.join_request_trial_counter + 1);
mac_hdr.value = 0;
mac_hdr.bits.mtype = FRAME_TYPE_JOIN_REQ;
if (_params.join_request_type == JOIN_REQUEST) {
mac_hdr.bits.mtype = FRAME_TYPE_JOIN_REQ;
_params.is_last_tx_join_request = true;
} else {
mac_hdr.bits.mtype = FRAME_TYPE_REJOIN_REQUEST;
_params.is_last_tx_join_request = false;
}
fctrl.value = 0;
fctrl.bits.adr = _params.sys_params.adr_on;
_params.is_last_tx_join_request = true;
/* In case of join request retransmissions, the stack must prepare
* the frame again, because the network server keeps track of the random
* LoRaMacDevNonce values to prevent reply attacks. */
* LoRaMacDevNonce values to prevent replay attacks. */
status = prepare_frame(&mac_hdr, &fctrl, 0, NULL, 0);
if (status == LORAWAN_STATUS_OK) {
@ -851,7 +944,8 @@ lorawan_status_t LoRaMac::send_join_request()
*/
lorawan_status_t LoRaMac::handle_retransmission()
{
if (!nwk_joined() && (_mlme_confirmation.req_type == MLME_JOIN)) {
//TODO: Rejoin requests are not retransmitted (except in case of ForceRejoinReq), in most of the cases server won't respond
if (!nwk_joined() && _params.is_last_tx_join_request) {
return send_join_request();
}
@ -966,7 +1060,7 @@ void LoRaMac::on_ack_timeout_timer_event(void)
lorawan_status_t status = handle_retransmission();
if (status == LORAWAN_STATUS_NO_CHANNEL_FOUND ||
status == LORAWAN_STATUS_NO_FREE_CHANNEL_FOUND) {
status == LORAWAN_STATUS_NO_FREE_CHANNEL_FOUND) {
// In a case when enabled channels are not found, PHY layer
// resorts to default channels. Next attempt should go forward as the
// default channels are always available if there is a base station in the
@ -1425,6 +1519,7 @@ void LoRaMac::set_device_class(const device_class_t &device_class,
tr_debug("Changing device class to -> CLASS_A");
_lora_phy->put_radio_to_sleep();
} else if (CLASS_C == _device_class) {
tr_debug("Changing device class to -> CLASS_C");
_params.is_node_ack_requested = false;
_lora_phy->put_radio_to_sleep();
_lora_phy->compute_rx_win_params(_params.sys_params.rx2_channel.datarate,
@ -1441,13 +1536,24 @@ void LoRaMac::set_device_class(const device_class_t &device_class,
void LoRaMac::setup_link_check_request()
{
reset_mlme_confirmation();
_mlme_confirmation.req_type = MLME_LINK_CHECK;
_mlme_confirmation.pending = true;
_mac_commands.add_link_check_req();
}
void LoRaMac::setup_reset_indication()
{
_mac_commands.add_reset_ind(1);
}
void LoRaMac::setup_rekey_indication()
{
_mac_commands.add_rekey_ind(1);
}
void LoRaMac::setup_device_mode_indication(uint8_t classType)
{
_mac_commands.add_device_mode_indication(classType);
}
lorawan_status_t LoRaMac::prepare_join(const lorawan_connect_t *params, bool is_otaa)
{
if (params) {
@ -1455,19 +1561,30 @@ lorawan_status_t LoRaMac::prepare_join(const lorawan_connect_t *params, bool is_
if ((params->connection_u.otaa.dev_eui == NULL)
|| (params->connection_u.otaa.app_eui == NULL)
|| (params->connection_u.otaa.app_key == NULL)
|| (params->connection_u.otaa.nwk_key == NULL )
|| (params->connection_u.otaa.nb_trials == 0)) {
return LORAWAN_STATUS_PARAMETER_INVALID;
}
_params.keys.dev_eui = params->connection_u.otaa.dev_eui;
_params.keys.app_eui = params->connection_u.otaa.app_eui;
_params.keys.app_key = params->connection_u.otaa.app_key;
_params.keys.nwk_key = params->connection_u.otaa.nwk_key;
if (MBED_CONF_LORA_VERSION == LORAWAN_VERSION_1_0_2) {
_params.keys.nwk_key = params->connection_u.otaa.app_key;
}
if (0 != _lora_crypto.compute_join_server_keys(_params.keys.nwk_key, APPKEY_KEY_LENGTH, _params.keys.dev_eui,
_params.keys.js_intkey, _params.keys.js_enckey)) {
return LORAWAN_STATUS_CRYPTO_FAIL;
}
_params.max_join_request_trials = params->connection_u.otaa.nb_trials;
if (!_lora_phy->verify_nb_join_trials(params->connection_u.otaa.nb_trials)) {
// Value not supported, get default
_params.max_join_request_trials = MBED_CONF_LORA_NB_TRIALS;
}
// Reset variable JoinRequestTrials
_params.join_request_trial_counter = 0;
reset_mac_parameters();
@ -1482,6 +1599,12 @@ lorawan_status_t LoRaMac::prepare_join(const lorawan_connect_t *params, bool is_
return LORAWAN_STATUS_PARAMETER_INVALID;
}
if (MBED_CONF_LORA_VERSION == LORAWAN_VERSION_1_1
&& ((params->connection_u.abp.snwk_sintkey == NULL)
|| (params->connection_u.abp.nwk_senckey == NULL))) {
return LORAWAN_STATUS_PARAMETER_INVALID;
}
_params.net_id = params->connection_u.abp.nwk_id;
_params.dev_addr = params->connection_u.abp.dev_addr;
@ -1490,18 +1613,47 @@ lorawan_status_t LoRaMac::prepare_join(const lorawan_connect_t *params, bool is_
memcpy(_params.keys.app_skey, params->connection_u.abp.app_skey,
sizeof(_params.keys.app_skey));
if (MBED_CONF_LORA_VERSION == LORAWAN_VERSION_1_1) {
memcpy(_params.keys.snwk_sintkey, params->connection_u.abp.snwk_sintkey,
sizeof(_params.keys.snwk_sintkey));
memcpy(_params.keys.nwk_senckey, params->connection_u.abp.nwk_senckey,
sizeof(_params.keys.nwk_senckey));
_params.server_type = LW1_1;
} else {
memcpy(_params.keys.snwk_sintkey, params->connection_u.abp.nwk_skey,
sizeof(_params.keys.snwk_sintkey));
memcpy(_params.keys.nwk_senckey, params->connection_u.abp.nwk_skey,
sizeof(_params.keys.nwk_senckey));
_params.server_type = LW1_0_2;
}
}
} else {
#if MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION
const static uint8_t dev_eui[] = MBED_CONF_LORA_DEVICE_EUI;
const static uint8_t app_eui[] = MBED_CONF_LORA_APPLICATION_EUI;
const static uint8_t app_key[] = MBED_CONF_LORA_APPLICATION_KEY;
const static uint8_t nwk_key[] = MBED_CONF_LORA_NETWORK_KEY;
_params.keys.app_eui = const_cast<uint8_t *>(app_eui);
_params.keys.dev_eui = const_cast<uint8_t *>(dev_eui);
_params.keys.app_key = const_cast<uint8_t *>(app_key);
_params.keys.nwk_key = const_cast<uint8_t *>(nwk_key);
_params.max_join_request_trials = MBED_CONF_LORA_NB_TRIALS;
if( MBED_CONF_LORA_VERSION == LORAWAN_VERSION_1_0_2 ) {
_params.keys.nwk_key = _params.keys.app_key;
}
if (0 != _lora_crypto.compute_join_server_keys(_params.keys.nwk_key, APPKEY_KEY_LENGTH, _params.keys.dev_eui,
_params.keys.js_intkey, _params.keys.js_enckey)) {
return LORAWAN_STATUS_CRYPTO_FAIL;
}
// Reset variable JoinRequestTrials
_params.join_request_trial_counter = 0;
@ -1513,13 +1665,23 @@ lorawan_status_t LoRaMac::prepare_join(const lorawan_connect_t *params, bool is_
#else
const static uint8_t nwk_skey[] = MBED_CONF_LORA_NWKSKEY;
const static uint8_t app_skey[] = MBED_CONF_LORA_APPSKEY;
const static uint8_t snwk_sintkey[] = MBED_CONF_LORA_SNWKSINTKEY;
const static uint8_t nwk_senckey[] = MBED_CONF_LORA_NWKSENCKEY;
_params.net_id = (MBED_CONF_LORA_DEVICE_ADDRESS & LORAWAN_NETWORK_ID_MASK) >> 25;
_params.dev_addr = MBED_CONF_LORA_DEVICE_ADDRESS;
memcpy(_params.keys.nwk_skey, nwk_skey, sizeof(_params.keys.nwk_skey));
memcpy(_params.keys.app_skey, app_skey, sizeof(_params.keys.app_skey));
if (MBED_CONF_LORA_VERSION == LORAWAN_VERSION_1_1) {
memcpy(_params.keys.snwk_sintkey, snwk_sintkey, sizeof(_params.keys.snwk_sintkey));
memcpy(_params.keys.nwk_senckey, nwk_senckey, sizeof(_params.keys.nwk_senckey));
_params.server_type = LW1_1;
} else {
memcpy(_params.keys.snwk_sintkey, nwk_skey, sizeof(_params.keys.snwk_sintkey));
memcpy(_params.keys.nwk_senckey, nwk_skey, sizeof(_params.keys.nwk_senckey));
_params.server_type = LW1_0_2;
}
#endif
}
@ -1533,8 +1695,14 @@ lorawan_status_t LoRaMac::join(bool is_otaa)
return LORAWAN_STATUS_OK;
}
reset_mlme_confirmation();
_mlme_confirmation.req_type = MLME_JOIN;
_params.join_request_type = JOIN_REQUEST;
return send_join_request();
}
lorawan_status_t LoRaMac::rejoin(join_req_type_t rejoin_type)
{
_params.join_request_type = rejoin_type;
return send_join_request();
}
@ -1558,9 +1726,11 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
uint16_t i;
uint8_t pkt_header_len = 0;
uint32_t mic = 0;
uint32_t mic2 = 0;
const void *payload = fbuffer;
uint8_t frame_port = fport;
lorawan_status_t status = LORAWAN_STATUS_OK;
uint32_t args = 0;
_params.tx_buffer_len = 0;
@ -1593,7 +1763,7 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
if (0 != _lora_crypto.compute_join_frame_mic(_params.tx_buffer,
_params.tx_buffer_len & 0xFF,
_params.keys.app_key,
_params.keys.nwk_key,
APPKEY_KEY_LENGTH,
&mic)) {
return LORAWAN_STATUS_CRYPTO_FAIL;
@ -1604,6 +1774,45 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
_params.tx_buffer[_params.tx_buffer_len++] = (mic >> 16) & 0xFF;
_params.tx_buffer[_params.tx_buffer_len++] = (mic >> 24) & 0xFF;
break;
case FRAME_TYPE_REJOIN_REQUEST:
_params.tx_buffer_len = pkt_header_len;
_params.tx_buffer[_params.tx_buffer_len++] = (uint8_t)_params.join_request_type;
if (_params.join_request_type == REJOIN_REQUEST_TYPE0 || _params.join_request_type == REJOIN_REQUEST_TYPE2) {
_params.tx_buffer[_params.tx_buffer_len++] = _params.net_id & 0xFF;
_params.tx_buffer[_params.tx_buffer_len++] = (_params.net_id >> 8) & 0xFF;
_params.tx_buffer[_params.tx_buffer_len++] = (_params.net_id >> 16) & 0xFF;
memcpy_convert_endianess(_params.tx_buffer + _params.tx_buffer_len,
_params.keys.dev_eui, 8);
_params.tx_buffer_len += 8;
_params.tx_buffer[_params.tx_buffer_len++] = _params.RJcount0 & 0xFF;
_params.tx_buffer[_params.tx_buffer_len++] = (_params.RJcount0 >> 8) & 0xFF;
} else { //_params.join_request_type == REJOIN_REQUEST_TYPE1
memcpy_convert_endianess(_params.tx_buffer + _params.tx_buffer_len,
_params.keys.app_eui, 8);
_params.tx_buffer_len += 8;
memcpy_convert_endianess(_params.tx_buffer + _params.tx_buffer_len,
_params.keys.dev_eui, 8);
_params.tx_buffer_len += 8;
_params.tx_buffer[_params.tx_buffer_len++] = _params.RJcount1 & 0xFF;
_params.tx_buffer[_params.tx_buffer_len++] = (_params.RJcount1 >> 8) & 0xFF;
}
if (0 != _lora_crypto.compute_join_frame_mic(_params.tx_buffer,
_params.tx_buffer_len & 0xFF,
_params.keys.snwk_sintkey,
APPKEY_KEY_LENGTH,
&mic)) {
return LORAWAN_STATUS_CRYPTO_FAIL;
}
_params.tx_buffer[_params.tx_buffer_len++] = mic & 0xFF;
_params.tx_buffer[_params.tx_buffer_len++] = (mic >> 8) & 0xFF;
_params.tx_buffer[_params.tx_buffer_len++] = (mic >> 16) & 0xFF;
_params.tx_buffer[_params.tx_buffer_len++] = (mic >> 24) & 0xFF;
break;
case FRAME_TYPE_DATA_CONFIRMED_UP:
_params.is_node_ack_requested = true;
@ -1643,6 +1852,7 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
const uint8_t mac_commands_len = _mac_commands.get_mac_cmd_length();
if ((payload != NULL) && (_params.tx_buffer_len > 0)) {
if (mac_commands_len <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH) {
fctrl->bits.fopts_len += mac_commands_len;
@ -1650,8 +1860,20 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
_params.tx_buffer[0x05] = fctrl->value;
const uint8_t *buffer = _mac_commands.get_mac_commands_buffer();
for (i = 0; i < mac_commands_len; i++) {
_params.tx_buffer[pkt_header_len++] = buffer[i];
if (_params.server_type == LW1_1) {
if (0 != _lora_crypto.encrypt_payload(buffer, mac_commands_len,
_params.keys.nwk_senckey,
sizeof(_params.keys.nwk_senckey) * 8,
_params.dev_addr, UP_LINK,
_params.ul_frame_counter,
&_params.tx_buffer[pkt_header_len])) {
status = LORAWAN_STATUS_CRYPTO_FAIL;
}
pkt_header_len += mac_commands_len;
} else {
for (i = 0; i < mac_commands_len; i++) {
_params.tx_buffer[pkt_header_len++] = buffer[i];
}
}
} else {
_params.tx_buffer_len = mac_commands_len;
@ -1659,11 +1881,9 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
frame_port = 0;
}
} else {
if (mac_commands_len > 0) {
_params.tx_buffer_len = mac_commands_len;
payload = _mac_commands.get_mac_commands_buffer();
frame_port = 0;
}
_params.tx_buffer_len = mac_commands_len;
payload = _mac_commands.get_mac_commands_buffer();
frame_port = 0;
}
_mac_commands.parse_mac_commands_to_repeat();
@ -1676,10 +1896,11 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
uint8_t *key = _params.keys.app_skey;
uint32_t key_length = sizeof(_params.keys.app_skey) * 8;
if (frame_port == 0) {
key = _params.keys.nwk_skey;
key_length = sizeof(_params.keys.nwk_skey) * 8;
key = _params.keys.nwk_senckey;
key_length = sizeof(_params.keys.nwk_senckey) * 8;
}
if (0 != _lora_crypto.encrypt_payload((uint8_t *) payload, _params.tx_buffer_len,
if (0 != _lora_crypto.encrypt_payload((uint8_t *) payload,
_params.tx_buffer_len,
key, key_length,
_params.dev_addr, UP_LINK,
_params.ul_frame_counter,
@ -1691,16 +1912,38 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
_params.tx_buffer_len = pkt_header_len + _params.tx_buffer_len;
if (0 != _lora_crypto.compute_mic(_params.tx_buffer, _params.tx_buffer_len,
_params.keys.nwk_skey, sizeof(_params.keys.nwk_skey) * 8,
_params.dev_addr,
_params.keys.nwk_skey,
sizeof(_params.keys.nwk_skey) * 8,
args, _params.dev_addr,
UP_LINK, _params.ul_frame_counter, &mic)) {
status = LORAWAN_STATUS_CRYPTO_FAIL;
}
_params.tx_buffer[_params.tx_buffer_len + 0] = mic & 0xFF;
_params.tx_buffer[_params.tx_buffer_len + 1] = (mic >> 8) & 0xFF;
_params.tx_buffer[_params.tx_buffer_len + 2] = (mic >> 16) & 0xFF;
_params.tx_buffer[_params.tx_buffer_len + 3] = (mic >> 24) & 0xFF;
if (_params.server_type == LW1_1) {
if (_params.is_srv_ack_requested) {
args = _params.counterForAck;
}
args |= _params.sys_params.channel_data_rate << 16;
args |= _params.channel << 24;
if (0 != _lora_crypto.compute_mic(_params.tx_buffer, _params.tx_buffer_len,
_params.keys.snwk_sintkey,
sizeof(_params.keys.snwk_sintkey) * 8,
args, _params.dev_addr,
UP_LINK, _params.ul_frame_counter, &mic2)) {
status = LORAWAN_STATUS_CRYPTO_FAIL;
}
_params.tx_buffer[_params.tx_buffer_len + 0] = mic2 & 0xFF;
_params.tx_buffer[_params.tx_buffer_len + 1] = (mic2 >> 8) & 0xFF;
_params.tx_buffer[_params.tx_buffer_len + 2] = mic & 0xFF;
_params.tx_buffer[_params.tx_buffer_len + 3] = (mic >> 8) & 0xFF;
} else {
_params.tx_buffer[_params.tx_buffer_len + 0] = mic & 0xFF;
_params.tx_buffer[_params.tx_buffer_len + 1] = (mic >> 8) & 0xFF;
_params.tx_buffer[_params.tx_buffer_len + 2] = (mic >> 16) & 0xFF;
_params.tx_buffer[_params.tx_buffer_len + 3] = (mic >> 24) & 0xFF;
}
_params.tx_buffer_len += LORAMAC_MFR_LEN;
}
@ -1743,7 +1986,6 @@ lorawan_status_t LoRaMac::send_frame_on_channel(uint8_t channel)
_mcps_confirmation.channel = channel;
_mcps_confirmation.tx_toa = _params.timers.tx_toa;
_mlme_confirmation.tx_toa = _params.timers.tx_toa;
if (!_is_nwk_joined) {
_params.join_request_trial_counter++;
@ -1760,12 +2002,6 @@ void LoRaMac::reset_mcps_confirmation()
_mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR;
}
void LoRaMac::reset_mlme_confirmation()
{
memset((uint8_t *) &_mlme_confirmation, 0, sizeof(_mlme_confirmation));
_mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR;
}
void LoRaMac::reset_mcps_indication()
{
memset((uint8_t *) &_mcps_indication, 0, sizeof(_mcps_indication));
@ -1841,7 +2077,6 @@ void LoRaMac::disconnect()
_mac_commands.clear_repeat_buffer();
reset_mcps_confirmation();
reset_mlme_confirmation();
reset_mcps_indication();
}
@ -1989,3 +2224,12 @@ uint8_t LoRaMac::get_prev_QOS_level()
return _prev_qos_level;
}
server_type_t LoRaMac::get_server_type()
{
return _params.server_type;
}
uint8_t LoRaMac::get_current_adr_ack_limit()
{
return _lora_phy->get_adr_ack_limit();
}

View File

@ -310,6 +310,30 @@ public:
*/
void setup_link_check_request();
/**
* @brief setup_reset_indication Adds reset indication command
* to be put on next uplink message
*/
void setup_reset_indication();
/**
* @brief setup_rekey_indication Adds rekey indication command
* to be put on next uplink message
*/
void setup_rekey_indication();
/**
* @brief setup_device_mode_indication Adds Device mode indication command
* to be put on next uplink message
*/
void setup_device_mode_indication(uint8_t classType);
/**
* @brief get_server_type Gets the server's type (Supported LoRaWAN spec version)
* @return Server type
*/
server_type_t get_server_type();
/**
* @brief prepare_join prepares arguments to be ready for join() call.
* @param params Join parameters to use, if NULL, the default will be used.
@ -326,6 +350,13 @@ public:
*/
lorawan_status_t join(bool is_otaa);
/**
* @brief rejoin Rejoins the network
* @param rejoin_type Rejoin type indicates the rejoin message payload
* @return LORAWAN_STATUS_OK or a negative error code on failure.
*/
lorawan_status_t rejoin(join_req_type_t rejoin_type);
/**
* MAC operations upon successful transmission
*/
@ -335,7 +366,8 @@ public:
* MAC operations upon reception
*/
void on_radio_rx_done(const uint8_t *const payload, uint16_t size,
int16_t rssi, int8_t snr);
int16_t rssi, int8_t snr,
mbed::Callback<void(loramac_mlme_confirm_t&)> confirm_handler);
/**
* MAC operations upon transmission timeout
@ -379,7 +411,6 @@ public:
*/
void post_process_mcps_req(void);
void post_process_mcps_ind(void);
void post_process_mlme_request(void);
void post_process_mlme_ind(void);
/**
@ -408,6 +439,8 @@ public:
*/
rx_slot_t get_current_slot(void);
uint8_t get_current_adr_ack_limit();
/**
* Indicates what level of QOS is set by network server. QOS level is set
* in response to a LinkADRReq for UNCONFIRMED messages
@ -472,13 +505,14 @@ private:
/**
* Handles a Join Accept frame
*/
void handle_join_accept_frame(const uint8_t *payload, uint16_t size);
loramac_event_info_status_t handle_join_accept_frame(const uint8_t *payload, uint16_t size);
/**
* Handles data frames
*/
void handle_data_frame(const uint8_t *payload, uint16_t size, uint8_t ptr_pos,
uint8_t msg_type, int16_t rssi, int8_t snr);
uint8_t msg_type, int16_t rssi, int8_t snr,
mbed::Callback<void(loramac_mlme_confirm_t&)> confirm_handler);
/**
* Send a Join Request
@ -499,7 +533,7 @@ private:
* Performs MIC
*/
bool message_integrity_check(const uint8_t *payload, uint16_t size,
uint8_t *ptr_pos, uint32_t address,
uint8_t *ptr_pos, uint16_t confFCnt, uint32_t address,
uint32_t *downlink_counter, const uint8_t *nwk_skey);
/**
@ -510,12 +544,15 @@ private:
uint8_t fopts_len, uint8_t *nwk_skey,
uint8_t *app_skey, uint32_t address,
uint32_t downlink_frame_counter,
int16_t rssi, int8_t snr);
int16_t rssi, int8_t snr,
mbed::Callback<void(loramac_mlme_confirm_t&)> confirm_handler);
/**
* Decrypts and extracts MAC commands from the received encrypted
* payload if there is no data
* @return True if successful, false otherwise
*/
void extract_mac_commands_only(const uint8_t *payload, int8_t snr, uint8_t fopts_len);
bool extract_mac_commands_only(const uint8_t *payload, int8_t snr, uint8_t fopts_len,
mbed::Callback<void(loramac_mlme_confirm_t&)> confirm_handler);
/**
* Callback function to be executed when the DC backoff timer expires

View File

@ -29,6 +29,8 @@ SPDX-License-Identifier: BSD-3-Clause
#include "mbed-trace/mbed_trace.h"
#define TRACE_GROUP "LMACC"
using namespace mbed;
/**
* LoRaMAC max EIRP (dBm) table.
*/
@ -77,7 +79,6 @@ void LoRaMacCommand::parse_mac_commands_to_repeat()
mac_cmd_buffer_to_repeat[cmd_cnt++] = mac_cmd_buffer[i];
break;
}
// NON-STICKY
case MOTE_MAC_DEV_STATUS_ANS: { // 2 bytes payload
i += 2;
@ -130,9 +131,9 @@ bool LoRaMacCommand::has_sticky_mac_cmd() const
lorawan_status_t LoRaMacCommand::process_mac_commands(const uint8_t *payload, uint8_t mac_index,
uint8_t commands_size, uint8_t snr,
loramac_mlme_confirm_t &mlme_conf,
lora_mac_system_params_t &mac_sys_params,
LoRaPHY &lora_phy)
LoRaPHY &lora_phy,
Callback<void(loramac_mlme_confirm_t&)> confirm_handler)
{
uint8_t status = 0;
lorawan_status_t ret_value = LORAWAN_STATUS_OK;
@ -140,10 +141,20 @@ lorawan_status_t LoRaMacCommand::process_mac_commands(const uint8_t *payload, ui
while (mac_index < commands_size) {
// Decode Frame MAC commands
switch (payload[mac_index++]) {
case SRV_MAC_LINK_CHECK_ANS:
case SRV_MAC_RESET_CONF: {
loramac_mlme_confirm_t mlme_conf;
mlme_conf.status = LORAMAC_EVENT_INFO_STATUS_OK;
mlme_conf.version = payload[mac_index++] & 0x0F;
confirm_handler(mlme_conf);
}
break;
case SRV_MAC_LINK_CHECK_ANS: {
loramac_mlme_confirm_t mlme_conf;
mlme_conf.status = LORAMAC_EVENT_INFO_STATUS_OK;
mlme_conf.demod_margin = payload[mac_index++];
mlme_conf.nb_gateways = payload[mac_index++];
confirm_handler(mlme_conf);
}
break;
case SRV_MAC_LINK_ADR_REQ: {
adr_req_params_t link_adr_req;
@ -291,7 +302,75 @@ lorawan_status_t LoRaMacCommand::process_mac_commands(const uint8_t *payload, ui
ret_value = add_dl_channel_ans(status);
}
break;
break;
case SRV_MAC_REKEY_CONF: {
loramac_mlme_confirm_t mlme_conf;
mlme_conf.status = LORAMAC_EVENT_INFO_STATUS_OK;
mlme_conf.version = payload[mac_index++] & 0x0F;
confirm_handler(mlme_conf);
}
break;
case SRV_MAC_ADR_PARAM_SETUP_REQ: {
uint16_t limit = 1;
uint16_t delay = 1;
uint8_t adrParam = payload[mac_index++];
delay = delay << (adrParam & 0x0f);
limit = limit << (adrParam >> 4 & 0x0f);
lora_phy.set_adr_ack_limit(limit);
lora_phy.set_adr_ack_delay(delay);
ret_value = add_adr_param_setup_ans();
}
break;
case SRV_MAC_DEVICE_TIME_ANS: {
uint32_t secs = (uint32_t) payload[mac_index++];
secs |= (uint32_t) payload[mac_index++] << 8;
secs |= (uint32_t) payload[mac_index++] << 16;
secs |= (uint32_t) payload[mac_index++] << 24;
uint32_t millis = ((uint32_t) payload[mac_index++] * 1000) >> 8;
lora_phy.time_received(secs, millis);
}
break;
case SRV_MAC_FORCE_REJOIN_REQ: {
loramac_mlme_confirm_t mlme_conf;
uint8_t data = payload[mac_index++];
uint8_t max_retries = data & 0x07;
uint8_t period = (data >> 3) & 0x07;
data = payload[mac_index++];
uint8_t datarate = data & 0x0F;
uint8_t rejoin_type = (data >> 4) & 0x07;
mlme_conf.status = LORAMAC_EVENT_INFO_STATUS_OK;
mlme_conf.type = MLME_FORCE_REJOIN;
mlme_conf.max_retries = max_retries;
mlme_conf.period = period;
mlme_conf.datarate = datarate;
mlme_conf.rejoin_type = rejoin_type;
confirm_handler(mlme_conf);
}
break;
case SRV_MAC_REJOIN_PARAM_SETUP_REQ: {
uint8_t data = payload[mac_index++];
uint8_t count = (data & 0x0F) + 4;
uint8_t time = ((data >> 4) & 0x0F) + 10;
uint32_t max_count = 1 << count;
uint32_t max_time = 1 << time;
uint8_t time_available = lora_phy.update_rejoin_params(max_time, max_count);
add_rejoin_param_setup_ans(time_available);
}
break;
case SRV_MAC_DEVICE_MODE_CONF: {
loramac_mlme_confirm_t mlme_conf;
uint8_t classType = payload[mac_index++];
mlme_conf.classType = classType;
mlme_conf.status = LORAMAC_EVENT_INFO_STATUS_OK;
mlme_conf.type = MLME_DEVICE_MODE;
confirm_handler(mlme_conf);
}
break;
default:
// Unknown command. ABORT MAC commands processing
tr_error("Invalid MAC command (0x%X)!", payload[mac_index]);
@ -323,6 +402,53 @@ lorawan_status_t LoRaMacCommand::add_link_check_req()
return ret;
}
lorawan_status_t LoRaMacCommand::add_reset_ind(uint8_t version)
{
lorawan_status_t ret = LORAWAN_STATUS_LENGTH_ERROR;
if (cmd_buffer_remaining() > 0) {
mac_cmd_buffer[mac_cmd_buf_idx++] = MOTE_MAC_RESET_IND;
mac_cmd_buffer[mac_cmd_buf_idx++] = version & 0x0F;
ret = LORAWAN_STATUS_OK;
mac_cmd_in_next_tx = true;
}
return ret;
}
lorawan_status_t LoRaMacCommand::add_rekey_ind(uint8_t version)
{
lorawan_status_t ret = LORAWAN_STATUS_LENGTH_ERROR;
if (cmd_buffer_remaining() > 0) {
mac_cmd_buffer[mac_cmd_buf_idx++] = MOTE_MAC_REKEY_IND;
mac_cmd_buffer[mac_cmd_buf_idx++] = version & 0x0F;
ret = LORAWAN_STATUS_OK;
mac_cmd_in_next_tx = true;
}
return ret;
}
lorawan_status_t LoRaMacCommand::add_device_mode_indication(uint8_t classType)
{
lorawan_status_t ret = LORAWAN_STATUS_LENGTH_ERROR;
if (cmd_buffer_remaining() > 0) {
mac_cmd_buffer[mac_cmd_buf_idx++] = MOTE_DEVICE_MODE_IND;
mac_cmd_buffer[mac_cmd_buf_idx++] = classType;
ret = LORAWAN_STATUS_OK;
mac_cmd_in_next_tx = true;
}
return ret;
}
lorawan_status_t LoRaMacCommand::add_device_time_req()
{
lorawan_status_t ret = LORAWAN_STATUS_LENGTH_ERROR;
if (cmd_buffer_remaining() > 0) {
mac_cmd_buffer[mac_cmd_buf_idx++] = MOTE_MAC_DEVICE_TIME_REQ;
ret = LORAWAN_STATUS_OK;
mac_cmd_in_next_tx = true;
}
return ret;
}
lorawan_status_t LoRaMacCommand::add_link_adr_ans(uint8_t status)
{
lorawan_status_t ret = LORAWAN_STATUS_LENGTH_ERROR;
@ -422,3 +548,22 @@ lorawan_status_t LoRaMacCommand::add_dl_channel_ans(uint8_t status)
}
return ret;
}
lorawan_status_t LoRaMacCommand::add_adr_param_setup_ans()
{
lorawan_status_t ret = LORAWAN_STATUS_LENGTH_ERROR;
if (cmd_buffer_remaining() > 0) {
mac_cmd_buffer[mac_cmd_buf_idx++] = MOTE_MAC_ADR_PARAM_SETUP_ANS;
}
return ret;
}
lorawan_status_t LoRaMacCommand::add_rejoin_param_setup_ans(uint8_t status)
{
lorawan_status_t ret = LORAWAN_STATUS_LENGTH_ERROR;
if (cmd_buffer_remaining() > 0) {
mac_cmd_buffer[mac_cmd_buf_idx++] = MOTE_MAC_REJOIN_PARAM_SETUP_ANS;
mac_cmd_buffer[mac_cmd_buf_idx++] = status & 0x01;
}
return ret;
}

View File

@ -119,9 +119,9 @@ public:
*/
lorawan_status_t process_mac_commands(const uint8_t *payload, uint8_t mac_index,
uint8_t commands_size, uint8_t snr,
loramac_mlme_confirm_t &mlme_conf,
lora_mac_system_params_t &mac_params,
LoRaPHY &lora_phy);
lora_mac_system_params_t& mac_params,
LoRaPHY& lora_phy,
mbed::Callback<void(loramac_mlme_confirm_t&)> confirm_handler);
/**
* @brief Adds a new LinkCheckReq MAC command to be sent.
@ -131,6 +131,40 @@ public:
*/
lorawan_status_t add_link_check_req();
/**
* @brief add_reset_ind Adds a ResetInd MAC command to be sent.
*
* @param version LoRaWAN version of the stack
* @return status Function status: LORAWAN_STATUS_OK: OK,
* LORAWAN_STATUS_LENGTH_ERROR: Buffer full
*/
lorawan_status_t add_reset_ind(uint8_t version);
/**
* @brief add_rekey_ind Adds a RekeyInd MAC command to be sent.
*
* @param version LoRaWAN version of the stack
* @return status Function status: LORAWAN_STATUS_OK: OK,
* LORAWAN_STATUS_LENGTH_ERROR: Buffer full
*/
lorawan_status_t add_rekey_ind(uint8_t version);
/**
* @brief add_device_mode_indication Adds a DeviceModeInd MAC command to be sent.
* @param classType Class type that is used from now on
* @return status Function status: LORAWAN_STATUS_OK: OK,
* LORAWAN_STATUS_LENGTH_ERROR: Buffer full
*/
lorawan_status_t add_device_mode_indication(uint8_t classType);
/**
* @brief add_device_time_req Adds DeviceTimeReq MAC command to be sent.
* Requests server's current time
* @return status Function status: LORAWAN_STATUS_OK: OK,
* LORAWAN_STATUS_LENGTH_ERROR: Buffer full
*/
lorawan_status_t add_device_time_req();
/**
* @brief Set battery level query callback method
* If callback is not set, BAT_LEVEL_NO_MEASURE is returned.
@ -220,6 +254,21 @@ private:
*/
lorawan_status_t add_dl_channel_ans(uint8_t status);
/**
* @brief Adds a new AdrParamSetupAns MAC command to be sent.
* @return status Function status: LORAWAN_STATUS_OK: OK,
* LORAWAN_STATUS_LENGTH_ERROR: Buffer full
*/
lorawan_status_t add_adr_param_setup_ans();
/**
* @brief Adds a new RejoinParamSetupAns MAC command to be sent.
* @param status 1 if time limitation was accepted, 0 otherwise.
* @return status Function status: LORAWAN_STATUS_OK: OK,
* LORAWAN_STATUS_LENGTH_ERROR: Buffer full
*/
lorawan_status_t add_rejoin_param_setup_ans(uint8_t status);
private:
/**
* Indicates if there are any pending sticky MAC commands

View File

@ -53,7 +53,8 @@ LoRaMacCrypto::~LoRaMacCrypto()
int LoRaMacCrypto::compute_mic(const uint8_t *buffer, uint16_t size,
const uint8_t *key, const uint32_t key_length,
uint32_t address, uint8_t dir, uint32_t seq_counter,
uint32_t args, uint32_t address,
uint8_t dir, uint32_t seq_counter,
uint32_t *mic)
{
uint8_t computed_mic[16] = {};
@ -61,6 +62,10 @@ int LoRaMacCrypto::compute_mic(const uint8_t *buffer, uint16_t size,
int ret = 0;
mic_block_b0[0] = 0x49;
mic_block_b0[1] = (args) & 0xFF;
mic_block_b0[2] = (args >> 8) & 0xFF;
mic_block_b0[3] = (args >> 16) & 0xFF;
mic_block_b0[4] = (args >> 24) & 0xFF;
mic_block_b0[5] = dir;
@ -266,15 +271,30 @@ exit:
}
int LoRaMacCrypto::compute_skeys_for_join_frame(const uint8_t *key, uint32_t key_length,
const uint8_t *app_nonce, uint16_t dev_nonce,
uint8_t *nwk_skey, uint8_t *app_skey)
const uint8_t *app_key, uint32_t app_key_length,
const uint8_t *args, uint8_t args_size,
uint8_t *nwk_skey, uint8_t *app_skey,
uint8_t *snwk_sintkey, uint8_t *nwk_senckey,
server_type_t stype)
{
uint8_t nonce[16];
uint8_t *p_dev_nonce = (uint8_t *) &dev_nonce;
int ret = 0;
mbedtls_aes_init(&aes_ctx);
ret = mbedtls_aes_setkey_enc(&aes_ctx, app_key, app_key_length);
if (0 != ret)
goto exit;
memset(nonce, 0, sizeof(nonce));
nonce[0] = 0x02;
memcpy(nonce + 1, args, args_size);
ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, nonce, app_skey);
if (0 != ret)
goto exit;
mbedtls_aes_free(&aes_ctx);
mbedtls_aes_init(&aes_ctx);
ret = mbedtls_aes_setkey_enc(&aes_ctx, key, key_length);
if (0 != ret) {
goto exit;
@ -282,23 +302,73 @@ int LoRaMacCrypto::compute_skeys_for_join_frame(const uint8_t *key, uint32_t key
memset(nonce, 0, sizeof(nonce));
nonce[0] = 0x01;
memcpy(nonce + 1, app_nonce, 6);
memcpy(nonce + 7, p_dev_nonce, 2);
memcpy(nonce + 1, args, args_size);
ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, nonce, nwk_skey);
if (0 != ret) {
goto exit;
}
if (stype == LW1_0_2) {
memcpy(nwk_senckey, nwk_skey, key_length / 8);
memcpy(snwk_sintkey, nwk_skey, key_length / 8);
} else {
memset(nonce, 0, sizeof(nonce));
nonce[0] = 0x03;
memcpy(nonce + 1, args, args_size);
ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, nonce, snwk_sintkey);
if (0 != ret)
goto exit;
memset(nonce, 0, sizeof(nonce));
nonce[0] = 0x04;
memcpy(nonce + 1, args, args_size);
ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, nonce, nwk_senckey);
if (0 != ret)
goto exit;
}
exit: mbedtls_aes_free(&aes_ctx);
return ret;
}
int LoRaMacCrypto::compute_join_server_keys(const uint8_t *key, uint32_t key_length, const uint8_t *eui,
uint8_t *js_intkey, uint8_t *js_enckey)
{
uint8_t nonce[16];
int ret = 0;
if( MBED_CONF_LORA_VERSION == LORAWAN_VERSION_1_0_2 ) {
memcpy(js_intkey, key, key_length/8);
memcpy(js_enckey, key, key_length/8);
return ret;
}
mbedtls_aes_init(&aes_ctx);
ret = mbedtls_aes_setkey_enc(&aes_ctx, key, key_length);
if (0 != ret)
goto exit;
memset(nonce, 0, sizeof(nonce));
nonce[0] = 0x02;
memcpy(nonce + 1, app_nonce, 6);
memcpy(nonce + 7, p_dev_nonce, 2);
ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, nonce, app_skey);
nonce[0] = 0x05;
memcpy(nonce + 1, eui, 8);
ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, nonce, js_enckey);
if (0 != ret)
goto exit;
mbedtls_aes_free(&aes_ctx);
memset(nonce, 0, sizeof(nonce));
nonce[0] = 0x06;
memcpy(nonce + 1, eui, 8);
ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, nonce, js_intkey);
if (0 != ret)
goto exit;
exit:
mbedtls_aes_free(&aes_ctx);
return ret;
}
#else
LoRaMacCrypto::LoRaMacCrypto()
@ -314,8 +384,8 @@ LoRaMacCrypto::~LoRaMacCrypto()
// user knows what is wrong and in addition to that these ensure that
// Mbed-OS compiles properly under normal conditions where LoRaWAN in conjunction
// with mbedTLS is not being used.
int LoRaMacCrypto::compute_mic(const uint8_t *, uint16_t, const uint8_t *, uint32_t, uint32_t,
uint8_t dir, uint32_t, uint32_t *)
int LoRaMacCrypto::compute_mic(const uint8_t *, uint16_t , const uint8_t *, uint32_t, uint32_t, uint32_t,
uint8_t, uint32_t, uint32_t *)
{
MBED_ASSERT(0 && "[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS");
@ -357,8 +427,19 @@ int LoRaMacCrypto::decrypt_join_frame(const uint8_t *, uint16_t, const uint8_t *
return LORAWAN_STATUS_CRYPTO_FAIL;
}
int LoRaMacCrypto::compute_skeys_for_join_frame(const uint8_t *, uint32_t, const uint8_t *, uint16_t,
uint8_t *, uint8_t *)
int LoRaMacCrypto::compute_skeys_for_join_frame(const uint8_t *, uint32_t, const uint8_t *, uint32_t,
const uint8_t *, uint8_t ,
uint8_t *, uint8_t *, uint8_t *, uint8_t *,
server_type_t)
{
MBED_ASSERT(0 && "[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS");
// Never actually reaches here
return LORAWAN_STATUS_CRYPTO_FAIL;
}
int compute_join_server_keys(const uint8_t *, const uint8_t *,
uint8_t *, uint8_t *)
{
MBED_ASSERT(0 && "[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS");

View File

@ -33,6 +33,7 @@ SPDX-License-Identifier: BSD-3-Clause
#include "mbedtls/config.h"
#include "mbedtls/aes.h"
#include "mbedtls/cmac.h"
#include "system/lorawan_data_structures.h"
class LoRaMacCrypto {
@ -54,6 +55,7 @@ public:
* @param [in] size - Data buffer size
* @param [in] key - AES key to be used
* @param [in] key_length - Length of the key (bits)
* @param [in] args - LW1.1 bytes 1-4 (ConfFCnt, TxDr, TxCh)
* @param [in] address - Frame address
* @param [in] dir - Frame direction [0: uplink, 1: downlink]
* @param [in] seq_counter - Frame sequence counter
@ -63,7 +65,8 @@ public:
*/
int compute_mic(const uint8_t *buffer, uint16_t size,
const uint8_t *key, uint32_t key_length,
uint32_t address, uint8_t dir, uint32_t seq_counter,
uint32_t args, uint32_t address,
uint8_t dir, uint32_t seq_counter,
uint32_t *mic);
/**
@ -138,18 +141,42 @@ public:
/**
* Computes the LoRaMAC join frame decryption
*
* @param [in] key - AES key to be used
* @param [in] key - AES nwk key to be used
* @param [in] key_length - Length of the key (bits)
* @param [in] app_nonce - Application nonce
* @param [in] dev_nonce - Device nonce
* @param [in] app_key - AES app key to be used
* @param [in] app_key_length - Length of the app key (bits)
* @param [in] args - Combined string of args needed for key derivation
* @param [in] args_size - Args size
* @param [out] nwk_skey - Network session key
* @param [out] app_skey - Application session key
* @param [out] snwk_sintkey - Serving Network Session Integrity Key
* @param [out] nwk_senckey - Network Session Encryption Key
* @param stype - Server type (LW1_0_2 or LW1_1) which defines
*
* @return 0 if successful, or a cipher specific error code
*/
int compute_skeys_for_join_frame(const uint8_t *key, uint32_t key_length,
const uint8_t *app_nonce, uint16_t dev_nonce,
uint8_t *nwk_skey, uint8_t *app_skey);
const uint8_t *app_key, uint32_t app_key_length,
const uint8_t *args, uint8_t args_size,
uint8_t *nwk_skey, uint8_t *app_skey,
uint8_t *snwk_sintkey, uint8_t *nwk_senckey,
server_type_t stype);
/**
* Computes the LoRaMAC join server keys
* In case of LoRaWAN 1.0.x key will be copied to outputs,
* so we can continue using same code for all versions
*
* @param [in] key - AES key to be used
* @param [in] key_length - Length of the key (bits)
* @param [in] eui - DevEUI
* @param [out] js_intkey - Join server integrity key
* @param [out] js_enckey - Join server encryption key
*
* @return 0 if successful, or a cipher specific error code
*/
int compute_join_server_keys(const uint8_t *key, uint32_t key_length, const uint8_t *eui,
uint8_t *js_intkey, uint8_t *js_enckey);
private:
/**

View File

@ -27,6 +27,7 @@ SPDX-License-Identifier: BSD-3-Clause
#include <stdint.h>
#include <math.h>
#include "mbed_rtc_time.h"
#include "LoRaPHY.h"
#define BACKOFF_DC_1_HOUR 100
@ -35,10 +36,16 @@ SPDX-License-Identifier: BSD-3-Clause
#define MAX_PREAMBLE_LENGTH 8.0f
#define TICK_GRANULARITY_JITTER 1.0f
#define CHANNELS_IN_MASK 16
#define GPS_EPOCH_DIFF_WITH_UTC 315964800
#define CHANNELS_IN_MASK 16
LoRaPHY::LoRaPHY()
: _radio(NULL),
_lora_time(NULL)
_lora_time(NULL),
_server_adr_ack_limit(0),
_server_adr_ack_delay(0),
_rejoin_max_time(MBED_CONF_LORA_REJOIN_DEFAULT_MAX_TIME),
_rejoin_max_count(MBED_CONF_LORA_REJOIN_DEFAULT_MAX_COUNT)
{
memset(&phy_params, 0, sizeof(phy_params));
}
@ -666,6 +673,26 @@ bool LoRaPHY::is_custom_channel_plan_supported()
return phy_params.custom_channelplans_supported;
}
void LoRaPHY::time_received(uint32_t secs, uint32_t milliseconds)
{
time_t seconds = time(NULL) + GPS_EPOCH_DIFF_WITH_UTC + secs;
//Since RTC does not support milliseconds, we round millis to closest second
if (milliseconds > 499) {
seconds++;
}
set_time(seconds);
//TODO: Do we need/want to inform application about updated time??
}
uint8_t LoRaPHY::update_rejoin_params(uint32_t max_time, uint32_t max_count)
{
//These will be taken into use at next rejoin "cycle"
_rejoin_max_time = max_time;
_rejoin_max_count = max_count;
return 1;
}
void LoRaPHY::restore_default_channels()
{
// Restore channels default mask
@ -678,7 +705,6 @@ bool LoRaPHY::verify_rx_datarate(uint8_t datarate)
{
if (is_datarate_supported(datarate)) {
if (phy_params.dl_dwell_time_setting == 0) {
//TODO: Check this! datarate must be same as minimum! Can be compared directly if OK
return val_in_range(datarate,
phy_params.min_rx_datarate,
phy_params.max_rx_datarate);
@ -731,6 +757,32 @@ bool LoRaPHY::verify_nb_join_trials(uint8_t nb_join_trials)
return true;
}
uint16_t LoRaPHY::get_adr_ack_limit() const
{
if (_server_adr_ack_limit != 0) {
return _server_adr_ack_limit;
}
return phy_params.adr_ack_limit;
}
void LoRaPHY::set_adr_ack_limit(const uint16_t& value)
{
_server_adr_ack_limit = value;
}
uint16_t LoRaPHY::get_adr_ack_delay() const
{
if (_server_adr_ack_delay != 0) {
return _server_adr_ack_delay;
}
return phy_params.adr_ack_delay;
}
void LoRaPHY::set_adr_ack_delay(const uint16_t& value)
{
_server_adr_ack_delay = value;
}
void LoRaPHY::apply_cf_list(const uint8_t *payload, uint8_t size)
{
// if the underlying PHY doesn't support CF-List, ignore the request
@ -791,14 +843,14 @@ bool LoRaPHY::get_next_ADR(bool restore_channel_mask, int8_t &dr_out,
{
bool set_adr_ack_bit = false;
uint16_t ack_limit_plus_delay = phy_params.adr_ack_limit + phy_params.adr_ack_delay;
uint16_t ack_limit_plus_delay = get_adr_ack_limit() + get_adr_ack_delay();
if (dr_out == phy_params.min_tx_datarate) {
adr_ack_cnt = 0;
return set_adr_ack_bit;
}
if (adr_ack_cnt < phy_params.adr_ack_limit) {
if (adr_ack_cnt < get_adr_ack_limit()) {
return set_adr_ack_bit;
}
@ -807,7 +859,7 @@ bool LoRaPHY::get_next_ADR(bool restore_channel_mask, int8_t &dr_out,
tx_power_out = phy_params.max_tx_power;
if (adr_ack_cnt >= ack_limit_plus_delay) {
if ((adr_ack_cnt % phy_params.adr_ack_delay) == 1) {
if ((adr_ack_cnt % get_adr_ack_delay()) == 1) {
// Decrease the datarate
dr_out = get_next_lower_tx_datarate(dr_out);

View File

@ -526,6 +526,22 @@ public:
*/
uint32_t get_rx_time_on_air(uint8_t modem, uint16_t pkt_len);
/**
* @brief time_received Function which is called in LW1.1 when time is received from network
* @param secs Seconds since GPS Epoch
* @param milliseconds Number of milliseconds
*/
void time_received(uint32_t secs, uint32_t milliseconds);
/**
* @brief update_rejoin_params Update Rejoin parameters
* @param max_time Maximum time in seconds between Rejoin requests
* @param max_count Maximum amount of messages allowed to be sent between Rejoin requests
* @return 1 If device supports a time variable, 0 otherwise.
* Currently we always support time!
*/
uint8_t update_rejoin_params(uint32_t max_time, uint32_t max_count);
public: //Verifiers
/**
@ -564,6 +580,30 @@ public: //Verifiers
*/
bool verify_nb_join_trials(uint8_t nb_join_trials);
/**
* @brief get_adr_ack_limit Gets the ADR ACK limit currently in use.
* @return ADR ACK limit used
*/
uint16_t get_adr_ack_limit() const;
/**
* @brief set_adr_ack_limit Sets ADR ACK limit to be used.
* @param value New value for ack limit
*/
void set_adr_ack_limit(const uint16_t& value);
/**
* @brief get_adr_ack_delay Gets the ADR ACK delay currently in use.
* @return ADR ACK delay used
*/
uint16_t get_adr_ack_delay() const;
/**
* @brief set_adr_ack_delay Sets ADR ACK delay to be used.
* @param value New value for ack delay
*/
void set_adr_ack_delay(const uint16_t& value);
protected:
LoRaPHY();
@ -678,6 +718,13 @@ protected:
LoRaRadio *_radio;
LoRaWANTimeHandler *_lora_time;
loraphy_params_t phy_params;
private:
uint16_t _server_adr_ack_limit;
uint16_t _server_adr_ack_delay;
uint32_t _rejoin_max_time;
uint32_t _rejoin_max_count;
};
#endif /* MBED_OS_LORAPHY_BASE_ */

View File

@ -51,6 +51,9 @@
#define MSG_MULTICAST_FLAG 0x04
#define MSG_PROPRIETARY_FLAG 0x08
#define LORAWAN_VERSION_1_0_2 0
#define LORAWAN_VERSION_1_1 10
/**
* LoRaWAN device classes definition.
*
@ -62,19 +65,19 @@ typedef enum {
*
* LoRaWAN Specification V1.0.2, chapter 3.
*/
CLASS_A,
CLASS_A = 0x00,
/**
* LoRaWAN device class B.
*
* LoRaWAN Specification V1.0.2, chapter 8.
*/
CLASS_B,
CLASS_B = 0x01,
/**
* LoRaWAN device class C.
*
* LoRaWAN Specification V1.0.2, chapter 17.
*/
CLASS_C,
CLASS_C = 0x02,
} device_class_t;
/**
@ -105,7 +108,7 @@ typedef enum lorawan_status {
LORAWAN_STATUS_NO_CHANNEL_FOUND = -1021, /**< None of the channels is enabled at the moment*/
LORAWAN_STATUS_NO_FREE_CHANNEL_FOUND = -1022, /**< None of the enabled channels is ready for another TX (duty cycle limited)*/
LORAWAN_STATUS_METADATA_NOT_AVAILABLE = -1023, /**< Meta-data after an RX or TX is stale*/
LORAWAN_STATUS_ALREADY_CONNECTED = -1024 /**< The device has already joined a network*/
LORAWAN_STATUS_ALREADY_CONNECTED = -1024 /**< The device has already joined a network*/
} lorawan_status_t;
/** The lorawan_connect_otaa structure.
@ -122,6 +125,7 @@ typedef struct {
/** Application identifier
*
* LoRaWAN Specification V1.0.2, chapter 6.1.2
* In case of LW1.1 or greater this is same as JoinEUI
*/
uint8_t *app_eui;
/** AES-128 application key
@ -129,6 +133,12 @@ typedef struct {
* LoRaWAN Specification V1.0.2, chapter 6.2.2
*/
uint8_t *app_key;
/** AES-128 network key
*
* In case of LoRaWAN Specification V1.0.2, must be same as app_key!
* LoRaWAN specification 1.1, chapter 6.2.2
*/
uint8_t *nwk_key;
/** Join request trials
*
* Number of trials for the join request.
@ -155,6 +165,7 @@ typedef struct {
/** Network session key
*
* LoRaWAN Specification V1.0.2, chapter 6.1.3
* LoRaWAN Spec V1.1 onwards this is used as FNwkSIntKey
*/
uint8_t *nwk_skey;
/** Application session key
@ -162,6 +173,18 @@ typedef struct {
* LoRaWAN Specification V1.0.2, chapter 6.1.4
*/
uint8_t *app_skey;
/** Serving Network session integrity key
*
* LoRaWAN Specification V1.1, chapter 6.1.2.3
*/
uint8_t *snwk_sintkey;
/** Network session encryption key
*
* LoRaWAN Specification V1.1, chapter 6.1.2.4
*/
uint8_t *nwk_senckey;
} lorawan_connect_abp_t;
/** lorawan_connect_t structure
@ -222,6 +245,9 @@ typedef enum lora_events {
JOIN_FAILURE,
UPLINK_REQUIRED,
AUTOMATIC_UPLINK_ERROR,
CLASS_CHANGED, //only in Lorawan 1.1 (ch 18.1)
SERVER_ACCEPTED_CLASS_IN_USE, //only in Lorawan 1.1 (ch 18.1)
SERVER_DOES_NOT_SUPPORT_CLASS_IN_USE //only in Lorawan 1.1 (ch 18.1)
} lorawan_event_t;
/**

View File

@ -1,6 +1,10 @@
{
"name": "lora",
"config": {
"version": {
"help": "LoRaWAN Version: LW1.0.2 = 0, LW1.1 = 10",
"value": 0
},
"phy": {
"help": "LoRa PHY region: EU868, AS923, AU915, CN470, CN779, EU433, IN865, KR920, US915",
"value": "EU868"
@ -18,19 +22,31 @@
"value": "{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}"
},
"application-eui": {
"help": "Application IEEE EUI",
"help": "Application IEEE EUI. In case of LW1.1 or greater this will be used as JoinEUI",
"value": "{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}"
},
"application-key": {
"help": "AES encryption/decryption cipher application key",
"value": "{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}"
},
"network-key": {
"help": "AES encryption/decryption cipher network key, in case of LW1.0.x not used",
"value": "{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}"
},
"device-address": {
"help": "Device address on the network",
"value": "0x00000000"
},
"nwkskey": {
"help": "AES encryption/decryption cipher network session key",
"help": "AES encryption/decryption cipher network session key. In case of LW1.1 or greater is used as FNwkSIntKey",
"value": "{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}"
},
"snwksintkey": {
"help": "Serving network session integrity key. For LW1.1 or greater",
"value": "{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}"
},
"nwksenckey": {
"help": "Network session encryption key. For LW1.1 or greater",
"value": "{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}"
},
"appskey": {
@ -92,6 +108,14 @@
"fsb-mask-china": {
"help": "FSB mask for upstream [CN470 PHY] Check lorawan/FSB_Usage.txt for more details",
"value": "{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}"
},
"rejoin-default-max-time": {
"help": "LW1.1 only! Maximum time in seconds between Rejoin requests.",
"value": 3600
},
"rejoin-default-max-count": {
"help": "LW1.1 only! Maximum amount of messages which can be sent between Rejoin requests.",
"value": 1000
}
}
}

View File

@ -153,6 +153,18 @@ typedef enum {
RX_SLOT_WIN_PING_SLOT
} rx_slot_t;
typedef enum {
REJOIN_REQUEST_TYPE0 = 0,
REJOIN_REQUEST_TYPE1 = 1,
REJOIN_REQUEST_TYPE2 = 2,
JOIN_REQUEST = 0xFF
} join_req_type_t;
typedef enum {
LW1_0_2,
LW1_1
} server_type_t;
/*!
* The global MAC layer parameters.
*/
@ -288,9 +300,9 @@ typedef enum {
*/
FRAME_TYPE_DATA_CONFIRMED_DOWN = 0x05,
/*!
* LoRaMAC RFU frame.
* LoRaMAC Rejoin request frame.
*/
FRAME_TYPE_RFU = 0x06,
FRAME_TYPE_REJOIN_REQUEST = 0x06,
/*!
* LoRaMAC proprietary frame.
*/
@ -303,6 +315,10 @@ typedef enum {
* LoRaWAN Specification V1.0.2, chapter 5, table 4.
*/
typedef enum {
/*!
* ResetInd
*/
MOTE_MAC_RESET_IND = 0x01,
/*!
* LinkCheckReq
*/
@ -338,7 +354,27 @@ typedef enum {
/*!
* DlChannelAns
*/
MOTE_MAC_DL_CHANNEL_ANS = 0x0A
MOTE_MAC_DL_CHANNEL_ANS = 0x0A,
/*!
* RekeyInd
*/
MOTE_MAC_REKEY_IND = 0x0B,
/*!
* ADRParamSetupAns
*/
MOTE_MAC_ADR_PARAM_SETUP_ANS = 0x0C,
/*!
* DeviceTimeReq
*/
MOTE_MAC_DEVICE_TIME_REQ = 0x0D,
/*!
* RejoinParamSetupAns
*/
MOTE_MAC_REJOIN_PARAM_SETUP_ANS = 0x0F,
/*!
* DeviceModeInd
*/
MOTE_DEVICE_MODE_IND = 0x20
} mote_mac_cmds_t;
/*!
@ -347,6 +383,10 @@ typedef enum {
* LoRaWAN Specification V1.0.2 chapter 5, table 4.
*/
typedef enum {
/*!
* ResetConf
*/
SRV_MAC_RESET_CONF = 0x01,
/*!
* LinkCheckAns
*/
@ -383,6 +423,30 @@ typedef enum {
* DlChannelReq
*/
SRV_MAC_DL_CHANNEL_REQ = 0x0A,
/*!
* RekeyConf
*/
SRV_MAC_REKEY_CONF = 0x0B,
/*!
* ADRParamSetupReq
*/
SRV_MAC_ADR_PARAM_SETUP_REQ = 0x0C,
/*!
* DeviceTimeAns
*/
SRV_MAC_DEVICE_TIME_ANS = 0x0D,
/*!
* ForceRejoinReq
*/
SRV_MAC_FORCE_REJOIN_REQ = 0x0E,
/*!
* RejoinParamSetupReq
*/
SRV_MAC_REJOIN_PARAM_SETUP_REQ = 0x0F,
/*!
* DeviceModeConf
*/
SRV_MAC_DEVICE_MODE_CONF = 0x20
} server_mac_cmds_t;
/*!
@ -699,9 +763,7 @@ typedef struct {
*
* Name | Request | Indication | Response | Confirm
* ---------------------------- | :-----: | :--------: | :------: | :-----:
* \ref MLME_JOIN | YES | NO | NO | YES
* \ref MLME_LINK_CHECK | YES | NO | NO | YES
* \ref MLME_TXCW | YES | NO | NO | YES
* \ref MLME_SCHEDULE_UPLINK | NO | YES | NO | NO
*
*/
@ -711,30 +773,48 @@ typedef enum {
*
* LoRaWAN Specification V1.0.2, chapter 6.2.
*/
MLME_JOIN,
MLME_JOIN_ACCEPT,
/*!
* Initiates Rejoin request
*
* LoRaWAN specification V1.1, chapter 6.2.4
*/
MLME_REJOIN,
/*!
* LinkCheckReq - Connectivity validation.
*
* LoRaWAN Specification V1.0.2, chapter 5, table 4.
*/
MLME_LINK_CHECK,
/*!
* Sets TX continuous wave mode.
*
* LoRaWAN end-device certification.
*/
MLME_TXCW,
/*!
* Sets TX continuous wave mode (new LoRa-Alliance CC definition).
*
* LoRaWAN end-device certification.
*/
MLME_TXCW_1,
/*!
* Indicates that the application shall perform an uplink as
* soon as possible.
*/
MLME_SCHEDULE_UPLINK
MLME_SCHEDULE_UPLINK,
/*!
* Indicates that ABP device has resetted
* LoRaWAN specification V1.1, chapter 5.1
*/
MLME_RESET,
/*!
* Indicates that OTAA device has updated keys
* LoRaWAN specification V1.1, chapter 5.10
*/
MLME_REKEY,
/*!
* Indicates that device has changed LoRa class
* LoRaWAN specification V1.1, chapter 18.1
*/
MLME_DEVICE_MODE,
/*!
* Indicates that device has received force rejoin MAC command
* LoRaWAN specification V1.1, chapter 5.13
*/
MLME_FORCE_REJOIN
} mlme_type_t;
/*!
@ -783,28 +863,19 @@ typedef struct {
uint8_t power;
} mlme_cw_tx_mode_t;
/*!
* LoRaMAC MLME-Confirm primitive.
*/
typedef struct {
/*!
* Indicates if a request is pending or not
*/
bool pending;
/*!
* The previously performed MLME-Request. i.e., the request type
* for which the confirmation is being generated
*/
mlme_type_t req_type;
mlme_type_t type;
/*!
* The status of the operation.
*/
loramac_event_info_status_t status;
/*!
* The transmission time on air of the frame.
*/
lorawan_time_t tx_toa;
/*!
* The demodulation margin. Contains the link margin [dB] of the last LinkCheckReq
* successfully received.
@ -815,9 +886,31 @@ typedef struct {
*/
uint8_t nb_gateways;
/*!
* The number of retransmissions.
* Number of retries done for RejoinRequest
*/
uint8_t nb_retries;
uint8_t max_retries;
/*!
* Period between RejoinRequest (2^period + rand(0, 32))
*/
uint8_t period;
/*!
* A datarate used to send RejoinRequest
*/
uint8_t datarate;
/*!
* Value 0 or 1 means RejoinRequest type 0 shall be transmitted,
* Value 2 means RejoinRequest type 2 shall be transmitted,
* Other values are RFU and shall not be processed.
*/
uint8_t rejoin_type;
/*!
* Class type from device mode conf
*/
uint8_t classType;
/*!
* LoRaWAN version
*/
uint8_t version;
} loramac_mlme_confirm_t;
/*!
@ -1067,6 +1160,8 @@ typedef struct {
/*!
* Application IEEE EUI
*
* In case of LW1.1 or greater this is same as JoinEUI
*/
uint8_t *app_eui;
@ -1075,9 +1170,15 @@ typedef struct {
*/
uint8_t *app_key;
/*!
* AES encryption/decryption cipher network key
*/
uint8_t *nwk_key;
/*!
* AES encryption/decryption cipher network session key
* NOTE! LoRaMac determines the length of the key based on sizeof this variable
* From LW1.1 onwards, this is used as FNwkSIntKey.
*/
uint8_t nwk_skey[16];
@ -1087,6 +1188,30 @@ typedef struct {
*/
uint8_t app_skey[16];
/*!
* AES encryption/decryption cipher Serving network session integrity key
* NOTE! LoRaMac determines the length of the key based on sizeof this variable
*/
uint8_t snwk_sintkey[16];
/*!
* AES encryption/decryption cipher network session encryption key
* NOTE! LoRaMac determines the length of the key based on sizeof this variable
*/
uint8_t nwk_senckey[16];
/*!
* AES encryption/decryption cipher Join server integrity key
* NOTE! LoRaMac determines the length of the key based on sizeof this variable
*/
uint8_t js_intkey[16];
/*!
* AES encryption/decryption cipher Join server encryption key
* NOTE! LoRaMac determines the length of the key based on sizeof this variable
*/
uint8_t js_enckey[16];
} loramac_keys;
/*!
@ -1252,6 +1377,21 @@ typedef struct {
*/
uint16_t dev_nonce;
/*!
* counterForAck is needed for LoRaWAN 1.1 spec onwards
*/
uint16_t counterForAck;
/*!
* Rejoin type 0 or 2 specific counter
*/
uint16_t RJcount0;
/*!
* Rejoin type 1 specific counter
*/
uint16_t RJcount1;
/*!
* Network ID ( 3 bytes )
*/
@ -1269,11 +1409,17 @@ typedef struct {
uint32_t ul_frame_counter;
/*!
* LoRaMAC frame counter. Each time a packet is received the counter is incremented.
* LoRaMAC NS frame counter. Each time a packet is received for port 0 the counter is incremented.
* Only the 16 LSB bits are received
*/
uint32_t dl_frame_counter;
/*!
* Application Server DL frame_counter. Each time a packet is received to port > 0 this counter is incremented.
* Only the 16 LSB bits are received
*/
uint32_t app_dl_frame_counter;
/*!
* Counts the number of missed ADR acknowledgements
*/
@ -1303,6 +1449,10 @@ typedef struct {
rx_config_params_t rx_window1_config;
rx_config_params_t rx_window2_config;
join_req_type_t join_request_type;
server_type_t server_type;
/*!
* Multicast channels linked list
*/