mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 04:33:26 +02:00
Now macsec on top of vlan can be offloaded to macsec offloading devices so that VLAN tag is sent in clear text on wire i.e, packet structure is DMAC|SMAC|VLAN|SECTAG. Offloading devices can simply enable NETIF_F_HW_MACSEC feature in netdev->vlan_features for this to work. But the logic in offloading drivers to retrieve the private structure from netdev needs to be changed to check whether the netdev received is real device or a vlan device and get private structure accordingly. This patch changes the offloading drivers to use helper macsec_netdev_priv instead of netdev_priv. Signed-off-by: Subbaraya Sundeep <sbhatta@marvell.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1771 lines
46 KiB
C
1771 lines
46 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Atlantic Network Driver
|
|
* Copyright (C) 2020 Marvell International Ltd.
|
|
*/
|
|
|
|
#include "aq_macsec.h"
|
|
#include "aq_nic.h"
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include "macsec/macsec_api.h"
|
|
#define AQ_MACSEC_KEY_LEN_128_BIT 16
|
|
#define AQ_MACSEC_KEY_LEN_192_BIT 24
|
|
#define AQ_MACSEC_KEY_LEN_256_BIT 32
|
|
|
|
enum aq_clear_type {
|
|
/* update HW configuration */
|
|
AQ_CLEAR_HW = BIT(0),
|
|
/* update SW configuration (busy bits, pointers) */
|
|
AQ_CLEAR_SW = BIT(1),
|
|
/* update both HW and SW configuration */
|
|
AQ_CLEAR_ALL = AQ_CLEAR_HW | AQ_CLEAR_SW,
|
|
};
|
|
|
|
static int aq_clear_txsc(struct aq_nic_s *nic, const int txsc_idx,
|
|
enum aq_clear_type clear_type);
|
|
static int aq_clear_txsa(struct aq_nic_s *nic, struct aq_macsec_txsc *aq_txsc,
|
|
const int sa_num, enum aq_clear_type clear_type);
|
|
static int aq_clear_rxsc(struct aq_nic_s *nic, const int rxsc_idx,
|
|
enum aq_clear_type clear_type);
|
|
static int aq_clear_rxsa(struct aq_nic_s *nic, struct aq_macsec_rxsc *aq_rxsc,
|
|
const int sa_num, enum aq_clear_type clear_type);
|
|
static int aq_clear_secy(struct aq_nic_s *nic, const struct macsec_secy *secy,
|
|
enum aq_clear_type clear_type);
|
|
static int aq_apply_macsec_cfg(struct aq_nic_s *nic);
|
|
static int aq_apply_secy_cfg(struct aq_nic_s *nic,
|
|
const struct macsec_secy *secy);
|
|
|
|
static void aq_ether_addr_to_mac(u32 mac[2], const unsigned char *emac)
|
|
{
|
|
u32 tmp[2] = { 0 };
|
|
|
|
memcpy(((u8 *)tmp) + 2, emac, ETH_ALEN);
|
|
|
|
mac[0] = swab32(tmp[1]);
|
|
mac[1] = swab32(tmp[0]);
|
|
}
|
|
|
|
/* There's a 1:1 mapping between SecY and TX SC */
|
|
static int aq_get_txsc_idx_from_secy(struct aq_macsec_cfg *macsec_cfg,
|
|
const struct macsec_secy *secy)
|
|
{
|
|
int i;
|
|
|
|
if (unlikely(!secy))
|
|
return -1;
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
|
|
if (macsec_cfg->aq_txsc[i].sw_secy == secy)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int aq_get_rxsc_idx_from_rxsc(struct aq_macsec_cfg *macsec_cfg,
|
|
const struct macsec_rx_sc *rxsc)
|
|
{
|
|
int i;
|
|
|
|
if (unlikely(!rxsc))
|
|
return -1;
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
|
|
if (macsec_cfg->aq_rxsc[i].sw_rxsc == rxsc)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int aq_get_txsc_idx_from_sc_idx(const enum aq_macsec_sc_sa sc_sa,
|
|
const int sc_idx)
|
|
{
|
|
switch (sc_sa) {
|
|
case aq_macsec_sa_sc_4sa_8sc:
|
|
return sc_idx >> 2;
|
|
case aq_macsec_sa_sc_2sa_16sc:
|
|
return sc_idx >> 1;
|
|
case aq_macsec_sa_sc_1sa_32sc:
|
|
return sc_idx;
|
|
default:
|
|
WARN_ONCE(true, "Invalid sc_sa");
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Rotate keys u32[8] */
|
|
static void aq_rotate_keys(u32 (*key)[8], const int key_len)
|
|
{
|
|
u32 tmp[8] = { 0 };
|
|
|
|
memcpy(&tmp, key, sizeof(tmp));
|
|
memset(*key, 0, sizeof(*key));
|
|
|
|
if (key_len == AQ_MACSEC_KEY_LEN_128_BIT) {
|
|
(*key)[0] = swab32(tmp[3]);
|
|
(*key)[1] = swab32(tmp[2]);
|
|
(*key)[2] = swab32(tmp[1]);
|
|
(*key)[3] = swab32(tmp[0]);
|
|
} else if (key_len == AQ_MACSEC_KEY_LEN_192_BIT) {
|
|
(*key)[0] = swab32(tmp[5]);
|
|
(*key)[1] = swab32(tmp[4]);
|
|
(*key)[2] = swab32(tmp[3]);
|
|
(*key)[3] = swab32(tmp[2]);
|
|
(*key)[4] = swab32(tmp[1]);
|
|
(*key)[5] = swab32(tmp[0]);
|
|
} else if (key_len == AQ_MACSEC_KEY_LEN_256_BIT) {
|
|
(*key)[0] = swab32(tmp[7]);
|
|
(*key)[1] = swab32(tmp[6]);
|
|
(*key)[2] = swab32(tmp[5]);
|
|
(*key)[3] = swab32(tmp[4]);
|
|
(*key)[4] = swab32(tmp[3]);
|
|
(*key)[5] = swab32(tmp[2]);
|
|
(*key)[6] = swab32(tmp[1]);
|
|
(*key)[7] = swab32(tmp[0]);
|
|
} else {
|
|
pr_warn("Rotate_keys: invalid key_len\n");
|
|
}
|
|
}
|
|
|
|
#define STATS_2x32_TO_64(stat_field) \
|
|
(((u64)stat_field[1] << 32) | stat_field[0])
|
|
|
|
static int aq_get_macsec_common_stats(struct aq_hw_s *hw,
|
|
struct aq_macsec_common_stats *stats)
|
|
{
|
|
struct aq_mss_ingress_common_counters ingress_counters;
|
|
struct aq_mss_egress_common_counters egress_counters;
|
|
int ret;
|
|
|
|
/* MACSEC counters */
|
|
ret = aq_mss_get_ingress_common_counters(hw, &ingress_counters);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
stats->in.ctl_pkts = STATS_2x32_TO_64(ingress_counters.ctl_pkts);
|
|
stats->in.tagged_miss_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.tagged_miss_pkts);
|
|
stats->in.untagged_miss_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.untagged_miss_pkts);
|
|
stats->in.notag_pkts = STATS_2x32_TO_64(ingress_counters.notag_pkts);
|
|
stats->in.untagged_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.untagged_pkts);
|
|
stats->in.bad_tag_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.bad_tag_pkts);
|
|
stats->in.no_sci_pkts = STATS_2x32_TO_64(ingress_counters.no_sci_pkts);
|
|
stats->in.unknown_sci_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.unknown_sci_pkts);
|
|
stats->in.ctrl_prt_pass_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.ctrl_prt_pass_pkts);
|
|
stats->in.unctrl_prt_pass_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.unctrl_prt_pass_pkts);
|
|
stats->in.ctrl_prt_fail_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.ctrl_prt_fail_pkts);
|
|
stats->in.unctrl_prt_fail_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.unctrl_prt_fail_pkts);
|
|
stats->in.too_long_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.too_long_pkts);
|
|
stats->in.igpoc_ctl_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.igpoc_ctl_pkts);
|
|
stats->in.ecc_error_pkts =
|
|
STATS_2x32_TO_64(ingress_counters.ecc_error_pkts);
|
|
stats->in.unctrl_hit_drop_redir =
|
|
STATS_2x32_TO_64(ingress_counters.unctrl_hit_drop_redir);
|
|
|
|
ret = aq_mss_get_egress_common_counters(hw, &egress_counters);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
stats->out.ctl_pkts = STATS_2x32_TO_64(egress_counters.ctl_pkt);
|
|
stats->out.unknown_sa_pkts =
|
|
STATS_2x32_TO_64(egress_counters.unknown_sa_pkts);
|
|
stats->out.untagged_pkts =
|
|
STATS_2x32_TO_64(egress_counters.untagged_pkts);
|
|
stats->out.too_long = STATS_2x32_TO_64(egress_counters.too_long);
|
|
stats->out.ecc_error_pkts =
|
|
STATS_2x32_TO_64(egress_counters.ecc_error_pkts);
|
|
stats->out.unctrl_hit_drop_redir =
|
|
STATS_2x32_TO_64(egress_counters.unctrl_hit_drop_redir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_get_rxsa_stats(struct aq_hw_s *hw, const int sa_idx,
|
|
struct aq_macsec_rx_sa_stats *stats)
|
|
{
|
|
struct aq_mss_ingress_sa_counters i_sa_counters;
|
|
int ret;
|
|
|
|
ret = aq_mss_get_ingress_sa_counters(hw, &i_sa_counters, sa_idx);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
stats->untagged_hit_pkts =
|
|
STATS_2x32_TO_64(i_sa_counters.untagged_hit_pkts);
|
|
stats->ctrl_hit_drop_redir_pkts =
|
|
STATS_2x32_TO_64(i_sa_counters.ctrl_hit_drop_redir_pkts);
|
|
stats->not_using_sa = STATS_2x32_TO_64(i_sa_counters.not_using_sa);
|
|
stats->unused_sa = STATS_2x32_TO_64(i_sa_counters.unused_sa);
|
|
stats->not_valid_pkts = STATS_2x32_TO_64(i_sa_counters.not_valid_pkts);
|
|
stats->invalid_pkts = STATS_2x32_TO_64(i_sa_counters.invalid_pkts);
|
|
stats->ok_pkts = STATS_2x32_TO_64(i_sa_counters.ok_pkts);
|
|
stats->late_pkts = STATS_2x32_TO_64(i_sa_counters.late_pkts);
|
|
stats->delayed_pkts = STATS_2x32_TO_64(i_sa_counters.delayed_pkts);
|
|
stats->unchecked_pkts = STATS_2x32_TO_64(i_sa_counters.unchecked_pkts);
|
|
stats->validated_octets =
|
|
STATS_2x32_TO_64(i_sa_counters.validated_octets);
|
|
stats->decrypted_octets =
|
|
STATS_2x32_TO_64(i_sa_counters.decrypted_octets);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_get_txsa_stats(struct aq_hw_s *hw, const int sa_idx,
|
|
struct aq_macsec_tx_sa_stats *stats)
|
|
{
|
|
struct aq_mss_egress_sa_counters e_sa_counters;
|
|
int ret;
|
|
|
|
ret = aq_mss_get_egress_sa_counters(hw, &e_sa_counters, sa_idx);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
stats->sa_hit_drop_redirect =
|
|
STATS_2x32_TO_64(e_sa_counters.sa_hit_drop_redirect);
|
|
stats->sa_protected2_pkts =
|
|
STATS_2x32_TO_64(e_sa_counters.sa_protected2_pkts);
|
|
stats->sa_protected_pkts =
|
|
STATS_2x32_TO_64(e_sa_counters.sa_protected_pkts);
|
|
stats->sa_encrypted_pkts =
|
|
STATS_2x32_TO_64(e_sa_counters.sa_encrypted_pkts);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_get_txsa_next_pn(struct aq_hw_s *hw, const int sa_idx, u32 *pn)
|
|
{
|
|
struct aq_mss_egress_sa_record sa_rec;
|
|
int ret;
|
|
|
|
ret = aq_mss_get_egress_sa_record(hw, &sa_rec, sa_idx);
|
|
if (likely(!ret))
|
|
*pn = sa_rec.next_pn;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_get_rxsa_next_pn(struct aq_hw_s *hw, const int sa_idx, u32 *pn)
|
|
{
|
|
struct aq_mss_ingress_sa_record sa_rec;
|
|
int ret;
|
|
|
|
ret = aq_mss_get_ingress_sa_record(hw, &sa_rec, sa_idx);
|
|
if (likely(!ret))
|
|
*pn = (!sa_rec.sat_nextpn) ? sa_rec.next_pn : 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_get_txsc_stats(struct aq_hw_s *hw, const int sc_idx,
|
|
struct aq_macsec_tx_sc_stats *stats)
|
|
{
|
|
struct aq_mss_egress_sc_counters e_sc_counters;
|
|
int ret;
|
|
|
|
ret = aq_mss_get_egress_sc_counters(hw, &e_sc_counters, sc_idx);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
stats->sc_protected_pkts =
|
|
STATS_2x32_TO_64(e_sc_counters.sc_protected_pkts);
|
|
stats->sc_encrypted_pkts =
|
|
STATS_2x32_TO_64(e_sc_counters.sc_encrypted_pkts);
|
|
stats->sc_protected_octets =
|
|
STATS_2x32_TO_64(e_sc_counters.sc_protected_octets);
|
|
stats->sc_encrypted_octets =
|
|
STATS_2x32_TO_64(e_sc_counters.sc_encrypted_octets);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_mdo_dev_open(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
int ret = 0;
|
|
|
|
if (netif_carrier_ok(nic->ndev))
|
|
ret = aq_apply_secy_cfg(nic, ctx->secy);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_dev_stop(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
int i;
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
|
|
if (nic->macsec_cfg->txsc_idx_busy & BIT(i))
|
|
aq_clear_secy(nic, nic->macsec_cfg->aq_txsc[i].sw_secy,
|
|
AQ_CLEAR_HW);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_set_txsc(struct aq_nic_s *nic, const int txsc_idx)
|
|
{
|
|
struct aq_macsec_txsc *aq_txsc = &nic->macsec_cfg->aq_txsc[txsc_idx];
|
|
struct aq_mss_egress_class_record tx_class_rec = { 0 };
|
|
const struct macsec_secy *secy = aq_txsc->sw_secy;
|
|
struct aq_mss_egress_sc_record sc_rec = { 0 };
|
|
unsigned int sc_idx = aq_txsc->hw_sc_idx;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
int ret = 0;
|
|
|
|
aq_ether_addr_to_mac(tx_class_rec.mac_sa, secy->netdev->dev_addr);
|
|
|
|
put_unaligned_be64((__force u64)secy->sci, tx_class_rec.sci);
|
|
tx_class_rec.sci_mask = 0;
|
|
|
|
tx_class_rec.sa_mask = 0x3f;
|
|
|
|
tx_class_rec.action = 0; /* forward to SA/SC table */
|
|
tx_class_rec.valid = 1;
|
|
|
|
tx_class_rec.sc_idx = sc_idx;
|
|
|
|
tx_class_rec.sc_sa = nic->macsec_cfg->sc_sa;
|
|
|
|
ret = aq_mss_set_egress_class_record(hw, &tx_class_rec, txsc_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
sc_rec.protect = secy->protect_frames;
|
|
if (secy->tx_sc.encrypt)
|
|
sc_rec.tci |= BIT(1);
|
|
if (secy->tx_sc.scb)
|
|
sc_rec.tci |= BIT(2);
|
|
if (secy->tx_sc.send_sci)
|
|
sc_rec.tci |= BIT(3);
|
|
if (secy->tx_sc.end_station)
|
|
sc_rec.tci |= BIT(4);
|
|
/* The C bit is clear if and only if the Secure Data is
|
|
* exactly the same as the User Data and the ICV is 16 octets long.
|
|
*/
|
|
if (!(secy->icv_len == 16 && !secy->tx_sc.encrypt))
|
|
sc_rec.tci |= BIT(0);
|
|
|
|
sc_rec.an_roll = 0;
|
|
|
|
switch (secy->key_len) {
|
|
case AQ_MACSEC_KEY_LEN_128_BIT:
|
|
sc_rec.sak_len = 0;
|
|
break;
|
|
case AQ_MACSEC_KEY_LEN_192_BIT:
|
|
sc_rec.sak_len = 1;
|
|
break;
|
|
case AQ_MACSEC_KEY_LEN_256_BIT:
|
|
sc_rec.sak_len = 2;
|
|
break;
|
|
default:
|
|
WARN_ONCE(true, "Invalid sc_sa");
|
|
return -EINVAL;
|
|
}
|
|
|
|
sc_rec.curr_an = secy->tx_sc.encoding_sa;
|
|
sc_rec.valid = 1;
|
|
sc_rec.fresh = 1;
|
|
|
|
return aq_mss_set_egress_sc_record(hw, &sc_rec, sc_idx);
|
|
}
|
|
|
|
static u32 aq_sc_idx_max(const enum aq_macsec_sc_sa sc_sa)
|
|
{
|
|
u32 result = 0;
|
|
|
|
switch (sc_sa) {
|
|
case aq_macsec_sa_sc_4sa_8sc:
|
|
result = 8;
|
|
break;
|
|
case aq_macsec_sa_sc_2sa_16sc:
|
|
result = 16;
|
|
break;
|
|
case aq_macsec_sa_sc_1sa_32sc:
|
|
result = 32;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static u32 aq_to_hw_sc_idx(const u32 sc_idx, const enum aq_macsec_sc_sa sc_sa)
|
|
{
|
|
switch (sc_sa) {
|
|
case aq_macsec_sa_sc_4sa_8sc:
|
|
return sc_idx << 2;
|
|
case aq_macsec_sa_sc_2sa_16sc:
|
|
return sc_idx << 1;
|
|
case aq_macsec_sa_sc_1sa_32sc:
|
|
return sc_idx;
|
|
default:
|
|
WARN_ONCE(true, "Invalid sc_sa");
|
|
}
|
|
|
|
return sc_idx;
|
|
}
|
|
|
|
static enum aq_macsec_sc_sa sc_sa_from_num_an(const int num_an)
|
|
{
|
|
enum aq_macsec_sc_sa sc_sa = aq_macsec_sa_sc_not_used;
|
|
|
|
switch (num_an) {
|
|
case 4:
|
|
sc_sa = aq_macsec_sa_sc_4sa_8sc;
|
|
break;
|
|
case 2:
|
|
sc_sa = aq_macsec_sa_sc_2sa_16sc;
|
|
break;
|
|
case 1:
|
|
sc_sa = aq_macsec_sa_sc_1sa_32sc;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return sc_sa;
|
|
}
|
|
|
|
static int aq_mdo_add_secy(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
const struct macsec_secy *secy = ctx->secy;
|
|
enum aq_macsec_sc_sa sc_sa;
|
|
u32 txsc_idx;
|
|
int ret = 0;
|
|
|
|
if (secy->xpn)
|
|
return -EOPNOTSUPP;
|
|
|
|
sc_sa = sc_sa_from_num_an(MACSEC_NUM_AN);
|
|
if (sc_sa == aq_macsec_sa_sc_not_used)
|
|
return -EINVAL;
|
|
|
|
if (hweight32(cfg->txsc_idx_busy) >= aq_sc_idx_max(sc_sa))
|
|
return -ENOSPC;
|
|
|
|
txsc_idx = ffz(cfg->txsc_idx_busy);
|
|
if (txsc_idx == AQ_MACSEC_MAX_SC)
|
|
return -ENOSPC;
|
|
|
|
cfg->sc_sa = sc_sa;
|
|
cfg->aq_txsc[txsc_idx].hw_sc_idx = aq_to_hw_sc_idx(txsc_idx, sc_sa);
|
|
cfg->aq_txsc[txsc_idx].sw_secy = secy;
|
|
|
|
if (netif_carrier_ok(nic->ndev) && netif_running(secy->netdev))
|
|
ret = aq_set_txsc(nic, txsc_idx);
|
|
|
|
set_bit(txsc_idx, &cfg->txsc_idx_busy);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_upd_secy(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
const struct macsec_secy *secy = ctx->secy;
|
|
int txsc_idx;
|
|
int ret = 0;
|
|
|
|
txsc_idx = aq_get_txsc_idx_from_secy(nic->macsec_cfg, secy);
|
|
if (txsc_idx < 0)
|
|
return -ENOENT;
|
|
|
|
if (netif_carrier_ok(nic->ndev) && netif_running(secy->netdev))
|
|
ret = aq_set_txsc(nic, txsc_idx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_clear_txsc(struct aq_nic_s *nic, const int txsc_idx,
|
|
enum aq_clear_type clear_type)
|
|
{
|
|
struct aq_macsec_txsc *tx_sc = &nic->macsec_cfg->aq_txsc[txsc_idx];
|
|
struct aq_mss_egress_class_record tx_class_rec = { 0 };
|
|
struct aq_mss_egress_sc_record sc_rec = { 0 };
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
int ret = 0;
|
|
int sa_num;
|
|
|
|
for_each_set_bit (sa_num, &tx_sc->tx_sa_idx_busy, AQ_MACSEC_MAX_SA) {
|
|
ret = aq_clear_txsa(nic, tx_sc, sa_num, clear_type);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (clear_type & AQ_CLEAR_HW) {
|
|
ret = aq_mss_set_egress_class_record(hw, &tx_class_rec,
|
|
txsc_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
sc_rec.fresh = 1;
|
|
ret = aq_mss_set_egress_sc_record(hw, &sc_rec,
|
|
tx_sc->hw_sc_idx);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (clear_type & AQ_CLEAR_SW) {
|
|
clear_bit(txsc_idx, &nic->macsec_cfg->txsc_idx_busy);
|
|
nic->macsec_cfg->aq_txsc[txsc_idx].sw_secy = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_del_secy(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
int ret = 0;
|
|
|
|
if (!nic->macsec_cfg)
|
|
return 0;
|
|
|
|
ret = aq_clear_secy(nic, ctx->secy, AQ_CLEAR_ALL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_update_txsa(struct aq_nic_s *nic, const unsigned int sc_idx,
|
|
const struct macsec_secy *secy,
|
|
const struct macsec_tx_sa *tx_sa,
|
|
const unsigned char *key, const unsigned char an)
|
|
{
|
|
const u32 next_pn = tx_sa->next_pn_halves.lower;
|
|
struct aq_mss_egress_sakey_record key_rec;
|
|
const unsigned int sa_idx = sc_idx | an;
|
|
struct aq_mss_egress_sa_record sa_rec;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
int ret = 0;
|
|
|
|
memset(&sa_rec, 0, sizeof(sa_rec));
|
|
sa_rec.valid = tx_sa->active;
|
|
sa_rec.fresh = 1;
|
|
sa_rec.next_pn = next_pn;
|
|
|
|
ret = aq_mss_set_egress_sa_record(hw, &sa_rec, sa_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!key)
|
|
return ret;
|
|
|
|
memset(&key_rec, 0, sizeof(key_rec));
|
|
memcpy(&key_rec.key, key, secy->key_len);
|
|
|
|
aq_rotate_keys(&key_rec.key, secy->key_len);
|
|
|
|
ret = aq_mss_set_egress_sakey_record(hw, &key_rec, sa_idx);
|
|
|
|
memzero_explicit(&key_rec, sizeof(key_rec));
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_add_txsa(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
const struct macsec_secy *secy = ctx->secy;
|
|
struct aq_macsec_txsc *aq_txsc;
|
|
int txsc_idx;
|
|
int ret = 0;
|
|
|
|
txsc_idx = aq_get_txsc_idx_from_secy(cfg, secy);
|
|
if (txsc_idx < 0)
|
|
return -EINVAL;
|
|
|
|
aq_txsc = &cfg->aq_txsc[txsc_idx];
|
|
set_bit(ctx->sa.assoc_num, &aq_txsc->tx_sa_idx_busy);
|
|
|
|
memcpy(aq_txsc->tx_sa_key[ctx->sa.assoc_num], ctx->sa.key,
|
|
secy->key_len);
|
|
|
|
if (netif_carrier_ok(nic->ndev) && netif_running(secy->netdev))
|
|
ret = aq_update_txsa(nic, aq_txsc->hw_sc_idx, secy,
|
|
ctx->sa.tx_sa, ctx->sa.key,
|
|
ctx->sa.assoc_num);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_upd_txsa(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
const struct macsec_secy *secy = ctx->secy;
|
|
struct aq_macsec_txsc *aq_txsc;
|
|
int txsc_idx;
|
|
int ret = 0;
|
|
|
|
txsc_idx = aq_get_txsc_idx_from_secy(cfg, secy);
|
|
if (txsc_idx < 0)
|
|
return -EINVAL;
|
|
|
|
aq_txsc = &cfg->aq_txsc[txsc_idx];
|
|
if (netif_carrier_ok(nic->ndev) && netif_running(secy->netdev))
|
|
ret = aq_update_txsa(nic, aq_txsc->hw_sc_idx, secy,
|
|
ctx->sa.tx_sa, NULL, ctx->sa.assoc_num);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_clear_txsa(struct aq_nic_s *nic, struct aq_macsec_txsc *aq_txsc,
|
|
const int sa_num, enum aq_clear_type clear_type)
|
|
{
|
|
const int sa_idx = aq_txsc->hw_sc_idx | sa_num;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
int ret = 0;
|
|
|
|
if (clear_type & AQ_CLEAR_SW)
|
|
clear_bit(sa_num, &aq_txsc->tx_sa_idx_busy);
|
|
|
|
if ((clear_type & AQ_CLEAR_HW) && netif_carrier_ok(nic->ndev)) {
|
|
struct aq_mss_egress_sakey_record key_rec;
|
|
struct aq_mss_egress_sa_record sa_rec;
|
|
|
|
memset(&sa_rec, 0, sizeof(sa_rec));
|
|
sa_rec.fresh = 1;
|
|
|
|
ret = aq_mss_set_egress_sa_record(hw, &sa_rec, sa_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
memset(&key_rec, 0, sizeof(key_rec));
|
|
return aq_mss_set_egress_sakey_record(hw, &key_rec, sa_idx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_mdo_del_txsa(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
int txsc_idx;
|
|
int ret = 0;
|
|
|
|
txsc_idx = aq_get_txsc_idx_from_secy(cfg, ctx->secy);
|
|
if (txsc_idx < 0)
|
|
return -EINVAL;
|
|
|
|
ret = aq_clear_txsa(nic, &cfg->aq_txsc[txsc_idx], ctx->sa.assoc_num,
|
|
AQ_CLEAR_ALL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_rxsc_validate_frames(const enum macsec_validation_type validate)
|
|
{
|
|
switch (validate) {
|
|
case MACSEC_VALIDATE_DISABLED:
|
|
return 2;
|
|
case MACSEC_VALIDATE_CHECK:
|
|
return 1;
|
|
case MACSEC_VALIDATE_STRICT:
|
|
return 0;
|
|
default:
|
|
WARN_ONCE(true, "Invalid validation type");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_set_rxsc(struct aq_nic_s *nic, const u32 rxsc_idx)
|
|
{
|
|
const struct aq_macsec_rxsc *aq_rxsc =
|
|
&nic->macsec_cfg->aq_rxsc[rxsc_idx];
|
|
struct aq_mss_ingress_preclass_record pre_class_record;
|
|
const struct macsec_rx_sc *rx_sc = aq_rxsc->sw_rxsc;
|
|
const struct macsec_secy *secy = aq_rxsc->sw_secy;
|
|
const u32 hw_sc_idx = aq_rxsc->hw_sc_idx;
|
|
struct aq_mss_ingress_sc_record sc_record;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
int ret = 0;
|
|
|
|
memset(&pre_class_record, 0, sizeof(pre_class_record));
|
|
put_unaligned_be64((__force u64)rx_sc->sci, pre_class_record.sci);
|
|
pre_class_record.sci_mask = 0xff;
|
|
/* match all MACSEC ethertype packets */
|
|
pre_class_record.eth_type = ETH_P_MACSEC;
|
|
pre_class_record.eth_type_mask = 0x3;
|
|
|
|
aq_ether_addr_to_mac(pre_class_record.mac_sa, (char *)&rx_sc->sci);
|
|
pre_class_record.sa_mask = 0x3f;
|
|
|
|
pre_class_record.an_mask = nic->macsec_cfg->sc_sa;
|
|
pre_class_record.sc_idx = hw_sc_idx;
|
|
/* strip SecTAG & forward for decryption */
|
|
pre_class_record.action = 0x0;
|
|
pre_class_record.valid = 1;
|
|
|
|
ret = aq_mss_set_ingress_preclass_record(hw, &pre_class_record,
|
|
2 * rxsc_idx + 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* If SCI is absent, then match by SA alone */
|
|
pre_class_record.sci_mask = 0;
|
|
pre_class_record.sci_from_table = 1;
|
|
|
|
ret = aq_mss_set_ingress_preclass_record(hw, &pre_class_record,
|
|
2 * rxsc_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
memset(&sc_record, 0, sizeof(sc_record));
|
|
sc_record.validate_frames =
|
|
aq_rxsc_validate_frames(secy->validate_frames);
|
|
if (secy->replay_protect) {
|
|
sc_record.replay_protect = 1;
|
|
sc_record.anti_replay_window = secy->replay_window;
|
|
}
|
|
sc_record.valid = 1;
|
|
sc_record.fresh = 1;
|
|
|
|
ret = aq_mss_set_ingress_sc_record(hw, &sc_record, hw_sc_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_add_rxsc(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
const u32 rxsc_idx_max = aq_sc_idx_max(cfg->sc_sa);
|
|
u32 rxsc_idx;
|
|
int ret = 0;
|
|
|
|
if (hweight32(cfg->rxsc_idx_busy) >= rxsc_idx_max)
|
|
return -ENOSPC;
|
|
|
|
rxsc_idx = ffz(cfg->rxsc_idx_busy);
|
|
if (rxsc_idx >= rxsc_idx_max)
|
|
return -ENOSPC;
|
|
|
|
cfg->aq_rxsc[rxsc_idx].hw_sc_idx = aq_to_hw_sc_idx(rxsc_idx,
|
|
cfg->sc_sa);
|
|
cfg->aq_rxsc[rxsc_idx].sw_secy = ctx->secy;
|
|
cfg->aq_rxsc[rxsc_idx].sw_rxsc = ctx->rx_sc;
|
|
|
|
if (netif_carrier_ok(nic->ndev) && netif_running(ctx->secy->netdev))
|
|
ret = aq_set_rxsc(nic, rxsc_idx);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
set_bit(rxsc_idx, &cfg->rxsc_idx_busy);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_mdo_upd_rxsc(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
int rxsc_idx;
|
|
int ret = 0;
|
|
|
|
rxsc_idx = aq_get_rxsc_idx_from_rxsc(nic->macsec_cfg, ctx->rx_sc);
|
|
if (rxsc_idx < 0)
|
|
return -ENOENT;
|
|
|
|
if (netif_carrier_ok(nic->ndev) && netif_running(ctx->secy->netdev))
|
|
ret = aq_set_rxsc(nic, rxsc_idx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_clear_rxsc(struct aq_nic_s *nic, const int rxsc_idx,
|
|
enum aq_clear_type clear_type)
|
|
{
|
|
struct aq_macsec_rxsc *rx_sc = &nic->macsec_cfg->aq_rxsc[rxsc_idx];
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
int ret = 0;
|
|
int sa_num;
|
|
|
|
for_each_set_bit (sa_num, &rx_sc->rx_sa_idx_busy, AQ_MACSEC_MAX_SA) {
|
|
ret = aq_clear_rxsa(nic, rx_sc, sa_num, clear_type);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (clear_type & AQ_CLEAR_HW) {
|
|
struct aq_mss_ingress_preclass_record pre_class_record;
|
|
struct aq_mss_ingress_sc_record sc_record;
|
|
|
|
memset(&pre_class_record, 0, sizeof(pre_class_record));
|
|
memset(&sc_record, 0, sizeof(sc_record));
|
|
|
|
ret = aq_mss_set_ingress_preclass_record(hw, &pre_class_record,
|
|
2 * rxsc_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = aq_mss_set_ingress_preclass_record(hw, &pre_class_record,
|
|
2 * rxsc_idx + 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
sc_record.fresh = 1;
|
|
ret = aq_mss_set_ingress_sc_record(hw, &sc_record,
|
|
rx_sc->hw_sc_idx);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (clear_type & AQ_CLEAR_SW) {
|
|
clear_bit(rxsc_idx, &nic->macsec_cfg->rxsc_idx_busy);
|
|
rx_sc->sw_secy = NULL;
|
|
rx_sc->sw_rxsc = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_del_rxsc(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
enum aq_clear_type clear_type = AQ_CLEAR_SW;
|
|
int rxsc_idx;
|
|
int ret = 0;
|
|
|
|
rxsc_idx = aq_get_rxsc_idx_from_rxsc(nic->macsec_cfg, ctx->rx_sc);
|
|
if (rxsc_idx < 0)
|
|
return -ENOENT;
|
|
|
|
if (netif_carrier_ok(nic->ndev))
|
|
clear_type = AQ_CLEAR_ALL;
|
|
|
|
ret = aq_clear_rxsc(nic, rxsc_idx, clear_type);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_update_rxsa(struct aq_nic_s *nic, const unsigned int sc_idx,
|
|
const struct macsec_secy *secy,
|
|
const struct macsec_rx_sa *rx_sa,
|
|
const unsigned char *key, const unsigned char an)
|
|
{
|
|
struct aq_mss_ingress_sakey_record sa_key_record;
|
|
const u32 next_pn = rx_sa->next_pn_halves.lower;
|
|
struct aq_mss_ingress_sa_record sa_record;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
const int sa_idx = sc_idx | an;
|
|
int ret = 0;
|
|
|
|
memset(&sa_record, 0, sizeof(sa_record));
|
|
sa_record.valid = rx_sa->active;
|
|
sa_record.fresh = 1;
|
|
sa_record.next_pn = next_pn;
|
|
|
|
ret = aq_mss_set_ingress_sa_record(hw, &sa_record, sa_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!key)
|
|
return ret;
|
|
|
|
memset(&sa_key_record, 0, sizeof(sa_key_record));
|
|
memcpy(&sa_key_record.key, key, secy->key_len);
|
|
|
|
switch (secy->key_len) {
|
|
case AQ_MACSEC_KEY_LEN_128_BIT:
|
|
sa_key_record.key_len = 0;
|
|
break;
|
|
case AQ_MACSEC_KEY_LEN_192_BIT:
|
|
sa_key_record.key_len = 1;
|
|
break;
|
|
case AQ_MACSEC_KEY_LEN_256_BIT:
|
|
sa_key_record.key_len = 2;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
aq_rotate_keys(&sa_key_record.key, secy->key_len);
|
|
|
|
ret = aq_mss_set_ingress_sakey_record(hw, &sa_key_record, sa_idx);
|
|
|
|
memzero_explicit(&sa_key_record, sizeof(sa_key_record));
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_add_rxsa(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
const struct macsec_rx_sc *rx_sc = ctx->sa.rx_sa->sc;
|
|
const struct macsec_secy *secy = ctx->secy;
|
|
struct aq_macsec_rxsc *aq_rxsc;
|
|
int rxsc_idx;
|
|
int ret = 0;
|
|
|
|
rxsc_idx = aq_get_rxsc_idx_from_rxsc(nic->macsec_cfg, rx_sc);
|
|
if (rxsc_idx < 0)
|
|
return -EINVAL;
|
|
|
|
aq_rxsc = &nic->macsec_cfg->aq_rxsc[rxsc_idx];
|
|
set_bit(ctx->sa.assoc_num, &aq_rxsc->rx_sa_idx_busy);
|
|
|
|
memcpy(aq_rxsc->rx_sa_key[ctx->sa.assoc_num], ctx->sa.key,
|
|
secy->key_len);
|
|
|
|
if (netif_carrier_ok(nic->ndev) && netif_running(secy->netdev))
|
|
ret = aq_update_rxsa(nic, aq_rxsc->hw_sc_idx, secy,
|
|
ctx->sa.rx_sa, ctx->sa.key,
|
|
ctx->sa.assoc_num);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_upd_rxsa(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
const struct macsec_rx_sc *rx_sc = ctx->sa.rx_sa->sc;
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
const struct macsec_secy *secy = ctx->secy;
|
|
int rxsc_idx;
|
|
int ret = 0;
|
|
|
|
rxsc_idx = aq_get_rxsc_idx_from_rxsc(cfg, rx_sc);
|
|
if (rxsc_idx < 0)
|
|
return -EINVAL;
|
|
|
|
if (netif_carrier_ok(nic->ndev) && netif_running(secy->netdev))
|
|
ret = aq_update_rxsa(nic, cfg->aq_rxsc[rxsc_idx].hw_sc_idx,
|
|
secy, ctx->sa.rx_sa, NULL,
|
|
ctx->sa.assoc_num);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_clear_rxsa(struct aq_nic_s *nic, struct aq_macsec_rxsc *aq_rxsc,
|
|
const int sa_num, enum aq_clear_type clear_type)
|
|
{
|
|
int sa_idx = aq_rxsc->hw_sc_idx | sa_num;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
int ret = 0;
|
|
|
|
if (clear_type & AQ_CLEAR_SW)
|
|
clear_bit(sa_num, &aq_rxsc->rx_sa_idx_busy);
|
|
|
|
if ((clear_type & AQ_CLEAR_HW) && netif_carrier_ok(nic->ndev)) {
|
|
struct aq_mss_ingress_sakey_record sa_key_record;
|
|
struct aq_mss_ingress_sa_record sa_record;
|
|
|
|
memset(&sa_key_record, 0, sizeof(sa_key_record));
|
|
memset(&sa_record, 0, sizeof(sa_record));
|
|
sa_record.fresh = 1;
|
|
ret = aq_mss_set_ingress_sa_record(hw, &sa_record, sa_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return aq_mss_set_ingress_sakey_record(hw, &sa_key_record,
|
|
sa_idx);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_del_rxsa(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
const struct macsec_rx_sc *rx_sc = ctx->sa.rx_sa->sc;
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
int rxsc_idx;
|
|
int ret = 0;
|
|
|
|
rxsc_idx = aq_get_rxsc_idx_from_rxsc(cfg, rx_sc);
|
|
if (rxsc_idx < 0)
|
|
return -EINVAL;
|
|
|
|
ret = aq_clear_rxsa(nic, &cfg->aq_rxsc[rxsc_idx], ctx->sa.assoc_num,
|
|
AQ_CLEAR_ALL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_get_dev_stats(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_common_stats *stats = &nic->macsec_cfg->stats;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
|
|
aq_get_macsec_common_stats(hw, stats);
|
|
|
|
ctx->stats.dev_stats->OutPktsUntagged = stats->out.untagged_pkts;
|
|
ctx->stats.dev_stats->InPktsUntagged = stats->in.untagged_pkts;
|
|
ctx->stats.dev_stats->OutPktsTooLong = stats->out.too_long;
|
|
ctx->stats.dev_stats->InPktsNoTag = stats->in.notag_pkts;
|
|
ctx->stats.dev_stats->InPktsBadTag = stats->in.bad_tag_pkts;
|
|
ctx->stats.dev_stats->InPktsUnknownSCI = stats->in.unknown_sci_pkts;
|
|
ctx->stats.dev_stats->InPktsNoSCI = stats->in.no_sci_pkts;
|
|
ctx->stats.dev_stats->InPktsOverrun = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_mdo_get_tx_sc_stats(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_tx_sc_stats *stats;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
struct aq_macsec_txsc *aq_txsc;
|
|
int txsc_idx;
|
|
|
|
txsc_idx = aq_get_txsc_idx_from_secy(nic->macsec_cfg, ctx->secy);
|
|
if (txsc_idx < 0)
|
|
return -ENOENT;
|
|
|
|
aq_txsc = &nic->macsec_cfg->aq_txsc[txsc_idx];
|
|
stats = &aq_txsc->stats;
|
|
aq_get_txsc_stats(hw, aq_txsc->hw_sc_idx, stats);
|
|
|
|
ctx->stats.tx_sc_stats->OutPktsProtected = stats->sc_protected_pkts;
|
|
ctx->stats.tx_sc_stats->OutPktsEncrypted = stats->sc_encrypted_pkts;
|
|
ctx->stats.tx_sc_stats->OutOctetsProtected = stats->sc_protected_octets;
|
|
ctx->stats.tx_sc_stats->OutOctetsEncrypted = stats->sc_encrypted_octets;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aq_mdo_get_tx_sa_stats(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
struct aq_macsec_tx_sa_stats *stats;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
const struct macsec_secy *secy;
|
|
struct aq_macsec_txsc *aq_txsc;
|
|
struct macsec_tx_sa *tx_sa;
|
|
unsigned int sa_idx;
|
|
int txsc_idx;
|
|
u32 next_pn;
|
|
int ret;
|
|
|
|
txsc_idx = aq_get_txsc_idx_from_secy(cfg, ctx->secy);
|
|
if (txsc_idx < 0)
|
|
return -EINVAL;
|
|
|
|
aq_txsc = &cfg->aq_txsc[txsc_idx];
|
|
sa_idx = aq_txsc->hw_sc_idx | ctx->sa.assoc_num;
|
|
stats = &aq_txsc->tx_sa_stats[ctx->sa.assoc_num];
|
|
ret = aq_get_txsa_stats(hw, sa_idx, stats);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ctx->stats.tx_sa_stats->OutPktsProtected = stats->sa_protected_pkts;
|
|
ctx->stats.tx_sa_stats->OutPktsEncrypted = stats->sa_encrypted_pkts;
|
|
|
|
secy = aq_txsc->sw_secy;
|
|
tx_sa = rcu_dereference_bh(secy->tx_sc.sa[ctx->sa.assoc_num]);
|
|
ret = aq_get_txsa_next_pn(hw, sa_idx, &next_pn);
|
|
if (ret == 0) {
|
|
spin_lock_bh(&tx_sa->lock);
|
|
tx_sa->next_pn = next_pn;
|
|
spin_unlock_bh(&tx_sa->lock);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_get_rx_sc_stats(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
struct aq_macsec_rx_sa_stats *stats;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
struct aq_macsec_rxsc *aq_rxsc;
|
|
unsigned int sa_idx;
|
|
int rxsc_idx;
|
|
int ret = 0;
|
|
int i;
|
|
|
|
rxsc_idx = aq_get_rxsc_idx_from_rxsc(cfg, ctx->rx_sc);
|
|
if (rxsc_idx < 0)
|
|
return -ENOENT;
|
|
|
|
aq_rxsc = &cfg->aq_rxsc[rxsc_idx];
|
|
for (i = 0; i < MACSEC_NUM_AN; i++) {
|
|
if (!test_bit(i, &aq_rxsc->rx_sa_idx_busy))
|
|
continue;
|
|
|
|
stats = &aq_rxsc->rx_sa_stats[i];
|
|
sa_idx = aq_rxsc->hw_sc_idx | i;
|
|
ret = aq_get_rxsa_stats(hw, sa_idx, stats);
|
|
if (ret)
|
|
break;
|
|
|
|
ctx->stats.rx_sc_stats->InOctetsValidated +=
|
|
stats->validated_octets;
|
|
ctx->stats.rx_sc_stats->InOctetsDecrypted +=
|
|
stats->decrypted_octets;
|
|
ctx->stats.rx_sc_stats->InPktsUnchecked +=
|
|
stats->unchecked_pkts;
|
|
ctx->stats.rx_sc_stats->InPktsDelayed += stats->delayed_pkts;
|
|
ctx->stats.rx_sc_stats->InPktsOK += stats->ok_pkts;
|
|
ctx->stats.rx_sc_stats->InPktsInvalid += stats->invalid_pkts;
|
|
ctx->stats.rx_sc_stats->InPktsLate += stats->late_pkts;
|
|
ctx->stats.rx_sc_stats->InPktsNotValid += stats->not_valid_pkts;
|
|
ctx->stats.rx_sc_stats->InPktsNotUsingSA += stats->not_using_sa;
|
|
ctx->stats.rx_sc_stats->InPktsUnusedSA += stats->unused_sa;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_mdo_get_rx_sa_stats(struct macsec_context *ctx)
|
|
{
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev);
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
struct aq_macsec_rx_sa_stats *stats;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
struct aq_macsec_rxsc *aq_rxsc;
|
|
struct macsec_rx_sa *rx_sa;
|
|
unsigned int sa_idx;
|
|
int rxsc_idx;
|
|
u32 next_pn;
|
|
int ret;
|
|
|
|
rxsc_idx = aq_get_rxsc_idx_from_rxsc(cfg, ctx->rx_sc);
|
|
if (rxsc_idx < 0)
|
|
return -EINVAL;
|
|
|
|
aq_rxsc = &cfg->aq_rxsc[rxsc_idx];
|
|
stats = &aq_rxsc->rx_sa_stats[ctx->sa.assoc_num];
|
|
sa_idx = aq_rxsc->hw_sc_idx | ctx->sa.assoc_num;
|
|
ret = aq_get_rxsa_stats(hw, sa_idx, stats);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ctx->stats.rx_sa_stats->InPktsOK = stats->ok_pkts;
|
|
ctx->stats.rx_sa_stats->InPktsInvalid = stats->invalid_pkts;
|
|
ctx->stats.rx_sa_stats->InPktsNotValid = stats->not_valid_pkts;
|
|
ctx->stats.rx_sa_stats->InPktsNotUsingSA = stats->not_using_sa;
|
|
ctx->stats.rx_sa_stats->InPktsUnusedSA = stats->unused_sa;
|
|
|
|
rx_sa = rcu_dereference_bh(aq_rxsc->sw_rxsc->sa[ctx->sa.assoc_num]);
|
|
ret = aq_get_rxsa_next_pn(hw, sa_idx, &next_pn);
|
|
if (ret == 0) {
|
|
spin_lock_bh(&rx_sa->lock);
|
|
rx_sa->next_pn = next_pn;
|
|
spin_unlock_bh(&rx_sa->lock);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int apply_txsc_cfg(struct aq_nic_s *nic, const int txsc_idx)
|
|
{
|
|
struct aq_macsec_txsc *aq_txsc = &nic->macsec_cfg->aq_txsc[txsc_idx];
|
|
const struct macsec_secy *secy = aq_txsc->sw_secy;
|
|
struct macsec_tx_sa *tx_sa;
|
|
int ret = 0;
|
|
int i;
|
|
|
|
if (!netif_running(secy->netdev))
|
|
return ret;
|
|
|
|
ret = aq_set_txsc(nic, txsc_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for (i = 0; i < MACSEC_NUM_AN; i++) {
|
|
tx_sa = rcu_dereference_bh(secy->tx_sc.sa[i]);
|
|
if (tx_sa) {
|
|
ret = aq_update_txsa(nic, aq_txsc->hw_sc_idx, secy,
|
|
tx_sa, aq_txsc->tx_sa_key[i], i);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int apply_rxsc_cfg(struct aq_nic_s *nic, const int rxsc_idx)
|
|
{
|
|
struct aq_macsec_rxsc *aq_rxsc = &nic->macsec_cfg->aq_rxsc[rxsc_idx];
|
|
const struct macsec_secy *secy = aq_rxsc->sw_secy;
|
|
struct macsec_rx_sa *rx_sa;
|
|
int ret = 0;
|
|
int i;
|
|
|
|
if (!netif_running(secy->netdev))
|
|
return ret;
|
|
|
|
ret = aq_set_rxsc(nic, rxsc_idx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for (i = 0; i < MACSEC_NUM_AN; i++) {
|
|
rx_sa = rcu_dereference_bh(aq_rxsc->sw_rxsc->sa[i]);
|
|
if (rx_sa) {
|
|
ret = aq_update_rxsa(nic, aq_rxsc->hw_sc_idx, secy,
|
|
rx_sa, aq_rxsc->rx_sa_key[i], i);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_clear_secy(struct aq_nic_s *nic, const struct macsec_secy *secy,
|
|
enum aq_clear_type clear_type)
|
|
{
|
|
struct macsec_rx_sc *rx_sc;
|
|
int txsc_idx;
|
|
int rxsc_idx;
|
|
int ret = 0;
|
|
|
|
txsc_idx = aq_get_txsc_idx_from_secy(nic->macsec_cfg, secy);
|
|
if (txsc_idx >= 0) {
|
|
ret = aq_clear_txsc(nic, txsc_idx, clear_type);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
for (rx_sc = rcu_dereference_bh(secy->rx_sc); rx_sc;
|
|
rx_sc = rcu_dereference_bh(rx_sc->next)) {
|
|
rxsc_idx = aq_get_rxsc_idx_from_rxsc(nic->macsec_cfg, rx_sc);
|
|
if (rxsc_idx < 0)
|
|
continue;
|
|
|
|
ret = aq_clear_rxsc(nic, rxsc_idx, clear_type);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_apply_secy_cfg(struct aq_nic_s *nic,
|
|
const struct macsec_secy *secy)
|
|
{
|
|
struct macsec_rx_sc *rx_sc;
|
|
int txsc_idx;
|
|
int rxsc_idx;
|
|
int ret = 0;
|
|
|
|
txsc_idx = aq_get_txsc_idx_from_secy(nic->macsec_cfg, secy);
|
|
if (txsc_idx >= 0)
|
|
apply_txsc_cfg(nic, txsc_idx);
|
|
|
|
for (rx_sc = rcu_dereference_bh(secy->rx_sc); rx_sc && rx_sc->active;
|
|
rx_sc = rcu_dereference_bh(rx_sc->next)) {
|
|
rxsc_idx = aq_get_rxsc_idx_from_rxsc(nic->macsec_cfg, rx_sc);
|
|
if (unlikely(rxsc_idx < 0))
|
|
continue;
|
|
|
|
ret = apply_rxsc_cfg(nic, rxsc_idx);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_apply_macsec_cfg(struct aq_nic_s *nic)
|
|
{
|
|
int ret = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
|
|
if (nic->macsec_cfg->txsc_idx_busy & BIT(i)) {
|
|
ret = apply_txsc_cfg(nic, i);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
|
|
if (nic->macsec_cfg->rxsc_idx_busy & BIT(i)) {
|
|
ret = apply_rxsc_cfg(nic, i);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aq_sa_from_sa_idx(const enum aq_macsec_sc_sa sc_sa, const int sa_idx)
|
|
{
|
|
switch (sc_sa) {
|
|
case aq_macsec_sa_sc_4sa_8sc:
|
|
return sa_idx & 3;
|
|
case aq_macsec_sa_sc_2sa_16sc:
|
|
return sa_idx & 1;
|
|
case aq_macsec_sa_sc_1sa_32sc:
|
|
return 0;
|
|
default:
|
|
WARN_ONCE(true, "Invalid sc_sa");
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int aq_sc_idx_from_sa_idx(const enum aq_macsec_sc_sa sc_sa,
|
|
const int sa_idx)
|
|
{
|
|
switch (sc_sa) {
|
|
case aq_macsec_sa_sc_4sa_8sc:
|
|
return sa_idx & ~3;
|
|
case aq_macsec_sa_sc_2sa_16sc:
|
|
return sa_idx & ~1;
|
|
case aq_macsec_sa_sc_1sa_32sc:
|
|
return sa_idx;
|
|
default:
|
|
WARN_ONCE(true, "Invalid sc_sa");
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void aq_check_txsa_expiration(struct aq_nic_s *nic)
|
|
{
|
|
u32 egress_sa_expired, egress_sa_threshold_expired;
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
struct aq_macsec_txsc *aq_txsc;
|
|
const struct macsec_secy *secy;
|
|
int sc_idx = 0, txsc_idx = 0;
|
|
enum aq_macsec_sc_sa sc_sa;
|
|
struct macsec_tx_sa *tx_sa;
|
|
unsigned char an = 0;
|
|
int ret;
|
|
int i;
|
|
|
|
sc_sa = cfg->sc_sa;
|
|
|
|
ret = aq_mss_get_egress_sa_expired(hw, &egress_sa_expired);
|
|
if (unlikely(ret))
|
|
return;
|
|
|
|
ret = aq_mss_get_egress_sa_threshold_expired(hw,
|
|
&egress_sa_threshold_expired);
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SA; i++) {
|
|
if (egress_sa_expired & BIT(i)) {
|
|
an = aq_sa_from_sa_idx(sc_sa, i);
|
|
sc_idx = aq_sc_idx_from_sa_idx(sc_sa, i);
|
|
txsc_idx = aq_get_txsc_idx_from_sc_idx(sc_sa, sc_idx);
|
|
if (txsc_idx < 0)
|
|
continue;
|
|
|
|
aq_txsc = &cfg->aq_txsc[txsc_idx];
|
|
if (!(cfg->txsc_idx_busy & BIT(txsc_idx))) {
|
|
netdev_warn(nic->ndev,
|
|
"PN threshold expired on invalid TX SC");
|
|
continue;
|
|
}
|
|
|
|
secy = aq_txsc->sw_secy;
|
|
if (!netif_running(secy->netdev)) {
|
|
netdev_warn(nic->ndev,
|
|
"PN threshold expired on down TX SC");
|
|
continue;
|
|
}
|
|
|
|
if (unlikely(!(aq_txsc->tx_sa_idx_busy & BIT(an)))) {
|
|
netdev_warn(nic->ndev,
|
|
"PN threshold expired on invalid TX SA");
|
|
continue;
|
|
}
|
|
|
|
tx_sa = rcu_dereference_bh(secy->tx_sc.sa[an]);
|
|
macsec_pn_wrapped((struct macsec_secy *)secy, tx_sa);
|
|
}
|
|
}
|
|
|
|
aq_mss_set_egress_sa_expired(hw, egress_sa_expired);
|
|
if (likely(!ret))
|
|
aq_mss_set_egress_sa_threshold_expired(hw,
|
|
egress_sa_threshold_expired);
|
|
}
|
|
|
|
#define AQ_LOCKED_MDO_DEF(mdo) \
|
|
static int aq_locked_mdo_##mdo(struct macsec_context *ctx) \
|
|
{ \
|
|
struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); \
|
|
int ret; \
|
|
mutex_lock(&nic->macsec_mutex); \
|
|
ret = aq_mdo_##mdo(ctx); \
|
|
mutex_unlock(&nic->macsec_mutex); \
|
|
return ret; \
|
|
}
|
|
|
|
AQ_LOCKED_MDO_DEF(dev_open)
|
|
AQ_LOCKED_MDO_DEF(dev_stop)
|
|
AQ_LOCKED_MDO_DEF(add_secy)
|
|
AQ_LOCKED_MDO_DEF(upd_secy)
|
|
AQ_LOCKED_MDO_DEF(del_secy)
|
|
AQ_LOCKED_MDO_DEF(add_rxsc)
|
|
AQ_LOCKED_MDO_DEF(upd_rxsc)
|
|
AQ_LOCKED_MDO_DEF(del_rxsc)
|
|
AQ_LOCKED_MDO_DEF(add_rxsa)
|
|
AQ_LOCKED_MDO_DEF(upd_rxsa)
|
|
AQ_LOCKED_MDO_DEF(del_rxsa)
|
|
AQ_LOCKED_MDO_DEF(add_txsa)
|
|
AQ_LOCKED_MDO_DEF(upd_txsa)
|
|
AQ_LOCKED_MDO_DEF(del_txsa)
|
|
AQ_LOCKED_MDO_DEF(get_dev_stats)
|
|
AQ_LOCKED_MDO_DEF(get_tx_sc_stats)
|
|
AQ_LOCKED_MDO_DEF(get_tx_sa_stats)
|
|
AQ_LOCKED_MDO_DEF(get_rx_sc_stats)
|
|
AQ_LOCKED_MDO_DEF(get_rx_sa_stats)
|
|
|
|
const struct macsec_ops aq_macsec_ops = {
|
|
.mdo_dev_open = aq_locked_mdo_dev_open,
|
|
.mdo_dev_stop = aq_locked_mdo_dev_stop,
|
|
.mdo_add_secy = aq_locked_mdo_add_secy,
|
|
.mdo_upd_secy = aq_locked_mdo_upd_secy,
|
|
.mdo_del_secy = aq_locked_mdo_del_secy,
|
|
.mdo_add_rxsc = aq_locked_mdo_add_rxsc,
|
|
.mdo_upd_rxsc = aq_locked_mdo_upd_rxsc,
|
|
.mdo_del_rxsc = aq_locked_mdo_del_rxsc,
|
|
.mdo_add_rxsa = aq_locked_mdo_add_rxsa,
|
|
.mdo_upd_rxsa = aq_locked_mdo_upd_rxsa,
|
|
.mdo_del_rxsa = aq_locked_mdo_del_rxsa,
|
|
.mdo_add_txsa = aq_locked_mdo_add_txsa,
|
|
.mdo_upd_txsa = aq_locked_mdo_upd_txsa,
|
|
.mdo_del_txsa = aq_locked_mdo_del_txsa,
|
|
.mdo_get_dev_stats = aq_locked_mdo_get_dev_stats,
|
|
.mdo_get_tx_sc_stats = aq_locked_mdo_get_tx_sc_stats,
|
|
.mdo_get_tx_sa_stats = aq_locked_mdo_get_tx_sa_stats,
|
|
.mdo_get_rx_sc_stats = aq_locked_mdo_get_rx_sc_stats,
|
|
.mdo_get_rx_sa_stats = aq_locked_mdo_get_rx_sa_stats,
|
|
};
|
|
|
|
int aq_macsec_init(struct aq_nic_s *nic)
|
|
{
|
|
struct aq_macsec_cfg *cfg;
|
|
u32 caps_lo;
|
|
|
|
if (!nic->aq_fw_ops->get_link_capabilities)
|
|
return 0;
|
|
|
|
caps_lo = nic->aq_fw_ops->get_link_capabilities(nic->aq_hw);
|
|
|
|
if (!(caps_lo & BIT(CAPS_LO_MACSEC)))
|
|
return 0;
|
|
|
|
nic->macsec_cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
|
if (!nic->macsec_cfg)
|
|
return -ENOMEM;
|
|
|
|
nic->ndev->features |= NETIF_F_HW_MACSEC;
|
|
nic->ndev->macsec_ops = &aq_macsec_ops;
|
|
mutex_init(&nic->macsec_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void aq_macsec_free(struct aq_nic_s *nic)
|
|
{
|
|
kfree(nic->macsec_cfg);
|
|
nic->macsec_cfg = NULL;
|
|
}
|
|
|
|
int aq_macsec_enable(struct aq_nic_s *nic)
|
|
{
|
|
u32 ctl_ether_types[1] = { ETH_P_PAE };
|
|
struct macsec_msg_fw_response resp = { 0 };
|
|
struct macsec_msg_fw_request msg = { 0 };
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
int num_ctl_ether_types = 0;
|
|
int index = 0, tbl_idx;
|
|
int ret;
|
|
|
|
if (!nic->macsec_cfg)
|
|
return 0;
|
|
|
|
mutex_lock(&nic->macsec_mutex);
|
|
|
|
if (nic->aq_fw_ops->send_macsec_req) {
|
|
struct macsec_cfg_request cfg = { 0 };
|
|
|
|
cfg.enabled = 1;
|
|
cfg.egress_threshold = 0xffffffff;
|
|
cfg.ingress_threshold = 0xffffffff;
|
|
cfg.interrupts_enabled = 1;
|
|
|
|
msg.msg_type = macsec_cfg_msg;
|
|
msg.cfg = cfg;
|
|
|
|
ret = nic->aq_fw_ops->send_macsec_req(hw, &msg, &resp);
|
|
if (ret)
|
|
goto unlock;
|
|
}
|
|
|
|
/* Init Ethertype bypass filters */
|
|
for (index = 0; index < ARRAY_SIZE(ctl_ether_types); index++) {
|
|
struct aq_mss_ingress_prectlf_record rx_prectlf_rec;
|
|
struct aq_mss_egress_ctlf_record tx_ctlf_rec;
|
|
|
|
if (ctl_ether_types[index] == 0)
|
|
continue;
|
|
|
|
memset(&tx_ctlf_rec, 0, sizeof(tx_ctlf_rec));
|
|
tx_ctlf_rec.eth_type = ctl_ether_types[index];
|
|
tx_ctlf_rec.match_type = 4; /* Match eth_type only */
|
|
tx_ctlf_rec.match_mask = 0xf; /* match for eth_type */
|
|
tx_ctlf_rec.action = 0; /* Bypass MACSEC modules */
|
|
tbl_idx = NUMROWS_EGRESSCTLFRECORD - num_ctl_ether_types - 1;
|
|
aq_mss_set_egress_ctlf_record(hw, &tx_ctlf_rec, tbl_idx);
|
|
|
|
memset(&rx_prectlf_rec, 0, sizeof(rx_prectlf_rec));
|
|
rx_prectlf_rec.eth_type = ctl_ether_types[index];
|
|
rx_prectlf_rec.match_type = 4; /* Match eth_type only */
|
|
rx_prectlf_rec.match_mask = 0xf; /* match for eth_type */
|
|
rx_prectlf_rec.action = 0; /* Bypass MACSEC modules */
|
|
tbl_idx =
|
|
NUMROWS_INGRESSPRECTLFRECORD - num_ctl_ether_types - 1;
|
|
aq_mss_set_ingress_prectlf_record(hw, &rx_prectlf_rec, tbl_idx);
|
|
|
|
num_ctl_ether_types++;
|
|
}
|
|
|
|
ret = aq_apply_macsec_cfg(nic);
|
|
|
|
unlock:
|
|
mutex_unlock(&nic->macsec_mutex);
|
|
return ret;
|
|
}
|
|
|
|
void aq_macsec_work(struct aq_nic_s *nic)
|
|
{
|
|
if (!nic->macsec_cfg)
|
|
return;
|
|
|
|
if (!netif_carrier_ok(nic->ndev))
|
|
return;
|
|
|
|
mutex_lock(&nic->macsec_mutex);
|
|
aq_check_txsa_expiration(nic);
|
|
mutex_unlock(&nic->macsec_mutex);
|
|
}
|
|
|
|
int aq_macsec_rx_sa_cnt(struct aq_nic_s *nic)
|
|
{
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
int i, cnt = 0;
|
|
|
|
if (!cfg)
|
|
return 0;
|
|
|
|
mutex_lock(&nic->macsec_mutex);
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
|
|
if (!test_bit(i, &cfg->rxsc_idx_busy))
|
|
continue;
|
|
cnt += hweight_long(cfg->aq_rxsc[i].rx_sa_idx_busy);
|
|
}
|
|
|
|
mutex_unlock(&nic->macsec_mutex);
|
|
return cnt;
|
|
}
|
|
|
|
int aq_macsec_tx_sc_cnt(struct aq_nic_s *nic)
|
|
{
|
|
int cnt;
|
|
|
|
if (!nic->macsec_cfg)
|
|
return 0;
|
|
|
|
mutex_lock(&nic->macsec_mutex);
|
|
cnt = hweight_long(nic->macsec_cfg->txsc_idx_busy);
|
|
mutex_unlock(&nic->macsec_mutex);
|
|
|
|
return cnt;
|
|
}
|
|
|
|
int aq_macsec_tx_sa_cnt(struct aq_nic_s *nic)
|
|
{
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
int i, cnt = 0;
|
|
|
|
if (!cfg)
|
|
return 0;
|
|
|
|
mutex_lock(&nic->macsec_mutex);
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
|
|
if (!test_bit(i, &cfg->txsc_idx_busy))
|
|
continue;
|
|
cnt += hweight_long(cfg->aq_txsc[i].tx_sa_idx_busy);
|
|
}
|
|
|
|
mutex_unlock(&nic->macsec_mutex);
|
|
return cnt;
|
|
}
|
|
|
|
static int aq_macsec_update_stats(struct aq_nic_s *nic)
|
|
{
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
struct aq_hw_s *hw = nic->aq_hw;
|
|
struct aq_macsec_txsc *aq_txsc;
|
|
struct aq_macsec_rxsc *aq_rxsc;
|
|
int i, sa_idx, assoc_num;
|
|
int ret = 0;
|
|
|
|
aq_get_macsec_common_stats(hw, &cfg->stats);
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
|
|
if (!(cfg->txsc_idx_busy & BIT(i)))
|
|
continue;
|
|
aq_txsc = &cfg->aq_txsc[i];
|
|
|
|
ret = aq_get_txsc_stats(hw, aq_txsc->hw_sc_idx,
|
|
&aq_txsc->stats);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for (assoc_num = 0; assoc_num < MACSEC_NUM_AN; assoc_num++) {
|
|
if (!test_bit(assoc_num, &aq_txsc->tx_sa_idx_busy))
|
|
continue;
|
|
sa_idx = aq_txsc->hw_sc_idx | assoc_num;
|
|
ret = aq_get_txsa_stats(hw, sa_idx,
|
|
&aq_txsc->tx_sa_stats[assoc_num]);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
|
|
if (!(test_bit(i, &cfg->rxsc_idx_busy)))
|
|
continue;
|
|
aq_rxsc = &cfg->aq_rxsc[i];
|
|
|
|
for (assoc_num = 0; assoc_num < MACSEC_NUM_AN; assoc_num++) {
|
|
if (!test_bit(assoc_num, &aq_rxsc->rx_sa_idx_busy))
|
|
continue;
|
|
sa_idx = aq_rxsc->hw_sc_idx | assoc_num;
|
|
|
|
ret = aq_get_rxsa_stats(hw, sa_idx,
|
|
&aq_rxsc->rx_sa_stats[assoc_num]);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
u64 *aq_macsec_get_stats(struct aq_nic_s *nic, u64 *data)
|
|
{
|
|
struct aq_macsec_cfg *cfg = nic->macsec_cfg;
|
|
struct aq_macsec_common_stats *common_stats;
|
|
struct aq_macsec_tx_sc_stats *txsc_stats;
|
|
struct aq_macsec_tx_sa_stats *txsa_stats;
|
|
struct aq_macsec_rx_sa_stats *rxsa_stats;
|
|
struct aq_macsec_txsc *aq_txsc;
|
|
struct aq_macsec_rxsc *aq_rxsc;
|
|
unsigned int assoc_num;
|
|
unsigned int sc_num;
|
|
unsigned int i = 0U;
|
|
|
|
if (!cfg)
|
|
return data;
|
|
|
|
mutex_lock(&nic->macsec_mutex);
|
|
|
|
aq_macsec_update_stats(nic);
|
|
|
|
common_stats = &cfg->stats;
|
|
data[i] = common_stats->in.ctl_pkts;
|
|
data[++i] = common_stats->in.tagged_miss_pkts;
|
|
data[++i] = common_stats->in.untagged_miss_pkts;
|
|
data[++i] = common_stats->in.notag_pkts;
|
|
data[++i] = common_stats->in.untagged_pkts;
|
|
data[++i] = common_stats->in.bad_tag_pkts;
|
|
data[++i] = common_stats->in.no_sci_pkts;
|
|
data[++i] = common_stats->in.unknown_sci_pkts;
|
|
data[++i] = common_stats->in.ctrl_prt_pass_pkts;
|
|
data[++i] = common_stats->in.unctrl_prt_pass_pkts;
|
|
data[++i] = common_stats->in.ctrl_prt_fail_pkts;
|
|
data[++i] = common_stats->in.unctrl_prt_fail_pkts;
|
|
data[++i] = common_stats->in.too_long_pkts;
|
|
data[++i] = common_stats->in.igpoc_ctl_pkts;
|
|
data[++i] = common_stats->in.ecc_error_pkts;
|
|
data[++i] = common_stats->in.unctrl_hit_drop_redir;
|
|
data[++i] = common_stats->out.ctl_pkts;
|
|
data[++i] = common_stats->out.unknown_sa_pkts;
|
|
data[++i] = common_stats->out.untagged_pkts;
|
|
data[++i] = common_stats->out.too_long;
|
|
data[++i] = common_stats->out.ecc_error_pkts;
|
|
data[++i] = common_stats->out.unctrl_hit_drop_redir;
|
|
|
|
for (sc_num = 0; sc_num < AQ_MACSEC_MAX_SC; sc_num++) {
|
|
if (!(test_bit(sc_num, &cfg->txsc_idx_busy)))
|
|
continue;
|
|
|
|
aq_txsc = &cfg->aq_txsc[sc_num];
|
|
txsc_stats = &aq_txsc->stats;
|
|
|
|
data[++i] = txsc_stats->sc_protected_pkts;
|
|
data[++i] = txsc_stats->sc_encrypted_pkts;
|
|
data[++i] = txsc_stats->sc_protected_octets;
|
|
data[++i] = txsc_stats->sc_encrypted_octets;
|
|
|
|
for (assoc_num = 0; assoc_num < MACSEC_NUM_AN; assoc_num++) {
|
|
if (!test_bit(assoc_num, &aq_txsc->tx_sa_idx_busy))
|
|
continue;
|
|
|
|
txsa_stats = &aq_txsc->tx_sa_stats[assoc_num];
|
|
|
|
data[++i] = txsa_stats->sa_hit_drop_redirect;
|
|
data[++i] = txsa_stats->sa_protected2_pkts;
|
|
data[++i] = txsa_stats->sa_protected_pkts;
|
|
data[++i] = txsa_stats->sa_encrypted_pkts;
|
|
}
|
|
}
|
|
|
|
for (sc_num = 0; sc_num < AQ_MACSEC_MAX_SC; sc_num++) {
|
|
if (!(test_bit(sc_num, &cfg->rxsc_idx_busy)))
|
|
continue;
|
|
|
|
aq_rxsc = &cfg->aq_rxsc[sc_num];
|
|
|
|
for (assoc_num = 0; assoc_num < MACSEC_NUM_AN; assoc_num++) {
|
|
if (!test_bit(assoc_num, &aq_rxsc->rx_sa_idx_busy))
|
|
continue;
|
|
|
|
rxsa_stats = &aq_rxsc->rx_sa_stats[assoc_num];
|
|
|
|
data[++i] = rxsa_stats->untagged_hit_pkts;
|
|
data[++i] = rxsa_stats->ctrl_hit_drop_redir_pkts;
|
|
data[++i] = rxsa_stats->not_using_sa;
|
|
data[++i] = rxsa_stats->unused_sa;
|
|
data[++i] = rxsa_stats->not_valid_pkts;
|
|
data[++i] = rxsa_stats->invalid_pkts;
|
|
data[++i] = rxsa_stats->ok_pkts;
|
|
data[++i] = rxsa_stats->late_pkts;
|
|
data[++i] = rxsa_stats->delayed_pkts;
|
|
data[++i] = rxsa_stats->unchecked_pkts;
|
|
data[++i] = rxsa_stats->validated_octets;
|
|
data[++i] = rxsa_stats->decrypted_octets;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
|
|
data += i;
|
|
|
|
mutex_unlock(&nic->macsec_mutex);
|
|
|
|
return data;
|
|
}
|