diff mbox

[wpan-tools,1/2] security: add nl802154 security support

Message ID 1443425134-27910-1-git-send-email-alex.aring@gmail.com
State Not Applicable, archived
Delegated to: David Miller
Headers show

Commit Message

Alexander Aring Sept. 28, 2015, 7:25 a.m. UTC
This patch introduce support for the experimental seucirty support for
nl802154. We currently support add/del settings for manipulating
security table entries. The dump functionality is a "really" keep it
short and stupid handling, the dump will printout the printout the right
add calls which was called to add the entry. This can be used for
storing the current security tables by some script. The interface
argument is replaced by $WPAN_DEV variable, so it's possible to move one
interface configuration to another one.

Signed-off-by: Alexander Aring <alex.aring@gmail.com>
---
 src/Makefile.am |    1 +
 src/interface.c |  100 +++++
 src/nl802154.h  |  191 ++++++++++
 src/security.c  | 1118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1410 insertions(+)
 create mode 100644 src/security.c

Comments

Stefan Schmidt Sept. 30, 2015, 2:46 p.m. UTC | #1
Hello.

A really huge patch. I will start on it. Not sure I can do a full review 
in one go though.

On 28/09/15 09:25, Alexander Aring wrote:
> This patch introduce support for the experimental seucirty support for

Type. Security.
> nl802154. We currently support add/del settings for manipulating
> security table entries. The dump functionality is a "really" keep it

is really a
> short and stupid handling, the dump will printout the printout the right

dump will printout the right calls to add the entry
> add calls which was called to add the entry. This can be used for
> storing the current security tables by some script. The interface
> argument is replaced by $WPAN_DEV variable, so it's possible to move one
> interface configuration to another one.
>
> Signed-off-by: Alexander Aring <alex.aring@gmail.com>
> ---
>   src/Makefile.am |    1 +
>   src/interface.c |  100 +++++
>   src/nl802154.h  |  191 ++++++++++
>   src/security.c  | 1118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 1410 insertions(+)
>   create mode 100644 src/security.c
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 2d54576..b2177a2 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -9,6 +9,7 @@ iwpan_SOURCES = \
>   	interface.c \
>   	phy.c \
>   	mac.c \
> +	security.c \
>   	nl_extras.h \
>   	nl802154.h
>   
> diff --git a/src/interface.c b/src/interface.c
> index 85d40a8..076e7c3 100644
> --- a/src/interface.c
> +++ b/src/interface.c
> @@ -10,6 +10,7 @@
>   #include <netlink/msg.h>
>   #include <netlink/attr.h>
>   
> +#define CONFIG_IEEE802154_NL802154_EXPERIMENTAL
>   #include "nl802154.h"
>   #include "nl_extras.h"
>   #include "iwpan.h"
> @@ -226,6 +227,105 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
>   	if (tb_msg[NL802154_ATTR_ACKREQ_DEFAULT])
>   		printf("%s\tackreq_default %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_ACKREQ_DEFAULT]));
>   
> +	if (tb_msg[NL802154_ATTR_SEC_ENABLED])
> +		printf("%s\tsecurity %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_SEC_ENABLED]));
> +	if (tb_msg[NL802154_ATTR_SEC_OUT_LEVEL])
> +		printf("%s\tout_level %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_SEC_OUT_LEVEL]));
> +	if (tb_msg[NL802154_ATTR_SEC_OUT_KEY_ID]) {
> +		struct nlattr *tb_key_id[NL802154_KEY_ID_ATTR_MAX + 1];
> +		static struct nla_policy key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
> +		        [NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
> +		        [NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
> +		        [NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
> +		        [NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
> +		        [NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
> +		};
> +
> +		nla_parse_nested(tb_key_id, NL802154_KEY_ID_ATTR_MAX,
> +				 tb_msg[NL802154_ATTR_SEC_OUT_KEY_ID], key_id_policy);
> +		printf("%s\tout_key_id\n", indent);
> +
> +		if (tb_key_id[NL802154_KEY_ID_ATTR_MODE]) {
> +			enum nl802154_key_id_modes key_id_mode;
> +
> +			key_id_mode = nla_get_u32(tb_key_id[NL802154_KEY_ID_ATTR_MODE]);
> +			switch (key_id_mode) {
> +			case NL802154_KEY_ID_MODE_IMPLICIT:
> +				printf("%s\t\tmode implicit\n", indent);
> +				if (tb_key_id[NL802154_KEY_ID_ATTR_IMPLICIT]) {
> +					struct nlattr *tb_dev_addr[NL802154_DEV_ADDR_ATTR_MAX + 1];
> +					static struct nla_policy dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = {
> +						[NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 },
> +						[NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 },
> +						[NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 },
> +						[NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 },
> +					};
> +
> +					nla_parse_nested(tb_dev_addr, NL802154_DEV_ADDR_ATTR_MAX,
> +							 tb_key_id[NL802154_KEY_ID_ATTR_IMPLICIT],
> +							 dev_addr_policy);
> +
> +					if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_PAN_ID])
> +						printf("%s\t\tpan_id 0x%04x\n", indent,
> +						       le16toh(nla_get_u16(tb_dev_addr[NL802154_DEV_ADDR_ATTR_PAN_ID])));
> +
> +					if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_MODE]) {
> +						enum nl802154_dev_addr_modes dev_addr_mode;
> +						dev_addr_mode = nla_get_u32(tb_dev_addr[NL802154_DEV_ADDR_ATTR_MODE]);
> +						printf("%s\t\taddr_mode %d\n", indent, dev_addr_mode);
> +						switch (dev_addr_mode) {
> +						case NL802154_DEV_ADDR_SHORT:
> +							if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_SHORT])
> +								printf("%s\t\tshort_addr 0x%04x\n", indent,
> +								       le16toh(nla_get_u16(tb_dev_addr[NL802154_DEV_ADDR_ATTR_SHORT])));
> +							break;
> +						case NL802154_DEV_ADDR_EXTENDED:
> +							if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_EXTENDED])
> +								printf("%s\t\textended_addr 0x%016" PRIx64 "\n", indent,
> +								       le64toh(nla_get_u64(tb_dev_addr[NL802154_DEV_ADDR_ATTR_EXTENDED])));
> +							break;
> +						default:
> +							printf("%s\t\tunkown address\n", indent);
> +							break;
> +						}
> +					}
> +				}
> +				break;
> +			case NL802154_KEY_ID_MODE_INDEX:
> +				printf("%s\t\tmode index\n", indent);
> +				if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
> +					printf("%s\t\tindex 0x%02x\n", indent,
> +					       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
> +				break;
> +			case NL802154_KEY_ID_MODE_INDEX_SHORT:
> +				printf("%s\t\tmode index_short\n", indent);
> +				if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
> +					printf("%s\t\tindex 0x%02x\n", indent,
> +					       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
> +
> +				if (tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_SHORT])
> +					printf("%s\t\tsource_short 0x%08lx\n", indent,
> +					       le32toh(nla_get_u32(tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_SHORT])));
> +				break;
> +			case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
> +				printf("%s\t\tmode index_extended\n", indent);
> +				if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
> +					printf("%s\t\tindex 0x%02x\n", indent,
> +					       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
> +
> +				if (tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])
> +					printf("%s\t\tsource_extended 0x%" PRIx64 "\n", indent,
> +					       le64toh(nla_get_u64(tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])));
> +				break;
> +			default:
> +				printf("%s\t\tkey_mode unknown\n", indent);
> +			}
> +		}
> +	}
> +
> +	if (tb_msg[NL802154_ATTR_SEC_FRAME_COUNTER])
> +		printf("%s\tframe_counter 0x%08lx\n", indent, be32toh(nla_get_u32(tb_msg[NL802154_ATTR_SEC_FRAME_COUNTER])));
> +
>   	return NL_SKIP;
>   }
>   
> diff --git a/src/nl802154.h b/src/nl802154.h
> index cf2713d..32cb3e5 100644
> --- a/src/nl802154.h
> +++ b/src/nl802154.h
> @@ -56,6 +56,22 @@ enum nl802154_commands {
>   
>   	/* add new commands above here */
>   
> +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
> +	NL802154_CMD_SET_SEC_PARAMS,
> +	NL802154_CMD_GET_SEC_KEY,		/* can dump */
> +	NL802154_CMD_NEW_SEC_KEY,
> +	NL802154_CMD_DEL_SEC_KEY,
> +	NL802154_CMD_GET_SEC_DEV,		/* can dump */
> +	NL802154_CMD_NEW_SEC_DEV,
> +	NL802154_CMD_DEL_SEC_DEV,
> +	NL802154_CMD_GET_SEC_DEVKEY,		/* can dump */
> +	NL802154_CMD_NEW_SEC_DEVKEY,
> +	NL802154_CMD_DEL_SEC_DEVKEY,
> +	NL802154_CMD_GET_SEC_LEVEL,		/* can dump */
> +	NL802154_CMD_NEW_SEC_LEVEL,
> +	NL802154_CMD_DEL_SEC_LEVEL,
> +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
> +
>   	/* used to define NL802154_CMD_MAX below */
>   	__NL802154_CMD_AFTER_LAST,
>   	NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
> @@ -110,6 +126,18 @@ enum nl802154_attrs {
>   
>   	/* add attributes here, update the policy in nl802154.c */
>   
> +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
> +	NL802154_ATTR_SEC_ENABLED,
> +	NL802154_ATTR_SEC_OUT_LEVEL,
> +	NL802154_ATTR_SEC_OUT_KEY_ID,
> +	NL802154_ATTR_SEC_FRAME_COUNTER,
> +
> +	NL802154_ATTR_SEC_LEVEL,
> +	NL802154_ATTR_SEC_DEVICE,
> +	NL802154_ATTR_SEC_DEVKEY,
> +	NL802154_ATTR_SEC_KEY,
> +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
> +
>   	__NL802154_ATTR_AFTER_LAST,
>   	NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
>   };
> @@ -247,4 +275,167 @@ enum nl802154_supported_bool_states {
>   	NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1
>   };
>   
> +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
> +
> +enum nl802154_dev_addr_modes {
> +	NL802154_DEV_ADDR_NONE,
> +	__NL802154_DEV_ADDR_INVALID,
> +	NL802154_DEV_ADDR_SHORT,
> +	NL802154_DEV_ADDR_EXTENDED,
> +
> +	/* keep last */
> +	__NL802154_DEV_ADDR_AFTER_LAST,

Hmm, why bother with AFTER_LAST here and not just use ADDR_MAX as 
sentinal for this enum? Looks redundant to me.

> +	NL802154_DEV_ADDR_MAX = __NL802154_DEV_ADDR_AFTER_LAST - 1
> +};
> +
> +enum nl802154_dev_addr_attrs {
> +	NL802154_DEV_ADDR_ATTR_UNSPEC,
> +
> +	NL802154_DEV_ADDR_ATTR_PAN_ID,
> +	NL802154_DEV_ADDR_ATTR_MODE,
> +	NL802154_DEV_ADDR_ATTR_SHORT,
> +	NL802154_DEV_ADDR_ATTR_EXTENDED,
> +
> +	/* keep last */
> +	__NL802154_DEV_ADDR_ATTR_AFTER_LAST,
> +	NL802154_DEV_ADDR_ATTR_MAX = __NL802154_DEV_ADDR_ATTR_AFTER_LAST - 1

Same as above

> +};
> +
> +enum nl802154_key_id_modes {
> +	NL802154_KEY_ID_MODE_IMPLICIT,
> +	NL802154_KEY_ID_MODE_INDEX,
> +	NL802154_KEY_ID_MODE_INDEX_SHORT,
> +	NL802154_KEY_ID_MODE_INDEX_EXTENDED,
> +
> +	/* keep last */
> +	__NL802154_KEY_ID_MODE_AFTER_LAST,
> +	NL802154_KEY_ID_MODE_MAX = __NL802154_KEY_ID_MODE_AFTER_LAST - 1

Same as above.

> +};
> +
> +enum nl802154_key_id_attrs {
> +	NL802154_KEY_ID_ATTR_UNSPEC,
> +
> +	NL802154_KEY_ID_ATTR_MODE,
> +	NL802154_KEY_ID_ATTR_INDEX,
> +	NL802154_KEY_ID_ATTR_IMPLICIT,
> +	NL802154_KEY_ID_ATTR_SOURCE_SHORT,
> +	NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
> +
> +	/* keep last */
> +	__NL802154_KEY_ID_ATTR_AFTER_LAST,
> +	NL802154_KEY_ID_ATTR_MAX = __NL802154_KEY_ID_ATTR_AFTER_LAST - 1

Above

> +};
> +
> +enum nl802154_seclevels {
> +	NL802154_SECLEVEL_NONE,
> +	NL802154_SECLEVEL_MIC32,
> +	NL802154_SECLEVEL_MIC64,
> +	NL802154_SECLEVEL_MIC128,
> +	NL802154_SECLEVEL_ENC,
> +	NL802154_SECLEVEL_ENC_MIC32,
> +	NL802154_SECLEVEL_ENC_MIC64,
> +	NL802154_SECLEVEL_ENC_MIC128,
> +
> +	/* keep last */
> +	__NL802154_SECLEVEL_AFTER_LAST,
> +	NL802154_SECLEVEL_MAX = __NL802154_SECLEVEL_AFTER_LAST - 1

Above
> +};
> +
> +enum nl802154_frames {
> +	NL802154_FRAME_BEACON,
> +	NL802154_FRAME_DATA,
> +	NL802154_FRAME_ACK,
> +	NL802154_FRAME_CMD,
> +
> +	/* keep last */
> +	__NL802154_FRAME_AFTER_LAST,
> +	NL802154_FRAME_MAX = __NL802154_FRAME_AFTER_LAST - 1

Above
> +};
> +
> +enum nl802154_cmd_frames {
> +	__NL802154_CMD_FRAME_INVALID,
> +	NL802154_CMD_FRAME_ASSOC_REQUEST,
> +	NL802154_CMD_FRAME_ASSOC_RESPONSE,
> +	NL802154_CMD_FRAME_DISASSOC_NOTIFY,
> +	NL802154_CMD_FRAME_DATA_REQUEST,
> +	NL802154_CMD_FRAME_PAN_ID_CONFLICT_NOTIFY,
> +	NL802154_CMD_FRAME_ORPHAN_NOTIFY,
> +	NL802154_CMD_FRAME_BEACON_REQUEST,
> +	NL802154_CMD_FRAME_COORD_REALIGNMENT,
> +	NL802154_CMD_FRAME_GTS_REQUEST,
> +
> +	/* keep last */
> +	__NL802154_CMD_FRAME_AFTER_LAST,
> +	NL802154_CMD_FRAME_MAX = __NL802154_CMD_FRAME_AFTER_LAST - 1

Above, and for all other below.
> +};
> +
> +enum nl802154_seclevel_attrs {
> +	NL802154_SECLEVEL_ATTR_UNSPEC,
> +
> +	NL802154_SECLEVEL_ATTR_LEVELS,
> +	NL802154_SECLEVEL_ATTR_FRAME,
> +	NL802154_SECLEVEL_ATTR_CMD_FRAME,
> +	NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
> +
> +	/* keep last */
> +	__NL802154_SECLEVEL_ATTR_AFTER_LAST,
> +	NL802154_SECLEVEL_ATTR_MAX = __NL802154_SECLEVEL_ATTR_AFTER_LAST - 1
> +};
> +
> +/* TODO what is this? couldn't find in mib */
> +enum {
> +	NL802154_DEVKEY_IGNORE,
> +	NL802154_DEVKEY_RESTRICT,
> +	NL802154_DEVKEY_RECORD,
> +
> +	/* keep last */
> +	__NL802154_DEVKEY_AFTER_LAST,
> +	NL802154_DEVKEY_MAX = __NL802154_DEVKEY_AFTER_LAST - 1
> +};
> +
> +enum nl802154_dev {
> +	NL802154_DEV_ATTR_UNSPEC,
> +
> +	NL802154_DEV_ATTR_FRAME_COUNTER,
> +	NL802154_DEV_ATTR_PAN_ID,
> +	NL802154_DEV_ATTR_SHORT_ADDR,
> +	NL802154_DEV_ATTR_EXTENDED_ADDR,
> +	NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
> +	NL802154_DEV_ATTR_KEY_MODE,
> +
> +	/* keep last */
> +	__NL802154_DEV_ATTR_AFTER_LAST,
> +	NL802154_DEV_ATTR_MAX = __NL802154_DEV_ATTR_AFTER_LAST - 1
> +};
> +
> +enum nl802154_devkey {
> +	NL802154_DEVKEY_ATTR_UNSPEC,
> +
> +	NL802154_DEVKEY_ATTR_FRAME_COUNTER,
> +	NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
> +	NL802154_DEVKEY_ATTR_ID,
> +
> +	/* keep last */
> +	__NL802154_DEVKEY_ATTR_AFTER_LAST,
> +	NL802154_DEVKEY_ATTR_MAX = __NL802154_DEVKEY_ATTR_AFTER_LAST - 1
> +};
> +
> +enum nl802154_key {
> +	NL802154_KEY_ATTR_UNSPEC,
> +
> +	NL802154_KEY_ATTR_ID,
> +	NL802154_KEY_ATTR_USAGE_FRAMES,
> +	NL802154_KEY_ATTR_USAGE_CMDS,
> +	NL802154_KEY_ATTR_BYTES,
> +
> +	/* keep last */
> +	__NL802154_KEY_ATTR_AFTER_LAST,
> +	NL802154_KEY_ATTR_MAX = __NL802154_KEY_ATTR_AFTER_LAST - 1
> +};
> +
> +#define NL802154_KEY_SIZE		16
> +#define NL802154_CMD_FRAME_NR_IDS	256
> +
> +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
> +
>   #endif /* __NL802154_H */
> diff --git a/src/security.c b/src/security.c
> new file mode 100644
> index 0000000..3928dee
> --- /dev/null
> +++ b/src/security.c
> @@ -0,0 +1,1118 @@
> +#include <net/if.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <inttypes.h>
> +
> +#include <netlink/genl/genl.h>
> +#include <netlink/genl/family.h>
> +#include <netlink/genl/ctrl.h>
> +#include <netlink/msg.h>
> +#include <netlink/attr.h>
> +
> +#include "nl_extras.h"
> +#define CONFIG_IEEE802154_NL802154_EXPERIMENTAL
> +#include "nl802154.h"
> +#include "iwpan.h"
> +
> +static int handle_security_set(struct nl802154_state *state, struct nl_cb *cb,
> +			       struct nl_msg *msg, int argc, char **argv,
> +			       enum id_input id)
> +{
> +	unsigned long enabled;
> +	char *end;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* enabled */
> +	enabled = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	if (enabled > UINT8_MAX)
> +		return 1;
> +
> +	NLA_PUT_U8(msg, NL802154_ATTR_SEC_ENABLED, enabled);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	return -ENOBUFS;
> +}
> +COMMAND(set, security, "<1|0>", NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
> +	handle_security_set, NULL);
> +
> +static int handle_parse_key_id(struct nl_msg *msg, int attrtype,
> +			       int *argc, char ***argv)
> +{
> +	struct nl_msg *key_id_msg, *dev_addr_msg = NULL;
> +	unsigned long key_mode, dev_addr_mode, short_addr, pan_id, index;
> +	unsigned long long extended_addr;
> +	char *end;
> +
> +	if ((*argc) < 1)
> +		return 1;
> +
> +	/* key_mode */
> +	key_mode = strtoul((*argv)[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	(*argc)--;
> +	(*argv)++;
> +
> +	switch (key_mode) {
> +	case NL802154_KEY_ID_MODE_IMPLICIT:
> +		if ((*argc) < 2)
> +			return 1;
> +
> +		/* pan_id */
> +		pan_id = strtoul((*argv)[0], &end, 0);
> +		if (*end != '\0')
> +			return 1;
> +
> +		if (pan_id > UINT16_MAX)
> +			return 1;
> +
> +		(*argc)--;
> +		(*argv)++;
> +
> +		/* dev_addr_mode */
> +		dev_addr_mode = strtoul((*argv)[0], &end, 0);
> +		if (*end != '\0')
> +			return 1;
> +
> +		(*argc)--;
> +		(*argv)++;
> +
> +		switch (dev_addr_mode) {
> +		case NL802154_DEV_ADDR_SHORT:
> +			if ((*argc) < 1)
> +				return 1;
> +
> +			/* dev_addr_short */
> +			short_addr = strtoul((*argv)[0], &end, 0);
> +			if (*end != '\0')
> +				return 1;
> +
> +			if (short_addr > UINT16_MAX)
> +				return 1;
> +			break;
> +		case NL802154_DEV_ADDR_EXTENDED:
> +			if ((*argc) < 1)
> +				return 1;
> +
> +			/* dev_addr_short */
> +			extended_addr = strtoull((*argv)[0], &end, 0);
> +			if (*end != '\0')
> +				return 1;
> +			break;
> +		default:
> +			return 1;
> +		}
> +
> +		key_id_msg = nlmsg_alloc();
> +		if (!key_id_msg)
> +			return -ENOMEM;
> +
> +		dev_addr_msg = nlmsg_alloc();
> +		if (!dev_addr_msg)
> +			return -ENOMEM;
> +
> +		NLA_PUT_U16(dev_addr_msg, NL802154_DEV_ADDR_ATTR_PAN_ID, pan_id);
> +		NLA_PUT_U32(dev_addr_msg, NL802154_DEV_ADDR_ATTR_MODE, dev_addr_mode);
> +		NLA_PUT_U16(dev_addr_msg, NL802154_DEV_ADDR_ATTR_SHORT, htole16(short_addr));
> +		NLA_PUT_U64(dev_addr_msg, NL802154_DEV_ADDR_ATTR_EXTENDED, htole64(extended_addr));
> +
> +		nla_put_nested(key_id_msg, NL802154_KEY_ID_ATTR_IMPLICIT, dev_addr_msg);
> +
> +		nlmsg_free(dev_addr_msg);
> +		dev_addr_msg = NULL;
> +
> +		break;
> +	case NL802154_KEY_ID_MODE_INDEX:
> +		if ((*argc) < 1)
> +			return 1;
> +
> +		/* index */
> +		index = strtoul((*argv)[0], &end, 0);
> +		if (*end != '\0')
> +			return 1;
> +
> +		if (index > UINT8_MAX)
> +			return 1;
> +
> +		key_id_msg = nlmsg_alloc();
> +		if (!key_id_msg)
> +			return -ENOMEM;
> +
> +		NLA_PUT_U8(key_id_msg, NL802154_KEY_ID_ATTR_INDEX, index);
> +		break;
> +	case NL802154_KEY_ID_MODE_INDEX_SHORT:
> +		if ((*argc) < 2)
> +			return 1;
> +
> +		/* index */
> +		index = strtoul((*argv)[0], &end, 0);
> +		if (*end != '\0')
> +			return 1;
> +
> +		if (index > UINT8_MAX)
> +			return 1;
> +
> +		(*argc)--;
> +		(*argv)++;
> +
> +		/* source_short */
> +		short_addr = strtoul((*argv)[0], &end, 0);
> +		if (*end != '\0')
> +			return 1;
> +
> +		key_id_msg = nlmsg_alloc();
> +		if (!key_id_msg)
> +			return -ENOMEM;
> +
> +		NLA_PUT_U8(key_id_msg, NL802154_KEY_ID_ATTR_INDEX, index);
> +		NLA_PUT_U32(key_id_msg, NL802154_KEY_ID_ATTR_SOURCE_SHORT,
> +			    htole32(short_addr));
> +		break;
> +	case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
> +		if ((*argc) < 2)
> +			return 1;
> +
> +		/* index */
> +		index = strtoul((*argv)[0], &end, 0);
> +		if (*end != '\0')
> +			return 1;
> +
> +		if (index > UINT8_MAX)
> +			return 1;
> +
> +		(*argc)--;
> +		(*argv)++;
> +
> +		/* source_extended */
> +		extended_addr = strtoull((*argv)[0], &end, 0);
> +		if (*end != '\0')
> +			return 1;
> +
> +		key_id_msg = nlmsg_alloc();
> +		if (!key_id_msg)
> +			return -ENOMEM;
> +
> +		NLA_PUT_U8(key_id_msg, NL802154_KEY_ID_ATTR_INDEX, index);
> +		NLA_PUT_U64(key_id_msg, NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
> +			    htole64(extended_addr));
> +		break;
> +	default:
> +		return 1;
> +	}
> +
> +	NLA_PUT_U32(key_id_msg, NL802154_KEY_ID_ATTR_MODE, key_mode);
> +	nla_put_nested(msg, attrtype, key_id_msg);
> +
> +	nlmsg_free(key_id_msg);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	if (!dev_addr_msg)
> +		nlmsg_free(dev_addr_msg);
> +
> +	nlmsg_free(key_id_msg);
> +	return -ENOBUFS;
> +}
> +
> +static int handle_out_key_id_set(struct nl802154_state *state, struct nl_cb *cb,
> +				 struct nl_msg *msg, int argc, char **argv,
> +				 enum id_input id)
> +{
> +	return handle_parse_key_id(msg, NL802154_ATTR_SEC_OUT_KEY_ID, &argc, &argv);
> +
> +}
> +COMMAND(set, out_key_id,
> +	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
> +	"<1 <index>>|"
> +	"<2 <index> <source_short>>|"
> +	"<3 <index> <source_extended>>",

What are these extra >>| for ?

> +	NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
> +	handle_out_key_id_set, NULL);
> +
> +static int handle_out_seclevel_set(struct nl802154_state *state, struct nl_cb *cb,
> +				   struct nl_msg *msg, int argc, char **argv,
> +				   enum id_input id)
> +{
> +	unsigned long seclevel;
> +	char *end;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* seclevel */
> +	seclevel = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	NLA_PUT_U32(msg, NL802154_ATTR_SEC_OUT_LEVEL, seclevel);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	return -ENOBUFS;
> +}
> +COMMAND(set, out_level, "<out_level>", NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
> +	handle_out_seclevel_set, NULL);
> +
> +static int handle_frame_counter_set(struct nl802154_state *state, struct nl_cb *cb,
> +				   struct nl_msg *msg, int argc, char **argv,
> +				   enum id_input id)
> +{
> +	unsigned long frame_counter;
> +	char *end;
> +
> +	/* frame_counter */

This command and the other above (index, etc) which just state the 
variable name below are not really needed as they give no extra 
information imho.
> +	frame_counter = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	NLA_PUT_U32(msg, NL802154_ATTR_SEC_FRAME_COUNTER, htobe32(frame_counter));
> +
> +	return 0;
> +
> +nla_put_failure:
> +	return -ENOBUFS;
> +}
> +COMMAND(set, frame_counter, "<frame_counter>", NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
> +	handle_frame_counter_set, NULL);
> +
> +SECTION(seclevel);
> +
> +static int print_seclevel_handler(struct nl_msg *msg, void *arg)
> +{
> +	struct nlattr *tb[NL802154_ATTR_MAX + 1];
> +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
> +
> +	nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
> +		  genlmsg_attrlen(gnlh, 0), NULL);
> +
> +	if (tb[NL802154_ATTR_SEC_LEVEL]) {
> +		struct nlattr *tb_seclevel[NL802154_SECLEVEL_ATTR_MAX + 1];
> +		static struct nla_policy seclevel_policy[NL802154_SECLEVEL_ATTR_MAX + 1] = {
> +			[NL802154_SECLEVEL_ATTR_LEVELS] = { .type = NLA_U32 },
> +			[NL802154_SECLEVEL_ATTR_FRAME] = { .type = NLA_U32 },
> +			[NL802154_SECLEVEL_ATTR_CMD_FRAME] = { .type = NLA_U32 },
> +			[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE] = { .type = NLA_U8 },
> +		};
> +
> +		if (nla_parse_nested(tb_seclevel, NL802154_SECLEVEL_ATTR_MAX,
> +				     tb[NL802154_ATTR_SEC_LEVEL],
> +				     seclevel_policy)) {
> +			fprintf(stderr, "failed to parse nested attributes!\n");
> +			return NL_SKIP;
> +		}
> +
> +		printf("iwpan dev $WPAN_DEV seclevel add ");
> +
> +		if (tb_seclevel[NL802154_SECLEVEL_ATTR_LEVELS])
> +			printf("0x%02lx ", nla_get_u8(tb_seclevel[NL802154_SECLEVEL_ATTR_LEVELS]));
> +		if (tb_seclevel[NL802154_SECLEVEL_ATTR_FRAME])
> +			printf("%d ", nla_get_u32(tb_seclevel[NL802154_SECLEVEL_ATTR_FRAME]));
> +		if (tb_seclevel[NL802154_SECLEVEL_ATTR_CMD_FRAME])
> +			printf("%d ", nla_get_u32(tb_seclevel[NL802154_SECLEVEL_ATTR_CMD_FRAME]));
> +		if (tb_seclevel[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE])
> +			printf("%d ", nla_get_u8(tb_seclevel[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]));
> +	}
> +
> +	printf("\n");
> +
> +	return NL_SKIP;
> +}
> +
> +static int handle_seclevel_dump(struct nl802154_state *state,
> +				struct nl_cb *cb,
> +				struct nl_msg *msg,
> +				int argc, char **argv,
> +				enum id_input id)
> +{
> +	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_seclevel_handler, NULL);
> +	return 0;
> +}
> +COMMAND(seclevel, dump, NULL,
> +	NL802154_CMD_GET_SEC_LEVEL, NLM_F_DUMP, CIB_NETDEV, handle_seclevel_dump,
> +	NULL);
> +
> +static int handle_seclevel_add(struct nl802154_state *state, struct nl_cb *cb,
> +			       struct nl_msg *msg, int argc, char **argv,
> +			       enum id_input id)
> +{
> +	struct nl_msg *seclevel_msg;
> +	unsigned long levels, frame, cmd_id, dev_override;
> +	char *end;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* levels */
> +	levels = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	argc--;
> +	argv++;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* frame */
> +	frame = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	if (frame == NL802154_FRAME_CMD) {
> +		argc--;
> +		argv++;
> +
> +		if (argc < 1)
> +			return 1;
> +
> +		/* cmd_frame */
> +		cmd_id = strtoul(argv[0], &end, 0);
> +		if (*end != '\0')
> +			return 1;
> +	}
> +
> +	argc--;
> +	argv++;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* dev_override */
> +	dev_override = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	if (dev_override > UINT8_MAX)
> +		return 1;
> +
> +	seclevel_msg = nlmsg_alloc();
> +	if (!seclevel_msg)
> +		return -ENOMEM;
> +
> +	NLA_PUT_U32(seclevel_msg, NL802154_SECLEVEL_ATTR_LEVELS, levels);
> +	NLA_PUT_U32(seclevel_msg, NL802154_SECLEVEL_ATTR_FRAME, frame);
> +	if (frame == NL802154_FRAME_CMD)
> +		NLA_PUT_U32(seclevel_msg, NL802154_SECLEVEL_ATTR_CMD_FRAME, cmd_id);
> +	NLA_PUT_U8(seclevel_msg, NL802154_SECLEVEL_ATTR_DEV_OVERRIDE, dev_override);
> +
> +	nla_put_nested(msg, NL802154_ATTR_SEC_LEVEL, seclevel_msg);
> +	nlmsg_free(seclevel_msg);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	nlmsg_free(seclevel_msg);
> +	return -ENOBUFS;
> +}
> +COMMAND(seclevel, add, "<levels> <frame_type|3 <cmd_id>> <dev_override>", NL802154_CMD_NEW_SEC_LEVEL, 0, CIB_NETDEV,
> +	handle_seclevel_add, NULL);
> +COMMAND(seclevel, del, "<levels> <frame_type|3 <cmd_id>> <dev_override>", NL802154_CMD_DEL_SEC_LEVEL, 0, CIB_NETDEV,
> +	handle_seclevel_add, NULL);
> +
> +SECTION(device);
> +
> +static int print_device_handler(struct nl_msg *msg, void *arg)
> +{
> +	struct nlattr *tb[NL802154_ATTR_MAX + 1];
> +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
> +
> +	nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
> +		  genlmsg_attrlen(gnlh, 0), NULL);
> +
> +	if (tb[NL802154_ATTR_SEC_DEVICE]) {
> +		struct nlattr *tb_device[NL802154_DEV_ATTR_MAX + 1];
> +		static struct nla_policy device_policy[NL802154_DEV_ATTR_MAX + 1] = {
> +			[NL802154_DEV_ATTR_FRAME_COUNTER] = { NLA_U32 },
> +			[NL802154_DEV_ATTR_PAN_ID] = { .type = NLA_U16 },
> +			[NL802154_DEV_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
> +			[NL802154_DEV_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
> +			[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] = { NLA_U8 },
> +			[NL802154_DEV_ATTR_KEY_MODE] = { NLA_U32 },
> +		};
> +
> +		if (nla_parse_nested(tb_device, NL802154_DEV_ATTR_MAX,
> +				     tb[NL802154_ATTR_SEC_DEVICE],
> +				     device_policy)) {
> +			fprintf(stderr, "failed to parse nested attributes!\n");
> +			return NL_SKIP;
> +		}
> +
> +		printf("iwpan dev $WPAN_DEV device add ");
> +
> +		if (tb_device[NL802154_DEV_ATTR_FRAME_COUNTER])
> +			printf("0x%08lx ", nla_get_u32(tb_device[NL802154_DEV_ATTR_FRAME_COUNTER]));
> +		if (tb_device[NL802154_DEV_ATTR_PAN_ID])
> +			printf("0x%04lx ", le16toh(nla_get_u16(tb_device[NL802154_DEV_ATTR_PAN_ID])));
> +		if (tb_device[NL802154_DEV_ATTR_SHORT_ADDR])
> +			printf("0x%04lx ", le16toh(nla_get_u16(tb_device[NL802154_DEV_ATTR_SHORT_ADDR])));
> +		if (tb_device[NL802154_DEV_ATTR_EXTENDED_ADDR])
> +			printf("0x%016" PRIx64 " ", le64toh(nla_get_u64(tb_device[NL802154_DEV_ATTR_EXTENDED_ADDR])));
> +		if (tb_device[NL802154_DEV_ATTR_SECLEVEL_EXEMPT])
> +			printf("%d ", nla_get_u8(tb_device[NL802154_DEV_ATTR_SECLEVEL_EXEMPT]));
> +		if (tb_device[NL802154_DEV_ATTR_KEY_MODE])
> +			printf("%d ", nla_get_u32(tb_device[NL802154_DEV_ATTR_KEY_MODE]));
> +	}
> +
> +	printf("\n");
> +
> +	return NL_SKIP;
> +}
> +
> +static int handle_device_dump(struct nl802154_state *state,
> +				struct nl_cb *cb,
> +				struct nl_msg *msg,
> +				int argc, char **argv,
> +				enum id_input id)
> +{
> +	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_device_handler, NULL);
> +	return 0;
> +}
> +COMMAND(device, dump, NULL,
> +	NL802154_CMD_GET_SEC_DEV, NLM_F_DUMP, CIB_NETDEV, handle_device_dump,
> +	NULL);
> +
> +static int handle_device_add(struct nl802154_state *state, struct nl_cb *cb,
> +			     struct nl_msg *msg, int argc, char **argv,
> +			     enum id_input id)
> +{
> +	struct nl_msg *device_msg;
> +	unsigned long long extended_addr;
> +	unsigned long frame_counter, pan_id, short_addr,
> +		      seclevel_exempt, key_mode;
> +	char *end;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* frame_counter */
> +	frame_counter = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	argc--;
> +	argv++;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* pan_id */
> +	pan_id = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	if (pan_id > UINT16_MAX)
> +		return 1;
> +
> +	argc--;
> +	argv++;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* short_addr */
> +	short_addr = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	if (short_addr > UINT16_MAX)
> +		return 1;
> +
> +	argc--;
> +	argv++;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* extended_addr */
> +	extended_addr = strtoull(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	argc--;
> +	argv++;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* seclevel_exempt */
> +	seclevel_exempt = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	if (seclevel_exempt > UINT8_MAX)
> +		return 1;
> +
> +	argc--;
> +	argv++;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* key_mode */
> +	key_mode = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	device_msg = nlmsg_alloc();
> +	if (!device_msg)
> +		return -ENOMEM;
> +
> +	NLA_PUT_U32(device_msg, NL802154_DEV_ATTR_FRAME_COUNTER, frame_counter);
> +	NLA_PUT_U16(device_msg, NL802154_DEV_ATTR_PAN_ID, htole16(pan_id));
> +	NLA_PUT_U16(device_msg, NL802154_DEV_ATTR_SHORT_ADDR, htole16(short_addr));
> +	NLA_PUT_U64(device_msg, NL802154_DEV_ATTR_EXTENDED_ADDR, htole64(extended_addr));
> +	NLA_PUT_U8(device_msg, NL802154_DEV_ATTR_SECLEVEL_EXEMPT, seclevel_exempt);
> +	NLA_PUT_U32(device_msg, NL802154_DEV_ATTR_KEY_MODE, key_mode);
> +
> +	nla_put_nested(msg, NL802154_ATTR_SEC_DEVICE, device_msg);
> +	nlmsg_free(device_msg);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	nlmsg_free(device_msg);
> +	return -ENOBUFS;
> +}
> +COMMAND(device, add, "<frame_counter> <pan_id> <short_addr> <extended_addr> <seclevel_exempt> <key_mode>",
> +	NL802154_CMD_NEW_SEC_DEV, 0, CIB_NETDEV, handle_device_add, NULL);
> +
> +static int handle_device_del(struct nl802154_state *state, struct nl_cb *cb,
> +			     struct nl_msg *msg, int argc, char **argv,
> +			     enum id_input id)
> +{
> +	struct nl_msg *device_msg;
> +	unsigned long long extended_addr;
> +	char *end;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* extended_addr */
> +	extended_addr = strtoull(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	device_msg = nlmsg_alloc();
> +	if (!device_msg)
> +		return -ENOMEM;
> +
> +	NLA_PUT_U64(device_msg, NL802154_DEV_ATTR_EXTENDED_ADDR, htole64(extended_addr));
> +
> +	nla_put_nested(msg, NL802154_ATTR_SEC_DEVICE, device_msg);
> +	nlmsg_free(device_msg);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	nlmsg_free(device_msg);
> +	return -ENOBUFS;
> +}
> +COMMAND(device, del, "<extended_addr>",
> +	NL802154_CMD_DEL_SEC_DEV, 0, CIB_NETDEV, handle_device_del, NULL);
> +
> +SECTION(devkey);
> +
> +static int print_key_id(struct nlattr *tb) {
> +	struct nlattr *tb_key_id[NL802154_KEY_ID_ATTR_MAX + 1];
> +	static struct nla_policy key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
> +		[NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
> +		[NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
> +		[NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
> +		[NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
> +		[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
> +	};
> +
> +	nla_parse_nested(tb_key_id, NL802154_KEY_ID_ATTR_MAX, tb, key_id_policy);
> +
> +	if (tb_key_id[NL802154_KEY_ID_ATTR_MODE]) {
> +		enum nl802154_key_id_modes key_id_mode;
> +
> +		key_id_mode = nla_get_u32(tb_key_id[NL802154_KEY_ID_ATTR_MODE]);
> +		printf("%d ", key_id_mode);
> +		switch (key_id_mode) {
> +		case NL802154_KEY_ID_MODE_IMPLICIT:
> +			if (tb_key_id[NL802154_KEY_ID_ATTR_IMPLICIT]) {
> +				struct nlattr *tb_dev_addr[NL802154_DEV_ADDR_ATTR_MAX + 1];
> +				static struct nla_policy dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = {
> +					[NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 },
> +					[NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 },
> +					[NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 },
> +					[NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 },
> +				};
> +
> +				nla_parse_nested(tb_dev_addr, NL802154_DEV_ADDR_ATTR_MAX,
> +						 tb_key_id[NL802154_KEY_ID_ATTR_IMPLICIT],
> +						 dev_addr_policy);
> +
> +				if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_PAN_ID])
> +					printf("0x%04x ",
> +					       le16toh(nla_get_u16(tb_dev_addr[NL802154_DEV_ADDR_ATTR_PAN_ID])));
> +
> +				if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_MODE]) {
> +					enum nl802154_dev_addr_modes dev_addr_mode;
> +					dev_addr_mode = nla_get_u32(tb_dev_addr[NL802154_DEV_ADDR_ATTR_MODE]);
> +					printf("%d ", dev_addr_mode);
> +					switch (dev_addr_mode) {
> +					case NL802154_DEV_ADDR_SHORT:
> +						printf("0x%04x ",
> +						       le16toh(nla_get_u16(tb_dev_addr[NL802154_DEV_ADDR_ATTR_SHORT])));
> +						break;
> +					case NL802154_DEV_ADDR_EXTENDED:
> +						printf("0x%016" PRIx64 " ",
> +						       le64toh(nla_get_u64(tb_dev_addr[NL802154_DEV_ADDR_ATTR_SHORT])));
> +						break;
> +					default:
> +						/* TODO error handling */
> +						break;
> +					}
> +				}
> +			}
> +			break;
> +		case NL802154_KEY_ID_MODE_INDEX:
> +			if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
> +				printf("0x%02x ",
> +				       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
> +			break;
> +		case NL802154_KEY_ID_MODE_INDEX_SHORT:
> +			if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
> +				printf("0x%02x ",
> +				       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
> +
> +			if (tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_SHORT])
> +				printf("0x%08lx ",
> +				       le32toh(nla_get_u32(tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_SHORT])));
> +			break;
> +		case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
> +			if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
> +				printf("0x%02x ",
> +				       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
> +
> +			if (tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])
> +				printf("0x%016" PRIx64 " ",
> +				       le64toh(nla_get_u64(tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])));
> +			break;
> +		default:
> +			/* TODO error handling */
> +			return 0;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int print_devkey_handler(struct nl_msg *msg, void *arg)
> +{
> +	struct nlattr *tb[NL802154_ATTR_MAX + 1];
> +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
> +
> +	nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
> +		  genlmsg_attrlen(gnlh, 0), NULL);
> +
> +	if (tb[NL802154_ATTR_SEC_DEVKEY]) {
> +		struct nlattr *tb_devkey[NL802154_DEVKEY_ATTR_MAX + 1];
> +		static struct nla_policy devkey_policy[NL802154_DEVKEY_ATTR_MAX + 1] = {
> +			[NL802154_DEVKEY_ATTR_FRAME_COUNTER] = { NLA_U32 },
> +			[NL802154_DEVKEY_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
> +			[NL802154_DEVKEY_ATTR_ID] = { .type = NLA_NESTED },
> +		};
> +
> +		if (nla_parse_nested(tb_devkey, NL802154_DEVKEY_ATTR_MAX,
> +				     tb[NL802154_ATTR_SEC_DEVKEY],
> +				     devkey_policy)) {
> +			fprintf(stderr, "failed to parse nested attributes!\n");
> +			return NL_SKIP;
> +		}
> +
> +		printf("iwpan dev $WPAN_DEV devkey add ");
> +
> +		if (tb_devkey[NL802154_DEV_ATTR_FRAME_COUNTER])
> +			printf("0x%08lx ", nla_get_u32(tb_devkey[NL802154_DEVKEY_ATTR_FRAME_COUNTER]));
> +		if (tb_devkey[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
> +			printf("0x%016" PRIx64 " ", le64toh(nla_get_u64(tb_devkey[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])));
> +
> +		if (tb_devkey[NL802154_DEVKEY_ATTR_ID])
> +			print_key_id(tb_devkey[NL802154_DEVKEY_ATTR_ID]);
> +	}
> +
> +	printf("\n");
> +
> +	return NL_SKIP;
> +}
> +
> +static int handle_devkey_dump(struct nl802154_state *state,
> +				struct nl_cb *cb,
> +				struct nl_msg *msg,
> +				int argc, char **argv,
> +				enum id_input id)
> +{
> +	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_devkey_handler, NULL);
> +	return 0;
> +}
> +COMMAND(devkey, dump, NULL,
> +	NL802154_CMD_GET_SEC_DEVKEY, NLM_F_DUMP, CIB_NETDEV, handle_devkey_dump,
> +	NULL);
> +
> +static int handle_devkey_add(struct nl802154_state *state, struct nl_cb *cb,
> +			     struct nl_msg *msg, int argc, char **argv,
> +			     enum id_input id)
> +{
> +	struct nl_msg *devkey_msg = NULL;
> +	unsigned long long extended_addr;
> +	unsigned long frame_counter;
> +	char *end;
> +	int ret;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* frame_counter */
> +	frame_counter = strtoul(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	argc--;
> +	argv++;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* extended_addr */
> +	extended_addr = strtoull(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	argc--;
> +	argv++;
> +
> +	devkey_msg = nlmsg_alloc();
> +	if (!devkey_msg)
> +		return -ENOMEM;
> +
> +	NLA_PUT_U32(devkey_msg, NL802154_DEVKEY_ATTR_FRAME_COUNTER, frame_counter);
> +	NLA_PUT_U64(devkey_msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR, htole64(extended_addr));
> +
> +	ret = handle_parse_key_id(devkey_msg, NL802154_DEVKEY_ATTR_ID, &argc, &argv);
> +	if (ret) {
> +		nlmsg_free(devkey_msg);
> +		return ret;
> +	}
> +
> +	nla_put_nested(msg, NL802154_ATTR_SEC_DEVKEY, devkey_msg);
> +	nlmsg_free(devkey_msg);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	nlmsg_free(devkey_msg);
> +	return -ENOBUFS;
> +
> +}
> +COMMAND(devkey, add, "<frame_counter> <extended_addr> "
> +	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
> +	"<1 <index>>|"
> +	"<2 <index> <source_short>>|"
> +	"<3 <index> <source_extended>>",
> +	NL802154_CMD_NEW_SEC_DEVKEY, 0, CIB_NETDEV, handle_devkey_add, NULL);
> +
> +static int handle_devkey_del(struct nl802154_state *state, struct nl_cb *cb,
> +			     struct nl_msg *msg, int argc, char **argv,
> +			     enum id_input id)
> +{
> +	struct nl_msg *devkey_msg = NULL;
> +	unsigned long long extended_addr;
> +	char *end;
> +	int ret;
> +
> +	if (argc < 1)
> +		return 1;
> +
> +	/* extended_addr */
> +	extended_addr = strtoull(argv[0], &end, 0);
> +	if (*end != '\0')
> +		return 1;
> +
> +	argc--;
> +	argv++;
> +
> +	devkey_msg = nlmsg_alloc();
> +	if (!devkey_msg)
> +		return -ENOMEM;
> +
> +	NLA_PUT_U64(devkey_msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR, htole64(extended_addr));
> +
> +	ret = handle_parse_key_id(devkey_msg, NL802154_DEVKEY_ATTR_ID, &argc, &argv);
> +	if (ret) {
> +		nlmsg_free(devkey_msg);
> +		return ret;
> +	}
> +
> +	nla_put_nested(msg, NL802154_ATTR_SEC_DEVKEY, devkey_msg);
> +	nlmsg_free(devkey_msg);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	nlmsg_free(devkey_msg);
> +	return -ENOBUFS;
> +
> +}
> +COMMAND(devkey, del, "<extended_addr> "
> +	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
> +	"<1 <index>>|"
> +	"<2 <index> <source_short>>|"
> +	"<3 <index> <source_extended>>",
> +	NL802154_CMD_DEL_SEC_DEVKEY, 0, CIB_NETDEV, handle_devkey_del, NULL);
> +
> +SECTION(key);
> +
> +static void key_to_str(char *key, unsigned char *arg)
> +{
> +	int i, l;
> +
> +	l = 0;
> +	for (i = 0; i < NL802154_KEY_SIZE ; i++) {
> +		if (i == 0) {
> +			sprintf(key+l, "%02x", arg[i]);
> +			l += 2;
> +		} else {
> +			sprintf(key+l, ":%02x", arg[i]);
> +			l += 3;
> +		}
> +	}
> +}
> +
> +static int print_key_handler(struct nl_msg *msg, void *arg)
> +{
> +	struct nlattr *tb[NL802154_ATTR_MAX + 1];
> +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
> +
> +	nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
> +		  genlmsg_attrlen(gnlh, 0), NULL);
> +
> +	if (tb[NL802154_ATTR_SEC_KEY]) {
> +		struct nlattr *tb_key[NL802154_KEY_ATTR_MAX + 1];
> +		static struct nla_policy key_policy[NL802154_KEY_ATTR_MAX + 1] = {
> +			[NL802154_KEY_ATTR_ID] = { NLA_NESTED },
> +			[NL802154_KEY_ATTR_USAGE_FRAMES] = { NLA_U8 },
> +			[NL802154_KEY_ATTR_USAGE_CMDS] = { .minlen = NL802154_CMD_FRAME_NR_IDS / 8 },
> +			[NL802154_KEY_ATTR_BYTES] = { .minlen = NL802154_KEY_SIZE },
> +		};
> +
> +		if (nla_parse_nested(tb_key, NL802154_KEY_ATTR_MAX,
> +				     tb[NL802154_ATTR_SEC_KEY],
> +				     key_policy)) {
> +			fprintf(stderr, "failed to parse nested attributes!\n");
> +			return NL_SKIP;
> +		}
> +
> +		printf("iwpan dev $WPAN_DEV key add ");
> +
> +		if (tb_key[NL802154_KEY_ATTR_USAGE_FRAMES])
> +			printf("0x%02x ", nla_get_u8(tb_key[NL802154_KEY_ATTR_USAGE_FRAMES]));
> +
> +		if (tb_key[NL802154_KEY_ATTR_USAGE_CMDS]) {
> +			uint32_t cmds[NL802154_CMD_FRAME_NR_IDS / 32];
> +
> +			nla_memcpy(cmds, tb_key[NL802154_KEY_ATTR_USAGE_CMDS],
> +				   NL802154_CMD_FRAME_NR_IDS / 8);
> +			printf("0x%08x ", cmds[7]);
> +		}
> +
> +		if (tb_key[NL802154_KEY_ATTR_BYTES]) {
> +			uint8_t key[NL802154_KEY_SIZE];
> +			char key_str[512] = "";
> +
> +			nla_memcpy(key, tb_key[NL802154_KEY_ATTR_BYTES],
> +				   NL802154_KEY_SIZE);
> +
> +			key_to_str(key_str, key);
> +			printf("%s ", key_str);
> +		}
> +
> +		if (tb_key[NL802154_KEY_ATTR_ID])
> +			print_key_id(tb_key[NL802154_KEY_ATTR_ID]);
> +	}
> +
> +	printf("\n");
> +
> +	return NL_SKIP;
> +}
> +
> +static int handle_key_dump(struct nl802154_state *state,
> +			   struct nl_cb *cb,
> +			   struct nl_msg *msg,
> +			   int argc, char **argv,
> +			   enum id_input id)
> +{
> +	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_key_handler, NULL);
> +	return 0;
> +}
> +COMMAND(key, dump, NULL,
> +	NL802154_CMD_GET_SEC_KEY, NLM_F_DUMP, CIB_NETDEV, handle_key_dump,
> +	NULL);
> +
> +#define BIT(x)  (1 << (x))
> +
> +static int str_to_key(unsigned char *key, char *arg)
> +{
> +	int i;
> +
> +	for (i = 0; i < NL802154_KEY_SIZE; i++) {
> +		int temp;
> +		char *cp = strchr(arg, ':');
> +		if (cp) {
> +			*cp = 0;
> +			cp++;
> +		}
> +		if (sscanf(arg, "%x", &temp) != 1)
> +			return -1;
> +		if (temp < 0 || temp > 255)
> +			return -1;
> +
> +		key[i] = temp;
> +		if (!cp)
> +			break;
> +		arg = cp;
> +	}
> +	if (i < NL802154_KEY_SIZE - 1)
> +		return -1;
> +
> +	return 0;
> +}
> +
> +static int handle_key_add(struct nl802154_state *state, struct nl_cb *cb,
> +			     struct nl_msg *msg, int argc, char **argv,
> +			     enum id_input id)
> +{
> +	struct nl_msg *key_msg = NULL;
> +	uint8_t key_bytes[NL802154_KEY_SIZE] = { };
> +	uint32_t commands[NL802154_CMD_FRAME_NR_IDS / 32] = { };
> +	unsigned long tmp;
> +	char *end;
> +	int ret, i;
> +
> +	key_msg = nlmsg_alloc();
> +	if (!key_msg)
> +		return -ENOMEM;
> +
> +	if (argc < 1) {
> +		nlmsg_free(key_msg);
> +		return 1;
> +	}
> +
> +	/* frame_types */
> +	tmp = strtoul(argv[0], &end, 0);
> +	if (*end != '\0') {
> +		nlmsg_free(key_msg);
> +		return 1;
> +	}
> +
> +	if (tmp > UINT8_MAX) {
> +		nlmsg_free(key_msg);
> +		return 1;
> +	}
> +
> +	NLA_PUT_U8(key_msg, NL802154_KEY_ATTR_USAGE_FRAMES, tmp);
> +
> +	argc--;
> +	argv++;
> +
> +	if (tmp & BIT(NL802154_FRAME_CMD)) {
> +		if (argc < 1) {
> +			nlmsg_free(key_msg);
> +			return 1;
> +		}
> +
> +		/* commands[7] */
> +		commands[7] = strtoul(argv[0], &end, 0);
> +		if (*end != '\0') {
> +			nlmsg_free(key_msg);
> +			return 1;
> +		}
> +
> +		NLA_PUT(key_msg, NL802154_KEY_ATTR_USAGE_CMDS,
> +			NL802154_CMD_FRAME_NR_IDS / 8, commands);
> +
> +		argc--;
> +		argv++;
> +	}
> +
> +	if (argc < 1) {
> +		nlmsg_free(key_msg);
> +		return 1;
> +	}
> +
> +	str_to_key(key_bytes, argv[0]);
> +
> +	NLA_PUT(key_msg, NL802154_KEY_ATTR_BYTES, NL802154_KEY_SIZE, key_bytes);
> +
> +	argc--;
> +	argv++;
> +
> +	ret = handle_parse_key_id(key_msg, NL802154_KEY_ATTR_ID, &argc, &argv);
> +	if (ret) {
> +		nlmsg_free(key_msg);
> +		return ret;
> +	}
> +
> +	nla_put_nested(msg, NL802154_ATTR_SEC_KEY, key_msg);
> +	nlmsg_free(key_msg);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	nlmsg_free(key_msg);
> +	return -ENOBUFS;
> +
> +}
> +COMMAND(key, add, "<frame_types <if 0x4 is set commands[7]>>> <key <hex as 00:11:..>> "
> +	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
> +	"<1 <index>>|"
> +	"<2 <index> <source_short>>|"
> +	"<3 <index> <source_extended>>",
> +	NL802154_CMD_NEW_SEC_KEY, 0, CIB_NETDEV, handle_key_add, NULL);
> +
> +static int handle_key_del(struct nl802154_state *state, struct nl_cb *cb,
> +			     struct nl_msg *msg, int argc, char **argv,
> +			     enum id_input id)
> +{
> +	struct nl_msg *key_msg = NULL;
> +	int ret;
> +
> +	key_msg = nlmsg_alloc();
> +	if (!key_msg)
> +		return -ENOMEM;
> +
> +	ret = handle_parse_key_id(key_msg, NL802154_KEY_ATTR_ID, &argc, &argv);
> +	if (ret) {
> +		nlmsg_free(key_msg);
> +		return ret;
> +	}
> +
> +	nla_put_nested(msg, NL802154_ATTR_SEC_KEY, key_msg);
> +	nlmsg_free(key_msg);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	nlmsg_free(key_msg);
> +	return -ENOBUFS;
> +
> +}
> +COMMAND(key, del,
> +	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
> +	"<1 <index>>|"
> +	"<2 <index> <source_short>>|"
> +	"<3 <index> <source_extended>>",
> +	NL802154_CMD_DEL_SEC_KEY, 0, CIB_NETDEV, handle_key_del, NULL);

regards
Stefan Schmidt
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alexander Aring Sept. 30, 2015, 5:46 p.m. UTC | #2
Hi,

On Wed, Sep 30, 2015 at 04:46:30PM +0200, Stefan Schmidt wrote:
> Hello.
> 
> A really huge patch. I will start on it. Not sure I can do a full review in
> one go though.
> 
> On 28/09/15 09:25, Alexander Aring wrote:
> >This patch introduce support for the experimental seucirty support for
> 
> Type. Security.
> >nl802154. We currently support add/del settings for manipulating
> >security table entries. The dump functionality is a "really" keep it
> 
> is really a
> >short and stupid handling, the dump will printout the printout the right
> 
> dump will printout the right calls to add the entry

ok.

> >add calls which was called to add the entry. This can be used for
> >storing the current security tables by some script. The interface
> >argument is replaced by $WPAN_DEV variable, so it's possible to move one
> >interface configuration to another one.
> >
> >Signed-off-by: Alexander Aring <alex.aring@gmail.com>
> >---
> >  src/Makefile.am |    1 +
> >  src/interface.c |  100 +++++
> >  src/nl802154.h  |  191 ++++++++++
> >  src/security.c  | 1118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 1410 insertions(+)
> >  create mode 100644 src/security.c
> >
> >diff --git a/src/Makefile.am b/src/Makefile.am
> >index 2d54576..b2177a2 100644
> >--- a/src/Makefile.am
> >+++ b/src/Makefile.am
> >@@ -9,6 +9,7 @@ iwpan_SOURCES = \
> >  	interface.c \
> >  	phy.c \
> >  	mac.c \
> >+	security.c \
> >  	nl_extras.h \
> >  	nl802154.h
> >diff --git a/src/interface.c b/src/interface.c
> >index 85d40a8..076e7c3 100644
> >--- a/src/interface.c
> >+++ b/src/interface.c
> >@@ -10,6 +10,7 @@
> >  #include <netlink/msg.h>
> >  #include <netlink/attr.h>
> >+#define CONFIG_IEEE802154_NL802154_EXPERIMENTAL
> >  #include "nl802154.h"
> >  #include "nl_extras.h"
> >  #include "iwpan.h"
> >@@ -226,6 +227,105 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
> >  	if (tb_msg[NL802154_ATTR_ACKREQ_DEFAULT])
> >  		printf("%s\tackreq_default %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_ACKREQ_DEFAULT]));
> >+	if (tb_msg[NL802154_ATTR_SEC_ENABLED])
> >+		printf("%s\tsecurity %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_SEC_ENABLED]));
> >+	if (tb_msg[NL802154_ATTR_SEC_OUT_LEVEL])
> >+		printf("%s\tout_level %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_SEC_OUT_LEVEL]));
> >+	if (tb_msg[NL802154_ATTR_SEC_OUT_KEY_ID]) {
> >+		struct nlattr *tb_key_id[NL802154_KEY_ID_ATTR_MAX + 1];
> >+		static struct nla_policy key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
> >+		        [NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
> >+		        [NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
> >+		        [NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
> >+		        [NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
> >+		        [NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
> >+		};
> >+
> >+		nla_parse_nested(tb_key_id, NL802154_KEY_ID_ATTR_MAX,
> >+				 tb_msg[NL802154_ATTR_SEC_OUT_KEY_ID], key_id_policy);
> >+		printf("%s\tout_key_id\n", indent);
> >+
> >+		if (tb_key_id[NL802154_KEY_ID_ATTR_MODE]) {
> >+			enum nl802154_key_id_modes key_id_mode;
> >+
> >+			key_id_mode = nla_get_u32(tb_key_id[NL802154_KEY_ID_ATTR_MODE]);
...
> >+enum nl802154_dev_addr_modes {
> >+	NL802154_DEV_ADDR_NONE,
> >+	__NL802154_DEV_ADDR_INVALID,
> >+	NL802154_DEV_ADDR_SHORT,
> >+	NL802154_DEV_ADDR_EXTENDED,
> >+
> >+	/* keep last */
> >+	__NL802154_DEV_ADDR_AFTER_LAST,
> 
> Hmm, why bother with AFTER_LAST here and not just use ADDR_MAX as sentinal
> for this enum? Looks redundant to me.
> 

At first I want to keep the wireless nl80211 userspace uapi header,
which declarate this hidden __FOOBAR enum in "mostly" every their enum
declaration. See [0], I simple adapt this convention for nl802154.

The reason is probaly they want some automatic mechanism to increment
the MAX value. Also it differs if you declare an array for netlink
policy [1] or give the length argument for parsing [2], which occurs
sometimes in off-by-one errors. 

...
> >+
> >+static int handle_out_key_id_set(struct nl802154_state *state, struct nl_cb *cb,
> >+				 struct nl_msg *msg, int argc, char **argv,
> >+				 enum id_input id)
> >+{
> >+	return handle_parse_key_id(msg, NL802154_ATTR_SEC_OUT_KEY_ID, &argc, &argv);
> >+
> >+}
> >+COMMAND(set, out_key_id,
> >+	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
> >+	"<1 <index>>|"
> >+	"<2 <index> <source_short>>|"
> >+	"<3 <index> <source_extended>>",
> 
> What are these extra >>| for ?
> 

The numbers are acutally the enums value which is usually some specific
mode, in this case the key_id_mode. Of course each of them has a proper
name and we should add some helper functions to map these enums to a
string.

The '>' should symbolize brackets and the '|' an "xor". This help text
shows you can use key_id_mode '0' xor '1' xor '2' ... and each of them
has different parameters.

> >+	NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
> >+	handle_out_key_id_set, NULL);
> >+
> >+static int handle_out_seclevel_set(struct nl802154_state *state, struct nl_cb *cb,
> >+				   struct nl_msg *msg, int argc, char **argv,
> >+				   enum id_input id)
> >+{
> >+	unsigned long seclevel;
> >+	char *end;
> >+
> >+	if (argc < 1)
> >+		return 1;
> >+
> >+	/* seclevel */
> >+	seclevel = strtoul(argv[0], &end, 0);
> >+	if (*end != '\0')
> >+		return 1;
> >+
> >+	NLA_PUT_U32(msg, NL802154_ATTR_SEC_OUT_LEVEL, seclevel);
> >+
> >+	return 0;
> >+
> >+nla_put_failure:
> >+	return -ENOBUFS;
> >+}
> >+COMMAND(set, out_level, "<out_level>", NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
> >+	handle_out_seclevel_set, NULL);
> >+
> >+static int handle_frame_counter_set(struct nl802154_state *state, struct nl_cb *cb,
> >+				   struct nl_msg *msg, int argc, char **argv,
> >+				   enum id_input id)
> >+{
> >+	unsigned long frame_counter;
> >+	char *end;
> >+
> >+	/* frame_counter */
> 
> This command and the other above (index, etc) which just state the variable
> name below are not really needed as they give no extra information imho.

ok.

- Alex

[0] http://lxr.free-electrons.com/source/include/uapi/linux/nl80211.h#L3159
    http://lxr.free-electrons.com/source/include/uapi/linux/nl80211.h#L3518
[1] http://lxr.free-electrons.com/source/net/ieee802154/nl802154.c#L196
[2] http://lxr.free-electrons.com/source/net/ieee802154/nl802154.c#L41
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index 2d54576..b2177a2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,6 +9,7 @@  iwpan_SOURCES = \
 	interface.c \
 	phy.c \
 	mac.c \
+	security.c \
 	nl_extras.h \
 	nl802154.h
 
diff --git a/src/interface.c b/src/interface.c
index 85d40a8..076e7c3 100644
--- a/src/interface.c
+++ b/src/interface.c
@@ -10,6 +10,7 @@ 
 #include <netlink/msg.h>
 #include <netlink/attr.h>
 
+#define CONFIG_IEEE802154_NL802154_EXPERIMENTAL
 #include "nl802154.h"
 #include "nl_extras.h"
 #include "iwpan.h"
@@ -226,6 +227,105 @@  static int print_iface_handler(struct nl_msg *msg, void *arg)
 	if (tb_msg[NL802154_ATTR_ACKREQ_DEFAULT])
 		printf("%s\tackreq_default %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_ACKREQ_DEFAULT]));
 
+	if (tb_msg[NL802154_ATTR_SEC_ENABLED])
+		printf("%s\tsecurity %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_SEC_ENABLED]));
+	if (tb_msg[NL802154_ATTR_SEC_OUT_LEVEL])
+		printf("%s\tout_level %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_SEC_OUT_LEVEL]));
+	if (tb_msg[NL802154_ATTR_SEC_OUT_KEY_ID]) {
+		struct nlattr *tb_key_id[NL802154_KEY_ID_ATTR_MAX + 1];
+		static struct nla_policy key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
+		        [NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
+		        [NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
+		        [NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
+		        [NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
+		        [NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
+		};
+
+		nla_parse_nested(tb_key_id, NL802154_KEY_ID_ATTR_MAX,
+				 tb_msg[NL802154_ATTR_SEC_OUT_KEY_ID], key_id_policy);
+		printf("%s\tout_key_id\n", indent);
+
+		if (tb_key_id[NL802154_KEY_ID_ATTR_MODE]) {
+			enum nl802154_key_id_modes key_id_mode;
+
+			key_id_mode = nla_get_u32(tb_key_id[NL802154_KEY_ID_ATTR_MODE]);
+			switch (key_id_mode) {
+			case NL802154_KEY_ID_MODE_IMPLICIT:
+				printf("%s\t\tmode implicit\n", indent);
+				if (tb_key_id[NL802154_KEY_ID_ATTR_IMPLICIT]) {
+					struct nlattr *tb_dev_addr[NL802154_DEV_ADDR_ATTR_MAX + 1];
+					static struct nla_policy dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = {
+						[NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 },
+						[NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 },
+						[NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 },
+						[NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 },
+					};
+
+					nla_parse_nested(tb_dev_addr, NL802154_DEV_ADDR_ATTR_MAX,
+							 tb_key_id[NL802154_KEY_ID_ATTR_IMPLICIT],
+							 dev_addr_policy);
+
+					if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_PAN_ID])
+						printf("%s\t\tpan_id 0x%04x\n", indent,
+						       le16toh(nla_get_u16(tb_dev_addr[NL802154_DEV_ADDR_ATTR_PAN_ID])));
+
+					if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_MODE]) {
+						enum nl802154_dev_addr_modes dev_addr_mode;
+						dev_addr_mode = nla_get_u32(tb_dev_addr[NL802154_DEV_ADDR_ATTR_MODE]);
+						printf("%s\t\taddr_mode %d\n", indent, dev_addr_mode);
+						switch (dev_addr_mode) {
+						case NL802154_DEV_ADDR_SHORT:
+							if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_SHORT])
+								printf("%s\t\tshort_addr 0x%04x\n", indent,
+								       le16toh(nla_get_u16(tb_dev_addr[NL802154_DEV_ADDR_ATTR_SHORT])));
+							break;
+						case NL802154_DEV_ADDR_EXTENDED:
+							if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_EXTENDED])
+								printf("%s\t\textended_addr 0x%016" PRIx64 "\n", indent,
+								       le64toh(nla_get_u64(tb_dev_addr[NL802154_DEV_ADDR_ATTR_EXTENDED])));
+							break;
+						default:
+							printf("%s\t\tunkown address\n", indent);
+							break;
+						}
+					}
+				}
+				break;
+			case NL802154_KEY_ID_MODE_INDEX:
+				printf("%s\t\tmode index\n", indent);
+				if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
+					printf("%s\t\tindex 0x%02x\n", indent,
+					       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
+				break;
+			case NL802154_KEY_ID_MODE_INDEX_SHORT:
+				printf("%s\t\tmode index_short\n", indent);
+				if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
+					printf("%s\t\tindex 0x%02x\n", indent,
+					       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
+
+				if (tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_SHORT])
+					printf("%s\t\tsource_short 0x%08lx\n", indent,
+					       le32toh(nla_get_u32(tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_SHORT])));
+				break;
+			case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
+				printf("%s\t\tmode index_extended\n", indent);
+				if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
+					printf("%s\t\tindex 0x%02x\n", indent,
+					       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
+
+				if (tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])
+					printf("%s\t\tsource_extended 0x%" PRIx64 "\n", indent,
+					       le64toh(nla_get_u64(tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])));
+				break;
+			default:
+				printf("%s\t\tkey_mode unknown\n", indent);
+			}
+		}
+	}
+
+	if (tb_msg[NL802154_ATTR_SEC_FRAME_COUNTER])
+		printf("%s\tframe_counter 0x%08lx\n", indent, be32toh(nla_get_u32(tb_msg[NL802154_ATTR_SEC_FRAME_COUNTER])));
+
 	return NL_SKIP;
 }
 
diff --git a/src/nl802154.h b/src/nl802154.h
index cf2713d..32cb3e5 100644
--- a/src/nl802154.h
+++ b/src/nl802154.h
@@ -56,6 +56,22 @@  enum nl802154_commands {
 
 	/* add new commands above here */
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+	NL802154_CMD_SET_SEC_PARAMS,
+	NL802154_CMD_GET_SEC_KEY,		/* can dump */
+	NL802154_CMD_NEW_SEC_KEY,
+	NL802154_CMD_DEL_SEC_KEY,
+	NL802154_CMD_GET_SEC_DEV,		/* can dump */
+	NL802154_CMD_NEW_SEC_DEV,
+	NL802154_CMD_DEL_SEC_DEV,
+	NL802154_CMD_GET_SEC_DEVKEY,		/* can dump */
+	NL802154_CMD_NEW_SEC_DEVKEY,
+	NL802154_CMD_DEL_SEC_DEVKEY,
+	NL802154_CMD_GET_SEC_LEVEL,		/* can dump */
+	NL802154_CMD_NEW_SEC_LEVEL,
+	NL802154_CMD_DEL_SEC_LEVEL,
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 	/* used to define NL802154_CMD_MAX below */
 	__NL802154_CMD_AFTER_LAST,
 	NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
@@ -110,6 +126,18 @@  enum nl802154_attrs {
 
 	/* add attributes here, update the policy in nl802154.c */
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+	NL802154_ATTR_SEC_ENABLED,
+	NL802154_ATTR_SEC_OUT_LEVEL,
+	NL802154_ATTR_SEC_OUT_KEY_ID,
+	NL802154_ATTR_SEC_FRAME_COUNTER,
+
+	NL802154_ATTR_SEC_LEVEL,
+	NL802154_ATTR_SEC_DEVICE,
+	NL802154_ATTR_SEC_DEVKEY,
+	NL802154_ATTR_SEC_KEY,
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 	__NL802154_ATTR_AFTER_LAST,
 	NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
 };
@@ -247,4 +275,167 @@  enum nl802154_supported_bool_states {
 	NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1
 };
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+
+enum nl802154_dev_addr_modes {
+	NL802154_DEV_ADDR_NONE,
+	__NL802154_DEV_ADDR_INVALID,
+	NL802154_DEV_ADDR_SHORT,
+	NL802154_DEV_ADDR_EXTENDED,
+
+	/* keep last */
+	__NL802154_DEV_ADDR_AFTER_LAST,
+	NL802154_DEV_ADDR_MAX = __NL802154_DEV_ADDR_AFTER_LAST - 1
+};
+
+enum nl802154_dev_addr_attrs {
+	NL802154_DEV_ADDR_ATTR_UNSPEC,
+
+	NL802154_DEV_ADDR_ATTR_PAN_ID,
+	NL802154_DEV_ADDR_ATTR_MODE,
+	NL802154_DEV_ADDR_ATTR_SHORT,
+	NL802154_DEV_ADDR_ATTR_EXTENDED,
+
+	/* keep last */
+	__NL802154_DEV_ADDR_ATTR_AFTER_LAST,
+	NL802154_DEV_ADDR_ATTR_MAX = __NL802154_DEV_ADDR_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_key_id_modes {
+	NL802154_KEY_ID_MODE_IMPLICIT,
+	NL802154_KEY_ID_MODE_INDEX,
+	NL802154_KEY_ID_MODE_INDEX_SHORT,
+	NL802154_KEY_ID_MODE_INDEX_EXTENDED,
+
+	/* keep last */
+	__NL802154_KEY_ID_MODE_AFTER_LAST,
+	NL802154_KEY_ID_MODE_MAX = __NL802154_KEY_ID_MODE_AFTER_LAST - 1
+};
+
+enum nl802154_key_id_attrs {
+	NL802154_KEY_ID_ATTR_UNSPEC,
+
+	NL802154_KEY_ID_ATTR_MODE,
+	NL802154_KEY_ID_ATTR_INDEX,
+	NL802154_KEY_ID_ATTR_IMPLICIT,
+	NL802154_KEY_ID_ATTR_SOURCE_SHORT,
+	NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
+
+	/* keep last */
+	__NL802154_KEY_ID_ATTR_AFTER_LAST,
+	NL802154_KEY_ID_ATTR_MAX = __NL802154_KEY_ID_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_seclevels {
+	NL802154_SECLEVEL_NONE,
+	NL802154_SECLEVEL_MIC32,
+	NL802154_SECLEVEL_MIC64,
+	NL802154_SECLEVEL_MIC128,
+	NL802154_SECLEVEL_ENC,
+	NL802154_SECLEVEL_ENC_MIC32,
+	NL802154_SECLEVEL_ENC_MIC64,
+	NL802154_SECLEVEL_ENC_MIC128,
+
+	/* keep last */
+	__NL802154_SECLEVEL_AFTER_LAST,
+	NL802154_SECLEVEL_MAX = __NL802154_SECLEVEL_AFTER_LAST - 1
+};
+
+enum nl802154_frames {
+	NL802154_FRAME_BEACON,
+	NL802154_FRAME_DATA,
+	NL802154_FRAME_ACK,
+	NL802154_FRAME_CMD,
+
+	/* keep last */
+	__NL802154_FRAME_AFTER_LAST,
+	NL802154_FRAME_MAX = __NL802154_FRAME_AFTER_LAST - 1
+};
+
+enum nl802154_cmd_frames {
+	__NL802154_CMD_FRAME_INVALID,
+	NL802154_CMD_FRAME_ASSOC_REQUEST,
+	NL802154_CMD_FRAME_ASSOC_RESPONSE,
+	NL802154_CMD_FRAME_DISASSOC_NOTIFY,
+	NL802154_CMD_FRAME_DATA_REQUEST,
+	NL802154_CMD_FRAME_PAN_ID_CONFLICT_NOTIFY,
+	NL802154_CMD_FRAME_ORPHAN_NOTIFY,
+	NL802154_CMD_FRAME_BEACON_REQUEST,
+	NL802154_CMD_FRAME_COORD_REALIGNMENT,
+	NL802154_CMD_FRAME_GTS_REQUEST,
+
+	/* keep last */
+	__NL802154_CMD_FRAME_AFTER_LAST,
+	NL802154_CMD_FRAME_MAX = __NL802154_CMD_FRAME_AFTER_LAST - 1
+};
+
+enum nl802154_seclevel_attrs {
+	NL802154_SECLEVEL_ATTR_UNSPEC,
+
+	NL802154_SECLEVEL_ATTR_LEVELS,
+	NL802154_SECLEVEL_ATTR_FRAME,
+	NL802154_SECLEVEL_ATTR_CMD_FRAME,
+	NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
+
+	/* keep last */
+	__NL802154_SECLEVEL_ATTR_AFTER_LAST,
+	NL802154_SECLEVEL_ATTR_MAX = __NL802154_SECLEVEL_ATTR_AFTER_LAST - 1
+};
+
+/* TODO what is this? couldn't find in mib */
+enum {
+	NL802154_DEVKEY_IGNORE,
+	NL802154_DEVKEY_RESTRICT,
+	NL802154_DEVKEY_RECORD,
+
+	/* keep last */
+	__NL802154_DEVKEY_AFTER_LAST,
+	NL802154_DEVKEY_MAX = __NL802154_DEVKEY_AFTER_LAST - 1
+};
+
+enum nl802154_dev {
+	NL802154_DEV_ATTR_UNSPEC,
+
+	NL802154_DEV_ATTR_FRAME_COUNTER,
+	NL802154_DEV_ATTR_PAN_ID,
+	NL802154_DEV_ATTR_SHORT_ADDR,
+	NL802154_DEV_ATTR_EXTENDED_ADDR,
+	NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
+	NL802154_DEV_ATTR_KEY_MODE,
+
+	/* keep last */
+	__NL802154_DEV_ATTR_AFTER_LAST,
+	NL802154_DEV_ATTR_MAX = __NL802154_DEV_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_devkey {
+	NL802154_DEVKEY_ATTR_UNSPEC,
+
+	NL802154_DEVKEY_ATTR_FRAME_COUNTER,
+	NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
+	NL802154_DEVKEY_ATTR_ID,
+
+	/* keep last */
+	__NL802154_DEVKEY_ATTR_AFTER_LAST,
+	NL802154_DEVKEY_ATTR_MAX = __NL802154_DEVKEY_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_key {
+	NL802154_KEY_ATTR_UNSPEC,
+
+	NL802154_KEY_ATTR_ID,
+	NL802154_KEY_ATTR_USAGE_FRAMES,
+	NL802154_KEY_ATTR_USAGE_CMDS,
+	NL802154_KEY_ATTR_BYTES,
+
+	/* keep last */
+	__NL802154_KEY_ATTR_AFTER_LAST,
+	NL802154_KEY_ATTR_MAX = __NL802154_KEY_ATTR_AFTER_LAST - 1
+};
+
+#define NL802154_KEY_SIZE		16
+#define NL802154_CMD_FRAME_NR_IDS	256
+
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 #endif /* __NL802154_H */
diff --git a/src/security.c b/src/security.c
new file mode 100644
index 0000000..3928dee
--- /dev/null
+++ b/src/security.c
@@ -0,0 +1,1118 @@ 
+#include <net/if.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+#include "nl_extras.h"
+#define CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+#include "nl802154.h"
+#include "iwpan.h"
+
+static int handle_security_set(struct nl802154_state *state, struct nl_cb *cb,
+			       struct nl_msg *msg, int argc, char **argv,
+			       enum id_input id)
+{
+	unsigned long enabled;
+	char *end;
+
+	if (argc < 1)
+		return 1;
+
+	/* enabled */
+	enabled = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	if (enabled > UINT8_MAX)
+		return 1;
+
+	NLA_PUT_U8(msg, NL802154_ATTR_SEC_ENABLED, enabled);
+
+	return 0;
+
+nla_put_failure:
+	return -ENOBUFS;
+}
+COMMAND(set, security, "<1|0>", NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
+	handle_security_set, NULL);
+
+static int handle_parse_key_id(struct nl_msg *msg, int attrtype,
+			       int *argc, char ***argv)
+{
+	struct nl_msg *key_id_msg, *dev_addr_msg = NULL;
+	unsigned long key_mode, dev_addr_mode, short_addr, pan_id, index;
+	unsigned long long extended_addr;
+	char *end;
+
+	if ((*argc) < 1)
+		return 1;
+
+	/* key_mode */
+	key_mode = strtoul((*argv)[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	(*argc)--;
+	(*argv)++;
+
+	switch (key_mode) {
+	case NL802154_KEY_ID_MODE_IMPLICIT:
+		if ((*argc) < 2)
+			return 1;
+
+		/* pan_id */
+		pan_id = strtoul((*argv)[0], &end, 0);
+		if (*end != '\0')
+			return 1;
+
+		if (pan_id > UINT16_MAX)
+			return 1;
+
+		(*argc)--;
+		(*argv)++;
+
+		/* dev_addr_mode */
+		dev_addr_mode = strtoul((*argv)[0], &end, 0);
+		if (*end != '\0')
+			return 1;
+
+		(*argc)--;
+		(*argv)++;
+
+		switch (dev_addr_mode) {
+		case NL802154_DEV_ADDR_SHORT:
+			if ((*argc) < 1)
+				return 1;
+
+			/* dev_addr_short */
+			short_addr = strtoul((*argv)[0], &end, 0);
+			if (*end != '\0')
+				return 1;
+
+			if (short_addr > UINT16_MAX)
+				return 1;
+			break;
+		case NL802154_DEV_ADDR_EXTENDED:
+			if ((*argc) < 1)
+				return 1;
+
+			/* dev_addr_short */
+			extended_addr = strtoull((*argv)[0], &end, 0);
+			if (*end != '\0')
+				return 1;
+			break;
+		default:
+			return 1;
+		}
+
+		key_id_msg = nlmsg_alloc();
+		if (!key_id_msg)
+			return -ENOMEM;
+
+		dev_addr_msg = nlmsg_alloc();
+		if (!dev_addr_msg)
+			return -ENOMEM;
+
+		NLA_PUT_U16(dev_addr_msg, NL802154_DEV_ADDR_ATTR_PAN_ID, pan_id);
+		NLA_PUT_U32(dev_addr_msg, NL802154_DEV_ADDR_ATTR_MODE, dev_addr_mode);
+		NLA_PUT_U16(dev_addr_msg, NL802154_DEV_ADDR_ATTR_SHORT, htole16(short_addr));
+		NLA_PUT_U64(dev_addr_msg, NL802154_DEV_ADDR_ATTR_EXTENDED, htole64(extended_addr));
+
+		nla_put_nested(key_id_msg, NL802154_KEY_ID_ATTR_IMPLICIT, dev_addr_msg);
+
+		nlmsg_free(dev_addr_msg);
+		dev_addr_msg = NULL;
+
+		break;
+	case NL802154_KEY_ID_MODE_INDEX:
+		if ((*argc) < 1)
+			return 1;
+
+		/* index */
+		index = strtoul((*argv)[0], &end, 0);
+		if (*end != '\0')
+			return 1;
+
+		if (index > UINT8_MAX)
+			return 1;
+
+		key_id_msg = nlmsg_alloc();
+		if (!key_id_msg)
+			return -ENOMEM;
+
+		NLA_PUT_U8(key_id_msg, NL802154_KEY_ID_ATTR_INDEX, index);
+		break;
+	case NL802154_KEY_ID_MODE_INDEX_SHORT:
+		if ((*argc) < 2)
+			return 1;
+
+		/* index */
+		index = strtoul((*argv)[0], &end, 0);
+		if (*end != '\0')
+			return 1;
+
+		if (index > UINT8_MAX)
+			return 1;
+
+		(*argc)--;
+		(*argv)++;
+
+		/* source_short */
+		short_addr = strtoul((*argv)[0], &end, 0);
+		if (*end != '\0')
+			return 1;
+
+		key_id_msg = nlmsg_alloc();
+		if (!key_id_msg)
+			return -ENOMEM;
+
+		NLA_PUT_U8(key_id_msg, NL802154_KEY_ID_ATTR_INDEX, index);
+		NLA_PUT_U32(key_id_msg, NL802154_KEY_ID_ATTR_SOURCE_SHORT,
+			    htole32(short_addr));
+		break;
+	case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
+		if ((*argc) < 2)
+			return 1;
+
+		/* index */
+		index = strtoul((*argv)[0], &end, 0);
+		if (*end != '\0')
+			return 1;
+
+		if (index > UINT8_MAX)
+			return 1;
+
+		(*argc)--;
+		(*argv)++;
+
+		/* source_extended */
+		extended_addr = strtoull((*argv)[0], &end, 0);
+		if (*end != '\0')
+			return 1;
+
+		key_id_msg = nlmsg_alloc();
+		if (!key_id_msg)
+			return -ENOMEM;
+
+		NLA_PUT_U8(key_id_msg, NL802154_KEY_ID_ATTR_INDEX, index);
+		NLA_PUT_U64(key_id_msg, NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
+			    htole64(extended_addr));
+		break;
+	default:
+		return 1;
+	}
+
+	NLA_PUT_U32(key_id_msg, NL802154_KEY_ID_ATTR_MODE, key_mode);
+	nla_put_nested(msg, attrtype, key_id_msg);
+
+	nlmsg_free(key_id_msg);
+
+	return 0;
+
+nla_put_failure:
+	if (!dev_addr_msg)
+		nlmsg_free(dev_addr_msg);
+
+	nlmsg_free(key_id_msg);
+	return -ENOBUFS;
+}
+
+static int handle_out_key_id_set(struct nl802154_state *state, struct nl_cb *cb,
+				 struct nl_msg *msg, int argc, char **argv,
+				 enum id_input id)
+{
+	return handle_parse_key_id(msg, NL802154_ATTR_SEC_OUT_KEY_ID, &argc, &argv);
+
+}
+COMMAND(set, out_key_id,
+	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
+	"<1 <index>>|"
+	"<2 <index> <source_short>>|"
+	"<3 <index> <source_extended>>",
+	NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
+	handle_out_key_id_set, NULL);
+
+static int handle_out_seclevel_set(struct nl802154_state *state, struct nl_cb *cb,
+				   struct nl_msg *msg, int argc, char **argv,
+				   enum id_input id)
+{
+	unsigned long seclevel;
+	char *end;
+
+	if (argc < 1)
+		return 1;
+
+	/* seclevel */
+	seclevel = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	NLA_PUT_U32(msg, NL802154_ATTR_SEC_OUT_LEVEL, seclevel);
+
+	return 0;
+
+nla_put_failure:
+	return -ENOBUFS;
+}
+COMMAND(set, out_level, "<out_level>", NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
+	handle_out_seclevel_set, NULL);
+
+static int handle_frame_counter_set(struct nl802154_state *state, struct nl_cb *cb,
+				   struct nl_msg *msg, int argc, char **argv,
+				   enum id_input id)
+{
+	unsigned long frame_counter;
+	char *end;
+
+	/* frame_counter */
+	frame_counter = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	NLA_PUT_U32(msg, NL802154_ATTR_SEC_FRAME_COUNTER, htobe32(frame_counter));
+
+	return 0;
+
+nla_put_failure:
+	return -ENOBUFS;
+}
+COMMAND(set, frame_counter, "<frame_counter>", NL802154_CMD_SET_SEC_PARAMS, 0, CIB_NETDEV,
+	handle_frame_counter_set, NULL);
+
+SECTION(seclevel);
+
+static int print_seclevel_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL802154_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL802154_ATTR_SEC_LEVEL]) {
+		struct nlattr *tb_seclevel[NL802154_SECLEVEL_ATTR_MAX + 1];
+		static struct nla_policy seclevel_policy[NL802154_SECLEVEL_ATTR_MAX + 1] = {
+			[NL802154_SECLEVEL_ATTR_LEVELS] = { .type = NLA_U32 },
+			[NL802154_SECLEVEL_ATTR_FRAME] = { .type = NLA_U32 },
+			[NL802154_SECLEVEL_ATTR_CMD_FRAME] = { .type = NLA_U32 },
+			[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE] = { .type = NLA_U8 },
+		};
+
+		if (nla_parse_nested(tb_seclevel, NL802154_SECLEVEL_ATTR_MAX,
+				     tb[NL802154_ATTR_SEC_LEVEL],
+				     seclevel_policy)) {
+			fprintf(stderr, "failed to parse nested attributes!\n");
+			return NL_SKIP;
+		}
+
+		printf("iwpan dev $WPAN_DEV seclevel add ");
+
+		if (tb_seclevel[NL802154_SECLEVEL_ATTR_LEVELS])
+			printf("0x%02lx ", nla_get_u8(tb_seclevel[NL802154_SECLEVEL_ATTR_LEVELS]));
+		if (tb_seclevel[NL802154_SECLEVEL_ATTR_FRAME])
+			printf("%d ", nla_get_u32(tb_seclevel[NL802154_SECLEVEL_ATTR_FRAME]));
+		if (tb_seclevel[NL802154_SECLEVEL_ATTR_CMD_FRAME])
+			printf("%d ", nla_get_u32(tb_seclevel[NL802154_SECLEVEL_ATTR_CMD_FRAME]));
+		if (tb_seclevel[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE])
+			printf("%d ", nla_get_u8(tb_seclevel[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]));
+	}
+
+	printf("\n");
+
+	return NL_SKIP;
+}
+
+static int handle_seclevel_dump(struct nl802154_state *state,
+				struct nl_cb *cb,
+				struct nl_msg *msg,
+				int argc, char **argv,
+				enum id_input id)
+{
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_seclevel_handler, NULL);
+	return 0;
+}
+COMMAND(seclevel, dump, NULL,
+	NL802154_CMD_GET_SEC_LEVEL, NLM_F_DUMP, CIB_NETDEV, handle_seclevel_dump,
+	NULL);
+
+static int handle_seclevel_add(struct nl802154_state *state, struct nl_cb *cb,
+			       struct nl_msg *msg, int argc, char **argv,
+			       enum id_input id)
+{
+	struct nl_msg *seclevel_msg;
+	unsigned long levels, frame, cmd_id, dev_override;
+	char *end;
+
+	if (argc < 1)
+		return 1;
+
+	/* levels */
+	levels = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	argc--;
+	argv++;
+
+	if (argc < 1)
+		return 1;
+
+	/* frame */
+	frame = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	if (frame == NL802154_FRAME_CMD) {
+		argc--;
+		argv++;
+
+		if (argc < 1)
+			return 1;
+
+		/* cmd_frame */
+		cmd_id = strtoul(argv[0], &end, 0);
+		if (*end != '\0')
+			return 1;
+	}
+
+	argc--;
+	argv++;
+
+	if (argc < 1)
+		return 1;
+
+	/* dev_override */
+	dev_override = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	if (dev_override > UINT8_MAX)
+		return 1;
+
+	seclevel_msg = nlmsg_alloc();
+	if (!seclevel_msg)
+		return -ENOMEM;
+
+	NLA_PUT_U32(seclevel_msg, NL802154_SECLEVEL_ATTR_LEVELS, levels);
+	NLA_PUT_U32(seclevel_msg, NL802154_SECLEVEL_ATTR_FRAME, frame);
+	if (frame == NL802154_FRAME_CMD)
+		NLA_PUT_U32(seclevel_msg, NL802154_SECLEVEL_ATTR_CMD_FRAME, cmd_id);
+	NLA_PUT_U8(seclevel_msg, NL802154_SECLEVEL_ATTR_DEV_OVERRIDE, dev_override);
+
+	nla_put_nested(msg, NL802154_ATTR_SEC_LEVEL, seclevel_msg);
+	nlmsg_free(seclevel_msg);
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(seclevel_msg);
+	return -ENOBUFS;
+}
+COMMAND(seclevel, add, "<levels> <frame_type|3 <cmd_id>> <dev_override>", NL802154_CMD_NEW_SEC_LEVEL, 0, CIB_NETDEV,
+	handle_seclevel_add, NULL);
+COMMAND(seclevel, del, "<levels> <frame_type|3 <cmd_id>> <dev_override>", NL802154_CMD_DEL_SEC_LEVEL, 0, CIB_NETDEV,
+	handle_seclevel_add, NULL);
+
+SECTION(device);
+
+static int print_device_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL802154_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL802154_ATTR_SEC_DEVICE]) {
+		struct nlattr *tb_device[NL802154_DEV_ATTR_MAX + 1];
+		static struct nla_policy device_policy[NL802154_DEV_ATTR_MAX + 1] = {
+			[NL802154_DEV_ATTR_FRAME_COUNTER] = { NLA_U32 },
+			[NL802154_DEV_ATTR_PAN_ID] = { .type = NLA_U16 },
+			[NL802154_DEV_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
+			[NL802154_DEV_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
+			[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] = { NLA_U8 },
+			[NL802154_DEV_ATTR_KEY_MODE] = { NLA_U32 },
+		};
+
+		if (nla_parse_nested(tb_device, NL802154_DEV_ATTR_MAX,
+				     tb[NL802154_ATTR_SEC_DEVICE],
+				     device_policy)) {
+			fprintf(stderr, "failed to parse nested attributes!\n");
+			return NL_SKIP;
+		}
+
+		printf("iwpan dev $WPAN_DEV device add ");
+
+		if (tb_device[NL802154_DEV_ATTR_FRAME_COUNTER])
+			printf("0x%08lx ", nla_get_u32(tb_device[NL802154_DEV_ATTR_FRAME_COUNTER]));
+		if (tb_device[NL802154_DEV_ATTR_PAN_ID])
+			printf("0x%04lx ", le16toh(nla_get_u16(tb_device[NL802154_DEV_ATTR_PAN_ID])));
+		if (tb_device[NL802154_DEV_ATTR_SHORT_ADDR])
+			printf("0x%04lx ", le16toh(nla_get_u16(tb_device[NL802154_DEV_ATTR_SHORT_ADDR])));
+		if (tb_device[NL802154_DEV_ATTR_EXTENDED_ADDR])
+			printf("0x%016" PRIx64 " ", le64toh(nla_get_u64(tb_device[NL802154_DEV_ATTR_EXTENDED_ADDR])));
+		if (tb_device[NL802154_DEV_ATTR_SECLEVEL_EXEMPT])
+			printf("%d ", nla_get_u8(tb_device[NL802154_DEV_ATTR_SECLEVEL_EXEMPT]));
+		if (tb_device[NL802154_DEV_ATTR_KEY_MODE])
+			printf("%d ", nla_get_u32(tb_device[NL802154_DEV_ATTR_KEY_MODE]));
+	}
+
+	printf("\n");
+
+	return NL_SKIP;
+}
+
+static int handle_device_dump(struct nl802154_state *state,
+				struct nl_cb *cb,
+				struct nl_msg *msg,
+				int argc, char **argv,
+				enum id_input id)
+{
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_device_handler, NULL);
+	return 0;
+}
+COMMAND(device, dump, NULL,
+	NL802154_CMD_GET_SEC_DEV, NLM_F_DUMP, CIB_NETDEV, handle_device_dump,
+	NULL);
+
+static int handle_device_add(struct nl802154_state *state, struct nl_cb *cb,
+			     struct nl_msg *msg, int argc, char **argv,
+			     enum id_input id)
+{
+	struct nl_msg *device_msg;
+	unsigned long long extended_addr;
+	unsigned long frame_counter, pan_id, short_addr,
+		      seclevel_exempt, key_mode;
+	char *end;
+
+	if (argc < 1)
+		return 1;
+
+	/* frame_counter */
+	frame_counter = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	argc--;
+	argv++;
+
+	if (argc < 1)
+		return 1;
+
+	/* pan_id */
+	pan_id = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	if (pan_id > UINT16_MAX)
+		return 1;
+
+	argc--;
+	argv++;
+
+	if (argc < 1)
+		return 1;
+
+	/* short_addr */
+	short_addr = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	if (short_addr > UINT16_MAX)
+		return 1;
+
+	argc--;
+	argv++;
+
+	if (argc < 1)
+		return 1;
+
+	/* extended_addr */
+	extended_addr = strtoull(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	argc--;
+	argv++;
+
+	if (argc < 1)
+		return 1;
+
+	/* seclevel_exempt */
+	seclevel_exempt = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	if (seclevel_exempt > UINT8_MAX)
+		return 1;
+
+	argc--;
+	argv++;
+
+	if (argc < 1)
+		return 1;
+
+	/* key_mode */
+	key_mode = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	device_msg = nlmsg_alloc();
+	if (!device_msg)
+		return -ENOMEM;
+
+	NLA_PUT_U32(device_msg, NL802154_DEV_ATTR_FRAME_COUNTER, frame_counter);
+	NLA_PUT_U16(device_msg, NL802154_DEV_ATTR_PAN_ID, htole16(pan_id));
+	NLA_PUT_U16(device_msg, NL802154_DEV_ATTR_SHORT_ADDR, htole16(short_addr));
+	NLA_PUT_U64(device_msg, NL802154_DEV_ATTR_EXTENDED_ADDR, htole64(extended_addr));
+	NLA_PUT_U8(device_msg, NL802154_DEV_ATTR_SECLEVEL_EXEMPT, seclevel_exempt);
+	NLA_PUT_U32(device_msg, NL802154_DEV_ATTR_KEY_MODE, key_mode);
+
+	nla_put_nested(msg, NL802154_ATTR_SEC_DEVICE, device_msg);
+	nlmsg_free(device_msg);
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(device_msg);
+	return -ENOBUFS;
+}
+COMMAND(device, add, "<frame_counter> <pan_id> <short_addr> <extended_addr> <seclevel_exempt> <key_mode>",
+	NL802154_CMD_NEW_SEC_DEV, 0, CIB_NETDEV, handle_device_add, NULL);
+
+static int handle_device_del(struct nl802154_state *state, struct nl_cb *cb,
+			     struct nl_msg *msg, int argc, char **argv,
+			     enum id_input id)
+{
+	struct nl_msg *device_msg;
+	unsigned long long extended_addr;
+	char *end;
+
+	if (argc < 1)
+		return 1;
+
+	/* extended_addr */
+	extended_addr = strtoull(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	device_msg = nlmsg_alloc();
+	if (!device_msg)
+		return -ENOMEM;
+
+	NLA_PUT_U64(device_msg, NL802154_DEV_ATTR_EXTENDED_ADDR, htole64(extended_addr));
+
+	nla_put_nested(msg, NL802154_ATTR_SEC_DEVICE, device_msg);
+	nlmsg_free(device_msg);
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(device_msg);
+	return -ENOBUFS;
+}
+COMMAND(device, del, "<extended_addr>",
+	NL802154_CMD_DEL_SEC_DEV, 0, CIB_NETDEV, handle_device_del, NULL);
+
+SECTION(devkey);
+
+static int print_key_id(struct nlattr *tb) {
+	struct nlattr *tb_key_id[NL802154_KEY_ID_ATTR_MAX + 1];
+	static struct nla_policy key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
+		[NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
+		[NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
+		[NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
+		[NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
+		[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
+	};
+
+	nla_parse_nested(tb_key_id, NL802154_KEY_ID_ATTR_MAX, tb, key_id_policy);
+
+	if (tb_key_id[NL802154_KEY_ID_ATTR_MODE]) {
+		enum nl802154_key_id_modes key_id_mode;
+
+		key_id_mode = nla_get_u32(tb_key_id[NL802154_KEY_ID_ATTR_MODE]);
+		printf("%d ", key_id_mode);
+		switch (key_id_mode) {
+		case NL802154_KEY_ID_MODE_IMPLICIT:
+			if (tb_key_id[NL802154_KEY_ID_ATTR_IMPLICIT]) {
+				struct nlattr *tb_dev_addr[NL802154_DEV_ADDR_ATTR_MAX + 1];
+				static struct nla_policy dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = {
+					[NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 },
+					[NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 },
+					[NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 },
+					[NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 },
+				};
+
+				nla_parse_nested(tb_dev_addr, NL802154_DEV_ADDR_ATTR_MAX,
+						 tb_key_id[NL802154_KEY_ID_ATTR_IMPLICIT],
+						 dev_addr_policy);
+
+				if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_PAN_ID])
+					printf("0x%04x ",
+					       le16toh(nla_get_u16(tb_dev_addr[NL802154_DEV_ADDR_ATTR_PAN_ID])));
+
+				if (tb_dev_addr[NL802154_DEV_ADDR_ATTR_MODE]) {
+					enum nl802154_dev_addr_modes dev_addr_mode;
+					dev_addr_mode = nla_get_u32(tb_dev_addr[NL802154_DEV_ADDR_ATTR_MODE]);
+					printf("%d ", dev_addr_mode);
+					switch (dev_addr_mode) {
+					case NL802154_DEV_ADDR_SHORT:
+						printf("0x%04x ",
+						       le16toh(nla_get_u16(tb_dev_addr[NL802154_DEV_ADDR_ATTR_SHORT])));
+						break;
+					case NL802154_DEV_ADDR_EXTENDED:
+						printf("0x%016" PRIx64 " ",
+						       le64toh(nla_get_u64(tb_dev_addr[NL802154_DEV_ADDR_ATTR_SHORT])));
+						break;
+					default:
+						/* TODO error handling */
+						break;
+					}
+				}
+			}
+			break;
+		case NL802154_KEY_ID_MODE_INDEX:
+			if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
+				printf("0x%02x ",
+				       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
+			break;
+		case NL802154_KEY_ID_MODE_INDEX_SHORT:
+			if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
+				printf("0x%02x ",
+				       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
+
+			if (tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_SHORT])
+				printf("0x%08lx ",
+				       le32toh(nla_get_u32(tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_SHORT])));
+			break;
+		case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
+			if (tb_key_id[NL802154_KEY_ID_ATTR_INDEX])
+				printf("0x%02x ",
+				       nla_get_u8(tb_key_id[NL802154_KEY_ID_ATTR_INDEX]));
+
+			if (tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])
+				printf("0x%016" PRIx64 " ",
+				       le64toh(nla_get_u64(tb_key_id[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])));
+			break;
+		default:
+			/* TODO error handling */
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+static int print_devkey_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL802154_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL802154_ATTR_SEC_DEVKEY]) {
+		struct nlattr *tb_devkey[NL802154_DEVKEY_ATTR_MAX + 1];
+		static struct nla_policy devkey_policy[NL802154_DEVKEY_ATTR_MAX + 1] = {
+			[NL802154_DEVKEY_ATTR_FRAME_COUNTER] = { NLA_U32 },
+			[NL802154_DEVKEY_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
+			[NL802154_DEVKEY_ATTR_ID] = { .type = NLA_NESTED },
+		};
+
+		if (nla_parse_nested(tb_devkey, NL802154_DEVKEY_ATTR_MAX,
+				     tb[NL802154_ATTR_SEC_DEVKEY],
+				     devkey_policy)) {
+			fprintf(stderr, "failed to parse nested attributes!\n");
+			return NL_SKIP;
+		}
+
+		printf("iwpan dev $WPAN_DEV devkey add ");
+
+		if (tb_devkey[NL802154_DEV_ATTR_FRAME_COUNTER])
+			printf("0x%08lx ", nla_get_u32(tb_devkey[NL802154_DEVKEY_ATTR_FRAME_COUNTER]));
+		if (tb_devkey[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
+			printf("0x%016" PRIx64 " ", le64toh(nla_get_u64(tb_devkey[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])));
+
+		if (tb_devkey[NL802154_DEVKEY_ATTR_ID])
+			print_key_id(tb_devkey[NL802154_DEVKEY_ATTR_ID]);
+	}
+
+	printf("\n");
+
+	return NL_SKIP;
+}
+
+static int handle_devkey_dump(struct nl802154_state *state,
+				struct nl_cb *cb,
+				struct nl_msg *msg,
+				int argc, char **argv,
+				enum id_input id)
+{
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_devkey_handler, NULL);
+	return 0;
+}
+COMMAND(devkey, dump, NULL,
+	NL802154_CMD_GET_SEC_DEVKEY, NLM_F_DUMP, CIB_NETDEV, handle_devkey_dump,
+	NULL);
+
+static int handle_devkey_add(struct nl802154_state *state, struct nl_cb *cb,
+			     struct nl_msg *msg, int argc, char **argv,
+			     enum id_input id)
+{
+	struct nl_msg *devkey_msg = NULL;
+	unsigned long long extended_addr;
+	unsigned long frame_counter;
+	char *end;
+	int ret;
+
+	if (argc < 1)
+		return 1;
+
+	/* frame_counter */
+	frame_counter = strtoul(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	argc--;
+	argv++;
+
+	if (argc < 1)
+		return 1;
+
+	/* extended_addr */
+	extended_addr = strtoull(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	argc--;
+	argv++;
+
+	devkey_msg = nlmsg_alloc();
+	if (!devkey_msg)
+		return -ENOMEM;
+
+	NLA_PUT_U32(devkey_msg, NL802154_DEVKEY_ATTR_FRAME_COUNTER, frame_counter);
+	NLA_PUT_U64(devkey_msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR, htole64(extended_addr));
+
+	ret = handle_parse_key_id(devkey_msg, NL802154_DEVKEY_ATTR_ID, &argc, &argv);
+	if (ret) {
+		nlmsg_free(devkey_msg);
+		return ret;
+	}
+
+	nla_put_nested(msg, NL802154_ATTR_SEC_DEVKEY, devkey_msg);
+	nlmsg_free(devkey_msg);
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(devkey_msg);
+	return -ENOBUFS;
+
+}
+COMMAND(devkey, add, "<frame_counter> <extended_addr> "
+	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
+	"<1 <index>>|"
+	"<2 <index> <source_short>>|"
+	"<3 <index> <source_extended>>",
+	NL802154_CMD_NEW_SEC_DEVKEY, 0, CIB_NETDEV, handle_devkey_add, NULL);
+
+static int handle_devkey_del(struct nl802154_state *state, struct nl_cb *cb,
+			     struct nl_msg *msg, int argc, char **argv,
+			     enum id_input id)
+{
+	struct nl_msg *devkey_msg = NULL;
+	unsigned long long extended_addr;
+	char *end;
+	int ret;
+
+	if (argc < 1)
+		return 1;
+
+	/* extended_addr */
+	extended_addr = strtoull(argv[0], &end, 0);
+	if (*end != '\0')
+		return 1;
+
+	argc--;
+	argv++;
+
+	devkey_msg = nlmsg_alloc();
+	if (!devkey_msg)
+		return -ENOMEM;
+
+	NLA_PUT_U64(devkey_msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR, htole64(extended_addr));
+
+	ret = handle_parse_key_id(devkey_msg, NL802154_DEVKEY_ATTR_ID, &argc, &argv);
+	if (ret) {
+		nlmsg_free(devkey_msg);
+		return ret;
+	}
+
+	nla_put_nested(msg, NL802154_ATTR_SEC_DEVKEY, devkey_msg);
+	nlmsg_free(devkey_msg);
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(devkey_msg);
+	return -ENOBUFS;
+
+}
+COMMAND(devkey, del, "<extended_addr> "
+	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
+	"<1 <index>>|"
+	"<2 <index> <source_short>>|"
+	"<3 <index> <source_extended>>",
+	NL802154_CMD_DEL_SEC_DEVKEY, 0, CIB_NETDEV, handle_devkey_del, NULL);
+
+SECTION(key);
+
+static void key_to_str(char *key, unsigned char *arg)
+{
+	int i, l;
+
+	l = 0;
+	for (i = 0; i < NL802154_KEY_SIZE ; i++) {
+		if (i == 0) {
+			sprintf(key+l, "%02x", arg[i]);
+			l += 2;
+		} else {
+			sprintf(key+l, ":%02x", arg[i]);
+			l += 3;
+		}
+	}
+}
+
+static int print_key_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL802154_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL802154_ATTR_SEC_KEY]) {
+		struct nlattr *tb_key[NL802154_KEY_ATTR_MAX + 1];
+		static struct nla_policy key_policy[NL802154_KEY_ATTR_MAX + 1] = {
+			[NL802154_KEY_ATTR_ID] = { NLA_NESTED },
+			[NL802154_KEY_ATTR_USAGE_FRAMES] = { NLA_U8 },
+			[NL802154_KEY_ATTR_USAGE_CMDS] = { .minlen = NL802154_CMD_FRAME_NR_IDS / 8 },
+			[NL802154_KEY_ATTR_BYTES] = { .minlen = NL802154_KEY_SIZE },
+		};
+
+		if (nla_parse_nested(tb_key, NL802154_KEY_ATTR_MAX,
+				     tb[NL802154_ATTR_SEC_KEY],
+				     key_policy)) {
+			fprintf(stderr, "failed to parse nested attributes!\n");
+			return NL_SKIP;
+		}
+
+		printf("iwpan dev $WPAN_DEV key add ");
+
+		if (tb_key[NL802154_KEY_ATTR_USAGE_FRAMES])
+			printf("0x%02x ", nla_get_u8(tb_key[NL802154_KEY_ATTR_USAGE_FRAMES]));
+
+		if (tb_key[NL802154_KEY_ATTR_USAGE_CMDS]) {
+			uint32_t cmds[NL802154_CMD_FRAME_NR_IDS / 32];
+
+			nla_memcpy(cmds, tb_key[NL802154_KEY_ATTR_USAGE_CMDS],
+				   NL802154_CMD_FRAME_NR_IDS / 8);
+			printf("0x%08x ", cmds[7]);
+		}
+
+		if (tb_key[NL802154_KEY_ATTR_BYTES]) {
+			uint8_t key[NL802154_KEY_SIZE];
+			char key_str[512] = "";
+
+			nla_memcpy(key, tb_key[NL802154_KEY_ATTR_BYTES],
+				   NL802154_KEY_SIZE);
+
+			key_to_str(key_str, key);
+			printf("%s ", key_str);
+		}
+
+		if (tb_key[NL802154_KEY_ATTR_ID])
+			print_key_id(tb_key[NL802154_KEY_ATTR_ID]);
+	}
+
+	printf("\n");
+
+	return NL_SKIP;
+}
+
+static int handle_key_dump(struct nl802154_state *state,
+			   struct nl_cb *cb,
+			   struct nl_msg *msg,
+			   int argc, char **argv,
+			   enum id_input id)
+{
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_key_handler, NULL);
+	return 0;
+}
+COMMAND(key, dump, NULL,
+	NL802154_CMD_GET_SEC_KEY, NLM_F_DUMP, CIB_NETDEV, handle_key_dump,
+	NULL);
+
+#define BIT(x)  (1 << (x))
+
+static int str_to_key(unsigned char *key, char *arg)
+{
+	int i;
+
+	for (i = 0; i < NL802154_KEY_SIZE; i++) {
+		int temp;
+		char *cp = strchr(arg, ':');
+		if (cp) {
+			*cp = 0;
+			cp++;
+		}
+		if (sscanf(arg, "%x", &temp) != 1)
+			return -1;
+		if (temp < 0 || temp > 255)
+			return -1;
+
+		key[i] = temp;
+		if (!cp)
+			break;
+		arg = cp;
+	}
+	if (i < NL802154_KEY_SIZE - 1)
+		return -1;
+
+	return 0;
+}
+
+static int handle_key_add(struct nl802154_state *state, struct nl_cb *cb,
+			     struct nl_msg *msg, int argc, char **argv,
+			     enum id_input id)
+{
+	struct nl_msg *key_msg = NULL;
+	uint8_t key_bytes[NL802154_KEY_SIZE] = { };
+	uint32_t commands[NL802154_CMD_FRAME_NR_IDS / 32] = { };
+	unsigned long tmp;
+	char *end;
+	int ret, i;
+
+	key_msg = nlmsg_alloc();
+	if (!key_msg)
+		return -ENOMEM;
+
+	if (argc < 1) {
+		nlmsg_free(key_msg);
+		return 1;
+	}
+
+	/* frame_types */
+	tmp = strtoul(argv[0], &end, 0);
+	if (*end != '\0') {
+		nlmsg_free(key_msg);
+		return 1;
+	}
+
+	if (tmp > UINT8_MAX) {
+		nlmsg_free(key_msg);
+		return 1;
+	}
+
+	NLA_PUT_U8(key_msg, NL802154_KEY_ATTR_USAGE_FRAMES, tmp);
+
+	argc--;
+	argv++;
+
+	if (tmp & BIT(NL802154_FRAME_CMD)) {
+		if (argc < 1) {
+			nlmsg_free(key_msg);
+			return 1;
+		}
+
+		/* commands[7] */
+		commands[7] = strtoul(argv[0], &end, 0);
+		if (*end != '\0') {
+			nlmsg_free(key_msg);
+			return 1;
+		}
+
+		NLA_PUT(key_msg, NL802154_KEY_ATTR_USAGE_CMDS,
+			NL802154_CMD_FRAME_NR_IDS / 8, commands);
+
+		argc--;
+		argv++;
+	}
+
+	if (argc < 1) {
+		nlmsg_free(key_msg);
+		return 1;
+	}
+
+	str_to_key(key_bytes, argv[0]);
+
+	NLA_PUT(key_msg, NL802154_KEY_ATTR_BYTES, NL802154_KEY_SIZE, key_bytes);
+
+	argc--;
+	argv++;
+
+	ret = handle_parse_key_id(key_msg, NL802154_KEY_ATTR_ID, &argc, &argv);
+	if (ret) {
+		nlmsg_free(key_msg);
+		return ret;
+	}
+
+	nla_put_nested(msg, NL802154_ATTR_SEC_KEY, key_msg);
+	nlmsg_free(key_msg);
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(key_msg);
+	return -ENOBUFS;
+
+}
+COMMAND(key, add, "<frame_types <if 0x4 is set commands[7]>>> <key <hex as 00:11:..>> "
+	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
+	"<1 <index>>|"
+	"<2 <index> <source_short>>|"
+	"<3 <index> <source_extended>>",
+	NL802154_CMD_NEW_SEC_KEY, 0, CIB_NETDEV, handle_key_add, NULL);
+
+static int handle_key_del(struct nl802154_state *state, struct nl_cb *cb,
+			     struct nl_msg *msg, int argc, char **argv,
+			     enum id_input id)
+{
+	struct nl_msg *key_msg = NULL;
+	int ret;
+
+	key_msg = nlmsg_alloc();
+	if (!key_msg)
+		return -ENOMEM;
+
+	ret = handle_parse_key_id(key_msg, NL802154_KEY_ATTR_ID, &argc, &argv);
+	if (ret) {
+		nlmsg_free(key_msg);
+		return ret;
+	}
+
+	nla_put_nested(msg, NL802154_ATTR_SEC_KEY, key_msg);
+	nlmsg_free(key_msg);
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(key_msg);
+	return -ENOBUFS;
+
+}
+COMMAND(key, del,
+	"<0 <pan_id> <2 <short_addr>|3 <extended_addr>>>|"
+	"<1 <index>>|"
+	"<2 <index> <source_short>>|"
+	"<3 <index> <source_extended>>",
+	NL802154_CMD_DEL_SEC_KEY, 0, CIB_NETDEV, handle_key_del, NULL);