-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gnrc/lorawan: add basic LoRaWAN 1.1 features #17884
Conversation
@@ -76,14 +76,34 @@ static void _sleep_radio(gnrc_lorawan_t *mac) | |||
dev->driver->set(dev, NETOPT_STATE, &state, sizeof(state)); | |||
} | |||
|
|||
void gnrc_lorawan_init(gnrc_lorawan_t *mac, uint8_t *nwkskey, uint8_t *appskey) | |||
#if IS_ACTIVE(CONFIG_LORAWAN_1_1) | |||
void gnrc_lorawan_init_11x(gnrc_lorawan_t *mac, uint8_t *joineui, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think at this point it makes sense to use a common gnrc_lorawan_init
function but with a "ctx" parameter. E.g
void gnrc_lorawan_init(gnrc_lorawan_t *mac, const gnrc_lorawan_ctx_t *ctx)
{
mac->nwkskey = ctx->nwskey;
....
}
Since this function would be resolved at compile time (e.g user cannot switch between 1.0.3 and 1.1 on runtime), I think it will simplify things a bit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a kind of hard cut between gnrc_netif_lorawan
and gnrc_lorawan
? Otherwise I could just pass in a pointer to gnrc_netif_lorawan_t
without needing to declare a new ctx struct ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, gnrc_netif_lorawan is the "GNRC Network Interface Implementation" for GNRC LoRaWAN. It could be possible to run GNRC LoRaWAN directly from the API without a network interface. That's why these two structures are decoupled.
If you make it const, you can e.g store that information on ROM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay I see :) I added the gnrc_lorawan_ctx_t
struct.
Another question regarding gnrc netif
: Right now to configure the NwkSKey for Lorawan 1.0x one has to set CONFIG_LORAMAC_FNWKSINT_KEY_DEFAULT
. As I think this is kind of confusing for someone wanting to configure a 1.0x node I think it would be better to use the old 1.0x terms in the netif layer and only switch to the new terms internally ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm good question... I usually tend to think that's better to reflect the actual key names depending on the version. For example, a user that explicitly wants to use LoRaWAN 1.1 will probably know the key names. Most LoRaWAN users will still use LoRaWAN 1.0.x, so it's probably to keep the original names there. You can always fallback to macro aliases that use the terms internally.
b7741c5
to
74caa2c
Compare
void gnrc_lorawan_calculate_mic(const le_uint32_t *dev_addr, uint32_t fcnt, | ||
uint8_t dir, iolist_t *frame, | ||
const uint8_t *nwkskey, le_uint32_t *out) | ||
#if IS_USED(CONFIG_LORAWAN_1_1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. You can merge these two functions and use a common signature. If you have different parameters, consider a structure.
You can also kind of merge the logic here: gnrc_lorawan_calculate_mic_uplink_11x
is (almost) a subset of gnrc_lorawan_calculate_mic_uplink_10x
.
Also, you can use a regular if
with IS_ACTIVE
or IS_USED
in some parts. The compiler will remove branches that are never reached. Of course, you cannot do that with struct members that don't exist on compile time (those who appear only with LoRaWAN 1.1).
For instance, you could write these functions as:
void gnrc_lorawan_calculate_mic_uplink(const le_uint32_t *dev_addr, uint32_t fcnt,
... lorawan_mic_t *ctx, ...
le_uint32_t *out)
{
orawan_block0_t block0 = { 0 };
block0.fb = MIC_B0_START;
block0.dir = 0x00;
memcpy(&block0.dev_addr, dev_addr, sizeof(le_uint32_t));
block0.fcnt = byteorder_htoll(fcnt);
block0.len = iolist_size(frame);
/* cmacF = aes128_cmac(FNwkSIntKey, B0 | msg) */
cmac_init(&CmacContext, fnwksintkey, LORAMAC_FNWKSINTKEY_LEN);
cmac_update(&CmacContext, &block0, sizeof(block0));
for (iolist_t *io = frame; io != NULL; io = io->iol_next) {
cmac_update(&CmacContext, io->iol_base, io->iol_len);
}
cmac_final(&CmacContext, digest);
if (IS_ACTIVE(GNRC_LORAWAN_1_1)) {
/* Do 1.1 specific stuff an get specific keys from `ctx`*/
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When exactly can I use if
with this macro ? I tried to e.g. use here and it didn't work.
I find it kinda weird to depend on compiler optimizations to make my code compile ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it probably failed because _appkey
exists only when you set LoRaWAN 1.1. Since the "topology" of the file is the same, the variables should always exist.
You can always use if
with this macro, since always resolves to 0 or 1.
For example:
/* foo */
if (IS_ACTIVE(MY_MODULE)) {
/* bar */
}
In this example the snippets "foo" and "bar" are compiled like regular C code. But in case "MY_MODULE" is not used, the compiler will optimize out "bar". The compiler will still find syntax errors in "bar", since the compiler parses the snippet anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it kinda weird to depend on compiler optimizations to make my code compile ...
What do you mean with that? You don't depend on compiler optimizations to compile your code (if you turn off compiler optimizations it will still compile without issues). Could you elaborate more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it probably failed because
_appkey
exists only when you set LoRaWAN 1.1. Since the "topology" of the file is the same, the variables should always exist.You can always use
if
with this macro, since always resolves to 0 or 1.For example:
/* foo */ if (IS_ACTIVE(MY_MODULE)) { /* bar */ }In this example the snippets "foo" and "bar" are compiled like regular C code. But in case "MY_MODULE" is not used, the compiler will optimize out "bar". The compiler will still find syntax errors in "bar", since the compiler parses the snippet anyway.
Having _appkey
exist all the time fixed the issue. Thank you !
I misunderstood how using if (IS_USED(...)
works. Your example and explanation cleared things up !
@@ -106,6 +107,8 @@ uint32_t gnrc_lorawan_pick_channel(gnrc_lorawan_t *mac) | |||
for (int i = 0; i < pos + 1; i++) { | |||
state = bitarithm_test_and_clear(state, &index); | |||
} | |||
|
|||
mac->last_chan_idx = index; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since this function only picks a random channel, I would be in favor of maintaining "mac->last_chan_idx" outside of this function. It makes it easier to maintain as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am now managing it inside gnrc_lorawan_mcps_request
by just looping through the channels and looking for the correct one. With this change I had removed the call to gnrc_lorawan_pick_channel
from gnrc_lorawan_event_retrans_timeout
which I had added because otherwise the last_chan_idx
wont be correct.
I am wondering if this is bad since gnrc_lorawan_event_retrans_timeout
might wan't to use a new channel ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if this is bad since gnrc_lorawan_event_retrans_timeout might wan't to use a new channel ?
Hmmmm yes, the retransmssion should follow the same rules of any transmission. So it should choose a different channel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to this I now moved the channel picking + the calculation of the uplink mic into the _transmit_pkt
function. This is the cleanest solution I could find.
static uint8_t _appskey[LORAMAC_APPSKEY_LEN]; | ||
#if IS_USED(CONFIG_LORAWAN_1_1) | ||
static uint8_t _appkey[LORAMAC_APPKEY_LEN]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is _appkey
not required in LoRaWAN 1.0.3?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is but its name has changed. The 1.1 spec uses the NwkKey in place of the AppKey.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I see. But maybe you could try to use the same variable names internally. Otherwise you cannot use regular if (IS_ACTIVE())
stuff
Overall the code looks nice and the changes make sense. I left some comments there and plan to run some tests when they are addressed. |
74caa2c
to
9e85b97
Compare
@jia200x I implement the Rekey MAC command and successfully tested it with TTN. Please test again if you can :) |
Hi. It works now! |
Should be fixed as well now ! |
I can confirm this still works as expected when compiling LoRaWAN 1.0.3. I will proceed with all tests for LoRaWAN 1.1 |
ok, everything seems to work now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested ACK. Please squash commits to make them more semantic (e.g "add support for GNRC LoRaWAN 1.1", "Adapt test". etc...)
a54092e
to
cd400ec
Compare
@jia200x Tests for some boards fail because they don't implement they flashpage API which I need for LoRaWAN 1.1. |
cd400ec
to
cf4fbac
Compare
cf4fbac
to
5ef2705
Compare
@jia200x Can you explain to me the doc and Murdock failures ? Neither seem to be related to my code ... |
There seemed to be an unrelated error. I retriggered the CI. Let's see what happens |
Looks like the same issue :( |
I just realized that the Murdock problem was because you added NETOPTs without updating |
5ef2705
to
32cef70
Compare
Thanks for this ! I should have checked the error message more closely... All green now ! |
finally! congrats @Ollrogge 🎉 🎉 🎉 |
Yaaayy :) Thank you for all the help !! |
Contribution description
This PR adds basic support for LoRaWAN version 1.1. This includes joining a network and sending / receiving data.
This PR also modifies the LoRaWAN 1.0 code to use the 1.1 terms. This should not change the behavior of the 1.0 code and everything should still work.
Not implemented are ReJoin-Requests and MAC commands such as RekeyInd and RekeyConf.
For testing purposes I setup a private LoRaWAN network using the Chirpstack and was able to join the network and send data.
Testing procedure
tests/gnrc_lorawan_11
examples/gnrc_lorawan
if able to setup a private LoRaWAN network