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