mirror of
https://github.com/vanhoefm/fragattacks.git
synced 2025-01-19 19:34:05 -05:00
2fc0675683
The purpose of the Lowest Acceptable PN (lpn) parameters in the MACsec SAK Use parameter set is to enforce delay protection. Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses MKA to communicate the lowest PN used for transmission with the SAK within the last two seconds, allowing receivers to bound transmission delays." When encoding the SAK Use parameter set the KaY should set llpn and olpn to the lowest PN transmitted by the latest SAK and oldest SAK (if active) within the last two seconds. Because MKPDUs are transmitted every 2 seconds (MKA_HELLO_TIME), the solution implemented here calculates lpn based on the txsc->next_pn read during the previous MKPDU transmit. Upon receiving and decoding a SAK Use parameter set with delay protection enabled, the KaY will update the SecY's lpn if the delay protect lpn is greater than the SecY's current lpn (which is a product of last PN received and replay protection and window size). Signed-off-by: Michael Siedzik <msiedzik@extremenetworks.com>
1438 lines
34 KiB
C
1438 lines
34 KiB
C
/*
|
|
* 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->sk)
|
|
return 0;
|
|
|
|
if (!drv->link)
|
|
return 0;
|
|
|
|
if (drv->controlled_port_enabled_set) {
|
|
struct rtnl_link *change = rtnl_link_alloc();
|
|
|
|
wpa_printf(MSG_DEBUG, DRV_PREFIX
|
|
"%s: try_commit controlled_port_enabled=%d",
|
|
drv->ifname, drv->controlled_port_enabled);
|
|
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) {
|
|
wpa_printf(MSG_DEBUG, DRV_PREFIX
|
|
"%s: try_commit protect_frames=%d",
|
|
drv->ifname, drv->protect_frames);
|
|
rtnl_link_macsec_set_protect(drv->link, drv->protect_frames);
|
|
}
|
|
|
|
if (drv->encrypt_set) {
|
|
wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: try_commit encrypt=%d",
|
|
drv->ifname, drv->encrypt);
|
|
rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt);
|
|
}
|
|
|
|
if (drv->replay_protect_set) {
|
|
wpa_printf(MSG_DEBUG, DRV_PREFIX
|
|
"%s: try_commit replay_protect=%d replay_window=%d",
|
|
drv->ifname, drv->replay_protect,
|
|
drv->replay_window);
|
|
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) {
|
|
wpa_printf(MSG_DEBUG, DRV_PREFIX
|
|
"%s: try_commit encoding_sa=%d",
|
|
drv->ifname, drv->encoding_sa);
|
|
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 int macsec_check_macsec(void)
|
|
{
|
|
struct nl_sock *sk;
|
|
int err = -1;
|
|
|
|
sk = nl_socket_alloc();
|
|
if (!sk) {
|
|
wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
|
|
return -1;
|
|
}
|
|
|
|
if (genl_connect(sk) < 0) {
|
|
wpa_printf(MSG_ERROR,
|
|
DRV_PREFIX "connection to genl socket failed");
|
|
goto out_free;
|
|
}
|
|
|
|
if (genl_ctrl_resolve(sk, "macsec") < 0) {
|
|
wpa_printf(MSG_ERROR,
|
|
DRV_PREFIX "genl resolve failed - macsec kernel module not present?");
|
|
goto out_free;
|
|
}
|
|
|
|
err = 0;
|
|
|
|
out_free:
|
|
nl_socket_free(sk);
|
|
return err;
|
|
}
|
|
|
|
|
|
static void * macsec_drv_wpa_init(void *ctx, const char *ifname)
|
|
{
|
|
struct macsec_drv_data *drv;
|
|
|
|
if (macsec_check_macsec() < 0)
|
|
return NULL;
|
|
|
|
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;
|
|
}
|
|
wpa_printf(MSG_DEBUG, DRV_PREFIX "ifname=%s parent_ifi=%d",
|
|
drv->common.ifname, drv->parent_ifi);
|
|
|
|
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 = NULL;
|
|
|
|
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_set_receive_lowest_pn - Set 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_set_receive_lowest_pn(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,
|
|
DRV_PREFIX "%s: set_receive_lowest_pn -> %d: %d",
|
|
drv->ifname, sa->an, sa->next_pn);
|
|
|
|
msg = msg_prepare(MACSEC_CMD_UPD_RXSA, 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;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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, DRV_PREFIX "%s: create_receive_sc -> " SCISTR
|
|
" (conf_offset=%u validation=%d)",
|
|
drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port),
|
|
conf_offset, validation);
|
|
|
|
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, DRV_PREFIX "%s: delete_receive_sc -> " SCISTR,
|
|
drv->ifname, 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,
|
|
DRV_PREFIX "%s: create_receive_sa -> %d on " SCISTR
|
|
" (enable_receive=%d next_pn=%u)",
|
|
drv->ifname, sa->an,
|
|
SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
|
|
sa->enable_receive, sa->next_pn);
|
|
wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
|
|
&sa->pkey->key_identifier,
|
|
sizeof(sa->pkey->key_identifier));
|
|
wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
|
|
sa->pkey->key, sa->pkey->key_len);
|
|
|
|
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, DRV_PREFIX "%s: delete_receive_sa -> %d on "
|
|
SCISTR, drv->ifname, 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, DRV_PREFIX "%s: enable_receive_sa -> %d on "
|
|
SCISTR, drv->ifname, 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, DRV_PREFIX "%s: disable_receive_sa -> %d on "
|
|
SCISTR, drv->ifname, 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,
|
|
unsigned int conf_offset)
|
|
{
|
|
struct macsec_drv_data *drv = priv;
|
|
struct rtnl_link *link;
|
|
char *ifname;
|
|
u64 sci;
|
|
int err;
|
|
|
|
wpa_printf(MSG_DEBUG, DRV_PREFIX
|
|
"%s: create_transmit_sc -> " SCISTR " (conf_offset=%d)",
|
|
drv->common.ifname, SCI2STR(sc->sci.addr, sc->sci.port),
|
|
conf_offset);
|
|
|
|
if (!drv->sk) {
|
|
wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket");
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
wpa_printf(MSG_DEBUG,
|
|
DRV_PREFIX "%s: create_transmit_sc: ifi=%d ifname=%s",
|
|
drv->common.ifname, drv->ifi, ifname);
|
|
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, DRV_PREFIX "%s: delete_transmit_sc -> " SCISTR,
|
|
drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
|
|
|
|
if (!drv->sk)
|
|
return 0;
|
|
|
|
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, DRV_PREFIX "%s: create_transmit_sa -> %d on "
|
|
SCISTR " (enable_transmit=%d next_pn=%u)",
|
|
drv->ifname, sa->an,
|
|
SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
|
|
sa->enable_transmit, sa->next_pn);
|
|
wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
|
|
&sa->pkey->key_identifier,
|
|
sizeof(sa->pkey->key_identifier));
|
|
wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
|
|
sa->pkey->key, sa->pkey->key_len);
|
|
|
|
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, DRV_PREFIX "%s: delete_transmit_sa -> %d on "
|
|
SCISTR, drv->ifname, sa->an,
|
|
SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
|
|
|
|
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, DRV_PREFIX "%s: enable_transmit_sa -> %d on "
|
|
SCISTR, drv->ifname, sa->an,
|
|
SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
|
|
|
|
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, DRV_PREFIX "%s: disable_transmit_sa -> %d on "
|
|
SCISTR, drv->ifname, sa->an,
|
|
SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
|
|
|
|
return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE);
|
|
}
|
|
|
|
|
|
static int macsec_drv_status(void *priv, char *buf, size_t buflen)
|
|
{
|
|
struct macsec_drv_data *drv = priv;
|
|
int res;
|
|
char *pos, *end;
|
|
|
|
pos = buf;
|
|
end = buf + buflen;
|
|
|
|
res = os_snprintf(pos, end - pos,
|
|
"ifname=%s\n"
|
|
"ifi=%d\n"
|
|
"parent_ifname=%s\n"
|
|
"parent_ifi=%d\n",
|
|
drv->common.ifname, drv->ifi,
|
|
drv->ifname, drv->parent_ifi);
|
|
if (os_snprintf_error(end - pos, res))
|
|
return pos - buf;
|
|
pos += res;
|
|
|
|
return pos - buf;
|
|
}
|
|
|
|
|
|
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,
|
|
.set_receive_lowest_pn = macsec_drv_set_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,
|
|
|
|
.status = macsec_drv_status,
|
|
};
|