blob: 999eb2367f2aef38ec1449cc11d2842f6292875b [file] [log] [blame]
/*
<:copyright-BRCM:2016:DUAL/GPL:standard
Broadcom Proprietary and Confidential.(c) 2016 Broadcom
All Rights Reserved
Unless you and Broadcom execute a separate written software license
agreement governing use of this software, this software is licensed
to you under the terms of the GNU General Public License version 2
(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
with the following added to such license:
As a special exception, the copyright holders of this software give
you permission to link this software with independent modules, and
to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent
module, the terms and conditions of the license of that module.
An independent module is a module which is not derived from this
software. The special exception does not apply to any modifications
of the software.
Not withstanding the above, under no circumstances may you combine
this software in any way with any other Broadcom software provided
under a license other than the GPL, without Broadcom's express prior
written consent.
:>
*/
/** @file mka.c
* @brief Publicly accessible APIs provided to process MKA request/responses.
*
* This file contains all of the public/private APIs used to process MKA frames.
*
* MKA Exchange Sequence :
*
* 1. If bidirectional encryption is enabled, the OLT will generate an MKPDU
* containing the basic parameter set and empty live and potential peer
* lists.
* 2. When the ONU receives this, it will add the OLT to its potential peers
* list.
* 3. Having discovered a new potential peer, the ONU will transmit an MKPDU
* containing the basic parameter set, and empty live peers list, and a
* potential peer list containing the OLT.
* 4. When the OLT receives this, it will add the ONU to its live peers list.
* 5. Having added a new live peer, the OLT will generate a new SAK and install
* it for receive (using the ONUs SCI).
* 6. The OLT then transmits an MKPDU containing the basic parameter set, a
* live peer list containing the ONU, an empty potential peer list, a
* distributed SAK, and a SAK usage parameter set indicating that the new
* SAK has been installed for receive.
* 7. When the ONU receives this, it will:
* a. Add the OLT to its live peers list
* b. Install the new SAK for receive (using the OLTs SCI)
* c. Because the OLT reported the SAK in use for receive, the ONU will
* install the SAK for transmit (using the ONUs SCI).
* 8. The ONU will then transmit an MKPDU containing the basic parameter set,
* a live peers list containing the OLT, an empty potential peer list,
* and a SAK usage parameter set indicating that the SAK has been installed
* for receive and transmit.
* 9. When the OLT receives this, it will see that the ONU has installed the
* SAK for receive and will install the SAK for transmit
* (using the OLTs SCI).
* 10. The OLT will then transmit an MKPDU containing the basic parameter set,
* a live peer list containing the ONU, an empty potential peer list and
* a SAK usage parameter set indicating the SAK has been installed for
* receive and transmit. (I¡®m not sure if this step is necessary)
* 11. This message will produce no state change at the ONU, so it will not
* respond immediately. (both the ONU and OLT will need to start a 2 second
* timer every time an MKPDU is transmitted ? if they have not transmitted
* an MKPDU due to a state change when the timer expires, they will
* transmit an MKPDU containing the basic parameter set and live and
* potential peer lists. If either side does not receive an MKPDU from
* the other for 6 seconds they must remove them from their peer lists.)
* 12. When the OLT determines that a new SAK is needed this protocol will
* repeat steps 5-11. (modifications to the peer lists, such as 7a, should
* not be required)
*
* @defgroup mka MKA
* @ingroup cmCtrl
*
*/
#include "mka.h"
#include "dpoe_sec_util.h"
#include "dpoe_sec_fsm.h"
/* The destination multicast MAC address for MKA packets. */
static const bcmos_mac_address mka_dst_mac_addr = { .u8 = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 } };
#if 0
static const char *mka_state_names[MKA_STATE__COUNT] =
{
"Initial",
"WaitingInitialPeerResp",
"SakSent",
"MkaDone"
};
#endif
#define MKA_VERSION 1
#define MKA_SERVER_PRIORITY 0x30
#define MKA_INIT_RETRY_CNT 3
#define MKA_BASIC_PARAM_SET_LEN 44 /* 48 - header len (4 byte) */
#define MKA_BASIC_PARAM_SET_OPT 0x60 /* KeyServer:0, MACsecDesired:1, MACsec Capability:2 & high 4 bits of
Parameter set body length (0) */
#define MKA_ALGORITHM_AGILITY 0x0080c201
#define MKA_MN_LEN 4 /* Message Number */
#define MKA_KN_LEN 4 /* Key Number */
#define MKA_PDU_MAX_FRAME_LEN 300
#define MKA_HELLO_TIME 2000 /* Ms, 2 seconds */
#define MKA_MAX_HELLO_RETRIES 3 /* Max Hello retries */
#define MKA_OLT_INIT_FRAME_LEN 76 /* basic param set(48) + null live peer list (4) + null potential peer list (4) +
Icv Indicator (4) + ICV(16) */
#define MKA_ONE_PEER_LEN 16 /* one live peer len */
#define MKA_DISTRIBUTED_SAK_LEN 32
#define MKA_ICV_LEN 16
#define MKA_PDU_MIN_LENGTH 32
#define MKA_KEY_WRAPPED_SAK_LEN 24
#define MKA_SAK_USE_PARAM_BODY_LEN 40
#define MKA_SAK_USE_PARAM_LEM 44
#define MKA_MAX_ACCEPTABLE_PN 0xC0000000
#define MKA_SAK_USE_PARAM_AN_SHIFT 6
#define MKA_SAK_USE_PARAM_LK_TX_SHIFT 5
#define MKA_SAK_USE_PARAM_LK_RX_SHIFT 4
#define MKA_SAK_USE_PARAM_OLD_KEY_AN_MASK 3
#define MKA_SAK_USE_PARAM_OLD_KEY_AN_SFT 2
#define MKA_SAK_USE_PARAM_OLD_KEY_TX_RX 3
#define MKA_SAK_USE_PARAM_SECOND_OPT_BYTE 0xC0
#define MKA_KEY_LEN 16 /* common key size */
#define MKA_KEK_LABEL_LEN 12 /* label length */
#define MKA_KEK_CONTEXT_LEN 16
#define MKA_SAK_KEY_WRAPPED_LEN 24
#define MKA_ICK_LABEL_LEN 12 /* label length */
#define MKA_ICK_CONTEXT_LEN 16
#define MKA_CKN_LABEL_LEN 16 /* label length */
#define MKA_CKN_CONTEXT_LEN (SIZE_OF_EAP_SESSION_ID + (BCMOS_ETH_ALEN * 2)) /* 77 */
#define MKA_CAK_LABEL_LEN 16 /* label length */
#define MKA_CAK_CONTEXT_LEN 12 /* context length */
/* MKPDU Parameter set type */
typedef enum
{
MKPDU_PARAM_SET_LIVE_PEER_LIST = 1,
MKPDU_PARAM_SET_POTENTIAL_PEER_LIST = 2,
MKPDU_PARAM_SET_MACSEC_SAK_USE = 3,
MKPDU_PARAM_SET_DIST_SAK = 4,
MKPDU_PARAM_SET_DIST_CAK = 5,
MKPDU_PARAM_SET_KMD = 6,
MKPDU_PARAM_SET_ANNOUNCEMENT = 7,
MKPDU_PARAM_SET_ICV_INDICATOR = 255,
} mkpdu_param_set_type;
typedef enum
{
MKA_MSG_TIMEOUT,
MKA_MSG_DATA_IND
} mka_msg_type;
typedef struct
{
mka_msg_type type;
eapol_msg_hdr *msg;
bcmolt_buf *buf;
} mka_event;
/* Calculate Key Encryption Key */
static void _mka_calc_kek(uint8_t *kek, uint8_t *cak, uint8_t *ckn)
{
char kek_label[MKA_KEK_LABEL_LEN + 1] = "IEEE8021 KEK";
/* Parameter checks. */
BUG_ON(kek == NULL);
BUG_ON(cak == NULL);
BUG_ON(ckn == NULL);
dpoe_sec_aes_cmac_kdf(cak, kek_label, ckn, MKA_KEK_CONTEXT_LEN, kek);
}
/* Calculate Integrity Check value Key */
static void _mka_calc_ick(uint8_t *ick, uint8_t *cak, uint8_t *ckn)
{
char ick_label[MKA_ICK_LABEL_LEN + 1] = "IEEE8021 ICK";
/* Parameter checks. */
BUG_ON(ick == NULL);
BUG_ON(cak == NULL);
BUG_ON(ckn == NULL);
dpoe_sec_aes_cmac_kdf(cak, ick_label, ckn, MKA_ICK_CONTEXT_LEN, ick);
}
/* calculate Integrity Check Value */
static void _mka_calc_icv(uint8_t *icv, uint8_t *ick, uint8_t *msdu, uint16_t msdu_len)
{
/* Parameter checks. */
BUG_ON(icv == NULL);
BUG_ON(ick == NULL);
BUG_ON(msdu == NULL);
dpoe_sec_aes_cmac(ick, msdu, msdu_len, icv);
}
/* Calculate Secure Connectivity Association Key Name */
static void _mka_calc_ckn(dpoe_sec_link_rec *link_rec, uint8_t eap_sess_id_len)
{
char ckn_label[MKA_CKN_LABEL_LEN + 1] = "IEEE8021 EAP CKN";
uint8_t ckn_context[MKA_CKN_CONTEXT_LEN];
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info->ckn == NULL);
BUG_ON(link_rec->auth_ctrl.trans_data.master_session_key == NULL);
BUG_ON(link_rec->auth_ctrl.trans_data.session_id == NULL);
memcpy(&ckn_context[0], link_rec->auth_ctrl.trans_data.session_id, eap_sess_id_len);
memcpy(&ckn_context[eap_sess_id_len], link_rec->mka_ctrl.mka_info->lesser_mac.u8, BCMOS_ETH_ALEN);
memcpy(&ckn_context[eap_sess_id_len + BCMOS_ETH_ALEN], link_rec->mka_ctrl.mka_info->greater_mac.u8, BCMOS_ETH_ALEN);
dpoe_sec_aes_cmac_kdf(link_rec->auth_ctrl.trans_data.master_session_key, ckn_label, ckn_context, MKA_CKN_CONTEXT_LEN, link_rec->mka_ctrl.mka_info->ckn);
}
/* Calculate Secure Connectivity Association Key */
static void _mka_calc_cak(dpoe_sec_link_rec *link_rec)
{
char cak_label[MKA_CAK_LABEL_LEN + 1] = "IEEE8021 EAP CAK";
uint8_t cak_context[MKA_CAK_CONTEXT_LEN];
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info->cak == NULL);
BUG_ON(link_rec->auth_ctrl.trans_data.master_session_key == NULL);
memcpy(&cak_context[0], link_rec->mka_ctrl.mka_info->lesser_mac.u8, BCMOS_ETH_ALEN);
memcpy(&cak_context[BCMOS_ETH_ALEN], link_rec->mka_ctrl.mka_info->greater_mac.u8, BCMOS_ETH_ALEN);
dpoe_sec_aes_cmac_kdf(link_rec->auth_ctrl.trans_data.master_session_key, cak_label, cak_context, MKA_CAK_CONTEXT_LEN, link_rec->mka_ctrl.mka_info->cak);
}
/* Get OLT port Mac and link Mac & compare. This is needed for calculating CAK */
static void _mka_get_olt_and_link_mac(dpoe_sec_link_rec *link_rec)
{
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
/* compare OLT and Link MAC addresses */
if (memcmp(link_rec->ni_mac.u8, link_rec->key.mac_address.u8, sizeof(BCMOS_ETH_ALEN)) >= 0)
{
link_rec->mka_ctrl.mka_info->lesser_mac = link_rec->key.mac_address;
link_rec->mka_ctrl.mka_info->greater_mac = link_rec->ni_mac;
}
else
{
link_rec->mka_ctrl.mka_info->lesser_mac = link_rec->ni_mac;
link_rec->mka_ctrl.mka_info->greater_mac = link_rec->key.mac_address;
}
}
/* Initialize link MKA info structure */
static void _mka_init_link_info(dpoe_sec_link_rec *link_rec)
{
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
/* initialize all local values */
memset(link_rec->mka_ctrl.mka_info, 0, sizeof(*link_rec->mka_ctrl.mka_info));
dpoe_sec_generate_n_byte_random_number(link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN);
link_rec->mka_ctrl.mka_info->curr_msg_num = 1; /* start from 1 */
link_rec->mka_ctrl.mka_info->key_number = 1; /* start from 1 */
_mka_get_olt_and_link_mac(link_rec);
_mka_calc_cak(link_rec);
_mka_calc_ckn(link_rec, SIZE_OF_EAP_SESSION_ID);
_mka_calc_ick(link_rec->mka_ctrl.mka_info->ick, link_rec->mka_ctrl.mka_info->cak, link_rec->mka_ctrl.mka_info->ckn);
_mka_calc_kek(link_rec->mka_ctrl.mka_info->kek, link_rec->mka_ctrl.mka_info->cak, link_rec->mka_ctrl.mka_info->ckn);
}
static void _mka_clear_link_info(dpoe_sec_link_rec *link_rec)
{
/* Parameter checks. */
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
memset(link_rec->mka_ctrl.mka_info, 0, sizeof(*link_rec->mka_ctrl.mka_info));
}
/* Build EAPOL header for MKPDU */
static bcmos_bool _mka_build_eapol_header(dpoe_sec_link_rec *link_rec, bcmolt_buf *out_buf, uint16_t length)
{
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(out_buf == NULL);
return
bcmolt_buf_write_mac_address(out_buf, mka_dst_mac_addr) &&
bcmolt_buf_write_mac_address(out_buf, link_rec->ni_mac) &&
bcmolt_buf_write_u16_be(out_buf, ETHERTYPE_EAPOL) &&
bcmolt_buf_write_u8(out_buf, EAPOL_PROTOCOL_VERSION_DPOE) &&
bcmolt_buf_write_u8(out_buf, EAPOL_TYPE_MKA) &&
bcmolt_buf_write_u16_be(out_buf, length);
}
/* Build Basic parameter set. This parameter set is required for every MKPDU */
static bcmos_bool _mka_build_basic_parameter_set(dpoe_sec_link_rec *link_rec, bcmolt_buf *out_buf)
{
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(out_buf == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
return
bcmolt_buf_write_u8(out_buf, MKA_VERSION) && /* Version Number, 802.1X Pg.98 */
bcmolt_buf_write_u8(out_buf, MKA_SERVER_PRIORITY) && /* Key Server Priority */
bcmolt_buf_write_u8(out_buf, MKA_BASIC_PARAM_SET_OPT) &&
bcmolt_buf_write_u8(out_buf, MKA_BASIC_PARAM_SET_LEN) &&
bcmolt_buf_write_mac_address(out_buf, link_rec->ni_mac) && /* 6 byte of SCI */
bcmolt_buf_write_u16_be(out_buf, link_rec->llid) && /* lower 2 byte SCI */
bcmolt_buf_write(out_buf, link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN) &&
bcmolt_buf_write_u32_be(out_buf, link_rec->mka_ctrl.mka_info->curr_msg_num++) && /* Important !! */
bcmolt_buf_write_u32_be(out_buf, MKA_ALGORITHM_AGILITY) &&
bcmolt_buf_write(out_buf, link_rec->mka_ctrl.mka_info->ckn, MKA_CKN_LEN / 8); /* CAK Name(CKN) */
}
/* Build an empty Live Peer or Potential Peer list parameter set */
static bcmos_bool _mka_build_empty_peer_list(bcmolt_buf *out_buf, uint8_t type)
{
/* Parameter checks. */
BUG_ON(out_buf == NULL);
return
bcmolt_buf_write_u8(out_buf, type) && /* Parameter set type */
bcmolt_buf_write_u8(out_buf, 0) &&
bcmolt_buf_write_u8(out_buf, 0) && /* options & 4 bit length */
bcmolt_buf_write_u8(out_buf, 0); /* Parameter set body length. */
}
/* Build Live Peer List or Potential Peer List parameter set */
static bcmos_bool _mka_build_peer_list(
bcmolt_buf *out_buf,
uint8_t type,
const uint8_t *member_id,
uint32_t message_num)
{
/* Parameter checks. */
BUG_ON(out_buf == NULL);
BUG_ON(member_id == NULL);
return
bcmolt_buf_write_u8(out_buf, type) && /* Parameter set type */
bcmolt_buf_write_u8(out_buf, 0) &&
bcmolt_buf_write_u8(out_buf, 0) && /* options & 4 bit length */
bcmolt_buf_write_u8(out_buf, MKA_MI_LEN + sizeof(uint32_t)) && /* Parameter set body length. */
bcmolt_buf_write(out_buf, member_id, MKA_MI_LEN) &&
bcmolt_buf_write_u32_be(out_buf, message_num);
}
/* Build ICV Indicator parameter set */
static bcmos_bool _mka_build_icv_indicator(bcmolt_buf *out_buf)
{
/* Parameter checks. */
BUG_ON(out_buf == NULL);
return
bcmolt_buf_write_u8(out_buf, MKPDU_PARAM_SET_ICV_INDICATOR) &&
bcmolt_buf_write_u8(out_buf, 0) &&
bcmolt_buf_write_u8(out_buf, 0) && /* options & 4 bit length */
bcmolt_buf_write_u8(out_buf, MKA_ICV_LEN); /* Parameter set body length. */
}
/* Build Distributed SAK parameter set (GCM-AES-128) */
static bcmos_bool _mka_build_distributed_sak(
const dpoe_sec_link_rec *link_rec,
bcmolt_buf *out_buf,
const uint8_t *key_wrapped_sak)
{
uint8_t option;
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
BUG_ON(out_buf == NULL);
BUG_ON(key_wrapped_sak == NULL);
/* need Distributed AN & Confidentiality Offset (bit 5,4. use 0) */
option = link_rec->mka_ctrl.mka_info->association_number;
option <<= MKA_SAK_USE_PARAM_AN_SHIFT; /* move to bit 7,6 */
/* key number & AN will be increased from the caller */
if (!bcmolt_buf_write_u8(out_buf, MKPDU_PARAM_SET_DIST_SAK) ||
!bcmolt_buf_write_u8(out_buf, option) ||
!bcmolt_buf_write_u8(out_buf, 0) || /* options & 4 bit length */
!bcmolt_buf_write_u8(out_buf, MKA_KEY_WRAPPED_SAK_LEN + MKA_KN_LEN) ||
!bcmolt_buf_write_u32_be(out_buf, link_rec->mka_ctrl.mka_info->key_number) ||
!bcmolt_buf_write(out_buf, key_wrapped_sak, MKA_KEY_WRAPPED_SAK_LEN))
{
return BCMOS_FALSE;
}
return BCMOS_TRUE;
}
/* Build MACsec SAK Use paramater set */
static bcmos_bool _mka_build_sak_use_param(
const dpoe_sec_link_rec *link_rec,
bcmolt_buf *out_buf,
bcmos_bool set_tx,
bcmos_bool set_rx)
{
uint8_t option1 = 0;
uint8_t option2 = 0;
uint8_t tmp = 0;
uint32_t old_kn;
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
BUG_ON(out_buf == NULL);
/* option 1: Latest Key AN(7:6), Latest Key Tx(5), Latest Key Rx(4) Old Key AN(3:2), Old Key Tx(1), Old Key Rx(0) */
option1 = link_rec->mka_ctrl.mka_info->association_number;
option1 <<= MKA_SAK_USE_PARAM_AN_SHIFT; /* move to bit 7,6 (AN) */
if (set_tx)
{
tmp = 1 << MKA_SAK_USE_PARAM_LK_TX_SHIFT; /* Latest Key TX */
}
if (set_rx)
{
tmp = tmp | (1 << MKA_SAK_USE_PARAM_LK_RX_SHIFT); /* Latest Key Rx */
}
/* for first frame, all 0 is used for old key info */
if (link_rec->mka_ctrl.mka_info->refresh_cnt != 0)
{
/* for OLD AN, tx, rx bits */
tmp = tmp | ((link_rec->mka_ctrl.mka_info->association_number & MKA_SAK_USE_PARAM_OLD_KEY_AN_MASK) << MKA_SAK_USE_PARAM_OLD_KEY_AN_SFT);
tmp = tmp | MKA_SAK_USE_PARAM_OLD_KEY_TX_RX; /* should have been tx & rx */
}
option1 |= tmp;
/* option 2: only set bit 7,6(plain tx & plain rx). all other bits including 4 bits length field should be 0 */
option2 = MKA_SAK_USE_PARAM_SECOND_OPT_BYTE;
/* need to decrement key number for old key (because key numer may be already incremented) */
if (link_rec->mka_ctrl.mka_info->key_number == 1) /* initial number */
{
old_kn = 1;
}
else
{
old_kn = link_rec->mka_ctrl.mka_info->key_number - 1;
}
if (!bcmolt_buf_write_u8(out_buf, MKPDU_PARAM_SET_MACSEC_SAK_USE) ||
!bcmolt_buf_write_u8(out_buf, option1) ||
!bcmolt_buf_write_u8(out_buf, option2) ||
!bcmolt_buf_write_u8(out_buf, MKA_SAK_USE_PARAM_BODY_LEN) ||
!bcmolt_buf_write(out_buf, link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN) ||
!bcmolt_buf_write_u32_be(out_buf, link_rec->mka_ctrl.mka_info->key_number) ||
!bcmolt_buf_write_u32_be(out_buf, 1) || /* Lowest acceptable PN, start from 1 */
!bcmolt_buf_write(out_buf, link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN) ||
!bcmolt_buf_write_u32_be(out_buf, old_kn) ||
!bcmolt_buf_write_u32_be(out_buf, 1))
{
return BCMOS_FALSE;
}
return BCMOS_TRUE;
}
/* Calculate ICV and attach it at the end of MKPDU */
static bcmos_bool _mka_calc_and_attach_icv(mka_link_info *mka_info, bcmolt_buf *out_buf)
{
uint8_t ick[MKA_KEY_LEN];
uint8_t icv[MKA_KEY_LEN];
/* Parameter checks. */
BUG_ON(mka_info == NULL);
BUG_ON(out_buf == NULL);
_mka_calc_ick(ick, mka_info->cak, mka_info->ckn);
/* ICV will be calculated from DA to end of ICV indicator */
_mka_calc_icv(icv, ick, out_buf->start, (uint16_t)bcmolt_buf_get_used(out_buf));
return bcmolt_buf_write(out_buf, icv, MKA_KEY_LEN);
}
/* Send the first MKA frame to ONU */
static bcmos_bool _mka_send_init_frame(dpoe_sec_link_rec *link_rec)
{
bcmolt_buf out_buf;
uint8_t frame[MKA_PDU_MAX_FRAME_LEN];
bcmos_bool ret = BCMOS_FALSE;
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
bcmolt_buf_init(&out_buf, MKA_PDU_MAX_FRAME_LEN, frame, BCMOLT_BUF_ENDIAN_FIXED);
/* construct frame */
if (!_mka_build_eapol_header(link_rec, &out_buf, MKA_OLT_INIT_FRAME_LEN) ||
!_mka_build_basic_parameter_set(link_rec, &out_buf) ||
!_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_LIVE_PEER_LIST) ||
!_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_POTENTIAL_PEER_LIST) ||
!_mka_build_icv_indicator(&out_buf) ||
!_mka_calc_and_attach_icv(link_rec->mka_ctrl.mka_info, &out_buf)) /* last important step. Calc & attach ICV */
{
return ret;
}
if (BCM_ERR_OK == dpoe_sec_send_eapol(link_rec->device, &link_rec->key, frame, bcmolt_buf_get_used(&out_buf)))
{
bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000);
bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->tx_timer, MKA_HELLO_TIME * 1000);
ret = BCMOS_TRUE;
}
return ret;
}
/* Generate a SAK using RNG */
void mka_generate_sak(dpoe_sec_link_rec *link_rec, bcmos_bool initial)
{
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
/* Generate SAK */
if (initial)
{
_mka_calc_kek(link_rec->mka_ctrl.mka_info->kek, link_rec->mka_ctrl.mka_info->cak, link_rec->mka_ctrl.mka_info->ckn);
dpoe_sec_generate_n_byte_random_number(link_rec->mka_ctrl.mka_info->sak, MKA_KEY_LEN);
}
else
{
dpoe_sec_generate_n_byte_random_number(link_rec->mka_ctrl.mka_info->new_sak, MKA_KEY_LEN);
}
}
/* Send the SAK to the ONU */
bcmos_errno mka_send_sak(dpoe_sec_link_rec *link_rec, uint8_t *sak)
{
bcmolt_buf out_buf;
uint8_t frame[MKA_PDU_MAX_FRAME_LEN];
uint8_t key_wrapped_sak[MKA_KEY_LEN * 2] = {};
bcmos_errno rc = BCM_ERR_COMM_FAIL;
dpoe_sec_aes_key aes_key;
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
bcmolt_buf_init(&out_buf, MKA_PDU_MAX_FRAME_LEN, frame, BCMOLT_BUF_ENDIAN_FIXED);
/* key wrap sak using kek */
dpoe_sec_aes_set_encrypt_key(link_rec->mka_ctrl.mka_info->kek, MKA_KEK_LEN, &aes_key);
dpoe_sec_aes_wrap_key(&aes_key, key_wrapped_sak, sak, MKA_SAK_LEN);
/* construct frame */
if (!_mka_build_eapol_header(
link_rec,
&out_buf,
MKA_OLT_INIT_FRAME_LEN + MKA_ONE_PEER_LEN + MKA_DISTRIBUTED_SAK_LEN + MKA_SAK_USE_PARAM_LEM) ||
!_mka_build_basic_parameter_set(link_rec, &out_buf) ||
!_mka_build_peer_list(
&out_buf,
MKPDU_PARAM_SET_LIVE_PEER_LIST,
link_rec->mka_ctrl.mka_info->link_memeber_id,
link_rec->mka_ctrl.mka_info->link_msg_num) ||
!_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_POTENTIAL_PEER_LIST) ||
!_mka_build_distributed_sak(link_rec, &out_buf, key_wrapped_sak) ||
!_mka_build_sak_use_param(link_rec, &out_buf, BCMOS_FALSE, BCMOS_TRUE) || /* set RX only */
!_mka_build_icv_indicator(&out_buf))
{
return BCM_ERR_OVERFLOW;
}
/* last important step. Calc & attach ICV */
_mka_calc_and_attach_icv(link_rec->mka_ctrl.mka_info, &out_buf);
if (BCM_ERR_OK == dpoe_sec_send_eapol(link_rec->device, &link_rec->key, frame, bcmolt_buf_get_used(&out_buf)))
{
bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->tx_timer, MKA_HELLO_TIME * 1000);
rc = BCM_ERR_OK;
}
return rc;
}
/* Received SAK response. Send confirm frame to ONU */
static bcmos_bool _mka_send_sak_confirm_frame(dpoe_sec_link_rec *link_rec)
{
bcmolt_buf out_buf;
uint8_t frame[MKA_PDU_MAX_FRAME_LEN];
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
bcmolt_buf_init(&out_buf, MKA_PDU_MAX_FRAME_LEN, frame, BCMOLT_BUF_ENDIAN_FIXED);
if (!_mka_build_eapol_header(link_rec, &out_buf, MKA_OLT_INIT_FRAME_LEN + MKA_ONE_PEER_LEN + MKA_SAK_USE_PARAM_LEM) ||
!_mka_build_basic_parameter_set(link_rec, &out_buf) ||
!_mka_build_peer_list(
&out_buf, MKPDU_PARAM_SET_LIVE_PEER_LIST,
link_rec->mka_ctrl.mka_info->link_memeber_id,
link_rec->mka_ctrl.mka_info->link_msg_num) ||
!_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_POTENTIAL_PEER_LIST) ||
!_mka_build_sak_use_param(link_rec, &out_buf, BCMOS_TRUE, BCMOS_TRUE) ||
!_mka_build_icv_indicator(&out_buf))
{
return BCMOS_FALSE;
}
/* last important step. Calc & attach ICV */
_mka_calc_and_attach_icv(link_rec->mka_ctrl.mka_info, &out_buf);
return BCM_ERR_OK == dpoe_sec_send_eapol(link_rec->device, &link_rec->key, frame, bcmolt_buf_get_used(&out_buf));
}
/* send keep-alive frame every 2 seconds */
static bcmos_bool _mka_send_keepalive_frame(dpoe_sec_link_rec *link_rec)
{
bcmolt_buf out_buf;
uint8_t frame[MKA_PDU_MAX_FRAME_LEN];
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
bcmolt_buf_init(&out_buf, MKA_PDU_MAX_FRAME_LEN, frame, BCMOLT_BUF_ENDIAN_FIXED);
if (!_mka_build_eapol_header(link_rec, &out_buf, MKA_OLT_INIT_FRAME_LEN + MKA_ONE_PEER_LEN) ||
!_mka_build_basic_parameter_set(link_rec, &out_buf) ||
!_mka_build_peer_list(
&out_buf,
MKPDU_PARAM_SET_LIVE_PEER_LIST,
link_rec->mka_ctrl.mka_info->link_memeber_id,
link_rec->mka_ctrl.mka_info->link_msg_num) ||
!_mka_build_empty_peer_list(&out_buf, MKPDU_PARAM_SET_POTENTIAL_PEER_LIST) ||
!_mka_build_icv_indicator(&out_buf))
{
return BCMOS_FALSE;
}
/* last important step. Calc & attach ICV */
_mka_calc_and_attach_icv(link_rec->mka_ctrl.mka_info, &out_buf);
return BCM_ERR_OK == dpoe_sec_send_eapol(link_rec->device, &link_rec->key, frame, bcmolt_buf_get_used(&out_buf));
}
/* Entry point for MKA process. It is called to start the MKA exchange with the ONU that owns the specified link. */
bcmos_errno mka_start(dpoe_sec_link_rec *link_rec)
{
bcmos_errno rc;
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
/* initialize link MKA info */
_mka_init_link_info(link_rec);
/* send initial frame (basic param + empty live & potential peer list */
if (_mka_send_init_frame(link_rec))
{
rc = BCM_ERR_OK;
}
else
{
rc = BCM_ERR_COMM_FAIL;
}
return rc;
}
/* Calculate the ICV over the message and compare with the ICV in the message. */
static bcmos_errno _mka_compare_icv(mka_link_info *info, const mka_event *msg, uint8_t *rx_icv)
{
uint16_t len = 0;
uint8_t icv[MKA_ICV_LEN];
/* Parameter checks. */
BUG_ON(info == NULL);
BUG_ON(msg == NULL);
/* If the ICV pointer is NULL, then there was no ICV in the received MKA packet. Just return an error. */
if (rx_icv == NULL)
{
return BCM_ERR_PARM;
}
/* Calculate the length of the buffer over which to calculate the ICV. */
len = (EAPOL_MSG_HDR_SIZE + msg->msg->eapol_length) - MKA_ICV_LEN;
_mka_calc_icv(icv, info->ick, msg->buf->start, len);
if (memcmp(icv, rx_icv, sizeof(icv)) != 0)
{
return BCM_ERR_ONU_ERR_RESP;
}
return BCM_ERR_OK;
}
/* Parse the Basic Parameter Set in MKA PDUs */
static bcmos_errno _mka_parse_basic_param_set(
dpoe_sec_link_rec *link_rec,
bcmolt_buf *buf,
uint16_t *bytes_read,
uint16_t body_len,
uint8_t *sci,
uint8_t *member_id,
uint32_t *msg_num)
{
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(buf == NULL);
BUG_ON(bytes_read == NULL);
/* Read the ONU SCI */
if (!bcmolt_buf_read(buf, sci, MKA_SCI_LEN))
{
return BCM_ERR_PARSE;
}
/* Read the Member ID */
if (!bcmolt_buf_read(buf, member_id, MKA_MI_LEN))
{
return BCM_ERR_PARSE;
}
/* Read the ONU message number. */
if (!bcmolt_buf_read_u32_be(buf, msg_num))
{
return BCM_ERR_PARSE;
}
/* The SCI, MI, and MN were read from the packet. */
*bytes_read = MKA_SCI_LEN + MKA_MI_LEN + MKA_MN_LEN;
/* Skip the rest */
if (!bcmolt_buf_skip(buf, body_len - *bytes_read))
{
return BCM_ERR_PARSE;
}
return BCM_ERR_OK;
}
static bcmos_errno _mka_waiting_initial_response_data_ind(mka_event *msg, dpoe_sec_link_rec *link_rec)
{
bcmos_errno rc = BCM_ERR_PARSE;
uint8_t *icv_ptr;
uint8_t param_set_type;
uint16_t body_len;
bcmos_bool basic_param_set_exist = BCMOS_FALSE;
bcmos_bool live_peer_list_exist = BCMOS_FALSE;
bcmos_bool potential_peer_list_exist = BCMOS_FALSE;
uint16_t bytes_read;
/* Validate the msg pointer. */
BUG_ON(msg->buf == NULL);
BUG_ON(msg->msg == NULL);
/* mark ICV field offset */
icv_ptr = msg->buf->curr + (msg->msg->eapol_length - MKA_ICV_LEN);
/* Validate the Integrity Check Value before continuing to process the packet. */
rc = _mka_compare_icv(link_rec->mka_ctrl.mka_info, msg, icv_ptr);
BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
while (bcmolt_buf_get_remaining_size(msg->buf) > 0)
{
/* Don't parse past the ICV Parameter Set */
if (bcmolt_buf_snap_get(msg->buf) >= icv_ptr)
{
break;
}
/* Read what should be the Basic Parameter Set header. */
if (!bcmolt_buf_read_u8(msg->buf, &param_set_type) ||
!bcmolt_buf_skip(msg->buf, 1) ||
!bcmolt_buf_read_u16_be(msg->buf, &body_len))
{
break;
}
/* The body length is really 12-bits. */
body_len &= 0x0fff;
if (param_set_type == MKPDU_PARAM_SET_LIVE_PEER_LIST)
{
/* need to find out whether it is Basic parameter set OR Live Peer List */
if (body_len >= MKA_BASIC_PARAM_SET_LEN)
{
rc = _mka_parse_basic_param_set(
link_rec,
msg->buf,
&bytes_read,
body_len,
link_rec->mka_ctrl.mka_info->onu_sci,
link_rec->mka_ctrl.mka_info->link_memeber_id,
&link_rec->mka_ctrl.mka_info->link_msg_num);
if (rc != BCM_ERR_OK)
{
DPOE_SEC_LINK_LOG(ERROR, link_rec, "parse basic param set failed!\n");
break;
}
/* Mark that the Basic Parameter Set was found. */
basic_param_set_exist = BCMOS_TRUE;
}
else
{
/* Live Peer List. skip */
if (!bcmolt_buf_skip(msg->buf, body_len))
{
DPOE_SEC_LINK_LOG(ERROR, link_rec, "parse live peer list failed!\n");
rc = BCM_ERR_PARSE;
break;
}
/* Mark that the Live Peer List Parameter Set was found. */
live_peer_list_exist = BCMOS_TRUE;
}
}
else if (param_set_type == MKPDU_PARAM_SET_POTENTIAL_PEER_LIST)
{
uint8_t mi[MKA_MI_LEN];
if (!bcmolt_buf_read(msg->buf, mi, sizeof(mi)))
{
DPOE_SEC_LINK_LOG(ERROR, link_rec, "parse potential peer list failed!\n");
rc = BCM_ERR_PARSE;
break;
}
/* Verify that the OLT member ID in the Potential Peer List Parameter Set matches the expected OLT member
ID. */
if (memcmp(mi, link_rec->mka_ctrl.mka_info->olt_member_id, MKA_MI_LEN) != 0)
{
DPOE_SEC_LINK_LOG(ERROR, link_rec, "wrong member ID!\n");
rc = BCM_ERR_ONU_ERR_RESP;
break;
}
/* Skip the remaining bytes. */
if (!bcmolt_buf_skip(msg->buf, body_len - MKA_MI_LEN))
{
DPOE_SEC_LINK_LOG(ERROR, link_rec, "buffer overflow!\n");
rc = BCM_ERR_PARSE;
break;
}
/* Mark that the Potential Peer List Parameter Set was found. */
potential_peer_list_exist = BCMOS_TRUE;
}
else
{
/* Skip other Parameter Set data. */
if (!bcmolt_buf_skip(msg->buf, body_len))
{
DPOE_SEC_LINK_LOG(ERROR, link_rec, "buffer overflow!\n");
rc = BCM_ERR_PARSE;
break;
}
}
}
if ((rc == BCM_ERR_OK) &&
basic_param_set_exist &&
live_peer_list_exist &&
potential_peer_list_exist &&
(link_rec->mka_ctrl.mka_info->peer_state != MKA_PEER_STATE_LIVE))
{
/* proceed to next state */
link_rec->mka_ctrl.mka_info->peer_state = MKA_PEER_STATE_LIVE;
}
else
{
rc = BCM_ERR_ONU_ERR_RESP;
}
return rc;
}
/* Handler for initial response from the ONU */
static bcmos_errno _mka_waiting_initial_response(mka_event *msg, dpoe_sec_link_rec *link_rec)
{
bcmos_errno rc = BCM_ERR_OK;
/* Parameter checks. */
BUG_ON(msg == NULL);
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
switch (msg->type)
{
case MKA_MSG_TIMEOUT:
/* retry 3 times */
if (link_rec->mka_ctrl.mka_info->retry_cnt < MKA_INIT_RETRY_CNT)
{
link_rec->mka_ctrl.mka_info->retry_cnt++;
(void)_mka_send_init_frame(link_rec);
}
else
{
_mka_clear_link_info(link_rec);
rc = BCM_ERR_TIMEOUT;
}
break;
case MKA_MSG_DATA_IND:
rc = _mka_waiting_initial_response_data_ind(msg, link_rec);
break;
default:
rc = BCM_ERR_ONU_ERR_RESP;
break;
}
return rc;
}
static bcmos_errno _mka_initial_sak_sent_data_ind(mka_event *msg, dpoe_sec_link_rec *link_rec)
{
bcmos_errno rc = BCM_ERR_PARSE;
uint8_t *icv_ptr;
uint8_t param_set_type;
uint8_t ks_priority;
uint16_t body_len;
bcmos_bool basic_param_ok = BCMOS_FALSE;
bcmos_bool live_peer_list_ok = BCMOS_FALSE;
bcmos_bool potential_peer_list_ok = BCMOS_FALSE;
bcmos_bool sak_use_param_ok = BCMOS_FALSE;
uint16_t bytes_read;
/* Validate the msg pointer. */
BUG_ON(msg->msg == NULL);
BUG_ON(msg->buf == NULL);
/* mark ICV field offset */
icv_ptr = msg->buf->curr + (msg->msg->eapol_length - MKA_ICV_LEN);
/* Validate the Integrity Check Value before continuing to process the packet. */
rc = _mka_compare_icv(link_rec->mka_ctrl.mka_info, msg, icv_ptr);
BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
while (bcmolt_buf_get_remaining_size(msg->buf) > 0)
{
/* Don't parse past the ICV Parameter Set */
if (bcmolt_buf_snap_get(msg->buf) >= icv_ptr)
{
break;
}
/* Read what should be the Basic Parameter Set header. */
if (!bcmolt_buf_read_u8(msg->buf, &param_set_type) ||
!bcmolt_buf_read_u8(msg->buf, &ks_priority) ||
!bcmolt_buf_read_u16_be(msg->buf, &body_len))
{
rc = BCM_ERR_PARSE;
break;
}
/* The body length is really 12-bits. */
body_len &= 0x0fff;
if (param_set_type == MKPDU_PARAM_SET_LIVE_PEER_LIST)
{
/* need to find out whether it is Basic parameter set OR Live Peer List */
if (body_len >= MKA_BASIC_PARAM_SET_LEN)
{
/* in our environment, ONU can not have more than one live peer. So, just seeing param set body len
should be good enough. */
uint32_t link_mn;
uint8_t sci[MKA_SCI_LEN];
uint8_t mi[MKA_MI_LEN];
rc = _mka_parse_basic_param_set(link_rec, msg->buf, &bytes_read, body_len, sci, mi, &link_mn);
if (rc != BCM_ERR_OK)
{
break;
}
/* Verify that ONU SCI is what's expected. */
if (memcmp(link_rec->mka_ctrl.mka_info->onu_sci, sci, MKA_SCI_LEN) != 0)
{
rc = BCM_ERR_ONU_ERR_RESP;
break;
}
/* Verify that ONU MI is what's expected. */
if (memcmp(link_rec->mka_ctrl.mka_info->link_memeber_id, mi, MKA_MI_LEN) != 0)
{
rc = BCM_ERR_ONU_ERR_RESP;
break;
}
/* Verify that the MN is what's expected. */
if (link_rec->mka_ctrl.mka_info->link_msg_num < link_mn)
{
/* correct MN! */
link_rec->mka_ctrl.mka_info->link_msg_num = link_mn;
basic_param_ok = BCMOS_TRUE;
}
}
else
{
/* Live Peer List. skip */
if (!bcmolt_buf_skip(msg->buf, body_len))
{
rc = BCM_ERR_PARSE;
break;
}
/* Mark that the Live Peer List Parameter Set was found. */
live_peer_list_ok = BCMOS_TRUE;
}
}
else if (param_set_type == MKPDU_PARAM_SET_POTENTIAL_PEER_LIST)
{
/* Skip Potential Peer List Parameter Set. */
if (!bcmolt_buf_skip(msg->buf, body_len))
{
rc = BCM_ERR_PARSE;
break;
}
/* Mark that the Potential Peer List Parameter Set was found. */
potential_peer_list_ok = BCMOS_TRUE;
}
else if (param_set_type == MKPDU_PARAM_SET_MACSEC_SAK_USE)
{
/* check only Latest key tx & rx bits (bit 4 & 5) */
if ((ks_priority & 0x30) == 0x30)
{
sak_use_param_ok = BCMOS_TRUE;
}
/* Skip the MKA SAK Use Parameter Set. */
if (!bcmolt_buf_skip(msg->buf, body_len))
{
rc = BCM_ERR_PARSE;
break;
}
}
else
{
/* Skip all other Parameter Set data. */
if (!bcmolt_buf_skip(msg->buf, body_len))
{
rc = BCM_ERR_PARSE;
break;
}
}
}
/* Everything looks good so far? */
if ((rc == BCM_ERR_OK) &&
basic_param_ok &&
live_peer_list_ok &&
potential_peer_list_ok &&
sak_use_param_ok)
{
DPOE_SEC_LINK_LOG(DEBUG, link_rec, "Got good MKA SAK response\n");
}
return rc;
}
/* Response frame handler for the first SAK from the OLT */
static bcmos_errno _mka_initial_sak_sent(mka_event *msg, dpoe_sec_link_rec *link_rec)
{
bcmos_errno rc = BCM_ERR_OK;
/* Parameter checks. */
BUG_ON(msg == NULL);
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
switch (msg->type)
{
case MKA_MSG_TIMEOUT:
_mka_clear_link_info(link_rec);
rc = BCM_ERR_TIMEOUT;
break;
case MKA_MSG_DATA_IND:
rc = _mka_initial_sak_sent_data_ind(msg, link_rec);
break;
default:
rc = BCM_ERR_ONU_ERR_RESP;
break;
}
return rc;
}
static bcmos_errno _mka_sak_done_data_ind(mka_event *msg, dpoe_sec_link_rec *link_rec)
{
bcmos_errno rc = BCM_ERR_PARSE;
uint8_t *icv_ptr;
uint8_t param_set_type;
uint16_t body_len;
uint8_t ks_priority;
uint16_t bytes_read;
/* Validate the msg pointer. */
BUG_ON(msg->msg == NULL);
BUG_ON(msg->buf == NULL);
/* mark ICV field offset */
icv_ptr = msg->buf->curr + (msg->msg->eapol_length - MKA_ICV_LEN);
/* Validate the Integrity Check Value before continuing to process the packet. */
rc = _mka_compare_icv(link_rec->mka_ctrl.mka_info, msg, icv_ptr);
BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
while (bcmolt_buf_get_remaining_size(msg->buf) > 0)
{
/* Don't parse past the ICV Parameter Set */
if (bcmolt_buf_snap_get(msg->buf) >= icv_ptr)
{
break;
}
/* Read what should be the Basic Parameter Set header. */
if (!bcmolt_buf_read_u8(msg->buf, &param_set_type) ||
!bcmolt_buf_read_u8(msg->buf, &ks_priority) ||
!bcmolt_buf_read_u16_be(msg->buf, &body_len))
{
rc = BCM_ERR_PARSE;
break;
}
/* The body length is really 12-bits. */
body_len &= 0x0fff;
if (param_set_type == MKPDU_PARAM_SET_LIVE_PEER_LIST)
{
/* need to find out whether it is Basic parameter set OR Live Peer List */
if (body_len >= MKA_BASIC_PARAM_SET_LEN)
{
/* in our environment, ONU can not have more than one live peer. So, just seeing param set body len
should be good enough. */
uint32_t link_mn;
uint8_t sci[MKA_SCI_LEN];
uint8_t mi[MKA_MI_LEN];
rc = _mka_parse_basic_param_set(link_rec, msg->buf, &bytes_read, body_len, sci, mi, &link_mn);
if (rc != BCM_ERR_OK)
{
break;
}
/* Verify that ONU SCI is what's expected. */
if (memcmp(link_rec->mka_ctrl.mka_info->onu_sci, sci, MKA_SCI_LEN) != 0)
{
rc = BCM_ERR_ONU_ERR_RESP;
break;
}
/* Verify that ONU MI is what's expected. */
if (memcmp(link_rec->mka_ctrl.mka_info->link_memeber_id, mi, MKA_MI_LEN) != 0)
{
rc = BCM_ERR_ONU_ERR_RESP;
break;
}
if (link_rec->mka_ctrl.mka_info->link_msg_num < link_mn)
{
/* correct MN! */
link_rec->mka_ctrl.mka_info->link_msg_num = link_mn;
}
}
else
{
/* Live Peer List. skip */
if (!bcmolt_buf_skip(msg->buf, body_len))
{
rc = BCM_ERR_PARSE;
break;
}
}
}
else if (param_set_type == MKPDU_PARAM_SET_MACSEC_SAK_USE)
{
uint32_t llpn;
/* Skip to the LLPN. */
if (!bcmolt_buf_skip(msg->buf, MKA_MI_LEN + MKA_KN_LEN))
{
rc = BCM_ERR_PARSE;
break;
}
/* read Latest Lowest Acceptable PN */
if (!bcmolt_buf_read_u32_be(msg->buf, &llpn))
{
rc = BCM_ERR_PARSE;
break;
}
if (llpn > MKA_MAX_ACCEPTABLE_PN)
{
/* Set flag to indicate SAK refresh is needed. */
link_rec->mka_ctrl.mka_info->sak_refresh_needed = BCMOS_TRUE;
}
/* check only Latest key tx & rx bits (bit 4 & 5) */
if ((ks_priority & 0x30) != 0x30)
{
/* Wrong LK Flag */ /* TODO: why isn't this an error? */
}
/* The MI, KN, and LLPN were read. */
bytes_read = body_len - (MKA_MI_LEN + MKA_KN_LEN + sizeof(uint32_t));
/* Skip the rest */
if (!bcmolt_buf_skip(msg->buf, bytes_read))
{
rc = BCM_ERR_PARSE;
break;
}
}
else
{
/* Skip all other Parameter Set data. */
if (!bcmolt_buf_skip(msg->buf, body_len))
{
rc = BCM_ERR_PARSE;
break;
}
}
}
/* refresh retry count. This is a Critical counter */
link_rec->mka_ctrl.mka_info->retry_cnt = 0;
return rc;
}
/* MKA frame handler for stable state */
static bcmos_errno _mka_sak_done(mka_event *msg, dpoe_sec_link_rec *link_rec)
{
bcmos_errno rc = BCM_ERR_OK;
/* Parameter checks. */
BUG_ON(msg == NULL);
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
switch (msg->type)
{
case MKA_MSG_TIMEOUT:
/* send keepalive frame at every 2 seconds */
(void)_mka_send_keepalive_frame(link_rec);
break;
case MKA_MSG_DATA_IND:
rc = _mka_sak_done_data_ind(msg, link_rec);
break;
default:
rc = BCM_ERR_ONU_ERR_RESP;
break;
}
return rc;
}
/* Validate the MKA packet. */
static bcmos_bool _mka_validate_pkt(bcmolt_u8_list_u16 mka_msg, eapol_msg_hdr *eapol)
{
/* Parameter checks. */
BUG_ON(eapol == NULL);
/* This had better be an MKA packet. */
if (eapol->eapol_packet_type != EAPOL_TYPE_MKA)
{
return BCMOS_FALSE;
}
/* The EAPOL length plus the size of the EapolMsgHdr must equal the length of the received packet. */
if (mka_msg.len != (eapol->eapol_length + EAPOL_MSG_HDR_SIZE))
{
return BCMOS_FALSE;
}
/* The EAPOL length must be greater than the minimum supported length, Ethernet + EAPOL header length. */
if (eapol->eapol_length < MKA_PDU_MIN_LENGTH)
{
return BCMOS_FALSE;
}
/* The EAPOL length must be a multiple of four. */
if ((eapol->eapol_length % 4) != 0)
{
return BCMOS_FALSE;
}
return BCMOS_TRUE;
}
/* Wrapper into the MKA code that sends an SAK Confirm response to the ONU. */
bcmos_errno mka_send_sak_confirm(dpoe_sec_link_rec *link_rec)
{
bcmos_errno rc = BCM_ERR_OK;
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(link_rec->mka_ctrl.mka_info == NULL);
/* proceed to next state */
if (_mka_send_sak_confirm_frame(link_rec))
{
/* Everything looks good so far. Update AN and Key Number */
link_rec->mka_ctrl.mka_info->key_number += 1;
link_rec->mka_ctrl.mka_info->association_number += 1;
if (link_rec->mka_ctrl.mka_info->association_number == 4)
{
/* The association number is concatenated with the OLT SCI to identify a secure association between OLT and
ONU. The AN value starts at zero and cycles through values 0 - 3 as a new SAK is distributed to the ONU.
*/
link_rec->mka_ctrl.mka_info->association_number = 0; /* AN = 0 ~ 3 */
}
link_rec->mka_ctrl.mka_info->state = MKA_STATE_MKA_DONE;
link_rec->mka_ctrl.mka_info->refresh_cnt += 1;
link_rec->mka_ctrl.mka_info->retry_cnt = 0;
/* start keepalive timer (2 second) */
bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->tx_timer, MKA_HELLO_TIME * 1000);
}
else
{
rc = BCM_ERR_COMM_FAIL;
}
return rc;
}
/* Wrapper into the MKA code that handles MKA PDUs from the ONU. */
bcmos_errno mka_process_packet(dpoe_sec_link_rec *link_rec, bcmolt_u8_list_u16 rx_frame, mka_op_type op_type)
{
bcmos_errno rc = BCM_ERR_PARM;
mka_event mka_msg;
bcmolt_buf buf;
eapol_msg_hdr eapol;
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON(rx_frame.val == NULL);
BUG_ON((op_type <= MKA_OP__INVALID) || (op_type > MKA_OP_KEEP_ALIVE));
bcmolt_buf_init(&buf, rx_frame.len, rx_frame.val, BCMOLT_BUF_ENDIAN_FIXED);
if (!dpoe_sec_eapol_unpack(&buf, &eapol))
{
DPOE_SEC_LINK_LOG(ERROR, link_rec, "failed to unpack EAPOL header!\n");
return BCM_ERR_PARSE;
}
/* Validate the MKA packet length before processing. */
if (!_mka_validate_pkt(rx_frame, &eapol))
{
DPOE_SEC_LINK_LOG(ERROR, link_rec, "Invalid MKA packet!\n");
return BCM_ERR_PARSE;
}
/* Pass the packet to the handler. */
mka_msg.type = MKA_MSG_DATA_IND;
mka_msg.msg = &eapol;
mka_msg.buf = &buf;
/* Process the MKA packet. */
switch (op_type)
{
case MKA_OP_START_RSP:
rc = _mka_waiting_initial_response(&mka_msg, link_rec);
break;
case MKA_OP_SAK_RSP:
rc = _mka_initial_sak_sent(&mka_msg, link_rec);
break;
case MKA_OP_KEEP_ALIVE:
rc = _mka_sak_done(&mka_msg, link_rec);
break;
default:
break;
}
return rc;
}
/* Wrapper into the MKA code that handles MKA timeouts. */
bcmos_errno mka_process_timeout(dpoe_sec_link_rec *link_rec, mka_op_type op_type)
{
bcmos_errno rc = BCM_ERR_TIMEOUT;
mka_event mka_msg;
/* Parameter checks. */
BUG_ON(link_rec == NULL);
BUG_ON((op_type < MKA_OP_START_TIMEOUT) || (op_type >= MKA_OP__COUNT));
/* Pass the packet to the handler. */
mka_msg.type = MKA_MSG_TIMEOUT;
/* Process the MKA packet. */
switch (op_type)
{
case MKA_OP_START_TIMEOUT:
rc = _mka_waiting_initial_response(&mka_msg, link_rec);
break;
case MKA_OP_SAK_TIMEOUT:
rc = _mka_initial_sak_sent(&mka_msg, link_rec);
break;
case MKA_OP_SEND_KEEP_ALIVE:
rc = _mka_sak_done(&mka_msg, link_rec);
break;
default:
break;
}
return rc;
}