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