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