| /* |
| <: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. |
| |
| :> |
| */ |
| |
| #include "dpoe_sec_fsm.h" |
| #include "dpoe_sec_mka_fsm.h" |
| #include "bcmolt_user_appl_epon_oam.h" |
| #include "dpoe_sec_util.h" |
| |
| typedef enum dpoe_sec_mka_fsm_event_type |
| { |
| DPOE_SEC_MKA_FSM_EVENT__INVALID = -1, /**< permits event validation */ |
| |
| DPOE_SEC_MKA_FSM_EVENT_INIT = 0, |
| DPOE_SEC_MKA_FSM_EVENT_RX_OAM , |
| DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL , |
| DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT , |
| DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT , |
| DPOE_SEC_MKA_FSM_EVENT_STOP , |
| |
| DPOE_SEC_MKA_FSM_EVENT__COUNT |
| } dpoe_sec_mka_fsm_event_type; |
| |
| /* MKA FSM Event contents. This is a union of information required by the MKA FSM. This data type permits a common |
| * function prototype for the state machine function table. */ |
| typedef struct dpoe_sec_mka_fsm_event |
| { |
| dpoe_sec_mka_fsm_event_type type; /**< Type of event*/ |
| union |
| { |
| bcmolt_u8_list_u16 rx_frame; |
| } data; |
| } dpoe_sec_mka_fsm_event; |
| |
| typedef bcmos_errno (*f_dpoe_sec_mka_fsm)(dpoe_sec_link_rec *mka_ctrl, dpoe_sec_mka_fsm_event *evt); |
| |
| static const char *dpoe_sec_mka_fsm_event_name[DPOE_SEC_MKA_FSM_EVENT__COUNT] = |
| { |
| [DPOE_SEC_MKA_FSM_EVENT_INIT] = "Initialization", |
| [DPOE_SEC_MKA_FSM_EVENT_RX_OAM] = "OAM Response", |
| [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = "RX EAPOL", |
| [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = "FSM Timeout", |
| [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = "MKA Timeout", |
| [DPOE_SEC_MKA_FSM_EVENT_STOP] = "Stop" |
| }; |
| |
| static const char *dpoe_sec_mka_fsm_state_name[DPOE_SEC_MKA_FSM_STATE__COUNT] = |
| { |
| [DPOE_SEC_MKA_FSM_STATE_NULL] = "Null", |
| [DPOE_SEC_MKA_FSM_STATE_SET_ENCRYPTION] = "Set Encryption", |
| [DPOE_SEC_MKA_FSM_STATE_START_MKA ] = "Start MKA", |
| [DPOE_SEC_MKA_FSM_STATE_SEND_SAK] = "Send SAK", |
| [DPOE_SEC_MKA_FSM_STATE_OPERATIONAL] = "Operational", |
| [DPOE_SEC_MKA_FSM_STATE_SEND_NEW_SAK] = "Send New SAK" |
| }; |
| |
| static f_dpoe_sec_mka_cb _mka_done_cb; |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_exec(dpoe_sec_link_rec *link, dpoe_sec_mka_fsm_event *evt); |
| |
| static bcmos_bool _dpoe_sec_mka_fsm_event_type_is_valid(dpoe_sec_mka_fsm_event_type type) |
| { |
| return (type > DPOE_SEC_MKA_FSM_EVENT__INVALID) && (type < DPOE_SEC_MKA_FSM_EVENT__COUNT); |
| } |
| |
| static bcmos_bool _dpoe_sec_mka_fsm_state_is_valid(dpoe_sec_mka_fsm_state state) |
| { |
| return (state > DPOE_SEC_MKA_FSM_STATE__INVALID) && (state < DPOE_SEC_MKA_FSM_STATE__COUNT); |
| } |
| |
| static const char *_dpoe_sec_mka_fsm_event_name(dpoe_sec_mka_fsm_event_type type) |
| { |
| if (_dpoe_sec_mka_fsm_event_type_is_valid(type)) |
| { |
| return dpoe_sec_mka_fsm_event_name[type]; |
| } |
| else |
| { |
| return "Unknown Event"; |
| } |
| } |
| |
| static const char *_dpoe_sec_mka_fsm_state_name(dpoe_sec_mka_fsm_state state) |
| { |
| if (_dpoe_sec_mka_fsm_state_is_valid(state)) |
| { |
| return dpoe_sec_mka_fsm_state_name[state]; |
| } |
| else |
| { |
| return "Unknown State"; |
| } |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_error(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| /* TODO: log unexpected event */ |
| |
| return BCM_ERR_OK; |
| } |
| |
| static void _dpoe_sec_mka_fsm_cleanup(dpoe_sec_mka_fsm_ctrl *mka_ctrl) |
| { |
| /* Parameter checks. */ |
| BUG_ON(mka_ctrl == NULL); |
| |
| if (mka_ctrl->mka_info != NULL) |
| { |
| /* Clear all MKA information before freeing. */ |
| memset(mka_ctrl->mka_info, 0, sizeof(*mka_ctrl->mka_info)); |
| bcmos_free(mka_ctrl->mka_info); |
| mka_ctrl->mka_info = NULL; |
| } |
| |
| if (mka_ctrl->mka_fsm_info != NULL) |
| { |
| /* Clear all MKA FSM information before freeing. */ |
| memset(mka_ctrl->mka_fsm_info, 0, sizeof(*mka_ctrl->mka_fsm_info)); |
| bcmos_free(mka_ctrl->mka_fsm_info); |
| mka_ctrl->mka_fsm_info = NULL; |
| } |
| |
| mka_ctrl->mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_NULL; |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_stop(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| /* The stop function is called each time the Link deregisters regardless of whether the MKA FSM is running or not. |
| Handle the case where it is not running. */ |
| if (link_rec->mka_ctrl.mka_fsm_info == NULL) |
| { |
| return BCM_ERR_OK; |
| } |
| |
| /* Stop any timers that may be running. */ |
| bcmos_timer_stop(&link_rec->mka_ctrl.mka_fsm_info->rx_timer); |
| bcmos_timer_stop(&link_rec->mka_ctrl.mka_fsm_info->tx_timer); |
| |
| /* Change to the NULL state */ |
| link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_NULL; |
| |
| /* Free dynamic MKA resources associated with the link. */ |
| _dpoe_sec_mka_fsm_cleanup(&link_rec->mka_ctrl); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_timer_rc _dpoe_sec_mka_fsm_rx_timer_expire(bcmos_timer *timer, long data) |
| { |
| dpoe_sec_link_rec *link_rec = (dpoe_sec_link_rec*)data; |
| dpoe_sec_mka_fsm_event evnt = {}; |
| |
| /* Parameter Checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| |
| /* Set up the event */ |
| evnt.type = DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT; |
| |
| /* Execute the state machine */ |
| _dpoe_sec_mka_fsm_exec(link_rec, &evnt); |
| |
| return BCMOS_TIMER_OK; |
| } |
| |
| static bcmos_timer_rc _dpoe_sec_mka_fsm_tx_timer_expire(bcmos_timer *timer, long data) |
| { |
| dpoe_sec_link_rec *link_rec = (dpoe_sec_link_rec*)data; |
| dpoe_sec_mka_fsm_event evnt = {}; |
| |
| /* Parameter Checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| |
| /* Set up the event */ |
| evnt.type = DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT; |
| |
| /* Execute the state machine */ |
| _dpoe_sec_mka_fsm_exec(link_rec, &evnt); |
| |
| return BCMOS_TIMER_OK; |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_initialize(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| bcmos_errno err; |
| bcmos_timer_parm rx_tp = |
| { |
| .name = "dpoe_sec_mka_fsm_timer", |
| .owner = BCMOS_MODULE_ID_USER_APPL_DPOE_SEC + link_rec->device, |
| .periodic = BCMOS_FALSE, |
| .handler = _dpoe_sec_mka_fsm_rx_timer_expire, |
| .data = (long)link_rec, |
| .flags = BCMOS_TIMER_PARM_FLAGS_NON_URGENT |
| }; |
| bcmos_timer_parm tx_tp = |
| { |
| .name = "dpoe_sec_mka_mka_timer", |
| .owner = BCMOS_MODULE_ID_USER_APPL_DPOE_SEC + link_rec->device, |
| .periodic = BCMOS_TRUE, |
| .handler = _dpoe_sec_mka_fsm_tx_timer_expire, |
| .data = (long)link_rec, |
| .flags = BCMOS_TIMER_PARM_FLAGS_NON_URGENT |
| }; |
| |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(evt == NULL); |
| |
| /* Allocate the MKA information structure. */ |
| link_rec->mka_ctrl.mka_info = bcmos_calloc(sizeof(*link_rec->mka_ctrl.mka_info)); |
| if (link_rec->mka_ctrl.mka_info == NULL) |
| { |
| return BCM_ERR_NOMEM; |
| } |
| |
| /* Allocate the MKA FSM information structure. */ |
| link_rec->mka_ctrl.mka_fsm_info = bcmos_calloc(sizeof(*link_rec->mka_ctrl.mka_fsm_info)); |
| if (link_rec->mka_ctrl.mka_fsm_info == NULL) |
| { |
| _dpoe_sec_mka_fsm_cleanup(&link_rec->mka_ctrl); |
| return BCM_ERR_NOMEM; |
| } |
| |
| err = bcmos_timer_create(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, &rx_tp); |
| if (BCM_ERR_OK != err) |
| { |
| _dpoe_sec_mka_fsm_cleanup(&link_rec->mka_ctrl); |
| return err; |
| } |
| |
| err = bcmos_timer_create(&link_rec->mka_ctrl.mka_fsm_info->tx_timer, &tx_tp); |
| if (BCM_ERR_OK != err) |
| { |
| _dpoe_sec_mka_fsm_cleanup(&link_rec->mka_ctrl); |
| return err; |
| } |
| |
| /* Change to the SET_LINK_ENCRYPTION_MODE state */ |
| link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_SET_ENCRYPTION; |
| bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, DPOE_SEC_OAM_TIMEOUT); |
| |
| return epon_oam_dpoe_set_encryption( |
| link_rec->device, |
| link_rec->key.epon_ni, |
| &link_rec->key.mac_address, |
| link_rec->cfg.enc_mode, |
| 0); |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_send_success(dpoe_sec_link_rec *link_rec) |
| { |
| /* Parameter checks. */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(_mka_done_cb == NULL); |
| |
| _mka_done_cb(link_rec, BCM_ERR_OK); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_send_failure(dpoe_sec_link_rec *link_rec, bcmos_errno reason) |
| { |
| dpoe_sec_mka_fsm_event event = { .type = DPOE_SEC_MKA_FSM_EVENT_STOP }; |
| |
| /* Parameter checks. */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(_mka_done_cb == NULL); |
| |
| DPOE_SEC_LINK_LOG(ERROR, link_rec, "MKA stopping!\n"); |
| |
| /* Notify the DPoE Security FSM of the MKA error. */ |
| _mka_done_cb(link_rec, reason); |
| |
| /* Stop the FSM due to the error. */ |
| return _dpoe_sec_mka_fsm_stop(link_rec, &event); |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_rx_timeout(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| DPOE_SEC_LINK_LOG(ERROR, link_rec, "TIMEOUT error!\n"); |
| |
| /* Notify the DPoE Security FSM of the MKA error. */ |
| return _dpoe_sec_mka_fsm_send_failure(link_rec, BCM_ERR_TIMEOUT); |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_rx_oam(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| bcmolt_epon_oam_ethernet_frame *oam_frame; |
| bcmolt_epon_oam_dpoe_vendor_extended_base *dpoe; |
| |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| BUG_ON(evt->data.rx_frame.val == NULL); |
| |
| oam_frame = epon_oam_unpack(link_rec->device, evt->data.rx_frame.len, evt->data.rx_frame.val); |
| if (NULL == oam_frame) |
| { |
| DPOE_SEC_LINK_LOG(ERROR, link_rec, "OAM unpack failed!\n"); |
| return BCM_ERR_OK; |
| } |
| |
| if (!epon_oam_is_dpoe(oam_frame)) |
| { |
| bcmos_free(oam_frame); |
| return BCM_ERR_OK; |
| } |
| |
| dpoe = &oam_frame->protocols[0].u.slow_protocol.value.u.oam.content.u.organization_specific.value.u.dpoe.value; |
| |
| if (epon_oam_is_dpoe_encrypt_response(dpoe)) |
| { |
| if ((dpoe->u.set_response.vars[0].u.extended_attribute.attribute.width == 0x80) && |
| (dpoe->u.set_response.vars[1].u.extended_attribute.attribute.width == 0x80)) |
| { |
| bcmos_timer_stop(&link_rec->mka_ctrl.mka_fsm_info->rx_timer); |
| |
| /* Change to the START_MKA state */ |
| link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_START_MKA; |
| |
| /* Send the initial MKA packet to the ONU for the specified link. */ |
| rc = mka_start(link_rec); |
| } |
| else |
| { |
| DPOE_SEC_LINK_LOG(ERROR, link_rec, "ONU rejected encryption: [0] = 0x%x, [1] = 0x%x\n", |
| dpoe->u.set_response.vars[0].u.extended_attribute.attribute.width, |
| dpoe->u.set_response.vars[1].u.extended_attribute.attribute.width); |
| /* Notify the DPoE Security FSM of the MKA error. */ |
| rc = _dpoe_sec_mka_fsm_send_failure(link_rec, BCM_ERR_ONU_ERR_RESP); |
| } |
| } |
| else |
| { |
| /* ignore other OAM */ |
| } |
| |
| bcmos_free(oam_frame); |
| |
| return rc; |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_start_timeout(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| return mka_process_timeout(link_rec, MKA_OP_START_TIMEOUT); |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_start_rx_eapol(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| rc = mka_process_packet(link_rec, evt->data.rx_frame, MKA_OP_START_RSP); |
| if (rc == BCM_ERR_OK) |
| { |
| bcmolt_encryption_information_container key_info; |
| |
| /* Re-start the rx timer */ |
| bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000); |
| |
| /* Generate the initial SAK. */ |
| mka_generate_sak(link_rec, BCMOS_TRUE); |
| |
| key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR; |
| memcpy(key_info.u.ctr.key, link_rec->mka_ctrl.mka_info->sak, sizeof(key_info.u.ctr.key)); |
| memcpy(key_info.u.ctr.sci, link_rec->mka_ctrl.mka_info->onu_sci, sizeof(key_info.u.ctr.sci)); |
| |
| /* Install the SAK as the US encryption key for this link. */ |
| rc = bcmolt_epon_link_encryption_key_set( |
| link_rec->device, |
| &link_rec->key, |
| BCMOS_TRUE, |
| BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES, |
| (bcmolt_epon_key_choice)((link_rec->mka_ctrl.mka_info->key_number + 1) % 2), |
| &key_info); |
| if (rc == BCM_ERR_OK) |
| { |
| rc = mka_send_sak(link_rec, link_rec->mka_ctrl.mka_info->sak); |
| if (rc == BCM_ERR_OK) |
| { |
| /* Change to the SEND_SAK state */ |
| link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_SEND_SAK; |
| } |
| else |
| { |
| DPOE_SEC_LINK_LOG(ERROR, link_rec, "send SAK failed!\n"); |
| } |
| } |
| else |
| { |
| DPOE_SEC_LINK_LOG(ERROR, link_rec, "key set failed %d!\n", rc); |
| } |
| } |
| else |
| { |
| DPOE_SEC_LINK_LOG(ERROR, link_rec, "MKA process packet failed!\n"); |
| } |
| |
| if (rc != BCM_ERR_OK) |
| { |
| /* Notify the DPoE Security FSM of the MKA error. */ |
| _dpoe_sec_mka_fsm_send_failure(link_rec, rc); |
| } |
| |
| return rc; |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_sak_timeout(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| return mka_process_timeout(link_rec, MKA_OP_SAK_TIMEOUT); |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_sak_rx_eapol(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| bcmos_errno rc; |
| |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| rc = mka_process_packet(link_rec, evt->data.rx_frame, MKA_OP_SAK_RSP); |
| if (rc == BCM_ERR_OK) |
| { |
| bcmolt_encryption_information_container key_info; |
| |
| /* Re-start the rx timer */ |
| bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000); |
| |
| key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR; |
| memcpy(key_info.u.ctr.key, link_rec->mka_ctrl.mka_info->sak, sizeof(key_info.u.ctr.key)); |
| memcpy(key_info.u.ctr.sci, link_rec->ni_mac.u8, BCMOS_ETH_ALEN); |
| key_info.u.ctr.sci[6] = (link_rec->llid >> 8) & 0xff; |
| key_info.u.ctr.sci[7] = (link_rec->llid >> 0) & 0xff; |
| |
| /* Install the SAK as the US and DS encryption key for this link. */ |
| rc = bcmolt_epon_link_encryption_key_set( |
| link_rec->device, |
| &link_rec->key, |
| BCMOS_FALSE, |
| BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES, |
| (bcmolt_epon_key_choice)((link_rec->mka_ctrl.mka_info->key_number + 1) % 2), |
| &key_info); |
| if (rc == BCM_ERR_OK) |
| { |
| rc = mka_send_sak_confirm(link_rec); |
| if (rc == BCM_ERR_OK) |
| { |
| /* Notify the DPoE Security FSM of the MKA result. */ |
| rc = _dpoe_sec_mka_fsm_send_success(link_rec); |
| |
| /* Change to the OPERATIONAL state */ |
| link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_OPERATIONAL; |
| } |
| } |
| } |
| |
| if (rc != BCM_ERR_OK) |
| { |
| /* Notify the DPoE Security FSM of the MKA error. */ |
| _dpoe_sec_mka_fsm_send_failure(link_rec, rc); |
| } |
| |
| return rc; |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_send_keep_alive(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| return mka_process_timeout(link_rec, MKA_OP_SEND_KEEP_ALIVE); |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_sak_refresh(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| bcmolt_encryption_information_container key_info; |
| |
| /* Generate the new SAK. */ |
| mka_generate_sak(link_rec, BCMOS_FALSE); |
| |
| key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR; |
| memcpy(key_info.u.ctr.key, link_rec->mka_ctrl.mka_info->sak, sizeof(key_info.u.ctr.key)); |
| memcpy(key_info.u.ctr.sci, link_rec->mka_ctrl.mka_info->onu_sci, sizeof(key_info.u.ctr.sci)); |
| |
| /* Install the new SAK as the US encryption key for this link. */ |
| rc = bcmolt_epon_link_encryption_key_set( |
| link_rec->device, |
| &link_rec->key, |
| BCMOS_TRUE, |
| BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES, |
| (bcmolt_epon_key_choice)((link_rec->mka_ctrl.mka_info->key_number + 1) % 2), |
| &key_info); |
| if (rc == BCM_ERR_OK) |
| { |
| rc = mka_send_sak(link_rec, link_rec->mka_ctrl.mka_info->new_sak); |
| if (rc == BCM_ERR_OK) |
| { |
| /* Change to the SEND_SAK state */ |
| link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_SEND_NEW_SAK; |
| } |
| } |
| |
| if (BCM_ERR_OK != rc) |
| { |
| /* Notify the DPoE Security FSM of the MKA error. */ |
| _dpoe_sec_mka_fsm_send_failure(link_rec, rc); |
| } |
| |
| return rc; |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_handle_keep_alive(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| /* Pass the keep alive message to the MKA proper code. It's okay to ignore the return status. Failure to maintain |
| the MKA connection through MKA keep alive messages will tear down the MKA connection. */ |
| rc = mka_process_packet(link_rec, evt->data.rx_frame, MKA_OP_KEEP_ALIVE); |
| |
| /* Check if a SAK refresh is needed. */ |
| if (rc == BCM_ERR_OK) |
| { |
| /* Re-start the rx timer */ |
| bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000); |
| |
| if (link_rec->mka_ctrl.mka_info->sak_refresh_needed) |
| { |
| /* Start the SAK refresh process. */ |
| _dpoe_sec_mka_fsm_sak_refresh(link_rec, evt); |
| |
| /* Clear the SAK refresh flag since the SAK refresh process has been started. */ |
| link_rec->mka_ctrl.mka_info->sak_refresh_needed = BCMOS_FALSE; |
| } |
| } |
| |
| return rc; |
| } |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_new_sak_rx_eapol(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| bcmos_errno rc; |
| |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_fsm_info == NULL); |
| BUG_ON(link_rec->mka_ctrl.mka_info == NULL); |
| BUG_ON(evt == NULL); |
| |
| rc = mka_process_packet(link_rec, evt->data.rx_frame, MKA_OP_SAK_RSP); |
| if (rc == BCM_ERR_OK) |
| { |
| /* Re-start the rx timer */ |
| bcmos_timer_start(&link_rec->mka_ctrl.mka_fsm_info->rx_timer, MKA_LIFE_TIME * 1000); |
| |
| bcmolt_encryption_information_container key_info; |
| |
| key_info.format = BCMOLT_EPON_ENCRYPTION_INFORMATION_FORMAT_CTR; |
| memcpy(key_info.u.ctr.key, link_rec->mka_ctrl.mka_info->sak, sizeof(key_info.u.ctr.key)); |
| memcpy(key_info.u.ctr.sci, link_rec->ni_mac.u8, BCMOS_ETH_ALEN); |
| key_info.u.ctr.sci[6] = (link_rec->llid >> 8) & 0xff; |
| key_info.u.ctr.sci[7] = (link_rec->llid >> 0) & 0xff; |
| |
| /* Install the new SAK as the US and DS encryption key for this link. */ |
| rc = bcmolt_epon_link_encryption_key_set( |
| link_rec->device, |
| &link_rec->key, |
| BCMOS_FALSE, |
| BCMOLT_EPON_ENCRYPTION_MODE_EPON_ZERO_OVERHEAD_AES, |
| (bcmolt_epon_key_choice)((link_rec->mka_ctrl.mka_info->key_number + 1) % 2), |
| &key_info); |
| if (rc == BCM_ERR_OK) |
| { |
| rc = mka_send_sak_confirm(link_rec); |
| if (rc == BCM_ERR_OK) |
| { |
| /* Update the current SAK with the new SAK */ |
| memcpy(link_rec->mka_ctrl.mka_info->sak, link_rec->mka_ctrl.mka_info->new_sak, |
| sizeof(link_rec->mka_ctrl.mka_info->sak)); |
| |
| /* Change to the OPERATIONAL state */ |
| link_rec->mka_ctrl.mka_fsm_state = DPOE_SEC_MKA_FSM_STATE_OPERATIONAL; |
| } |
| } |
| } |
| |
| if (rc != BCM_ERR_OK) |
| { |
| /* Notify the DPoE Security FSM of the MKA error. */ |
| _dpoe_sec_mka_fsm_send_failure(link_rec, rc); |
| } |
| |
| return rc; |
| } |
| |
| /** This is the ONU FSM Jump Table, indexed by STATE and EVENT. */ |
| static f_dpoe_sec_mka_fsm dpoe_sec_mka_fsm[DPOE_SEC_MKA_FSM_STATE__COUNT][DPOE_SEC_MKA_FSM_EVENT__COUNT] = |
| { |
| [DPOE_SEC_MKA_FSM_STATE_NULL] = |
| { |
| [DPOE_SEC_MKA_FSM_EVENT_INIT] = _dpoe_sec_mka_fsm_initialize |
| }, |
| [DPOE_SEC_MKA_FSM_STATE_SET_ENCRYPTION] = |
| { |
| [DPOE_SEC_MKA_FSM_EVENT_RX_OAM] = _dpoe_sec_mka_fsm_rx_oam, |
| [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout, |
| [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop, |
| }, |
| [DPOE_SEC_MKA_FSM_STATE_START_MKA] = |
| { |
| [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = _dpoe_sec_mka_fsm_start_rx_eapol, |
| [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout, |
| [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = _dpoe_sec_mka_fsm_start_timeout, |
| [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop |
| }, |
| [DPOE_SEC_MKA_FSM_STATE_SEND_SAK] = |
| { |
| [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = _dpoe_sec_mka_fsm_sak_rx_eapol, |
| [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout, |
| [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = _dpoe_sec_mka_fsm_sak_timeout, |
| [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop |
| }, |
| [DPOE_SEC_MKA_FSM_STATE_OPERATIONAL] = |
| { |
| [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = _dpoe_sec_mka_fsm_handle_keep_alive, |
| [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout, |
| [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = _dpoe_sec_mka_fsm_send_keep_alive, |
| [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop |
| }, |
| [DPOE_SEC_MKA_FSM_STATE_SEND_NEW_SAK] = |
| { |
| [DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL] = _dpoe_sec_mka_fsm_new_sak_rx_eapol, |
| [DPOE_SEC_MKA_FSM_EVENT_RX_TIMEOUT] = _dpoe_sec_mka_fsm_rx_timeout, |
| [DPOE_SEC_MKA_FSM_EVENT_TX_TIMEOUT] = _dpoe_sec_mka_fsm_send_keep_alive, |
| [DPOE_SEC_MKA_FSM_EVENT_STOP] = _dpoe_sec_mka_fsm_stop |
| } |
| }; |
| |
| static bcmos_errno _dpoe_sec_mka_fsm_exec(dpoe_sec_link_rec *link_rec, dpoe_sec_mka_fsm_event *evt) |
| { |
| bcmos_errno rc = BCM_ERR_PARM; |
| |
| /* Parameter checks */ |
| BUG_ON(link_rec == NULL); |
| BUG_ON(evt == NULL); |
| |
| if (_dpoe_sec_mka_fsm_event_type_is_valid(evt->type)) |
| { |
| dpoe_sec_mka_fsm_state pre_state = link_rec->mka_ctrl.mka_fsm_state; |
| |
| DPOE_SEC_LINK_LOG(DEBUG, link_rec, "MKA: processing event %s in state %s\n", |
| _dpoe_sec_mka_fsm_event_name(evt->type), _dpoe_sec_mka_fsm_state_name(pre_state)); |
| |
| /* call the FSM */ |
| if (NULL != dpoe_sec_mka_fsm[link_rec->mka_ctrl.mka_fsm_state][evt->type]) |
| { |
| rc = dpoe_sec_mka_fsm[link_rec->mka_ctrl.mka_fsm_state][evt->type](link_rec, evt); |
| } |
| else |
| { |
| rc = _dpoe_sec_mka_fsm_error(link_rec, evt); |
| } |
| |
| if (pre_state != link_rec->mka_ctrl.mka_fsm_state) |
| { |
| DPOE_SEC_LINK_LOG(DEBUG, link_rec, "MKA: transitioning from %s to %s\n", |
| _dpoe_sec_mka_fsm_state_name(pre_state), |
| _dpoe_sec_mka_fsm_state_name(link_rec->mka_ctrl.mka_fsm_state)); |
| } |
| } |
| |
| return rc; |
| } |
| |
| void dpoe_sec_mka_fsm_rx_oam(dpoe_sec_link_rec *link_rec, uint8_t *frame, uint16_t length) |
| { |
| dpoe_sec_mka_fsm_event evnt = {}; |
| |
| BUG_ON(link_rec == NULL); |
| |
| /* Send the OAM PDU to the state machine. */ |
| evnt.type = DPOE_SEC_MKA_FSM_EVENT_RX_OAM; |
| evnt.data.rx_frame.val = frame; |
| evnt.data.rx_frame.len = length; |
| |
| /* Execute the state machine */ |
| _dpoe_sec_mka_fsm_exec(link_rec, &evnt); |
| } |
| |
| void dpoe_sec_mka_fsm_rx_eapol(dpoe_sec_link_rec *link_rec, uint8_t *frame, uint16_t length) |
| { |
| dpoe_sec_mka_fsm_event evnt = {}; |
| |
| BUG_ON(link_rec == NULL); |
| |
| /* Send the OAM PDU to the state machine. */ |
| evnt.type = DPOE_SEC_MKA_FSM_EVENT_RX_EAPOL; |
| evnt.data.rx_frame.val = frame; |
| evnt.data.rx_frame.len = length; |
| |
| /* Execute the state machine */ |
| _dpoe_sec_mka_fsm_exec(link_rec, &evnt); |
| } |
| |
| bcmos_errno dpoe_sec_mka_fsm_start(dpoe_sec_link_rec *link_rec) |
| { |
| dpoe_sec_mka_fsm_event event = {}; |
| |
| /* Parameter checks. */ |
| BUG_ON(link_rec == NULL); |
| |
| /* Inject an INIT_EVENT into the FSM. */ |
| event.type = DPOE_SEC_MKA_FSM_EVENT_INIT; |
| |
| return _dpoe_sec_mka_fsm_exec(link_rec, &event); |
| } |
| |
| bcmos_bool dpoe_sec_mka_fsm_running(dpoe_sec_link_rec *link_rec) |
| { |
| bcmos_bool rc = BCMOS_FALSE; |
| |
| /* Parameter checks. */ |
| BUG_ON(link_rec == NULL); |
| |
| if ((link_rec->mka_ctrl.mka_fsm_info != NULL) && |
| (link_rec->mka_ctrl.mka_fsm_state > DPOE_SEC_MKA_FSM_STATE_NULL)) |
| { |
| rc = BCMOS_TRUE; |
| } |
| |
| return rc; |
| } |
| |
| bcmos_errno dpoe_sec_mka_fsm_deregister(dpoe_sec_link_rec *link_rec) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| dpoe_sec_mka_fsm_event evnt = {}; |
| |
| /* Parameter checks. */ |
| BUG_ON(link_rec == NULL); |
| |
| /* The deregister function is called each time the Link deregisters regardless of whether the MKA FSM is running or |
| not. Handle the case where it is not running. */ |
| if (link_rec->mka_ctrl.mka_fsm_info != NULL) |
| { |
| /* Inject a stop into the MKA FSM. */ |
| evnt.type = DPOE_SEC_MKA_FSM_EVENT_STOP; |
| rc = _dpoe_sec_mka_fsm_exec(link_rec, &evnt); |
| } |
| |
| return rc; |
| } |
| |
| void dpoe_sec_mka_fsm_init(f_dpoe_sec_mka_cb mka_cb) |
| { |
| _mka_done_cb = mka_cb; |
| } |
| |