zephyr/subsys/net/l2/ieee802154/ieee802154_shell.c
Robert Lubos 51726f3648 net: l2: ieee802154: shell: Validate address on input
Associate command handler did not validate the provided address length.
In result, if provided address string was longer than the expected
extended address size, strncpy() would not NULL terminate the buffer,
which could lead to unexpected behavior in parse_extended_address(), as
it expects NULL terminated string.

Fix this by validating the length of the provided address string before
parsing.

Additionally, make parse_extended_address() return the parsing result,
so that it can be detected when provided extended address has incorrect
format.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2023-07-20 10:47:39 +00:00

672 lines
15 KiB
C

/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief IEEE 802.15.4 shell module
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_ieee802154_shell, CONFIG_NET_L2_IEEE802154_LOG_LEVEL);
#include <zephyr/kernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/printk.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/ieee802154_mgmt.h>
#include "ieee802154_frame.h"
#define EXT_ADDR_STR_SIZE sizeof("xx:xx:xx:xx:xx:xx:xx:xx")
#define EXT_ADDR_STR_LEN (EXT_ADDR_STR_SIZE - 1U)
struct ieee802154_req_params params;
static struct net_mgmt_event_callback scan_cb;
static const struct shell *cb_shell;
static int cmd_ieee802154_ack(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
ARG_UNUSED(argc);
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
if (!strcmp(argv[1], "set") || !strcmp(argv[1], "1")) {
net_mgmt(NET_REQUEST_IEEE802154_SET_ACK, iface, NULL, 0);
shell_fprintf(sh, SHELL_NORMAL,
"ACK flag set on outgoing packets\n");
return 0;
}
if (!strcmp(argv[1], "unset") || !strcmp(argv[1], "0")) {
net_mgmt(NET_REQUEST_IEEE802154_UNSET_ACK, iface, NULL, 0);
shell_fprintf(sh, SHELL_NORMAL,
"ACK flag unset on outgoing packets\n");
return 0;
}
return 0;
}
/**
* Parse a string representing an extended address in ASCII HEX
* format into a big endian binary representation of the address.
*
* @param addr Extended address as a string.
* @param ext_addr Extended address in big endian byte ordering.
*
* @return 0 on success, negative error code otherwise
*/
static inline int parse_extended_address(char *addr, uint8_t *ext_addr)
{
return net_bytes_from_str(ext_addr, IEEE802154_EXT_ADDR_LENGTH, addr);
}
static int cmd_ieee802154_associate(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
char ext_addr[EXT_ADDR_STR_SIZE];
if (argc < 3) {
shell_help(sh);
return -ENOEXEC;
}
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
if (strlen(argv[2]) > EXT_ADDR_STR_LEN) {
shell_fprintf(sh, SHELL_INFO, "Address too long\n");
return -ENOEXEC;
}
params = (struct ieee802154_req_params){0};
params.pan_id = atoi(argv[1]);
strncpy(ext_addr, argv[2], sizeof(ext_addr));
if (strlen(ext_addr) == EXT_ADDR_STR_LEN) {
if (parse_extended_address(ext_addr, params.addr) < 0) {
shell_fprintf(sh, SHELL_INFO,
"Failed to parse extended address\n");
return -ENOEXEC;
}
params.len = IEEE802154_EXT_ADDR_LENGTH;
} else {
params.short_addr = (uint16_t) atoi(ext_addr);
params.len = IEEE802154_SHORT_ADDR_LENGTH;
}
if (net_mgmt(NET_REQUEST_IEEE802154_ASSOCIATE, iface,
&params, sizeof(struct ieee802154_req_params))) {
shell_fprintf(sh, SHELL_WARNING,
"Could not associate to %s on PAN ID %u\n",
argv[2], params.pan_id);
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"Associated to PAN ID %u\n", params.pan_id);
}
return 0;
}
static int cmd_ieee802154_disassociate(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
int ret;
ARG_UNUSED(argc);
ARG_UNUSED(argv);
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
ret = net_mgmt(NET_REQUEST_IEEE802154_DISASSOCIATE, iface, NULL, 0);
if (ret == -EALREADY) {
shell_fprintf(sh, SHELL_INFO,
"Interface is not associated\n");
return -ENOEXEC;
} else if (ret) {
shell_fprintf(sh, SHELL_WARNING,
"Could not disassociate? (status: %i)\n", ret);
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"Interface is now disassociated\n");
}
return 0;
}
static inline uint32_t parse_channel_set(char *str_set)
{
uint32_t channel_set = 0U;
char *p, *n;
p = str_set;
do {
uint32_t chan;
n = strchr(p, ':');
if (n) {
*n = '\0';
}
chan = atoi(p);
if (chan > 0 && chan < 32) {
channel_set |= BIT(chan - 1);
}
p = n ? n + 1 : n;
} while (n);
return channel_set;
}
static inline char *print_coordinator_address(char *buf, int buf_len)
{
if (params.len == IEEE802154_EXT_ADDR_LENGTH) {
int i, pos = 0;
pos += snprintk(buf + pos, buf_len - pos, "(extended) ");
for (i = 0; i < IEEE802154_EXT_ADDR_LENGTH; i++) {
pos += snprintk(buf + pos, buf_len - pos,
"%02X:", params.addr[i]);
}
buf[pos - 1] = '\0';
} else {
snprintk(buf, buf_len, "(short) %u", params.short_addr);
}
return buf;
}
static void scan_result_cb(struct net_mgmt_event_callback *cb,
uint32_t mgmt_event, struct net_if *iface)
{
char buf[64];
shell_fprintf(cb_shell, SHELL_NORMAL,
"\nChannel: %u\tPAN ID: %u\tCoordinator Address: %s\t "
"LQI: %u\n", params.channel, params.pan_id,
print_coordinator_address(buf, sizeof(buf)), params.lqi);
}
static int cmd_ieee802154_scan(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
uint32_t scan_type;
int ret = 0;
if (argc < 3) {
shell_help(sh);
return -ENOEXEC;
}
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
params = (struct ieee802154_req_params){0};
net_mgmt_init_event_callback(&scan_cb, scan_result_cb,
NET_EVENT_IEEE802154_SCAN_RESULT);
net_mgmt_add_event_callback(&scan_cb);
if (!strcmp(argv[1], "active")) {
scan_type = NET_REQUEST_IEEE802154_ACTIVE_SCAN;
} else if (!strcmp(argv[1], "passive")) {
scan_type = NET_REQUEST_IEEE802154_PASSIVE_SCAN;
} else {
ret = -ENOEXEC;
goto release_event_cb;
}
if (!strcmp(argv[2], "all")) {
params.channel_set = IEEE802154_ALL_CHANNELS;
} else {
params.channel_set = parse_channel_set(argv[2]);
}
if (!params.channel_set) {
ret = -ENOEXEC;
goto release_event_cb;
}
params.duration = atoi(argv[3]);
shell_fprintf(sh, SHELL_NORMAL,
"%s Scanning (channel set: 0x%08x, duration %u ms)...\n",
scan_type == NET_REQUEST_IEEE802154_ACTIVE_SCAN ?
"Active" : "Passive", params.channel_set,
params.duration);
cb_shell = sh;
if (scan_type == NET_REQUEST_IEEE802154_ACTIVE_SCAN) {
ret = net_mgmt(NET_REQUEST_IEEE802154_ACTIVE_SCAN, iface,
&params, sizeof(struct ieee802154_req_params));
} else {
ret = net_mgmt(NET_REQUEST_IEEE802154_PASSIVE_SCAN, iface,
&params, sizeof(struct ieee802154_req_params));
}
if (ret) {
shell_fprintf(sh, SHELL_WARNING,
"Could not raise a scan (status: %i)\n", ret);
ret = -ENOEXEC;
goto release_event_cb;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"Done\n");
}
release_event_cb:
net_mgmt_del_event_callback(&scan_cb);
return ret;
}
static int cmd_ieee802154_set_chan(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
uint16_t channel;
if (argc < 2) {
shell_help(sh);
return -ENOEXEC;
}
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
channel = (uint16_t)atoi(argv[1]);
if (net_mgmt(NET_REQUEST_IEEE802154_SET_CHANNEL, iface,
&channel, sizeof(uint16_t))) {
shell_fprintf(sh, SHELL_WARNING,
"Could not set channel %u\n", channel);
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"Channel %u set\n", channel);
}
return 0;
}
static int cmd_ieee802154_get_chan(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
uint16_t channel;
ARG_UNUSED(argc);
ARG_UNUSED(argv);
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
if (net_mgmt(NET_REQUEST_IEEE802154_GET_CHANNEL, iface,
&channel, sizeof(uint16_t))) {
shell_fprintf(sh, SHELL_WARNING,
"Could not get channel\n");
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"Channel %u\n", channel);
}
return 0;
}
static int cmd_ieee802154_set_pan_id(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
uint16_t pan_id;
ARG_UNUSED(argc);
if (argc < 2) {
shell_help(sh);
return -ENOEXEC;
}
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
pan_id = (uint16_t)atoi(argv[1]);
if (net_mgmt(NET_REQUEST_IEEE802154_SET_PAN_ID, iface,
&pan_id, sizeof(uint16_t))) {
shell_fprintf(sh, SHELL_WARNING,
"Could not set PAN ID %u\n", pan_id);
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"PAN ID %u set\n", pan_id);
}
return 0;
}
static int cmd_ieee802154_get_pan_id(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
uint16_t pan_id;
ARG_UNUSED(argc);
ARG_UNUSED(argv);
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
if (net_mgmt(NET_REQUEST_IEEE802154_GET_PAN_ID, iface,
&pan_id, sizeof(uint16_t))) {
shell_fprintf(sh, SHELL_WARNING,
"Could not get PAN ID\n");
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"PAN ID %u (0x%x)\n", pan_id, pan_id);
}
return 0;
}
static int cmd_ieee802154_set_ext_addr(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
uint8_t addr[IEEE802154_EXT_ADDR_LENGTH]; /* in big endian */
if (argc < 2) {
shell_help(sh);
return -ENOEXEC;
}
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
if (strlen(argv[1]) != EXT_ADDR_STR_LEN) {
shell_fprintf(sh, SHELL_INFO,
"%zd characters needed\n", EXT_ADDR_STR_LEN);
return -ENOEXEC;
}
if (parse_extended_address(argv[1], addr) < 0) {
shell_fprintf(sh, SHELL_INFO,
"Failed to parse extended address\n");
return -ENOEXEC;
}
if (net_mgmt(NET_REQUEST_IEEE802154_SET_EXT_ADDR, iface,
addr, IEEE802154_EXT_ADDR_LENGTH)) {
shell_fprintf(sh, SHELL_WARNING,
"Could not set extended address\n");
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"Extended address set\n");
}
return 0;
}
static int cmd_ieee802154_get_ext_addr(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
uint8_t addr[IEEE802154_EXT_ADDR_LENGTH]; /* in big endian */
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
if (net_mgmt(NET_REQUEST_IEEE802154_GET_EXT_ADDR, iface,
addr, IEEE802154_EXT_ADDR_LENGTH)) {
shell_fprintf(sh, SHELL_WARNING,
"Could not get extended address\n");
return -ENOEXEC;
} else {
static char ext_addr[EXT_ADDR_STR_SIZE];
int i, pos = 0;
for (i = 0; i < IEEE802154_EXT_ADDR_LENGTH; i++) {
pos += snprintk(ext_addr + pos,
EXT_ADDR_STR_SIZE - pos,
"%02X:", addr[i]);
}
ext_addr[EXT_ADDR_STR_SIZE - 1] = '\0';
shell_fprintf(sh, SHELL_NORMAL,
"Extended address: %s\n", ext_addr);
}
return 0;
}
static int cmd_ieee802154_set_short_addr(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
uint16_t short_addr; /* in CPU byte order */
if (argc < 2) {
shell_help(sh);
return -ENOEXEC;
}
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
short_addr = (uint16_t)atoi(argv[1]);
if (net_mgmt(NET_REQUEST_IEEE802154_SET_SHORT_ADDR, iface,
&short_addr, sizeof(uint16_t))) {
shell_fprintf(sh, SHELL_WARNING,
"Could not set short address %u\n", short_addr);
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"Short address %u set\n", short_addr);
}
return 0;
}
static int cmd_ieee802154_get_short_addr(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
uint16_t short_addr; /* in CPU byte order */
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
if (net_mgmt(NET_REQUEST_IEEE802154_GET_SHORT_ADDR, iface,
&short_addr, sizeof(uint16_t))) {
shell_fprintf(sh, SHELL_WARNING,
"Could not get short address\n");
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"Short address %u\n", short_addr);
}
return 0;
}
static int cmd_ieee802154_set_tx_power(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
int16_t tx_power;
if (argc < 2) {
shell_help(sh);
return -ENOEXEC;
}
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
tx_power = (int16_t)atoi(argv[1]);
if (net_mgmt(NET_REQUEST_IEEE802154_SET_TX_POWER, iface,
&tx_power, sizeof(int16_t))) {
shell_fprintf(sh, SHELL_WARNING,
"Could not set TX power %d\n", tx_power);
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"TX power %d set\n", tx_power);
}
return 0;
}
static int cmd_ieee802154_get_tx_power(const struct shell *sh,
size_t argc, char *argv[])
{
struct net_if *iface = net_if_get_ieee802154();
int16_t tx_power;
if (!iface) {
shell_fprintf(sh, SHELL_INFO,
"No IEEE 802.15.4 interface found.\n");
return -ENOEXEC;
}
if (net_mgmt(NET_REQUEST_IEEE802154_GET_TX_POWER, iface,
&tx_power, sizeof(int16_t))) {
shell_fprintf(sh, SHELL_WARNING,
"Could not get TX power\n");
return -ENOEXEC;
} else {
shell_fprintf(sh, SHELL_NORMAL,
"TX power (in dbm) %d\n", tx_power);
}
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(ieee802154_commands,
SHELL_CMD(ack, NULL,
"<set/1 | unset/0> Set auto-ack flag",
cmd_ieee802154_ack),
SHELL_CMD(associate, NULL,
"<pan_id> <PAN coordinator short or long address (EUI-64)>",
cmd_ieee802154_associate),
SHELL_CMD(disassociate, NULL,
"Disassociate from network",
cmd_ieee802154_disassociate),
SHELL_CMD(get_chan, NULL,
"Get currently used channel",
cmd_ieee802154_get_chan),
SHELL_CMD(get_ext_addr, NULL,
"Get currently used extended address",
cmd_ieee802154_get_ext_addr),
SHELL_CMD(get_pan_id, NULL,
"Get currently used PAN id",
cmd_ieee802154_get_pan_id),
SHELL_CMD(get_short_addr, NULL,
"Get currently used short address",
cmd_ieee802154_get_short_addr),
SHELL_CMD(get_tx_power, NULL,
"Get currently used TX power",
cmd_ieee802154_get_tx_power),
SHELL_CMD(scan, NULL,
"<passive|active> <channels set n[:m:...]:x|all>"
" <per-channel duration in ms>",
cmd_ieee802154_scan),
SHELL_CMD(set_chan, NULL,
"<channel> Set used channel",
cmd_ieee802154_set_chan),
SHELL_CMD(set_ext_addr, NULL,
"<long/extended address (EUI-64)> Set extended address",
cmd_ieee802154_set_ext_addr),
SHELL_CMD(set_pan_id, NULL,
"<pan_id> Set used PAN id",
cmd_ieee802154_set_pan_id),
SHELL_CMD(set_short_addr, NULL,
"<short address> Set short address",
cmd_ieee802154_set_short_addr),
SHELL_CMD(set_tx_power, NULL,
"<-18/-7/-4/-2/0/1/2/3/5> Set TX power",
cmd_ieee802154_set_tx_power),
SHELL_SUBCMD_SET_END
);
SHELL_CMD_REGISTER(ieee802154, &ieee802154_commands, "IEEE 802.15.4 commands",
NULL);