Blame SOURCES/macsec-0029-macsec_linux-Add-a-driver-for-macsec-on-Linux-kernel.patch

6c9f0c
From f014d9dbf04d0637429f7eb85c915def87f2f7d8 Mon Sep 17 00:00:00 2001
6c9f0c
Message-Id: <f014d9dbf04d0637429f7eb85c915def87f2f7d8.1488376602.git.dcaratti@redhat.com>
6c9f0c
From: Sabrina Dubroca <sd@queasysnail.net>
6c9f0c
Date: Sun, 27 Nov 2016 20:08:55 +0100
6c9f0c
Subject: [PATCH] macsec_linux: Add a driver for macsec on Linux kernels
6c9f0c
6c9f0c
This uses libnl3 to communicate with the macsec module available on
6c9f0c
Linux. A recent enough version of libnl is needed for the macsec.h file
6c9f0c
(which is not yet available in a formal libnl release at the time of
6c9f0c
this commit).
6c9f0c
6c9f0c
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
6c9f0c
---
6c9f0c
 src/drivers/driver.h              |    4 +
6c9f0c
 src/drivers/driver_macsec_linux.c | 1265 +++++++++++++++++++++++++++++++++++++
6c9f0c
 src/drivers/drivers.c             |    3 +
6c9f0c
 src/drivers/drivers.mak           |    7 +
6c9f0c
 src/drivers/drivers.mk            |    7 +
6c9f0c
 src/pae/ieee802_1x_kay.c          |   11 +
6c9f0c
 src/pae/ieee802_1x_kay.h          |    2 +
6c9f0c
 7 files changed, 1299 insertions(+)
6c9f0c
 create mode 100644 src/drivers/driver_macsec_linux.c
6c9f0c
6c9f0c
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
6c9f0c
index ffe5560..71ad006 100644
6c9f0c
--- a/src/drivers/driver.h
6c9f0c
+++ b/src/drivers/driver.h
6c9f0c
@@ -5050,6 +5050,10 @@ extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
6c9f0c
 /* driver_macsec_qca.c */
6c9f0c
 extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops;
6c9f0c
 #endif /* CONFIG_DRIVER_MACSEC_QCA */
6c9f0c
+#ifdef CONFIG_DRIVER_MACSEC_LINUX
6c9f0c
+/* driver_macsec_linux.c */
6c9f0c
+extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops;
6c9f0c
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
6c9f0c
 #ifdef CONFIG_DRIVER_ROBOSWITCH
6c9f0c
 /* driver_roboswitch.c */
6c9f0c
 extern const struct wpa_driver_ops wpa_driver_roboswitch_ops;
6c9f0c
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
6c9f0c
new file mode 100644
6c9f0c
index 0000000..5dab77a
6c9f0c
--- /dev/null
6c9f0c
+++ b/src/drivers/driver_macsec_linux.c
6c9f0c
@@ -0,0 +1,1265 @@
6c9f0c
+/*
6c9f0c
+ * Driver interaction with Linux MACsec kernel module
6c9f0c
+ * Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc.
6c9f0c
+ *
6c9f0c
+ * This software may be distributed under the terms of the BSD license.
6c9f0c
+ * See README for more details.
6c9f0c
+ */
6c9f0c
+
6c9f0c
+#include "includes.h"
6c9f0c
+#include <sys/ioctl.h>
6c9f0c
+#include <net/if.h>
6c9f0c
+#include <netpacket/packet.h>
6c9f0c
+#include <net/if_arp.h>
6c9f0c
+#include <net/if.h>
6c9f0c
+#include <netlink/netlink.h>
6c9f0c
+#include <netlink/genl/genl.h>
6c9f0c
+#include <netlink/genl/ctrl.h>
6c9f0c
+#include <netlink/route/link.h>
6c9f0c
+#include <netlink/route/link/macsec.h>
6c9f0c
+#include <linux/if_macsec.h>
6c9f0c
+#include <inttypes.h>
6c9f0c
+
6c9f0c
+#include "utils/common.h"
6c9f0c
+#include "utils/eloop.h"
6c9f0c
+#include "pae/ieee802_1x_kay.h"
6c9f0c
+#include "driver.h"
6c9f0c
+#include "driver_wired_common.h"
6c9f0c
+
6c9f0c
+#define DRV_PREFIX "macsec_linux: "
6c9f0c
+
6c9f0c
+#define UNUSED_SCI 0xffffffffffffffff
6c9f0c
+
6c9f0c
+struct cb_arg {
6c9f0c
+	struct macsec_drv_data *drv;
6c9f0c
+	u32 *pn;
6c9f0c
+	int ifindex;
6c9f0c
+	u8 txsa;
6c9f0c
+	u8 rxsa;
6c9f0c
+	u64 rxsci;
6c9f0c
+};
6c9f0c
+
6c9f0c
+struct macsec_genl_ctx {
6c9f0c
+	struct nl_sock *sk;
6c9f0c
+	int macsec_genl_id;
6c9f0c
+	struct cb_arg cb_arg;
6c9f0c
+};
6c9f0c
+
6c9f0c
+struct macsec_drv_data {
6c9f0c
+	struct driver_wired_common_data common;
6c9f0c
+	struct rtnl_link *link;
6c9f0c
+	struct nl_cache *link_cache;
6c9f0c
+	struct nl_sock *sk;
6c9f0c
+	struct macsec_genl_ctx ctx;
6c9f0c
+
6c9f0c
+	struct netlink_data *netlink;
6c9f0c
+	struct nl_handle *nl;
6c9f0c
+	char ifname[IFNAMSIZ + 1];
6c9f0c
+	int ifi;
6c9f0c
+	int parent_ifi;
6c9f0c
+
6c9f0c
+	Boolean created_link;
6c9f0c
+
6c9f0c
+	Boolean controlled_port_enabled;
6c9f0c
+	Boolean controlled_port_enabled_set;
6c9f0c
+
6c9f0c
+	Boolean protect_frames;
6c9f0c
+	Boolean protect_frames_set;
6c9f0c
+
6c9f0c
+	Boolean encrypt;
6c9f0c
+	Boolean encrypt_set;
6c9f0c
+
6c9f0c
+	Boolean replay_protect;
6c9f0c
+	Boolean replay_protect_set;
6c9f0c
+
6c9f0c
+	u32 replay_window;
6c9f0c
+
6c9f0c
+	u8 encoding_sa;
6c9f0c
+	Boolean encoding_sa_set;
6c9f0c
+};
6c9f0c
+
6c9f0c
+
6c9f0c
+static int dump_callback(struct nl_msg *msg, void *argp);
6c9f0c
+
6c9f0c
+
6c9f0c
+static struct nl_msg * msg_prepare(enum macsec_nl_commands cmd,
6c9f0c
+				   const struct macsec_genl_ctx *ctx,
6c9f0c
+				   unsigned int ifindex)
6c9f0c
+{
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+
6c9f0c
+	msg = nlmsg_alloc();
6c9f0c
+	if (!msg) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc message");
6c9f0c
+		return NULL;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	if (!genlmsg_put(msg, 0, 0, ctx->macsec_genl_id, 0, 0, cmd, 0)) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to put header");
6c9f0c
+		goto nla_put_failure;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	NLA_PUT_U32(msg, MACSEC_ATTR_IFINDEX, ifindex);
6c9f0c
+
6c9f0c
+	return msg;
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return NULL;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int nla_put_rxsc_config(struct nl_msg *msg, u64 sci)
6c9f0c
+{
6c9f0c
+	struct nlattr *nest = nla_nest_start(msg, MACSEC_ATTR_RXSC_CONFIG);
6c9f0c
+
6c9f0c
+	if (!nest)
6c9f0c
+		return -1;
6c9f0c
+
6c9f0c
+	NLA_PUT_U64(msg, MACSEC_RXSC_ATTR_SCI, sci);
6c9f0c
+
6c9f0c
+	nla_nest_end(msg, nest);
6c9f0c
+
6c9f0c
+	return 0;
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	return -1;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int init_genl_ctx(struct macsec_drv_data *drv)
6c9f0c
+{
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+
6c9f0c
+	ctx->sk = nl_socket_alloc();
6c9f0c
+	if (!ctx->sk) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
6c9f0c
+		return -1;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	if (genl_connect(ctx->sk) < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "connection to genl socket failed");
6c9f0c
+		goto out_free;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	ctx->macsec_genl_id = genl_ctrl_resolve(ctx->sk, "macsec");
6c9f0c
+	if (ctx->macsec_genl_id < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "genl resolve failed");
6c9f0c
+		goto out_free;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	memset(&ctx->cb_arg, 0, sizeof(ctx->cb_arg));
6c9f0c
+	ctx->cb_arg.drv = drv;
6c9f0c
+
6c9f0c
+	nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback,
6c9f0c
+			    &ctx->cb_arg);
6c9f0c
+
6c9f0c
+	return 0;
6c9f0c
+
6c9f0c
+out_free:
6c9f0c
+	nl_socket_free(ctx->sk);
6c9f0c
+	ctx->sk = NULL;
6c9f0c
+	return -1;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int try_commit(struct macsec_drv_data *drv)
6c9f0c
+{
6c9f0c
+	int err;
6c9f0c
+
6c9f0c
+	if (!drv->link)
6c9f0c
+		return 0;
6c9f0c
+
6c9f0c
+	if (drv->controlled_port_enabled_set) {
6c9f0c
+		struct rtnl_link *change = rtnl_link_alloc();
6c9f0c
+
6c9f0c
+		if (!change)
6c9f0c
+			return -1;
6c9f0c
+
6c9f0c
+		rtnl_link_set_name(change, drv->ifname);
6c9f0c
+
6c9f0c
+		if (drv->controlled_port_enabled)
6c9f0c
+			rtnl_link_set_flags(change, IFF_UP);
6c9f0c
+		else
6c9f0c
+			rtnl_link_unset_flags(change, IFF_UP);
6c9f0c
+
6c9f0c
+		err = rtnl_link_change(drv->sk, change, change, 0);
6c9f0c
+		if (err < 0)
6c9f0c
+			return err;
6c9f0c
+
6c9f0c
+		rtnl_link_put(change);
6c9f0c
+
6c9f0c
+		drv->controlled_port_enabled_set = FALSE;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	if (drv->protect_frames_set)
6c9f0c
+		rtnl_link_macsec_set_protect(drv->link, drv->protect_frames);
6c9f0c
+
6c9f0c
+	if (drv->encrypt_set)
6c9f0c
+		rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt);
6c9f0c
+
6c9f0c
+	if (drv->replay_protect_set) {
6c9f0c
+		rtnl_link_macsec_set_replay_protect(drv->link,
6c9f0c
+						    drv->replay_protect);
6c9f0c
+		if (drv->replay_protect)
6c9f0c
+			rtnl_link_macsec_set_window(drv->link,
6c9f0c
+						    drv->replay_window);
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	if (drv->encoding_sa_set)
6c9f0c
+		rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa);
6c9f0c
+
6c9f0c
+	err = rtnl_link_add(drv->sk, drv->link, 0);
6c9f0c
+	if (err < 0)
6c9f0c
+		return err;
6c9f0c
+
6c9f0c
+	drv->protect_frames_set = FALSE;
6c9f0c
+	drv->encrypt_set = FALSE;
6c9f0c
+	drv->replay_protect_set = FALSE;
6c9f0c
+
6c9f0c
+	return 0;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static void macsec_drv_wpa_deinit(void *priv)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+
6c9f0c
+	driver_wired_deinit_common(&drv->common);
6c9f0c
+	os_free(drv);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static void * macsec_drv_wpa_init(void *ctx, const char *ifname)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv;
6c9f0c
+
6c9f0c
+	drv = os_zalloc(sizeof(*drv));
6c9f0c
+	if (!drv)
6c9f0c
+		return NULL;
6c9f0c
+
6c9f0c
+	if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
6c9f0c
+		os_free(drv);
6c9f0c
+		return NULL;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	return drv;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	int err;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s", __func__);
6c9f0c
+
6c9f0c
+	drv->sk = nl_socket_alloc();
6c9f0c
+	if (!drv->sk)
6c9f0c
+		return -1;
6c9f0c
+
6c9f0c
+	err = nl_connect(drv->sk, NETLINK_ROUTE);
6c9f0c
+	if (err < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX
6c9f0c
+			   "Unable to connect NETLINK_ROUTE socket: %s",
6c9f0c
+			   strerror(errno));
6c9f0c
+		goto sock;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	err = rtnl_link_alloc_cache(drv->sk, AF_UNSPEC, &drv->link_cache);
6c9f0c
+	if (err < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "Unable to get link cache: %s",
6c9f0c
+			   strerror(errno));
6c9f0c
+		goto sock;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	drv->parent_ifi = rtnl_link_name2i(drv->link_cache, drv->common.ifname);
6c9f0c
+	if (drv->parent_ifi == 0) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX
6c9f0c
+			   "couldn't find ifindex for interface %s",
6c9f0c
+			   drv->common.ifname);
6c9f0c
+		goto cache;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	err = init_genl_ctx(drv);
6c9f0c
+	if (err < 0)
6c9f0c
+		goto cache;
6c9f0c
+
6c9f0c
+	return 0;
6c9f0c
+
6c9f0c
+cache:
6c9f0c
+	nl_cache_free(drv->link_cache);
6c9f0c
+	drv->link_cache = NULL;
6c9f0c
+sock:
6c9f0c
+	nl_socket_free(drv->sk);
6c9f0c
+	drv->sk = NULL;
6c9f0c
+	return -1;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int macsec_drv_macsec_deinit(void *priv)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s", __func__);
6c9f0c
+
6c9f0c
+	if (drv->sk)
6c9f0c
+		nl_socket_free(drv->sk);
6c9f0c
+	drv->sk = NULL;
6c9f0c
+
6c9f0c
+	if (drv->link_cache)
6c9f0c
+		nl_cache_free(drv->link_cache);
6c9f0c
+	drv->link_cache = NULL;
6c9f0c
+
6c9f0c
+	if (drv->ctx.sk)
6c9f0c
+		nl_socket_free(drv->ctx.sk);
6c9f0c
+
6c9f0c
+	return 0;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int macsec_drv_get_capability(void *priv, enum macsec_cap *cap)
6c9f0c
+{
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s", __func__);
6c9f0c
+
6c9f0c
+	*cap = MACSEC_CAP_INTEG_AND_CONF;
6c9f0c
+
6c9f0c
+	return 0;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_enable_protect_frames - Set protect frames status
6c9f0c
+ * @priv: Private driver interface data
6c9f0c
+ * @enabled: TRUE = protect frames enabled
6c9f0c
+ *           FALSE = protect frames disabled
6c9f0c
+ * Returns: 0 on success, -1 on failure (or if not supported)
6c9f0c
+ */
6c9f0c
+static int macsec_drv_enable_protect_frames(void *priv, Boolean enabled)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
6c9f0c
+
6c9f0c
+	drv->protect_frames_set = TRUE;
6c9f0c
+	drv->protect_frames = enabled;
6c9f0c
+
6c9f0c
+	return try_commit(drv);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_enable_encrypt - Set protect frames status
6c9f0c
+ * @priv: Private driver interface data
6c9f0c
+ * @enabled: TRUE = protect frames enabled
6c9f0c
+ *           FALSE = protect frames disabled
6c9f0c
+ * Returns: 0 on success, -1 on failure (or if not supported)
6c9f0c
+ */
6c9f0c
+static int macsec_drv_enable_encrypt(void *priv, Boolean enabled)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
6c9f0c
+
6c9f0c
+	drv->encrypt_set = TRUE;
6c9f0c
+	drv->encrypt = enabled;
6c9f0c
+
6c9f0c
+	return try_commit(drv);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_set_replay_protect - Set replay protect status and window size
6c9f0c
+ * @priv: Private driver interface data
6c9f0c
+ * @enabled: TRUE = replay protect enabled
6c9f0c
+ *           FALSE = replay protect disabled
6c9f0c
+ * @window: replay window size, valid only when replay protect enabled
6c9f0c
+ * Returns: 0 on success, -1 on failure (or if not supported)
6c9f0c
+ */
6c9f0c
+static int macsec_drv_set_replay_protect(void *priv, Boolean enabled,
6c9f0c
+					 u32 window)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %s, %u", __func__,
6c9f0c
+		   enabled ? "TRUE" : "FALSE", window);
6c9f0c
+
6c9f0c
+	drv->replay_protect_set = TRUE;
6c9f0c
+	drv->replay_protect = enabled;
6c9f0c
+	if (enabled)
6c9f0c
+		drv->replay_window = window;
6c9f0c
+
6c9f0c
+	return try_commit(drv);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_set_current_cipher_suite - Set current cipher suite
6c9f0c
+ * @priv: Private driver interface data
6c9f0c
+ * @cs: EUI64 identifier
6c9f0c
+ * Returns: 0 on success, -1 on failure (or if not supported)
6c9f0c
+ */
6c9f0c
+static int macsec_drv_set_current_cipher_suite(void *priv, u64 cs)
6c9f0c
+{
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs);
6c9f0c
+	return 0;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_enable_controlled_port - Set controlled port status
6c9f0c
+ * @priv: Private driver interface data
6c9f0c
+ * @enabled: TRUE = controlled port enabled
6c9f0c
+ *           FALSE = controlled port disabled
6c9f0c
+ * Returns: 0 on success, -1 on failure (or if not supported)
6c9f0c
+ */
6c9f0c
+static int macsec_drv_enable_controlled_port(void *priv, Boolean enabled)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
6c9f0c
+
6c9f0c
+	drv->controlled_port_enabled = enabled;
6c9f0c
+	drv->controlled_port_enabled_set = TRUE;
6c9f0c
+
6c9f0c
+	return try_commit(drv);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static struct nla_policy sa_policy[MACSEC_SA_ATTR_MAX + 1] = {
6c9f0c
+	[MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
6c9f0c
+	[MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
6c9f0c
+	[MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
6c9f0c
+	[MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY },
6c9f0c
+};
6c9f0c
+
6c9f0c
+static struct nla_policy sc_policy[MACSEC_RXSC_ATTR_MAX + 1] = {
6c9f0c
+	[MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
6c9f0c
+	[MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
6c9f0c
+	[MACSEC_RXSC_ATTR_SA_LIST] = { .type = NLA_NESTED },
6c9f0c
+};
6c9f0c
+
6c9f0c
+static struct nla_policy main_policy[MACSEC_ATTR_MAX + 1] = {
6c9f0c
+	[MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
6c9f0c
+	[MACSEC_ATTR_SECY] = { .type = NLA_NESTED },
6c9f0c
+	[MACSEC_ATTR_TXSA_LIST] = { .type = NLA_NESTED },
6c9f0c
+	[MACSEC_ATTR_RXSC_LIST] = { .type = NLA_NESTED },
6c9f0c
+};
6c9f0c
+
6c9f0c
+static int dump_callback(struct nl_msg *msg, void *argp)
6c9f0c
+{
6c9f0c
+	struct nlmsghdr *ret_hdr = nlmsg_hdr(msg);
6c9f0c
+	struct nlattr *tb_msg[MACSEC_ATTR_MAX + 1];
6c9f0c
+	struct cb_arg *arg = (struct cb_arg *) argp;
6c9f0c
+	struct genlmsghdr *gnlh = (struct genlmsghdr *) nlmsg_data(ret_hdr);
6c9f0c
+	int err;
6c9f0c
+
6c9f0c
+	if (ret_hdr->nlmsg_type != arg->drv->ctx.macsec_genl_id)
6c9f0c
+		return 0;
6c9f0c
+
6c9f0c
+	err = nla_parse(tb_msg, MACSEC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
6c9f0c
+			genlmsg_attrlen(gnlh, 0), main_policy);
6c9f0c
+	if (err < 0)
6c9f0c
+		return 0;
6c9f0c
+
6c9f0c
+	if (!tb_msg[MACSEC_ATTR_IFINDEX])
6c9f0c
+		return 0;
6c9f0c
+
6c9f0c
+	if (nla_get_u32(tb_msg[MACSEC_ATTR_IFINDEX]) != (u32) arg->ifindex)
6c9f0c
+		return 0;
6c9f0c
+
6c9f0c
+	if (arg->txsa < 4 && !tb_msg[MACSEC_ATTR_TXSA_LIST]) {
6c9f0c
+		return 0;
6c9f0c
+	} else if (arg->txsa < 4) {
6c9f0c
+		struct nlattr *nla;
6c9f0c
+		int rem;
6c9f0c
+
6c9f0c
+		nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_TXSA_LIST], rem) {
6c9f0c
+			struct nlattr *tb[MACSEC_SA_ATTR_MAX + 1];
6c9f0c
+
6c9f0c
+			err = nla_parse_nested(tb, MACSEC_SA_ATTR_MAX, nla,
6c9f0c
+					       sa_policy);
6c9f0c
+			if (err < 0)
6c9f0c
+				continue;
6c9f0c
+			if (!tb[MACSEC_SA_ATTR_AN])
6c9f0c
+				continue;
6c9f0c
+			if (nla_get_u8(tb[MACSEC_SA_ATTR_AN]) != arg->txsa)
6c9f0c
+				continue;
6c9f0c
+			if (!tb[MACSEC_SA_ATTR_PN])
6c9f0c
+				return 0;
6c9f0c
+			*arg->pn = nla_get_u32(tb[MACSEC_SA_ATTR_PN]);
6c9f0c
+			return 0;
6c9f0c
+		}
6c9f0c
+
6c9f0c
+		return 0;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	if (arg->rxsci == UNUSED_SCI)
6c9f0c
+		return 0;
6c9f0c
+
6c9f0c
+	if (tb_msg[MACSEC_ATTR_RXSC_LIST]) {
6c9f0c
+		struct nlattr *nla;
6c9f0c
+		int rem;
6c9f0c
+
6c9f0c
+		nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_RXSC_LIST], rem) {
6c9f0c
+			struct nlattr *tb[MACSEC_RXSC_ATTR_MAX + 1];
6c9f0c
+
6c9f0c
+			err = nla_parse_nested(tb, MACSEC_RXSC_ATTR_MAX, nla,
6c9f0c
+					       sc_policy);
6c9f0c
+			if (err < 0)
6c9f0c
+				return 0;
6c9f0c
+			if (!tb[MACSEC_RXSC_ATTR_SCI])
6c9f0c
+				continue;
6c9f0c
+			if (nla_get_u64(tb[MACSEC_RXSC_ATTR_SCI]) != arg->rxsci)
6c9f0c
+				continue;
6c9f0c
+			if (!tb[MACSEC_RXSC_ATTR_SA_LIST])
6c9f0c
+				return 0;
6c9f0c
+
6c9f0c
+			nla_for_each_nested(nla, tb[MACSEC_RXSC_ATTR_SA_LIST],
6c9f0c
+					    rem) {
6c9f0c
+				struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
6c9f0c
+
6c9f0c
+				err = nla_parse_nested(tb_sa,
6c9f0c
+						       MACSEC_SA_ATTR_MAX, nla,
6c9f0c
+						       sa_policy);
6c9f0c
+				if (err < 0)
6c9f0c
+					continue;
6c9f0c
+				if (!tb_sa[MACSEC_SA_ATTR_AN])
6c9f0c
+					continue;
6c9f0c
+				if (nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]) !=
6c9f0c
+				    arg->rxsa)
6c9f0c
+					continue;
6c9f0c
+				if (!tb_sa[MACSEC_SA_ATTR_PN])
6c9f0c
+					return 0;
6c9f0c
+				*arg->pn =
6c9f0c
+					nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
6c9f0c
+
6c9f0c
+				return 0;
6c9f0c
+			}
6c9f0c
+
6c9f0c
+			return 0;
6c9f0c
+		}
6c9f0c
+
6c9f0c
+		return 0;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	return 0;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int nl_send_recv(struct nl_sock *sk, struct nl_msg *msg)
6c9f0c
+{
6c9f0c
+	int ret;
6c9f0c
+
6c9f0c
+	ret = nl_send_auto_complete(sk, msg);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to send: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+		return ret;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	ret = nl_recvmsgs_default(sk);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to recv: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int do_dump(struct macsec_drv_data *drv, u8 txsa, u64 rxsci, u8 rxsa,
6c9f0c
+		   u32 *pn)
6c9f0c
+{
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	int ret = 1;
6c9f0c
+
6c9f0c
+	ctx->cb_arg.ifindex = drv->ifi;
6c9f0c
+	ctx->cb_arg.rxsci = rxsci;
6c9f0c
+	ctx->cb_arg.rxsa = rxsa;
6c9f0c
+	ctx->cb_arg.txsa = txsa;
6c9f0c
+	ctx->cb_arg.pn = pn;
6c9f0c
+
6c9f0c
+	msg = nlmsg_alloc();
6c9f0c
+	if (!msg) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to alloc message",
6c9f0c
+			   __func__);
6c9f0c
+		return 1;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->macsec_genl_id, 0,
6c9f0c
+			 NLM_F_DUMP, MACSEC_CMD_GET_TXSC, 0)) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to put header",
6c9f0c
+			   __func__);
6c9f0c
+		goto out_free_msg;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0)
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "failed to communicate: %d (%s)",
6c9f0c
+			   ret, nl_geterror(-ret));
6c9f0c
+
6c9f0c
+	ctx->cb_arg.pn = 0;
6c9f0c
+
6c9f0c
+out_free_msg:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_get_receive_lowest_pn - Get receive lowest PN
6c9f0c
+ * @priv: Private driver interface data
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure (or if not supported)
6c9f0c
+ */
6c9f0c
+static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	int err;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s", __func__);
6c9f0c
+
6c9f0c
+	err = do_dump(drv, 0xff, mka_sci_u64(&sa->sc->sci), sa->an,
6c9f0c
+		      &sa->lowest_pn);
6c9f0c
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: result %d", __func__,
6c9f0c
+		   sa->lowest_pn);
6c9f0c
+
6c9f0c
+	return err;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_get_transmit_next_pn - Get transmit next PN
6c9f0c
+ * @priv: Private driver interface data
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure (or if not supported)
6c9f0c
+ */
6c9f0c
+static int macsec_drv_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	int err;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s", __func__);
6c9f0c
+
6c9f0c
+	err = do_dump(drv, sa->an, UNUSED_SCI, 0xff, &sa->next_pn);
6c9f0c
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: err %d result %d", __func__, err,
6c9f0c
+		   sa->next_pn);
6c9f0c
+	return err;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_set_transmit_next_pn - Set transmit next pn
6c9f0c
+ * @priv: Private driver interface data
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure (or if not supported)
6c9f0c
+ */
6c9f0c
+static int macsec_drv_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	struct nlattr *nest;
6c9f0c
+	int ret = -1;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn);
6c9f0c
+
6c9f0c
+	msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, drv->ifi);
6c9f0c
+	if (!msg)
6c9f0c
+		return ret;
6c9f0c
+
6c9f0c
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
6c9f0c
+	if (!nest)
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
6c9f0c
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
6c9f0c
+
6c9f0c
+	nla_nest_end(msg, nest);
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "failed to communicate: %d (%s)",
6c9f0c
+			   ret, nl_geterror(-ret));
6c9f0c
+	}
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+#define SCISTR MACSTR "::%hx"
6c9f0c
+#define SCI2STR(addr, port) MAC2STR(addr), htons(port)
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_create_receive_sc - Create secure channel for receiving
6c9f0c
+ * @priv: Private driver interface data
6c9f0c
+ * @sc: secure channel
6c9f0c
+ * @sci_addr: secure channel identifier - address
6c9f0c
+ * @sci_port: secure channel identifier - port
6c9f0c
+ * @conf_offset: confidentiality offset (0, 30, or 50)
6c9f0c
+ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
6c9f0c
+ *	2 = Strict)
6c9f0c
+ * Returns: 0 on success, -1 on failure (or if not supported)
6c9f0c
+ */
6c9f0c
+static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc,
6c9f0c
+					unsigned int conf_offset,
6c9f0c
+					int validation)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	int ret = -1;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__,
6c9f0c
+		   SCI2STR(sc->sci.addr, sc->sci.port));
6c9f0c
+
6c9f0c
+	msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi);
6c9f0c
+	if (!msg)
6c9f0c
+		return ret;
6c9f0c
+
6c9f0c
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+	}
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_delete_receive_sc - Delete secure connection for receiving
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sc: secure channel
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	int ret = -1;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__,
6c9f0c
+		   SCI2STR(sc->sci.addr, sc->sci.port));
6c9f0c
+
6c9f0c
+	msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi);
6c9f0c
+	if (!msg)
6c9f0c
+		return ret;
6c9f0c
+
6c9f0c
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+	}
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_create_receive_sa - Create secure association for receive
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	struct nlattr *nest;
6c9f0c
+	int ret = -1;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
6c9f0c
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
6c9f0c
+
6c9f0c
+	msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi);
6c9f0c
+	if (!msg)
6c9f0c
+		return ret;
6c9f0c
+
6c9f0c
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
6c9f0c
+	if (!nest)
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_receive);
6c9f0c
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
6c9f0c
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
6c9f0c
+		&sa->pkey->key_identifier);
6c9f0c
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
6c9f0c
+
6c9f0c
+	nla_nest_end(msg, nest);
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+	}
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_delete_receive_sa - Delete secure association for receive
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	struct nlattr *nest;
6c9f0c
+	int ret = -1;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
6c9f0c
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
6c9f0c
+
6c9f0c
+	msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi);
6c9f0c
+	if (!msg)
6c9f0c
+		return ret;
6c9f0c
+
6c9f0c
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
6c9f0c
+	if (!nest)
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
6c9f0c
+
6c9f0c
+	nla_nest_end(msg, nest);
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+	}
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int set_active_rx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
6c9f0c
+			    u64 sci, unsigned char an, Boolean state)
6c9f0c
+{
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	struct nlattr *nest;
6c9f0c
+	int ret = -1;
6c9f0c
+
6c9f0c
+	msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, ifindex);
6c9f0c
+	if (!msg)
6c9f0c
+		return ret;
6c9f0c
+
6c9f0c
+	if (nla_put_rxsc_config(msg, sci))
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
6c9f0c
+	if (!nest)
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
6c9f0c
+
6c9f0c
+	nla_nest_end(msg, nest);
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0)
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_enable_receive_sa - Enable the SA for receive
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
6c9f0c
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
6c9f0c
+
6c9f0c
+	return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
6c9f0c
+				sa->an, TRUE);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_disable_receive_sa - Disable SA for receive
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
6c9f0c
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
6c9f0c
+
6c9f0c
+	return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
6c9f0c
+				sa->an, FALSE);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci)
6c9f0c
+{
6c9f0c
+	struct rtnl_link *needle;
6c9f0c
+	void *match;
6c9f0c
+
6c9f0c
+	needle = rtnl_link_macsec_alloc();
6c9f0c
+	if (!needle)
6c9f0c
+		return NULL;
6c9f0c
+
6c9f0c
+	rtnl_link_set_link(needle, parent);
6c9f0c
+	rtnl_link_macsec_set_sci(needle, sci);
6c9f0c
+
6c9f0c
+	match = nl_cache_find(cache, (struct nl_object *) needle);
6c9f0c
+	rtnl_link_put(needle);
6c9f0c
+
6c9f0c
+	return (struct rtnl_link *) match;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_create_transmit_sc - Create secure connection for transmit
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sc: secure channel
6c9f0c
+ * @conf_offset: confidentiality offset
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_create_transmit_sc(
6c9f0c
+	void *priv, struct transmit_sc *sc,
6c9f0c
+	enum confidentiality_offset conf_offset)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct rtnl_link *link;
6c9f0c
+	char *ifname;
6c9f0c
+	u64 sci;
6c9f0c
+	int err;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s", __func__);
6c9f0c
+
6c9f0c
+	link = rtnl_link_macsec_alloc();
6c9f0c
+	if (!link) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
6c9f0c
+		return -1;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	rtnl_link_set_link(link, drv->parent_ifi);
6c9f0c
+
6c9f0c
+	sci = mka_sci_u64(&sc->sci);
6c9f0c
+	rtnl_link_macsec_set_sci(link, sci);
6c9f0c
+
6c9f0c
+	drv->created_link = TRUE;
6c9f0c
+
6c9f0c
+	err = rtnl_link_add(drv->sk, link, NLM_F_CREATE);
6c9f0c
+	if (err == -NLE_BUSY) {
6c9f0c
+		wpa_printf(MSG_INFO,
6c9f0c
+			   DRV_PREFIX "link already exists, using it");
6c9f0c
+		drv->created_link = FALSE;
6c9f0c
+	} else if (err < 0) {
6c9f0c
+		rtnl_link_put(link);
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d",
6c9f0c
+			   err);
6c9f0c
+		return err;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	rtnl_link_put(link);
6c9f0c
+
6c9f0c
+	nl_cache_refill(drv->sk, drv->link_cache);
6c9f0c
+	link = lookup_sc(drv->link_cache, drv->parent_ifi, sci);
6c9f0c
+	if (!link) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link");
6c9f0c
+		return -1;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	drv->ifi = rtnl_link_get_ifindex(link);
6c9f0c
+	ifname = rtnl_link_get_name(link);
6c9f0c
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
6c9f0c
+	rtnl_link_put(link);
6c9f0c
+
6c9f0c
+	drv->link = rtnl_link_macsec_alloc();
6c9f0c
+	if (!drv->link) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
6c9f0c
+		return -1;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	rtnl_link_set_name(drv->link, drv->ifname);
6c9f0c
+
6c9f0c
+	/* In case some settings have already been done but we couldn't apply
6c9f0c
+	 * them. */
6c9f0c
+	return try_commit(drv);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_delete_transmit_sc - Delete secure connection for transmit
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sc: secure channel
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	int err;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s", __func__);
6c9f0c
+
6c9f0c
+	if (!drv->created_link) {
6c9f0c
+		rtnl_link_put(drv->link);
6c9f0c
+		drv->link = NULL;
6c9f0c
+		wpa_printf(MSG_DEBUG, DRV_PREFIX
6c9f0c
+			   "we didn't create the link, leave it alone");
6c9f0c
+		return 0;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	err = rtnl_link_delete(drv->sk, drv->link);
6c9f0c
+	if (err < 0)
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link");
6c9f0c
+	rtnl_link_put(drv->link);
6c9f0c
+	drv->link = NULL;
6c9f0c
+
6c9f0c
+	return err;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_create_transmit_sa - Create secure association for transmit
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	struct nlattr *nest;
6c9f0c
+	int ret = -1;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
6c9f0c
+
6c9f0c
+	msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi);
6c9f0c
+	if (!msg)
6c9f0c
+		return ret;
6c9f0c
+
6c9f0c
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
6c9f0c
+	if (!nest)
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
6c9f0c
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
6c9f0c
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
6c9f0c
+		&sa->pkey->key_identifier);
6c9f0c
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_transmit);
6c9f0c
+
6c9f0c
+	nla_nest_end(msg, nest);
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+	}
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_delete_transmit_sa - Delete secure association for transmit
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	struct nlattr *nest;
6c9f0c
+	int ret = -1;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
6c9f0c
+
6c9f0c
+	msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi);
6c9f0c
+	if (!msg)
6c9f0c
+		return ret;
6c9f0c
+
6c9f0c
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
6c9f0c
+	if (!nest)
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
6c9f0c
+
6c9f0c
+	nla_nest_end(msg, nest);
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+	}
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+static int set_active_tx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
6c9f0c
+			    unsigned char an, Boolean state)
6c9f0c
+{
6c9f0c
+	struct nl_msg *msg;
6c9f0c
+	struct nlattr *nest;
6c9f0c
+	int ret = -1;
6c9f0c
+
6c9f0c
+	msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, ifindex);
6c9f0c
+	if (!msg)
6c9f0c
+		return ret;
6c9f0c
+
6c9f0c
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
6c9f0c
+	if (!nest)
6c9f0c
+		goto nla_put_failure;
6c9f0c
+
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
6c9f0c
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
6c9f0c
+
6c9f0c
+	nla_nest_end(msg, nest);
6c9f0c
+
6c9f0c
+	ret = nl_send_recv(ctx->sk, msg);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR,
6c9f0c
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
6c9f0c
+			   __func__, ret, nl_geterror(-ret));
6c9f0c
+	}
6c9f0c
+
6c9f0c
+nla_put_failure:
6c9f0c
+	nlmsg_free(msg);
6c9f0c
+	return ret;
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_enable_transmit_sa - Enable SA for transmit
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+	int ret;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
6c9f0c
+
6c9f0c
+	ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE);
6c9f0c
+	if (ret < 0) {
6c9f0c
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to enable txsa");
6c9f0c
+		return ret;
6c9f0c
+	}
6c9f0c
+
6c9f0c
+	drv->encoding_sa_set = TRUE;
6c9f0c
+	drv->encoding_sa = sa->an;
6c9f0c
+
6c9f0c
+	return try_commit(drv);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+/**
6c9f0c
+ * macsec_drv_disable_transmit_sa - Disable SA for transmit
6c9f0c
+ * @priv: private driver interface data from init()
6c9f0c
+ * @sa: secure association
6c9f0c
+ * Returns: 0 on success, -1 on failure
6c9f0c
+ */
6c9f0c
+static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa)
6c9f0c
+{
6c9f0c
+	struct macsec_drv_data *drv = priv;
6c9f0c
+	struct macsec_genl_ctx *ctx = &drv->ctx;
6c9f0c
+
6c9f0c
+	wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
6c9f0c
+
6c9f0c
+	return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
+const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
6c9f0c
+	.name = "macsec_linux",
6c9f0c
+	.desc = "MACsec Ethernet driver for Linux",
6c9f0c
+	.get_ssid = driver_wired_get_ssid,
6c9f0c
+	.get_bssid = driver_wired_get_bssid,
6c9f0c
+	.get_capa = driver_wired_get_capa,
6c9f0c
+	.init = macsec_drv_wpa_init,
6c9f0c
+	.deinit = macsec_drv_wpa_deinit,
6c9f0c
+
6c9f0c
+	.macsec_init = macsec_drv_macsec_init,
6c9f0c
+	.macsec_deinit = macsec_drv_macsec_deinit,
6c9f0c
+	.macsec_get_capability = macsec_drv_get_capability,
6c9f0c
+	.enable_protect_frames = macsec_drv_enable_protect_frames,
6c9f0c
+	.enable_encrypt = macsec_drv_enable_encrypt,
6c9f0c
+	.set_replay_protect = macsec_drv_set_replay_protect,
6c9f0c
+	.set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
6c9f0c
+	.enable_controlled_port = macsec_drv_enable_controlled_port,
6c9f0c
+	.get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
6c9f0c
+	.get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
6c9f0c
+	.set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
6c9f0c
+	.create_receive_sc = macsec_drv_create_receive_sc,
6c9f0c
+	.delete_receive_sc = macsec_drv_delete_receive_sc,
6c9f0c
+	.create_receive_sa = macsec_drv_create_receive_sa,
6c9f0c
+	.delete_receive_sa = macsec_drv_delete_receive_sa,
6c9f0c
+	.enable_receive_sa = macsec_drv_enable_receive_sa,
6c9f0c
+	.disable_receive_sa = macsec_drv_disable_receive_sa,
6c9f0c
+	.create_transmit_sc = macsec_drv_create_transmit_sc,
6c9f0c
+	.delete_transmit_sc = macsec_drv_delete_transmit_sc,
6c9f0c
+	.create_transmit_sa = macsec_drv_create_transmit_sa,
6c9f0c
+	.delete_transmit_sa = macsec_drv_delete_transmit_sa,
6c9f0c
+	.enable_transmit_sa = macsec_drv_enable_transmit_sa,
6c9f0c
+	.disable_transmit_sa = macsec_drv_disable_transmit_sa,
6c9f0c
+};
6c9f0c
diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
6c9f0c
index 00773a7..e95df6d 100644
6c9f0c
--- a/src/drivers/drivers.c
6c9f0c
+++ b/src/drivers/drivers.c
6c9f0c
@@ -34,6 +34,9 @@ const struct wpa_driver_ops *const wpa_drivers[] =
6c9f0c
 #ifdef CONFIG_DRIVER_WIRED
6c9f0c
 	&wpa_driver_wired_ops,
6c9f0c
 #endif /* CONFIG_DRIVER_WIRED */
6c9f0c
+#ifdef CONFIG_DRIVER_MACSEC_LINUX
6c9f0c
+	&wpa_driver_macsec_linux_ops,
6c9f0c
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
6c9f0c
 #ifdef CONFIG_DRIVER_MACSEC_QCA
6c9f0c
 	&wpa_driver_macsec_qca_ops,
6c9f0c
 #endif /* CONFIG_DRIVER_MACSEC_QCA */
6c9f0c
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
6c9f0c
index 282da50..1496b47 100644
6c9f0c
--- a/src/drivers/drivers.mak
6c9f0c
+++ b/src/drivers/drivers.mak
6c9f0c
@@ -18,6 +18,13 @@ DRV_OBJS += ../src/drivers/driver_wired.o
6c9f0c
 NEED_DRV_WIRED_COMMON=1
6c9f0c
 endif
6c9f0c
 
6c9f0c
+ifdef CONFIG_DRIVER_MACSEC_LINUX
6c9f0c
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
6c9f0c
+DRV_OBJS += ../src/drivers/driver_macsec_linux.o
6c9f0c
+NEED_DRV_WIRED_COMMON=1
6c9f0c
+CONFIG_LIBNL3_ROUTE=y
6c9f0c
+endif
6c9f0c
+
6c9f0c
 ifdef CONFIG_DRIVER_MACSEC_QCA
6c9f0c
 DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
6c9f0c
 DRV_OBJS += ../src/drivers/driver_macsec_qca.o
6c9f0c
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
6c9f0c
index 508f834..cd25133 100644
6c9f0c
--- a/src/drivers/drivers.mk
6c9f0c
+++ b/src/drivers/drivers.mk
6c9f0c
@@ -18,6 +18,13 @@ DRV_OBJS += src/drivers/driver_wired.c
6c9f0c
 NEED_DRV_WIRED_COMMON=1
6c9f0c
 endif
6c9f0c
 
6c9f0c
+ifdef CONFIG_DRIVER_MACSEC_LINUX
6c9f0c
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
6c9f0c
+DRV_OBJS += src/drivers/driver_macsec_linux.c
6c9f0c
+NEED_DRV_WIRED_COMMON=1
6c9f0c
+CONFIG_LIBNL3_ROUTE=y
6c9f0c
+endif
6c9f0c
+
6c9f0c
 ifdef NEED_DRV_WIRED_COMMON
6c9f0c
 DRV_OBJS += src/drivers/driver_wired_common.c
6c9f0c
 endif
6c9f0c
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
6c9f0c
index 3a495ca..1d6d9a9 100644
6c9f0c
--- a/src/pae/ieee802_1x_kay.c
6c9f0c
+++ b/src/pae/ieee802_1x_kay.c
6c9f0c
@@ -379,6 +379,17 @@ ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
6c9f0c
 }
6c9f0c
 
6c9f0c
 
6c9f0c
+u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci)
6c9f0c
+{
6c9f0c
+	struct ieee802_1x_mka_sci tmp;
6c9f0c
+
6c9f0c
+	os_memcpy(tmp.addr, sci->addr, ETH_ALEN);
6c9f0c
+	tmp.port = sci->port;
6c9f0c
+
6c9f0c
+	return *((u64 *) &tmp);
6c9f0c
+}
6c9f0c
+
6c9f0c
+
6c9f0c
 static Boolean sci_equal(const struct ieee802_1x_mka_sci *a,
6c9f0c
 			 const struct ieee802_1x_mka_sci *b)
6c9f0c
 {
6c9f0c
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
6c9f0c
index ea5a0dd..9a92d1c 100644
6c9f0c
--- a/src/pae/ieee802_1x_kay.h
6c9f0c
+++ b/src/pae/ieee802_1x_kay.h
6c9f0c
@@ -231,6 +231,8 @@ struct ieee802_1x_kay {
6c9f0c
 };
6c9f0c
 
6c9f0c
 
6c9f0c
+u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci);
6c9f0c
+
6c9f0c
 struct ieee802_1x_kay *
6c9f0c
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
6c9f0c
 		    u16 port, const char *ifname, const u8 *addr);
6c9f0c
-- 
6c9f0c
2.7.4
6c9f0c