| /* |
| <: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, ¶m_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, ¶m_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, ¶m_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; |
| } |
| |