blob: 3174d63d3fd7252d20e81db6c233cf9eddd99cae [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.
:>
*/
#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;
}