hostapd/src/drivers/driver_macsec_linux.c
Andrei Otcheretianski 172b0a9a2b AP/driver: Add link ID to send EAPOL callbacks
EAPOL frames may need to be transmitted from the link address and not
MLD address. For example, in case of authentication between AP MLD and
legacy STA. Add link_id parameter to EAPOL send APIs.

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
2023-06-12 16:26:56 +03:00

1720 lines
41 KiB
C

/*
* Driver interaction with Linux MACsec kernel module
* Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc.
* Copyright (c) 2019, The Linux Foundation
*
* 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 "common/eapol_common.h"
#include "pae/ieee802_1x_kay.h"
#include "driver.h"
#include "driver_wired_common.h"
#define DRV_PREFIX "macsec_linux: "
#define UNUSED_SCI 0xffffffffffffffff
#if LIBNL_VER_NUM >= LIBNL_VER(3, 6)
#define LIBNL_HAS_OFFLOAD
#endif
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;
char ifname[IFNAMSIZ + 1];
int ifi;
int parent_ifi;
int use_pae_group_addr;
bool created_link;
bool controlled_port_enabled;
bool controlled_port_enabled_set;
bool protect_frames;
bool protect_frames_set;
bool encrypt;
bool encrypt_set;
bool replay_protect;
bool replay_protect_set;
#ifdef LIBNL_HAS_OFFLOAD
enum macsec_offload offload;
bool offload_set;
#endif /* LIBNL_HAS_OFFLOAD */
u32 replay_window;
u8 encoding_sa;
bool encoding_sa_set;
u64 cipher_suite;
bool cipher_suite_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);
}
#ifdef LIBNL_HAS_OFFLOAD
if (drv->offload_set) {
wpa_printf(MSG_DEBUG, DRV_PREFIX
"%s: try_commit offload=%d",
drv->ifname, drv->offload);
rtnl_link_macsec_set_offload(drv->link, drv->offload);
}
#endif /* LIBNL_HAS_OFFLOAD */
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",
nl_geterror(err));
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",
nl_geterror(err));
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, bool 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, bool 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, bool 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_offload - Set offload status
* @priv: Private driver interface data
* @offload: 0 = MACSEC_OFFLOAD_OFF
* 1 = MACSEC_OFFLOAD_PHY
* 2 = MACSEC_OFFLOAD_MAC
* Returns: 0 on success, -1 on failure (or if not supported)
*/
static int macsec_drv_set_offload(void *priv, u8 offload)
{
#ifdef LIBNL_HAS_OFFLOAD
struct macsec_drv_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s -> %02" PRIx8, __func__, offload);
drv->offload_set = true;
drv->offload = offload;
return try_commit(drv);
#else /* LIBNL_HAS_OFFLOAD */
if (offload == 0)
return 0;
wpa_printf(MSG_INFO,
"%s: libnl version does not include support for MACsec offload",
__func__);
return -1;
#endif /* LIBNL_HAS_OFFLOAD */
}
/**
* 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)
{
struct macsec_drv_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs);
drv->cipher_suite_set = true;
drv->cipher_suite = cs;
return try_commit(drv);
}
/**
* 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, bool 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;
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_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, bool 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,
u64 cs)
{
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);
if (cs)
rtnl_link_macsec_set_cipher_suite(needle, cs);
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;
u64 cs = 0;
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;
if (drv->cipher_suite_set) {
cs = drv->cipher_suite;
drv->cipher_suite_set = false;
rtnl_link_macsec_set_cipher_suite(link, cs);
}
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, cs);
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, bool 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;
}
#ifdef __linux__
static void macsec_drv_handle_data(void *ctx, unsigned char *buf, size_t len)
{
#ifdef HOSTAPD
struct ieee8023_hdr *hdr;
u8 *pos, *sa;
size_t left;
union wpa_event_data event;
/* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
* 2 byte ethertype */
if (len < 14) {
wpa_printf(MSG_MSGDUMP, "%s: too short (%lu)",
__func__, (unsigned long) len);
return;
}
hdr = (struct ieee8023_hdr *) buf;
switch (ntohs(hdr->ethertype)) {
case ETH_P_PAE:
wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
sa = hdr->src;
os_memset(&event, 0, sizeof(event));
event.new_sta.addr = sa;
wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
pos = (u8 *) (hdr + 1);
left = len - sizeof(*hdr);
drv_event_eapol_rx(ctx, sa, pos, left);
break;
default:
wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
ntohs(hdr->ethertype));
break;
}
#endif /* HOSTAPD */
}
static void macsec_drv_handle_read(int sock, void *eloop_ctx, void *sock_ctx)
{
int len;
unsigned char buf[3000];
len = recv(sock, buf, sizeof(buf), 0);
if (len < 0) {
wpa_printf(MSG_ERROR, "macsec_linux: recv: %s",
strerror(errno));
return;
}
macsec_drv_handle_data(eloop_ctx, buf, len);
}
#endif /* __linux__ */
static int macsec_drv_init_sockets(struct macsec_drv_data *drv, u8 *own_addr)
{
#ifdef __linux__
struct ifreq ifr;
struct sockaddr_ll addr;
drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
if (drv->common.sock < 0) {
wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
strerror(errno));
return -1;
}
if (eloop_register_read_sock(drv->common.sock, macsec_drv_handle_read,
drv->common.ctx, NULL)) {
wpa_printf(MSG_INFO, "Could not register read socket");
return -1;
}
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
strerror(errno));
return -1;
}
os_memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_ifindex = ifr.ifr_ifindex;
wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
addr.sll_ifindex);
if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
return -1;
}
/* filter multicast address */
if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
pae_group_addr, 1) < 0) {
wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
"membership");
return -1;
}
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
strerror(errno));
return -1;
}
if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
ifr.ifr_hwaddr.sa_family);
return -1;
}
os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
return 0;
#else /* __linux__ */
return -1;
#endif /* __linux__ */
}
static void * macsec_drv_hapd_init(struct hostapd_data *hapd,
struct wpa_init_params *params)
{
struct macsec_drv_data *drv;
drv = os_zalloc(sizeof(struct macsec_drv_data));
if (drv == NULL) {
wpa_printf(MSG_INFO,
"Could not allocate memory for wired driver data");
return NULL;
}
drv->common.ctx = hapd;
os_strlcpy(drv->common.ifname, params->ifname,
sizeof(drv->common.ifname));
drv->use_pae_group_addr = params->use_pae_group_addr;
if (macsec_drv_init_sockets(drv, params->own_addr)) {
os_free(drv);
return NULL;
}
return drv;
}
static void macsec_drv_hapd_deinit(void *priv)
{
struct macsec_drv_data *drv = priv;
if (drv->common.sock >= 0) {
eloop_unregister_read_sock(drv->common.sock);
close(drv->common.sock);
}
os_free(drv);
}
static int macsec_drv_send_eapol(void *priv, const u8 *addr,
const u8 *data, size_t data_len, int encrypt,
const u8 *own_addr, u32 flags, int link_id)
{
struct macsec_drv_data *drv = priv;
struct ieee8023_hdr *hdr;
size_t len;
u8 *pos;
int res;
len = sizeof(*hdr) + data_len;
hdr = os_zalloc(len);
if (hdr == NULL) {
wpa_printf(MSG_INFO,
"%s: malloc() failed (len=%lu)",
__func__, (unsigned long) len);
return -1;
}
os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
ETH_ALEN);
os_memcpy(hdr->src, own_addr, ETH_ALEN);
hdr->ethertype = htons(ETH_P_PAE);
pos = (u8 *) (hdr + 1);
os_memcpy(pos, data, data_len);
res = send(drv->common.sock, (u8 *) hdr, len, 0);
os_free(hdr);
if (res < 0) {
wpa_printf(MSG_ERROR,
"%s: packet len: %lu - failed: send: %s",
__func__, (unsigned long) len, strerror(errno));
}
return res;
}
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,
.hapd_init = macsec_drv_hapd_init,
.hapd_deinit = macsec_drv_hapd_deinit,
.hapd_send_eapol = macsec_drv_send_eapol,
.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_offload = macsec_drv_set_offload,
.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,
};