1 /*
2 * Copyright (c) 2016 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 * @brief IEEE 802.15.4 Net Management Implementation
10 *
11 * All references to the spec refer to IEEE 802.15.4-2020.
12 */
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(net_ieee802154_mgmt, CONFIG_NET_L2_IEEE802154_LOG_LEVEL);
16
17 #include <zephyr/net/net_core.h>
18
19 #include <errno.h>
20
21 #include <zephyr/net/net_if.h>
22 #include <zephyr/net/ieee802154_radio.h>
23 #include <zephyr/net/ieee802154_mgmt.h>
24 #include <zephyr/net/ieee802154.h>
25
26 #include "ieee802154_frame.h"
27 #include "ieee802154_mgmt_priv.h"
28 #include "ieee802154_priv.h"
29 #include "ieee802154_security.h"
30 #include "ieee802154_utils.h"
31
32 /**
33 * Implements (part of) the MLME-BEACON.notify primitive, see section 8.2.5.2.
34 */
ieee802154_handle_beacon(struct net_if * iface,struct ieee802154_mpdu * mpdu,uint8_t lqi)35 enum net_verdict ieee802154_handle_beacon(struct net_if *iface,
36 struct ieee802154_mpdu *mpdu,
37 uint8_t lqi)
38 {
39 struct ieee802154_context *ctx = net_if_l2_data(iface);
40 int beacon_hdr_len;
41
42 NET_DBG("Beacon received");
43
44 if (!ctx->scan_ctx) {
45 return NET_DROP;
46 }
47
48 ctx->scan_ctx->association_permitted = mpdu->beacon->sf.association;
49
50 k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
51
52 ctx->scan_ctx->pan_id = mpdu->mhr.src_addr->plain.pan_id;
53 ctx->scan_ctx->lqi = lqi;
54
55 if (mpdu->mhr.fs->fc.src_addr_mode == IEEE802154_ADDR_MODE_SHORT) {
56 ctx->scan_ctx->len = IEEE802154_SHORT_ADDR_LENGTH;
57 ctx->scan_ctx->short_addr =
58 sys_le16_to_cpu(mpdu->mhr.src_addr->plain.addr.short_addr);
59 } else {
60 ctx->scan_ctx->len = IEEE802154_EXT_ADDR_LENGTH;
61 sys_memcpy_swap(ctx->scan_ctx->addr,
62 mpdu->mhr.src_addr->plain.addr.ext_addr,
63 IEEE802154_EXT_ADDR_LENGTH);
64 }
65
66 beacon_hdr_len = ieee802514_beacon_header_length(mpdu->payload, mpdu->payload_length);
67 ctx->scan_ctx->beacon_payload_len = mpdu->payload_length - beacon_hdr_len;
68 ctx->scan_ctx->beacon_payload = (uint8_t *)mpdu->payload + beacon_hdr_len;
69
70 net_mgmt_event_notify(NET_EVENT_IEEE802154_SCAN_RESULT, iface);
71
72 k_sem_give(&ctx->scan_ctx_lock);
73
74 return NET_CONTINUE;
75 }
76
ieee802154_cancel_scan(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)77 static int ieee802154_cancel_scan(uint32_t mgmt_request, struct net_if *iface,
78 void *data, size_t len)
79 {
80 struct ieee802154_context *ctx = net_if_l2_data(iface);
81
82 ARG_UNUSED(data);
83 ARG_UNUSED(len);
84
85 NET_DBG("Cancelling scan request");
86
87 k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
88 ctx->scan_ctx = NULL;
89 k_sem_give(&ctx->scan_ctx_lock);
90
91 return 0;
92 }
93
94 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_CANCEL_SCAN,
95 ieee802154_cancel_scan);
96
ieee802154_scan(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)97 static int ieee802154_scan(uint32_t mgmt_request, struct net_if *iface,
98 void *data, size_t len)
99 {
100 const struct ieee802154_phy_supported_channels *supported_channels;
101 struct ieee802154_context *ctx = net_if_l2_data(iface);
102 struct ieee802154_attr_value attr_value;
103 struct ieee802154_req_params *scan;
104 struct net_pkt *pkt = NULL;
105 int ret;
106
107 if (len != sizeof(struct ieee802154_req_params) || !data) {
108 return -EINVAL;
109 }
110
111 scan = (struct ieee802154_req_params *)data;
112
113 NET_DBG("%s scan requested",
114 mgmt_request == NET_REQUEST_IEEE802154_ACTIVE_SCAN ?
115 "Active" : "Passive");
116
117 k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
118
119 if (ctx->scan_ctx) {
120 ret = -EALREADY;
121 goto out;
122 }
123
124 if (mgmt_request == NET_REQUEST_IEEE802154_ACTIVE_SCAN) {
125 struct ieee802154_frame_params params = {0};
126
127 params.dst.len = IEEE802154_SHORT_ADDR_LENGTH;
128 params.dst.short_addr = IEEE802154_BROADCAST_ADDRESS;
129 params.dst.pan_id = IEEE802154_BROADCAST_PAN_ID;
130
131 pkt = ieee802154_create_mac_cmd_frame(
132 iface, IEEE802154_CFI_BEACON_REQUEST, ¶ms);
133 if (!pkt) {
134 k_sem_give(&ctx->scan_ctx_lock);
135 NET_ERR("Could not create Beacon Request");
136 ret = -ENOBUFS;
137 goto out;
138 }
139
140 ieee802154_mac_cmd_finalize(pkt, IEEE802154_CFI_BEACON_REQUEST);
141 }
142
143 ctx->scan_ctx = scan;
144 k_sem_give(&ctx->scan_ctx_lock);
145
146 ret = 0;
147
148 k_sem_take(&ctx->ctx_lock, K_FOREVER);
149 ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
150 k_sem_give(&ctx->ctx_lock);
151 ieee802154_radio_filter_pan_id(iface, IEEE802154_BROADCAST_PAN_ID);
152
153 if (ieee802154_radio_start(iface)) {
154 NET_ERR("Scan request failed: could not start device");
155 ret = -EIO;
156 goto out;
157 }
158
159 if (ieee802154_radio_attr_get(iface, IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_RANGES,
160 &attr_value)) {
161 NET_ERR("Scan request failed: could not determine supported channels");
162 ret = -ENOENT;
163 goto out;
164 }
165 supported_channels = attr_value.phy_supported_channels;
166
167 for (int channel_range = 0; channel_range < supported_channels->num_ranges;
168 channel_range++) {
169 for (uint16_t channel = supported_channels->ranges[channel_range].from_channel;
170 channel <= supported_channels->ranges[channel_range].to_channel; channel++) {
171 if (IEEE802154_IS_CHAN_UNSCANNED(scan->channel_set, channel)) {
172 continue;
173 }
174
175 scan->channel = channel;
176 NET_DBG("Scanning channel %u", channel);
177 ieee802154_radio_set_channel(iface, channel);
178
179 /* Active scan sends a beacon request */
180 if (mgmt_request == NET_REQUEST_IEEE802154_ACTIVE_SCAN) {
181 net_pkt_ref(pkt);
182 net_pkt_frag_ref(pkt->buffer);
183
184 ret = ieee802154_radio_send(iface, pkt, pkt->buffer);
185 if (ret) {
186 NET_ERR("Scan request failed: could not send Beacon "
187 "Request (%d)",
188 ret);
189 net_pkt_unref(pkt);
190 goto out;
191 }
192 }
193
194 /* Context aware sleep */
195 k_sleep(K_MSEC(scan->duration));
196
197 k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
198
199 if (!ctx->scan_ctx) {
200 NET_DBG("Scan request cancelled");
201 ret = -ECANCELED;
202 goto out;
203 }
204
205 k_sem_give(&ctx->scan_ctx_lock);
206 }
207 }
208
209 out:
210 /* Let's come back to context's settings. */
211 ieee802154_radio_remove_pan_id(iface, IEEE802154_BROADCAST_PAN_ID);
212 k_sem_take(&ctx->ctx_lock, K_FOREVER);
213 ieee802154_radio_filter_pan_id(iface, ctx->pan_id);
214 ieee802154_radio_set_channel(iface, ctx->channel);
215 k_sem_give(&ctx->ctx_lock);
216
217 k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
218 if (ctx->scan_ctx) {
219 ctx->scan_ctx = NULL;
220 }
221 k_sem_give(&ctx->scan_ctx_lock);
222
223 if (pkt) {
224 net_pkt_unref(pkt);
225 }
226
227 return ret;
228 }
229
230 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_PASSIVE_SCAN,
231 ieee802154_scan);
232 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_ACTIVE_SCAN,
233 ieee802154_scan);
234
235 /* Requires the context lock to be held. */
update_net_if_link_addr(struct net_if * iface,struct ieee802154_context * ctx)236 static inline void update_net_if_link_addr(struct net_if *iface, struct ieee802154_context *ctx)
237 {
238 bool was_if_up;
239
240 was_if_up = net_if_flag_test_and_clear(iface, NET_IF_RUNNING);
241 net_if_set_link_addr(iface, ctx->linkaddr.addr, ctx->linkaddr.len, ctx->linkaddr.type);
242
243 if (was_if_up) {
244 net_if_flag_set(iface, NET_IF_RUNNING);
245 }
246 }
247
248 /* Requires the context lock to be held. */
set_linkaddr_to_ext_addr(struct net_if * iface,struct ieee802154_context * ctx)249 static inline void set_linkaddr_to_ext_addr(struct net_if *iface, struct ieee802154_context *ctx)
250 {
251 ctx->linkaddr.len = IEEE802154_EXT_ADDR_LENGTH;
252 sys_memcpy_swap(ctx->linkaddr.addr, ctx->ext_addr, IEEE802154_EXT_ADDR_LENGTH);
253
254 update_net_if_link_addr(iface, ctx);
255 }
256
257 /* Requires the context lock to be held and the PAN ID to be set. */
set_association(struct net_if * iface,struct ieee802154_context * ctx,uint16_t short_addr)258 static inline void set_association(struct net_if *iface, struct ieee802154_context *ctx,
259 uint16_t short_addr)
260 {
261 uint16_t short_addr_be;
262
263 __ASSERT_NO_MSG(ctx->pan_id != IEEE802154_PAN_ID_NOT_ASSOCIATED);
264 __ASSERT_NO_MSG(short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED);
265
266 ieee802154_radio_remove_src_short_addr(iface, ctx->short_addr);
267
268 ctx->short_addr = short_addr;
269
270 if (short_addr == IEEE802154_NO_SHORT_ADDRESS_ASSIGNED) {
271 set_linkaddr_to_ext_addr(iface, ctx);
272 } else {
273 ctx->linkaddr.len = IEEE802154_SHORT_ADDR_LENGTH;
274 short_addr_be = htons(short_addr);
275 memcpy(ctx->linkaddr.addr, &short_addr_be, IEEE802154_SHORT_ADDR_LENGTH);
276 update_net_if_link_addr(iface, ctx);
277 ieee802154_radio_filter_short_addr(iface, ctx->short_addr);
278 }
279 }
280
281 /* Requires the context lock to be held. */
remove_association(struct net_if * iface,struct ieee802154_context * ctx)282 static inline void remove_association(struct net_if *iface, struct ieee802154_context *ctx)
283 {
284 /* An associated device shall disassociate itself by removing all
285 * references to the PAN; the MLME shall set macPanId, macShortAddress,
286 * macAssociatedPanCoord [TODO: implement], macCoordShortAddress, and
287 * macCoordExtendedAddress to the default values, see section 6.4.2.
288 */
289
290 ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
291 ieee802154_radio_remove_src_short_addr(iface, ctx->short_addr);
292
293 ctx->pan_id = IEEE802154_PAN_ID_NOT_ASSOCIATED;
294 ctx->short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
295
296 memset(ctx->coord_ext_addr, 0, sizeof(ctx->coord_ext_addr));
297 ctx->coord_short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
298
299 set_linkaddr_to_ext_addr(iface, ctx);
300
301 ieee802154_radio_filter_pan_id(iface, IEEE802154_BROADCAST_PAN_ID);
302 ieee802154_radio_filter_short_addr(iface, IEEE802154_BROADCAST_ADDRESS);
303 }
304
305 /* Requires the context lock to be held. */
is_associated(struct ieee802154_context * ctx)306 static inline bool is_associated(struct ieee802154_context *ctx)
307 {
308 /* see section 8.4.3.1, table 8-94, macPanId and macShortAddress */
309 return ctx->pan_id != IEEE802154_PAN_ID_NOT_ASSOCIATED &&
310 ctx->short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
311 }
312
ieee802154_handle_mac_command(struct net_if * iface,struct ieee802154_mpdu * mpdu)313 enum net_verdict ieee802154_handle_mac_command(struct net_if *iface,
314 struct ieee802154_mpdu *mpdu)
315 {
316 struct ieee802154_context *ctx = net_if_l2_data(iface);
317
318 if (mpdu->command->cfi == IEEE802154_CFI_ASSOCIATION_RESPONSE) {
319 if (mpdu->command->assoc_res.status !=
320 IEEE802154_ASF_SUCCESSFUL) {
321 return NET_DROP;
322 }
323
324 /* Validation of the association response, see section 7.5.3:
325 * * The Destination Addressing Mode and Source Addressing Mode
326 * fields shall each be set to indicate extended addressing.
327 * * The Frame Pending field shall be set to zero and ignored
328 * upon reception, and the AR field shall be set to one.
329 * * The Destination PAN ID field shall contain the value of
330 * macPanId, while the Source PAN ID field shall be omitted.
331 * * The Destination Address field shall contain the extended
332 * address of the device requesting association (has been
333 * tested during generic filtering already).
334 *
335 * Note: Unless the packet is authenticated, it cannot be verified
336 * that the response comes from the requested coordinator.
337 */
338 if (mpdu->mhr.fs->fc.src_addr_mode !=
339 IEEE802154_ADDR_MODE_EXTENDED ||
340 mpdu->mhr.fs->fc.dst_addr_mode !=
341 IEEE802154_ADDR_MODE_EXTENDED ||
342 mpdu->mhr.fs->fc.ar != 1 ||
343 mpdu->mhr.fs->fc.pan_id_comp != 1 ||
344 mpdu->mhr.dst_addr->plain.pan_id != sys_cpu_to_le16(ctx->pan_id) ||
345 mpdu->command->assoc_res.short_addr == IEEE802154_PAN_ID_NOT_ASSOCIATED) {
346 return NET_DROP;
347 }
348
349 k_sem_take(&ctx->ctx_lock, K_FOREVER);
350
351 if (is_associated(ctx)) {
352 k_sem_give(&ctx->ctx_lock);
353 return NET_DROP;
354 }
355
356 /* If the Association Status field of the Association Response
357 * command indicates that the association was successful, the
358 * device shall store the address contained in the Short Address
359 * field of the command in macShortAddress, see section 6.4.1.
360 */
361 set_association(iface, ctx, sys_le16_to_cpu(mpdu->command->assoc_res.short_addr));
362
363 /* If the [association request] contained the short address of
364 * the coordinator, the extended address of the coordinator,
365 * contained in the MHR of the Association Response command,
366 * shall be stored in macCoordExtendedAddress, see section 6.4.1.
367 */
368 memcpy(ctx->coord_ext_addr, mpdu->mhr.src_addr->comp.addr.ext_addr,
369 IEEE802154_EXT_ADDR_LENGTH);
370
371 k_sem_give(&ctx->ctx_lock);
372
373 k_sem_give(&ctx->scan_ctx_lock);
374
375 return NET_CONTINUE;
376 }
377
378 if (mpdu->command->cfi == IEEE802154_CFI_DISASSOCIATION_NOTIFICATION) {
379 enum net_verdict ret = NET_DROP;
380
381 if (mpdu->command->disassoc_note.reason !=
382 IEEE802154_DRF_COORDINATOR_WISH) {
383 return NET_DROP;
384 }
385
386 k_sem_take(&ctx->ctx_lock, K_FOREVER);
387
388 if (!is_associated(ctx)) {
389 goto out;
390 }
391
392 /* Validation of the disassociation notification, see section 7.5.4:
393 * * The Source Addressing Mode field shall be set to indicate
394 * extended addressing.
395 * * The Frame Pending field shall be set to zero and ignored
396 * upon reception, and the AR field shall be set to one.
397 * * The Destination PAN ID field shall contain the value of macPanId.
398 * * The Source PAN ID field shall be omitted.
399 * * If the coordinator is disassociating a device from the
400 * PAN, then the Destination Address field shall contain the
401 * address of the device being removed from the PAN (asserted
402 * during generic package filtering).
403 *
404 * Note: Unless the packet is authenticated, it cannot be verified
405 * that the command comes from the requested coordinator.
406 */
407
408 if (mpdu->mhr.fs->fc.src_addr_mode !=
409 IEEE802154_ADDR_MODE_EXTENDED ||
410 mpdu->mhr.fs->fc.ar != 1 ||
411 mpdu->mhr.fs->fc.pan_id_comp != 1 ||
412 mpdu->mhr.dst_addr->plain.pan_id != sys_cpu_to_le16(ctx->pan_id)) {
413 goto out;
414 }
415
416 /* If the source address contained in the Disassociation
417 * Notification command is equal to macCoordExtendedAddress,
418 * the device should consider itself disassociated,
419 * see section 6.4.2.
420 */
421 if (memcmp(ctx->coord_ext_addr,
422 mpdu->mhr.src_addr->comp.addr.ext_addr,
423 IEEE802154_EXT_ADDR_LENGTH)) {
424 goto out;
425 }
426
427 remove_association(iface, ctx);
428 ret = NET_OK;
429
430 out:
431 k_sem_give(&ctx->ctx_lock);
432 return ret;
433 }
434
435 NET_WARN("Drop MAC command, unsupported CFI: 0x%x", mpdu->command->cfi);
436
437 return NET_DROP;
438 }
439
ieee802154_associate(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)440 static int ieee802154_associate(uint32_t mgmt_request, struct net_if *iface,
441 void *data, size_t len)
442 {
443 struct ieee802154_context *ctx = net_if_l2_data(iface);
444 struct ieee802154_frame_params params = {0};
445 struct ieee802154_req_params *req;
446 struct ieee802154_command *cmd;
447 struct net_pkt *pkt;
448 int ret = 0;
449
450 if (len != sizeof(struct ieee802154_req_params) || !data) {
451 NET_ERR("Could not associate: invalid request");
452 return -EINVAL;
453 }
454
455 req = (struct ieee802154_req_params *)data;
456
457 /* Validate the coordinator's PAN ID. */
458 if (req->pan_id == IEEE802154_PAN_ID_NOT_ASSOCIATED) {
459 NET_ERR("Could not associate: PAN ID is special value 'not associated'");
460 return -EINVAL;
461 }
462
463 params.dst.pan_id = req->pan_id;
464
465 /* If the Version field is set to 0b10, the Source PAN ID field is
466 * omitted. Otherwise, the Source PAN ID field shall contain the
467 * broadcast PAN ID.
468 */
469 params.pan_id = IEEE802154_BROADCAST_PAN_ID;
470
471 /* Validate the coordinator's short address - if any. */
472 if (req->len == IEEE802154_SHORT_ADDR_LENGTH) {
473 if (req->short_addr == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED ||
474 req->short_addr == IEEE802154_NO_SHORT_ADDRESS_ASSIGNED) {
475 NET_ERR("Could not associate: invalid short address ('not associated' or "
476 "'not assigned')");
477 return -EINVAL;
478 }
479
480 params.dst.short_addr = req->short_addr;
481 } else if (req->len == IEEE802154_EXT_ADDR_LENGTH) {
482 memcpy(params.dst.ext_addr, req->addr, sizeof(params.dst.ext_addr));
483 } else {
484 NET_ERR("Could not associate: invalid address type");
485 return -EINVAL;
486 }
487
488 params.dst.len = req->len;
489
490 k_sem_take(&ctx->ctx_lock, K_FOREVER);
491
492 if (is_associated(ctx)) {
493 k_sem_give(&ctx->ctx_lock);
494 NET_WARN("Could not associate: already associated");
495 return -EALREADY;
496 }
497
498 k_sem_give(&ctx->ctx_lock);
499
500 pkt = ieee802154_create_mac_cmd_frame(
501 iface, IEEE802154_CFI_ASSOCIATION_REQUEST, ¶ms);
502 if (!pkt) {
503 ret = -ENOBUFS;
504 NET_ERR("Could not associate: cannot allocate association request frame");
505 goto out;
506 }
507
508 k_sem_take(&ctx->ctx_lock, K_FOREVER);
509
510 cmd = ieee802154_get_mac_command(pkt);
511
512 cmd->assoc_req.ci.reserved_1 = 0U; /* Reserved */
513 cmd->assoc_req.ci.dev_type = 0U; /* RFD */
514 cmd->assoc_req.ci.power_src = 0U; /* TODO: set right power source */
515 cmd->assoc_req.ci.rx_on = 1U; /* TODO: derive from PM settings */
516 cmd->assoc_req.ci.association_type = 0U; /* normal association */
517 cmd->assoc_req.ci.reserved_2 = 0U; /* Reserved */
518 #ifdef CONFIG_NET_L2_IEEE802154_SECURITY
519 cmd->assoc_req.ci.sec_capability = ctx->sec_ctx.level > IEEE802154_SECURITY_LEVEL_NONE;
520 #else
521 cmd->assoc_req.ci.sec_capability = 0U;
522 #endif
523 /* request short address
524 * TODO: support operation with ext addr.
525 */
526 cmd->assoc_req.ci.alloc_addr = 1U;
527
528 ieee802154_mac_cmd_finalize(pkt, IEEE802154_CFI_ASSOCIATION_REQUEST);
529
530 /* section 6.4.1, Association: Set phyCurrentPage [TODO: implement] and
531 * phyCurrentChannel to the requested channel and channel page
532 * parameters.
533 */
534 if (ieee802154_radio_set_channel(iface, req->channel)) {
535 ret = -EIO;
536 NET_ERR("Could not associate: cannot set channel %d", req->channel);
537 goto release;
538 }
539
540 /* section 6.4.1, Association: Set macPanId to the coordinator's PAN ID. */
541 ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
542 ctx->pan_id = req->pan_id;
543 ieee802154_radio_filter_pan_id(iface, req->pan_id);
544
545 /* section 6.4.1, Association: Set macCoordExtendedAddress or
546 * macCoordShortAddress, depending on which is known from the Beacon
547 * frame from the coordinator through which the device wishes to
548 * associate.
549 */
550 if (req->len == IEEE802154_SHORT_ADDR_LENGTH) {
551 ctx->coord_short_addr = req->short_addr;
552 } else {
553 ctx->coord_short_addr = IEEE802154_NO_SHORT_ADDRESS_ASSIGNED;
554 sys_memcpy_swap(ctx->coord_ext_addr, req->addr, IEEE802154_EXT_ADDR_LENGTH);
555 }
556
557 k_sem_give(&ctx->ctx_lock);
558
559 /* Acquire the scan lock so that the next k_sem_take() blocks. */
560 k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
561
562 /* section 6.4.1, Association: The MAC sublayer of an unassociated device
563 * shall initiate the association procedure by sending an Association
564 * Request command, as described in 7.5.2, to the coordinator of an
565 * existing PAN.
566 */
567 if (ieee802154_radio_send(iface, pkt, pkt->buffer)) {
568 net_pkt_unref(pkt);
569 ret = -EIO;
570 k_sem_give(&ctx->scan_ctx_lock);
571 NET_ERR("Could not associate: cannot send association request");
572 goto out;
573 }
574
575 /* Wait macResponseWaitTime PHY symbols for the association response, see
576 * ieee802154_handle_mac_command() and section 6.4.1.
577 *
578 * TODO: The Association Response command shall be sent to the device
579 * requesting association using indirect transmission.
580 */
581 k_sem_take(&ctx->scan_ctx_lock, K_USEC(ieee802154_get_response_wait_time_us(iface)));
582
583 /* Release the scan lock in case an association response was not received
584 * within macResponseWaitTime and we got a timeout instead.
585 */
586 k_sem_give(&ctx->scan_ctx_lock);
587
588 k_sem_take(&ctx->ctx_lock, K_FOREVER);
589
590 if (is_associated(ctx)) {
591 bool validated = false;
592
593 if (req->len == IEEE802154_SHORT_ADDR_LENGTH &&
594 ctx->coord_short_addr == req->short_addr) {
595 validated = true;
596 } else {
597 uint8_t ext_addr_le[IEEE802154_EXT_ADDR_LENGTH];
598
599 __ASSERT_NO_MSG(req->len == IEEE802154_EXT_ADDR_LENGTH);
600
601 sys_memcpy_swap(ext_addr_le, req->addr, sizeof(ext_addr_le));
602 if (!memcmp(ctx->coord_ext_addr, ext_addr_le, IEEE802154_EXT_ADDR_LENGTH)) {
603 validated = true;
604 }
605 }
606
607 if (!validated) {
608 ret = -EFAULT;
609 NET_ERR("Could not associate: invalid address assigned by coordinator");
610 goto release;
611 }
612
613 ctx->channel = req->channel;
614 } else {
615 ret = -EACCES;
616 }
617
618 release:
619 k_sem_give(&ctx->ctx_lock);
620 out:
621 if (ret) {
622 k_sem_take(&ctx->ctx_lock, K_FOREVER);
623 remove_association(iface, ctx);
624 ieee802154_radio_set_channel(iface, ctx->channel);
625 k_sem_give(&ctx->ctx_lock);
626 }
627
628 return ret;
629 }
630
631 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_ASSOCIATE,
632 ieee802154_associate);
633
ieee802154_disassociate(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)634 static int ieee802154_disassociate(uint32_t mgmt_request, struct net_if *iface,
635 void *data, size_t len)
636 {
637 struct ieee802154_context *ctx = net_if_l2_data(iface);
638 struct ieee802154_frame_params params = {0};
639 struct ieee802154_command *cmd;
640 struct net_pkt *pkt;
641
642 ARG_UNUSED(data);
643 ARG_UNUSED(len);
644
645 k_sem_take(&ctx->ctx_lock, K_FOREVER);
646
647 if (!is_associated(ctx)) {
648 k_sem_give(&ctx->ctx_lock);
649 NET_WARN("Could not disassociate: not associated");
650 return -EALREADY;
651 }
652
653 /* See section 7.5.4:
654 * * The Destination PAN ID field shall contain the value of macPanId.
655 * * If an associated device is disassociating from the PAN, then the
656 * Destination Address field shall contain the value of either
657 * macCoordShortAddress, if the Destination Addressing Mode field is
658 * set to indicated short addressing, or macCoordExtendedAddress, if
659 * the Destination Addressing Mode field is set to indicated extended
660 * addressing.
661 */
662 params.dst.pan_id = ctx->pan_id;
663
664 if (ctx->coord_short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED &&
665 ctx->coord_short_addr != IEEE802154_NO_SHORT_ADDRESS_ASSIGNED) {
666 params.dst.len = IEEE802154_SHORT_ADDR_LENGTH;
667 params.dst.short_addr = ctx->coord_short_addr;
668 } else {
669 params.dst.len = IEEE802154_EXT_ADDR_LENGTH;
670 sys_memcpy_swap(params.dst.ext_addr, ctx->coord_ext_addr,
671 sizeof(params.dst.ext_addr));
672 }
673
674 k_sem_give(&ctx->ctx_lock);
675
676 /* If an associated device wants to leave the PAN, the MLME of the device
677 * shall send a Disassociation Notification command to its coordinator.
678 */
679 pkt = ieee802154_create_mac_cmd_frame(
680 iface, IEEE802154_CFI_DISASSOCIATION_NOTIFICATION, ¶ms);
681 if (!pkt) {
682 NET_ERR("Could not disassociate: cannot allocate disassociation notification "
683 "frame");
684 return -ENOBUFS;
685 }
686
687 cmd = ieee802154_get_mac_command(pkt);
688 cmd->disassoc_note.reason = IEEE802154_DRF_DEVICE_WISH;
689
690 ieee802154_mac_cmd_finalize(
691 pkt, IEEE802154_CFI_DISASSOCIATION_NOTIFICATION);
692
693 if (ieee802154_radio_send(iface, pkt, pkt->buffer)) {
694 net_pkt_unref(pkt);
695 NET_ERR("Could not disassociate: cannot send disassociation notification");
696 return -EIO;
697 }
698
699 k_sem_take(&ctx->ctx_lock, K_FOREVER);
700 remove_association(iface, ctx);
701 k_sem_give(&ctx->ctx_lock);
702
703 return 0;
704 }
705
706 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_DISASSOCIATE,
707 ieee802154_disassociate);
708
ieee802154_set_ack(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)709 static int ieee802154_set_ack(uint32_t mgmt_request, struct net_if *iface,
710 void *data, size_t len)
711 {
712 struct ieee802154_context *ctx = net_if_l2_data(iface);
713
714 ARG_UNUSED(data);
715 ARG_UNUSED(len);
716
717 k_sem_take(&ctx->ctx_lock, K_FOREVER);
718
719 if (mgmt_request == NET_REQUEST_IEEE802154_SET_ACK) {
720 ctx->ack_requested = true;
721 } else if (mgmt_request == NET_REQUEST_IEEE802154_UNSET_ACK) {
722 ctx->ack_requested = false;
723 }
724
725 k_sem_give(&ctx->ctx_lock);
726
727 return 0;
728 }
729
730 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_ACK,
731 ieee802154_set_ack);
732
733 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_UNSET_ACK,
734 ieee802154_set_ack);
735
ieee802154_set_parameters(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)736 static int ieee802154_set_parameters(uint32_t mgmt_request,
737 struct net_if *iface,
738 void *data, size_t len)
739 {
740 struct ieee802154_context *ctx = net_if_l2_data(iface);
741 uint16_t value;
742 int ret = 0;
743
744 if (!data) {
745 return -EINVAL;
746 }
747
748 if (mgmt_request == NET_REQUEST_IEEE802154_SET_EXT_ADDR) {
749 if (len != IEEE802154_EXT_ADDR_LENGTH) {
750 return -EINVAL;
751 }
752 } else {
753 if (len != sizeof(uint16_t)) {
754 return -EINVAL;
755 }
756 }
757
758 value = *((uint16_t *) data);
759
760 k_sem_take(&ctx->ctx_lock, K_FOREVER);
761
762 if (is_associated(ctx) && !(mgmt_request == NET_REQUEST_IEEE802154_SET_SHORT_ADDR &&
763 value == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED)) {
764 ret = -EBUSY;
765 NET_ERR("Could not set parameter: already associated to a PAN");
766 goto out;
767 }
768
769 if (mgmt_request == NET_REQUEST_IEEE802154_SET_CHANNEL) {
770 if (ctx->channel != value) {
771 if (!ieee802154_radio_verify_channel(iface, value)) {
772 ret = -EINVAL;
773 NET_ERR("Could not set channel: channel %d not supported by "
774 "driver",
775 value);
776 goto out;
777 }
778
779 ret = ieee802154_radio_set_channel(iface, value);
780 if (ret) {
781 NET_ERR("Could not set channel: driver error (%d)", ret);
782 } else {
783 ctx->channel = value;
784 }
785 }
786 } else if (mgmt_request == NET_REQUEST_IEEE802154_SET_PAN_ID) {
787 if (ctx->pan_id != value) {
788 ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
789 ctx->pan_id = value;
790 ieee802154_radio_filter_pan_id(iface, ctx->pan_id);
791 }
792 } else if (mgmt_request == NET_REQUEST_IEEE802154_SET_EXT_ADDR) {
793 uint8_t ext_addr_le[IEEE802154_EXT_ADDR_LENGTH];
794
795 sys_memcpy_swap(ext_addr_le, data, IEEE802154_EXT_ADDR_LENGTH);
796
797 if (memcmp(ctx->ext_addr, ext_addr_le, IEEE802154_EXT_ADDR_LENGTH)) {
798 memcpy(ctx->ext_addr, ext_addr_le, IEEE802154_EXT_ADDR_LENGTH);
799
800 if (net_if_get_link_addr(iface)->len == IEEE802154_EXT_ADDR_LENGTH) {
801 set_linkaddr_to_ext_addr(iface, ctx);
802 }
803
804 ieee802154_radio_filter_ieee_addr(iface, ctx->ext_addr);
805 }
806 } else if (mgmt_request == NET_REQUEST_IEEE802154_SET_SHORT_ADDR) {
807 if (ctx->short_addr != value) {
808 if (value == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED) {
809 remove_association(iface, ctx);
810 } else {
811 /* A PAN is required when associating,
812 * see section 8.4.3.1, table 8-94.
813 */
814 if (ctx->pan_id == IEEE802154_PAN_ID_NOT_ASSOCIATED) {
815 ret = -EPERM;
816 NET_ERR("Could not set short address: not yet associated "
817 "to a PAN");
818 goto out;
819 }
820 set_association(iface, ctx, value);
821 }
822 }
823 } else if (mgmt_request == NET_REQUEST_IEEE802154_SET_TX_POWER) {
824 if (ctx->tx_power != (int16_t)value) {
825 ret = ieee802154_radio_set_tx_power(iface, (int16_t)value);
826 if (ret) {
827 NET_ERR("Could not set TX power (%d dB)", value);
828 } else {
829 ctx->tx_power = (int16_t)value;
830 }
831 }
832 }
833
834 out:
835 k_sem_give(&ctx->ctx_lock);
836 return ret;
837 }
838
839 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_CHANNEL,
840 ieee802154_set_parameters);
841
842 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_PAN_ID,
843 ieee802154_set_parameters);
844
845 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_EXT_ADDR,
846 ieee802154_set_parameters);
847
848 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_SHORT_ADDR,
849 ieee802154_set_parameters);
850
851 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_TX_POWER,
852 ieee802154_set_parameters);
853
ieee802154_get_parameters(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)854 static int ieee802154_get_parameters(uint32_t mgmt_request,
855 struct net_if *iface,
856 void *data, size_t len)
857 {
858 struct ieee802154_context *ctx = net_if_l2_data(iface);
859 uint16_t *value;
860 int ret = 0;
861
862 if (!data) {
863 return -EINVAL;
864 }
865
866 if (mgmt_request == NET_REQUEST_IEEE802154_GET_EXT_ADDR) {
867 if (len != IEEE802154_EXT_ADDR_LENGTH) {
868 NET_ERR("Could not get parameter: invalid extended address length");
869 return -EINVAL;
870 }
871 } else {
872 if (len != sizeof(uint16_t)) {
873 NET_ERR("Could not get parameter: invalid short address length");
874 return -EINVAL;
875 }
876 }
877
878 value = (uint16_t *)data;
879
880 k_sem_take(&ctx->ctx_lock, K_FOREVER);
881
882 if (mgmt_request == NET_REQUEST_IEEE802154_GET_CHANNEL) {
883 *value = ctx->channel;
884 } else if (mgmt_request == NET_REQUEST_IEEE802154_GET_PAN_ID) {
885 *value = ctx->pan_id;
886 } else if (mgmt_request == NET_REQUEST_IEEE802154_GET_EXT_ADDR) {
887 sys_memcpy_swap(data, ctx->ext_addr, IEEE802154_EXT_ADDR_LENGTH);
888 } else if (mgmt_request == NET_REQUEST_IEEE802154_GET_SHORT_ADDR) {
889 *value = ctx->short_addr;
890 } else if (mgmt_request == NET_REQUEST_IEEE802154_GET_TX_POWER) {
891 int16_t *s_value = (int16_t *)data;
892
893 *s_value = ctx->tx_power;
894 }
895
896 k_sem_give(&ctx->ctx_lock);
897 return ret;
898 }
899
900 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_CHANNEL,
901 ieee802154_get_parameters);
902
903 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_PAN_ID,
904 ieee802154_get_parameters);
905
906 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_EXT_ADDR,
907 ieee802154_get_parameters);
908
909 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_SHORT_ADDR,
910 ieee802154_get_parameters);
911
912 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_TX_POWER,
913 ieee802154_get_parameters);
914
915 #ifdef CONFIG_NET_L2_IEEE802154_SECURITY
916
ieee802154_set_security_settings(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)917 static int ieee802154_set_security_settings(uint32_t mgmt_request,
918 struct net_if *iface,
919 void *data, size_t len)
920 {
921 struct ieee802154_context *ctx = net_if_l2_data(iface);
922 struct ieee802154_security_params *params;
923 int ret = 0;
924
925 if (len != sizeof(struct ieee802154_security_params) || !data) {
926 return -EINVAL;
927 }
928
929 params = (struct ieee802154_security_params *)data;
930
931 k_sem_take(&ctx->ctx_lock, K_FOREVER);
932
933 if (is_associated(ctx)) {
934 ret = -EBUSY;
935 NET_ERR("Could not set security parameters: already associated to a PAN");
936 goto out;
937 }
938
939 ieee802154_security_teardown_session(&ctx->sec_ctx);
940
941 if (ieee802154_security_setup_session(&ctx->sec_ctx, params->level,
942 params->key_mode, params->key,
943 params->key_len)) {
944 NET_ERR("Could not set security parameters: invalid parameters");
945 ret = -EINVAL;
946 }
947
948 out:
949 k_sem_give(&ctx->ctx_lock);
950 return ret;
951 }
952
953 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_SECURITY_SETTINGS,
954 ieee802154_set_security_settings);
955
ieee802154_get_security_settings(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)956 static int ieee802154_get_security_settings(uint32_t mgmt_request,
957 struct net_if *iface,
958 void *data, size_t len)
959 {
960 struct ieee802154_context *ctx = net_if_l2_data(iface);
961 struct ieee802154_security_params *params;
962
963 if (len != sizeof(struct ieee802154_security_params) || !data) {
964 return -EINVAL;
965 }
966
967 params = (struct ieee802154_security_params *)data;
968
969 k_sem_take(&ctx->ctx_lock, K_FOREVER);
970
971 memcpy(params->key, ctx->sec_ctx.key, ctx->sec_ctx.key_len);
972 params->key_len = ctx->sec_ctx.key_len;
973 params->key_mode = ctx->sec_ctx.key_mode;
974 params->level = ctx->sec_ctx.level;
975
976 k_sem_give(&ctx->ctx_lock);
977
978 return 0;
979 }
980
981 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_SECURITY_SETTINGS,
982 ieee802154_get_security_settings);
983
984 #endif /* CONFIG_NET_L2_IEEE802154_SECURITY */
985