Shad Ansari | 2f7f9be | 2017-06-07 13:34:53 -0700 | [diff] [blame^] | 1 | /* |
| 2 | <:copyright-BRCM:2016:DUAL/GPL:standard |
| 3 | |
| 4 | Broadcom Proprietary and Confidential.(c) 2016 Broadcom |
| 5 | All Rights Reserved |
| 6 | |
| 7 | Unless you and Broadcom execute a separate written software license |
| 8 | agreement governing use of this software, this software is licensed |
| 9 | to you under the terms of the GNU General Public License version 2 |
| 10 | (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| 11 | with the following added to such license: |
| 12 | |
| 13 | As a special exception, the copyright holders of this software give |
| 14 | you permission to link this software with independent modules, and |
| 15 | to copy and distribute the resulting executable under terms of your |
| 16 | choice, provided that you also meet, for each linked independent |
| 17 | module, the terms and conditions of the license of that module. |
| 18 | An independent module is a module which is not derived from this |
| 19 | software. The special exception does not apply to any modifications |
| 20 | of the software. |
| 21 | |
| 22 | Not withstanding the above, under no circumstances may you combine |
| 23 | this software in any way with any other Broadcom software provided |
| 24 | under a license other than the GPL, without Broadcom's express prior |
| 25 | written consent. |
| 26 | |
| 27 | :> |
| 28 | */ |
| 29 | |
| 30 | #include "bcmos_system.h" |
| 31 | #include "bcmolt_model_types.h" |
| 32 | #include "bcmolt_api.h" |
| 33 | #include "oam_defs.h" |
| 34 | #include "bcmolt_math.h" |
| 35 | #include <encrypt_oam.h> |
| 36 | |
| 37 | static const bcmos_mac_address slow_prot_mcast_mac = |
| 38 | { |
| 39 | .u8 = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x02 } |
| 40 | }; |
| 41 | |
| 42 | static uint8_t oam_buffer[OAM_MAX_PACKET_SIZE]; |
| 43 | |
| 44 | static bcmos_bool oam_var_add_tlv_header(bcmolt_buf *buf, oam_var_branch branch, uint16_t leaf, uint8_t len) |
| 45 | { |
| 46 | bcmos_bool ok = BCMOS_TRUE; |
| 47 | uint8_t *snap = bcmolt_buf_snap_get(buf); |
| 48 | |
| 49 | ok = ok && (len <= OAM_MAX_TLV_LENGTH); |
| 50 | ok = ok && bcmolt_buf_write_u8(buf, (uint8_t) branch); |
| 51 | ok = ok && bcmolt_buf_write_u16_be(buf, leaf); |
| 52 | ok = ok && bcmolt_buf_write_u8(buf, oam_var_tlv_length_encode(len)); |
| 53 | ok = ok && (bcmolt_buf_get_remaining_size(buf) >= len); |
| 54 | if (!ok) |
| 55 | { |
| 56 | bcmolt_buf_snap_restore(buf, snap); |
| 57 | } |
| 58 | return ok; |
| 59 | } |
| 60 | |
| 61 | static bcmos_bool dpoe_encrypt_tlv_add(bcmolt_buf *oambuf, uint16_t period, dpoe_encrypt_mode mode) |
| 62 | { |
| 63 | bcmos_bool ok = BCMOS_TRUE; |
| 64 | |
| 65 | // encryption mode TLV |
| 66 | ok = ok && oam_var_add_tlv_header(oambuf, OAM_VAR_BRANCH_DPOE_ATTRIBUTE, (uint16_t) OAM_DPOE_ATTR_ENCRYPT_MODE, 1); |
| 67 | ok = ok && bcmolt_buf_write_u8(oambuf, (uint8_t) mode); |
| 68 | |
| 69 | /* Key Expiry Time TLV */ |
| 70 | ok = ok && oam_var_add_tlv_header(oambuf, OAM_VAR_BRANCH_DPOE_ATTRIBUTE, (uint16_t) OAM_DPOE_ATTR_KEY_EXPIRY_TIME, 2); |
| 71 | ok = ok && bcmolt_buf_write_u16_be(oambuf, period); |
| 72 | |
| 73 | return ok; |
| 74 | } |
| 75 | |
| 76 | static bcmos_bool oam_pdu_build_common(bcmos_mac_address pon_mac, bcmolt_buf *outbuf) |
| 77 | { |
| 78 | bcmos_bool ok = BCMOS_TRUE; |
| 79 | |
| 80 | ok = ok && bcmolt_buf_write_mac_address(outbuf, slow_prot_mcast_mac); // DA |
| 81 | ok = ok && bcmolt_buf_write_mac_address(outbuf, pon_mac); // SA |
| 82 | ok = ok && bcmolt_buf_write_u16_be(outbuf, ETHERTYPE_SLOWPROTOCOLS); // Type |
| 83 | ok = ok && bcmolt_buf_write_u8(outbuf, (uint8_t) SLOW_PROTOCOL_SUBTYPE_OAM); // Subtype |
| 84 | |
| 85 | return ok; |
| 86 | } |
| 87 | |
| 88 | /* Sends OAM frame 'frame' to a link via the inject frame operation */ |
| 89 | static bcmos_bool oam_pdu_transmit(const hde_key* hde_link, uint8_t *frame, uint16_t len) |
| 90 | { |
| 91 | bcmolt_ethernet_frame_unmasked oam_frame; |
| 92 | bcmolt_epon_link_inject_frame inject_frame; |
| 93 | |
| 94 | oam_frame.frame_octets.len = len; |
| 95 | oam_frame.frame_octets.val = frame; |
| 96 | bcmolt_epon_link_key link_key = |
| 97 | { |
| 98 | .epon_ni = hde_link->epon_ni, |
| 99 | .mac_address = hde_link->mac_addr |
| 100 | }; |
| 101 | |
| 102 | BCMOLT_PROXY_INIT(&inject_frame, epon_link, inject_frame, link_key); |
| 103 | BCMOLT_PROXY_PROP_SET(&inject_frame, epon_link, inject_frame, frame, oam_frame); |
| 104 | if (bcmolt_proxy_send(hde_link->device_id, &inject_frame.hdr) != BCM_ERR_OK) |
| 105 | { |
| 106 | return BCMOS_FALSE; |
| 107 | } |
| 108 | |
| 109 | return BCMOS_TRUE; |
| 110 | } |
| 111 | |
| 112 | static bcmos_bool oam_var_get_next(bcmolt_buf *buf, oam_var_generic *oamvar) |
| 113 | { |
| 114 | bcmos_bool ok = BCMOS_TRUE; |
| 115 | uint8_t branch = OAM_VAR_BRANCH_TERMINATION; |
| 116 | |
| 117 | memset(oamvar, 0, sizeof(*oamvar)); |
| 118 | ok = ok && (bcmolt_buf_get_remaining_size(buf) != 0); |
| 119 | ok = ok && bcmolt_buf_read_u8(buf, &branch); // Read the branch. |
| 120 | if (ok) |
| 121 | { |
| 122 | switch (branch) |
| 123 | { |
| 124 | case OAM_VAR_BRANCH_ATTRIBUTE: |
| 125 | case OAM_VAR_BRANCH_ACTION: |
| 126 | case OAM_VAR_BRANCH_DPOE_ACT: |
| 127 | case OAM_VAR_BRANCH_DPOE_ATTRIBUTE: |
| 128 | case OAM_VAR_BRANCH_DPOE_OBJECT: |
| 129 | oamvar->branch = (oam_var_branch) branch; |
| 130 | ok = ok && bcmolt_buf_read_u16_be(buf, &oamvar->leaf); // Read leaf |
| 131 | ok = ok && bcmolt_buf_read_u8(buf, &oamvar->width); // Read Width |
| 132 | oamvar->value = bcmolt_buf_snap_get(buf); // Keep the position of value |
| 133 | oamvar->width = oam_var_tlv_length_encode(oamvar->width); // Re-encode width via OAM spec |
| 134 | ok = ok && bcmolt_buf_skip(buf, oamvar->width); // Skip a 'width' amount |
| 135 | break; |
| 136 | |
| 137 | case OAM_VAR_BRANCH_TERMINATION: |
| 138 | default: |
| 139 | ok = BCMOS_FALSE; |
| 140 | break; |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | ok = ok && bcmolt_buf_skip(buf, oamvar->width); |
| 145 | return ok; |
| 146 | } |
| 147 | |
| 148 | /* Loops through 'oam_resp' looking for the provided branch/leaf. If found it will return it. */ |
| 149 | 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) |
| 150 | { |
| 151 | bcmolt_buf buf; |
| 152 | oam_var_generic var; |
| 153 | |
| 154 | bcmolt_buf_init(&buf, oam_resp_len, oam_resp, (bcmos_endian) BCMOLT_BUF_ENDIAN_FIXED); |
| 155 | |
| 156 | while (oam_var_get_next(&buf, &var)) // Keep going through all OAM vars. |
| 157 | { |
| 158 | if ((var.branch == branch) && (var.leaf == leaf)) // Check to see it is the one we are looking for. |
| 159 | { |
| 160 | *oam_var = var; |
| 161 | return BCMOS_TRUE; |
| 162 | } |
| 163 | } |
| 164 | return BCMOS_FALSE; |
| 165 | } |
| 166 | |
| 167 | static bcmos_bool oam_parse_to_dpoe_opcode(bcmolt_buf *buf, oam_dpoe_opcode opcode) |
| 168 | { |
| 169 | bcmos_bool ok = BCMOS_TRUE; |
| 170 | uint24_t dpoe_oui = { .low_hi = { .hi = 0x00, .mid = 0x10, .low = 0x00 } }; |
| 171 | uint16_t u16; |
| 172 | uint8_t u8; |
| 173 | uint24_t u24; |
| 174 | ok = ok && bcmolt_buf_skip(buf, 12); // Skip both mac addresses |
| 175 | ok = ok && bcmolt_buf_read_u16_be(buf, &u16); // Read ethertype |
| 176 | ok = ok && u16 == ETHERTYPE_SLOWPROTOCOLS; // Make sure it is slow protocol |
| 177 | ok = ok && bcmolt_buf_read_u8(buf, &u8); // Read the subtype |
| 178 | ok = ok && u8 == SLOW_PROTOCOL_SUBTYPE_OAM; // Make sure it is OAM |
| 179 | ok = ok && bcmolt_buf_skip(buf, 2); // Skip flags |
| 180 | ok = ok && bcmolt_buf_read_u8(buf, &u8); // Read the code |
| 181 | ok = ok && u8 == OAM_PDU_CODE_ORG_EXT; // Make sure it is organization specific |
| 182 | ok = ok && bcmolt_buf_read_u24(buf, &u24); // Read the OUI |
| 183 | ok = ok && memcmp(&dpoe_oui, &u24, sizeof(uint24_t)) == 0; // Make sure it is the DPoE OUI |
| 184 | ok = ok && bcmolt_buf_read_u8(buf, &u8); // Read the opcode |
| 185 | ok = ok && opcode == u8; // Make sure it matches the provided opcode |
| 186 | return ok; |
| 187 | } |
| 188 | |
| 189 | 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) |
| 190 | { |
| 191 | // send the encrypt mode and key expiry time TLVs to ONU. |
| 192 | bcmolt_buf buf; |
| 193 | ieee_oui dpoe_oui = { { 0x00, 0x10, 0x00 } }; |
| 194 | bcmos_bool ok = BCMOS_TRUE; |
| 195 | |
| 196 | memset(oam_buffer, 0, sizeof(oam_buffer)); |
| 197 | |
| 198 | bcmolt_buf_init(&buf, OAM_MAX_PACKET_SIZE, oam_buffer, BCMOLT_BUF_ENDIAN_FIXED); |
| 199 | ok = ok && oam_pdu_build_common(pon_mac, &buf); |
| 200 | ok = ok && bcmolt_buf_write_u16_be(&buf, (uint16_t) OAM_PDU_FLAG_SEND_ANY); /* write flags */ |
| 201 | ok = ok && bcmolt_buf_write_u8(&buf, (uint8_t) OAM_PDU_CODE_ORG_EXT); /* write code */ |
| 202 | ok = ok && bcmolt_buf_write(&buf, dpoe_oui.byte, 3); /* write oui */ |
| 203 | ok = ok && bcmolt_buf_write_u8(&buf, OAM_DPOE_OPCODE_SET_REQUEST); /* write opcode */ |
| 204 | ok = ok && dpoe_encrypt_tlv_add(&buf, period, encrypt_mode); /* write encryption TLVs */ |
| 205 | ok = ok && bcmolt_buf_write_u16_be(&buf, 0); /* write END */ |
| 206 | ok = ok && oam_pdu_transmit(hde_link, oam_buffer, MAX(bcmolt_buf_get_used(&buf), 60)); /* Send it */ |
| 207 | |
| 208 | if (!ok) |
| 209 | { |
| 210 | return BCM_ERR_NOMEM; |
| 211 | } |
| 212 | else |
| 213 | { |
| 214 | return BCM_ERR_OK; |
| 215 | } |
| 216 | return ok; |
| 217 | } |
| 218 | |
| 219 | bcmos_errno dpoe_encrypt_oam_parse_set_response(const bcmolt_u8_list_u32 *frame, uint8_t *encrypt_mode_response, uint8_t *key_expiry_response) |
| 220 | { |
| 221 | oam_var_generic encrypt_mode; |
| 222 | oam_var_generic key_expiry_time; |
| 223 | bcmolt_buf buf; |
| 224 | bcmolt_buf_init(&buf, frame->len, frame->val, BCMOLT_BUF_ENDIAN_FIXED); |
| 225 | |
| 226 | // Move the OAM buffer forward to the opcode |
| 227 | if (!oam_parse_to_dpoe_opcode(&buf, OAM_DPOE_OPCODE_SET_RESPONSE)) |
| 228 | { |
| 229 | return BCM_ERR_PARSE; |
| 230 | } |
| 231 | |
| 232 | // Check for encrypt mode TLV |
| 233 | if (!oam_var_get_branch_leaf( |
| 234 | bcmolt_buf_snap_get(&buf), |
| 235 | bcmolt_buf_get_remaining_size(&buf), |
| 236 | OAM_VAR_BRANCH_DPOE_ATTRIBUTE, |
| 237 | OAM_DPOE_ATTR_ENCRYPT_MODE, |
| 238 | &encrypt_mode)) |
| 239 | { |
| 240 | return BCM_ERR_PARSE; |
| 241 | } |
| 242 | |
| 243 | // Check for key expiry time TLV |
| 244 | if (!oam_var_get_branch_leaf( |
| 245 | bcmolt_buf_snap_get(&buf), |
| 246 | bcmolt_buf_get_remaining_size(&buf), |
| 247 | OAM_VAR_BRANCH_DPOE_ATTRIBUTE, |
| 248 | OAM_DPOE_ATTR_KEY_EXPIRY_TIME, |
| 249 | &key_expiry_time)) |
| 250 | { |
| 251 | return BCM_ERR_PARSE; |
| 252 | } |
| 253 | |
| 254 | *encrypt_mode_response = encrypt_mode.width; |
| 255 | *key_expiry_response = key_expiry_time.width; |
| 256 | return BCM_ERR_OK; |
| 257 | } |
| 258 | |
| 259 | bcmos_errno dpoe_encrypt_oam_parse_new_key(const bcmolt_u8_list_u32 *frame, uint8_t *new_key, bcmolt_epon_key_choice *key_choice) |
| 260 | { |
| 261 | bcmos_bool ok = BCMOS_TRUE; |
| 262 | uint8_t key_number; |
| 263 | uint8_t key_length; |
| 264 | bcmolt_buf buf; |
| 265 | bcmolt_buf_init(&buf, frame->len, frame->val, BCMOLT_BUF_ENDIAN_FIXED); |
| 266 | ok = oam_parse_to_dpoe_opcode(&buf, OAM_DPOE_OPCODE_KEY_EXCHANGE); |
| 267 | ok = ok && bcmolt_buf_read_u8(&buf, &key_number); // Read key number |
| 268 | ok = ok && bcmolt_buf_read_u8(&buf, &key_length); // Read key length |
| 269 | ok = ok && key_length == 16; // Make sure the key is 16 byte length |
| 270 | ok = ok && bcmolt_buf_read(&buf, new_key, 16); // Read the key |
| 271 | |
| 272 | if (!ok) |
| 273 | { |
| 274 | return BCM_ERR_PARSE; |
| 275 | } |
| 276 | |
| 277 | if (key_number == 0) |
| 278 | { |
| 279 | *key_choice = BCMOLT_EPON_KEY_CHOICE_KEY_0; |
| 280 | } |
| 281 | else if (key_number == 1) |
| 282 | { |
| 283 | *key_choice = BCMOLT_EPON_KEY_CHOICE_KEY_1; |
| 284 | } |
| 285 | else |
| 286 | { |
| 287 | return BCM_ERR_PARSE; |
| 288 | } |
| 289 | |
| 290 | return BCM_ERR_OK; |
| 291 | } |