| /* |
| <: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 "bcmos_system.h" |
| #include "bcmolt_model_types.h" |
| #include "bcmolt_api.h" |
| #include "oam_defs.h" |
| #include "bcmolt_math.h" |
| #include <encrypt_oam.h> |
| |
| static const bcmos_mac_address slow_prot_mcast_mac = |
| { |
| .u8 = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x02 } |
| }; |
| |
| static uint8_t oam_buffer[OAM_MAX_PACKET_SIZE]; |
| |
| static bcmos_bool oam_var_add_tlv_header(bcmolt_buf *buf, oam_var_branch branch, uint16_t leaf, uint8_t len) |
| { |
| bcmos_bool ok = BCMOS_TRUE; |
| uint8_t *snap = bcmolt_buf_snap_get(buf); |
| |
| ok = ok && (len <= OAM_MAX_TLV_LENGTH); |
| ok = ok && bcmolt_buf_write_u8(buf, (uint8_t) branch); |
| ok = ok && bcmolt_buf_write_u16_be(buf, leaf); |
| ok = ok && bcmolt_buf_write_u8(buf, oam_var_tlv_length_encode(len)); |
| ok = ok && (bcmolt_buf_get_remaining_size(buf) >= len); |
| if (!ok) |
| { |
| bcmolt_buf_snap_restore(buf, snap); |
| } |
| return ok; |
| } |
| |
| static bcmos_bool dpoe_encrypt_tlv_add(bcmolt_buf *oambuf, uint16_t period, dpoe_encrypt_mode mode) |
| { |
| bcmos_bool ok = BCMOS_TRUE; |
| |
| // encryption mode TLV |
| ok = ok && oam_var_add_tlv_header(oambuf, OAM_VAR_BRANCH_DPOE_ATTRIBUTE, (uint16_t) OAM_DPOE_ATTR_ENCRYPT_MODE, 1); |
| ok = ok && bcmolt_buf_write_u8(oambuf, (uint8_t) mode); |
| |
| /* Key Expiry Time TLV */ |
| ok = ok && oam_var_add_tlv_header(oambuf, OAM_VAR_BRANCH_DPOE_ATTRIBUTE, (uint16_t) OAM_DPOE_ATTR_KEY_EXPIRY_TIME, 2); |
| ok = ok && bcmolt_buf_write_u16_be(oambuf, period); |
| |
| return ok; |
| } |
| |
| static bcmos_bool oam_pdu_build_common(bcmos_mac_address pon_mac, bcmolt_buf *outbuf) |
| { |
| bcmos_bool ok = BCMOS_TRUE; |
| |
| ok = ok && bcmolt_buf_write_mac_address(outbuf, slow_prot_mcast_mac); // DA |
| ok = ok && bcmolt_buf_write_mac_address(outbuf, pon_mac); // SA |
| ok = ok && bcmolt_buf_write_u16_be(outbuf, ETHERTYPE_SLOWPROTOCOLS); // Type |
| ok = ok && bcmolt_buf_write_u8(outbuf, (uint8_t) SLOW_PROTOCOL_SUBTYPE_OAM); // Subtype |
| |
| return ok; |
| } |
| |
| /* Sends OAM frame 'frame' to a link via the inject frame operation */ |
| static bcmos_bool oam_pdu_transmit(const hde_key* hde_link, uint8_t *frame, uint16_t len) |
| { |
| bcmolt_ethernet_frame_unmasked oam_frame; |
| bcmolt_epon_link_inject_frame inject_frame; |
| |
| oam_frame.frame_octets.len = len; |
| oam_frame.frame_octets.val = frame; |
| bcmolt_epon_link_key link_key = |
| { |
| .epon_ni = hde_link->epon_ni, |
| .mac_address = hde_link->mac_addr |
| }; |
| |
| BCMOLT_PROXY_INIT(&inject_frame, epon_link, inject_frame, link_key); |
| BCMOLT_PROXY_PROP_SET(&inject_frame, epon_link, inject_frame, frame, oam_frame); |
| if (bcmolt_proxy_send(hde_link->device_id, &inject_frame.hdr) != BCM_ERR_OK) |
| { |
| return BCMOS_FALSE; |
| } |
| |
| return BCMOS_TRUE; |
| } |
| |
| static bcmos_bool oam_var_get_next(bcmolt_buf *buf, oam_var_generic *oamvar) |
| { |
| bcmos_bool ok = BCMOS_TRUE; |
| uint8_t branch = OAM_VAR_BRANCH_TERMINATION; |
| |
| memset(oamvar, 0, sizeof(*oamvar)); |
| ok = ok && (bcmolt_buf_get_remaining_size(buf) != 0); |
| ok = ok && bcmolt_buf_read_u8(buf, &branch); // Read the branch. |
| if (ok) |
| { |
| switch (branch) |
| { |
| case OAM_VAR_BRANCH_ATTRIBUTE: |
| case OAM_VAR_BRANCH_ACTION: |
| case OAM_VAR_BRANCH_DPOE_ACT: |
| case OAM_VAR_BRANCH_DPOE_ATTRIBUTE: |
| case OAM_VAR_BRANCH_DPOE_OBJECT: |
| oamvar->branch = (oam_var_branch) branch; |
| ok = ok && bcmolt_buf_read_u16_be(buf, &oamvar->leaf); // Read leaf |
| ok = ok && bcmolt_buf_read_u8(buf, &oamvar->width); // Read Width |
| oamvar->value = bcmolt_buf_snap_get(buf); // Keep the position of value |
| oamvar->width = oam_var_tlv_length_encode(oamvar->width); // Re-encode width via OAM spec |
| ok = ok && bcmolt_buf_skip(buf, oamvar->width); // Skip a 'width' amount |
| break; |
| |
| case OAM_VAR_BRANCH_TERMINATION: |
| default: |
| ok = BCMOS_FALSE; |
| break; |
| } |
| } |
| |
| ok = ok && bcmolt_buf_skip(buf, oamvar->width); |
| return ok; |
| } |
| |
| /* Loops through 'oam_resp' looking for the provided branch/leaf. If found it will return it. */ |
| static bcmos_bool oam_var_get_branch_leaf(uint8_t *oam_resp, uint32_t oam_resp_len, oam_var_branch branch, uint16_t leaf, oam_var_generic *oam_var) |
| { |
| bcmolt_buf buf; |
| oam_var_generic var; |
| |
| bcmolt_buf_init(&buf, oam_resp_len, oam_resp, (bcmos_endian) BCMOLT_BUF_ENDIAN_FIXED); |
| |
| while (oam_var_get_next(&buf, &var)) // Keep going through all OAM vars. |
| { |
| if ((var.branch == branch) && (var.leaf == leaf)) // Check to see it is the one we are looking for. |
| { |
| *oam_var = var; |
| return BCMOS_TRUE; |
| } |
| } |
| return BCMOS_FALSE; |
| } |
| |
| static bcmos_bool oam_parse_to_dpoe_opcode(bcmolt_buf *buf, oam_dpoe_opcode opcode) |
| { |
| bcmos_bool ok = BCMOS_TRUE; |
| uint24_t dpoe_oui = { .low_hi = { .hi = 0x00, .mid = 0x10, .low = 0x00 } }; |
| uint16_t u16; |
| uint8_t u8; |
| uint24_t u24; |
| ok = ok && bcmolt_buf_skip(buf, 12); // Skip both mac addresses |
| ok = ok && bcmolt_buf_read_u16_be(buf, &u16); // Read ethertype |
| ok = ok && u16 == ETHERTYPE_SLOWPROTOCOLS; // Make sure it is slow protocol |
| ok = ok && bcmolt_buf_read_u8(buf, &u8); // Read the subtype |
| ok = ok && u8 == SLOW_PROTOCOL_SUBTYPE_OAM; // Make sure it is OAM |
| ok = ok && bcmolt_buf_skip(buf, 2); // Skip flags |
| ok = ok && bcmolt_buf_read_u8(buf, &u8); // Read the code |
| ok = ok && u8 == OAM_PDU_CODE_ORG_EXT; // Make sure it is organization specific |
| ok = ok && bcmolt_buf_read_u24(buf, &u24); // Read the OUI |
| ok = ok && memcmp(&dpoe_oui, &u24, sizeof(uint24_t)) == 0; // Make sure it is the DPoE OUI |
| ok = ok && bcmolt_buf_read_u8(buf, &u8); // Read the opcode |
| ok = ok && opcode == u8; // Make sure it matches the provided opcode |
| return ok; |
| } |
| |
| bcmos_errno dpoe_encrypt_oam_set_request_send(const hde_key *hde_link, uint16_t period, dpoe_encrypt_mode encrypt_mode, bcmos_mac_address pon_mac) |
| { |
| // send the encrypt mode and key expiry time TLVs to ONU. |
| bcmolt_buf buf; |
| ieee_oui dpoe_oui = { { 0x00, 0x10, 0x00 } }; |
| bcmos_bool ok = BCMOS_TRUE; |
| |
| memset(oam_buffer, 0, sizeof(oam_buffer)); |
| |
| bcmolt_buf_init(&buf, OAM_MAX_PACKET_SIZE, oam_buffer, BCMOLT_BUF_ENDIAN_FIXED); |
| ok = ok && oam_pdu_build_common(pon_mac, &buf); |
| ok = ok && bcmolt_buf_write_u16_be(&buf, (uint16_t) OAM_PDU_FLAG_SEND_ANY); /* write flags */ |
| ok = ok && bcmolt_buf_write_u8(&buf, (uint8_t) OAM_PDU_CODE_ORG_EXT); /* write code */ |
| ok = ok && bcmolt_buf_write(&buf, dpoe_oui.byte, 3); /* write oui */ |
| ok = ok && bcmolt_buf_write_u8(&buf, OAM_DPOE_OPCODE_SET_REQUEST); /* write opcode */ |
| ok = ok && dpoe_encrypt_tlv_add(&buf, period, encrypt_mode); /* write encryption TLVs */ |
| ok = ok && bcmolt_buf_write_u16_be(&buf, 0); /* write END */ |
| ok = ok && oam_pdu_transmit(hde_link, oam_buffer, MAX(bcmolt_buf_get_used(&buf), 60)); /* Send it */ |
| |
| if (!ok) |
| { |
| return BCM_ERR_NOMEM; |
| } |
| else |
| { |
| return BCM_ERR_OK; |
| } |
| return ok; |
| } |
| |
| bcmos_errno dpoe_encrypt_oam_parse_set_response(const bcmolt_u8_list_u32 *frame, uint8_t *encrypt_mode_response, uint8_t *key_expiry_response) |
| { |
| oam_var_generic encrypt_mode; |
| oam_var_generic key_expiry_time; |
| bcmolt_buf buf; |
| bcmolt_buf_init(&buf, frame->len, frame->val, BCMOLT_BUF_ENDIAN_FIXED); |
| |
| // Move the OAM buffer forward to the opcode |
| if (!oam_parse_to_dpoe_opcode(&buf, OAM_DPOE_OPCODE_SET_RESPONSE)) |
| { |
| return BCM_ERR_PARSE; |
| } |
| |
| // Check for encrypt mode TLV |
| if (!oam_var_get_branch_leaf( |
| bcmolt_buf_snap_get(&buf), |
| bcmolt_buf_get_remaining_size(&buf), |
| OAM_VAR_BRANCH_DPOE_ATTRIBUTE, |
| OAM_DPOE_ATTR_ENCRYPT_MODE, |
| &encrypt_mode)) |
| { |
| return BCM_ERR_PARSE; |
| } |
| |
| // Check for key expiry time TLV |
| if (!oam_var_get_branch_leaf( |
| bcmolt_buf_snap_get(&buf), |
| bcmolt_buf_get_remaining_size(&buf), |
| OAM_VAR_BRANCH_DPOE_ATTRIBUTE, |
| OAM_DPOE_ATTR_KEY_EXPIRY_TIME, |
| &key_expiry_time)) |
| { |
| return BCM_ERR_PARSE; |
| } |
| |
| *encrypt_mode_response = encrypt_mode.width; |
| *key_expiry_response = key_expiry_time.width; |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno dpoe_encrypt_oam_parse_new_key(const bcmolt_u8_list_u32 *frame, uint8_t *new_key, bcmolt_epon_key_choice *key_choice) |
| { |
| bcmos_bool ok = BCMOS_TRUE; |
| uint8_t key_number; |
| uint8_t key_length; |
| bcmolt_buf buf; |
| bcmolt_buf_init(&buf, frame->len, frame->val, BCMOLT_BUF_ENDIAN_FIXED); |
| ok = oam_parse_to_dpoe_opcode(&buf, OAM_DPOE_OPCODE_KEY_EXCHANGE); |
| ok = ok && bcmolt_buf_read_u8(&buf, &key_number); // Read key number |
| ok = ok && bcmolt_buf_read_u8(&buf, &key_length); // Read key length |
| ok = ok && key_length == 16; // Make sure the key is 16 byte length |
| ok = ok && bcmolt_buf_read(&buf, new_key, 16); // Read the key |
| |
| if (!ok) |
| { |
| return BCM_ERR_PARSE; |
| } |
| |
| if (key_number == 0) |
| { |
| *key_choice = BCMOLT_EPON_KEY_CHOICE_KEY_0; |
| } |
| else if (key_number == 1) |
| { |
| *key_choice = BCMOLT_EPON_KEY_CHOICE_KEY_1; |
| } |
| else |
| { |
| return BCM_ERR_PARSE; |
| } |
| |
| return BCM_ERR_OK; |
| } |