lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 28 Sep 2015 09:25:33 +0200
From:	Alexander Aring <alex.aring@...il.com>
To:	linux-wpan@...r.kernel.org
Cc:	kernel@...gutronix.de, netdev@...r.kernel.org,
	phoebe.buckheister@...m.fraunhofer.de,
	Alexander Aring <alex.aring@...il.com>
Subject: [PATCH wpan-tools 1/2] security: add nl802154 security support

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@...il.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,
+	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);
-- 
2.5.3

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ