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, &params);
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, &params);
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, &params);
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