pe_mode.c (18271B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: pe_mode.c 8 */ 9 10 #include <shared/bsl.h> 11 #include <appl/diag/system.h> 12 13 #ifdef PE_MODE_CMD 14 15 #include <netinet/in.h> 16 #include <bcm/extender.h> 17 #include <bcm/link.h> 18 19 20 #define PE_MODE_ID_ETHERTYPE 0x869f 21 #define LLDP_TLV_HDR_TYPE_MASK 0xfe00 22 #define LLDP_TLV_HDR_TYPE_SHIFT 9 23 #define LLDP_TLV_HDR_LEN_MASK 0x01ff 24 25 #define BCM_TLV_TYPE 127 26 #define BCM_TLV_OUI_VAL 0x18c086 27 #define BCM_MODE_TLV_SUBTYPE_VAL 0x01 28 #define BCM_TAG_TLV_SUBTYPE_VAL 0x02 29 #define BCM_ECID_TLV_SUBTYPE_VAL 0x03 30 #define BCM_PRE_DEFINED_ECID0 0x11 31 #define BCM_PRE_DEFINED_ECID1 0x22 32 #define BCM_PRE_DEFINED_ECID2 0x33 33 #define BCM_PRE_DEFINED_ECID3 0x44 34 35 #define BCM_MODE_TLV_DETECTED_FLAG 1 36 #define BCM_TAG_TLV_DETECTED_FLAG 2 37 #define BCM_ECID_TLV_DETECTED_FLAG 4 38 #define LLDP_TLV_PARSER_DONE 0xFF 39 40 #define LLDP_TLV_HDR_LEN 2 41 #define LLDP_TLV_OUI_LEN 3 42 #define BCM_ECID_VAL_LEN 4 43 44 typedef struct pe_port_info_s { 45 int count; 46 bcm_gport_t pe_gport; 47 bcm_gport_t extender_port_id[4]; 48 } pe_port_info_t; 49 50 int pemode_init(int unit); 51 int pe_mode_enable(int unit); 52 53 #endif /* PE_MODE_CMD */ 54 55 56 /* 57 * Function: cmd_pemode 58 * Purpose: initialize pemode 59 * Parameters: u - SOC unit # 60 * a - pointer to args 61 * Returns: CMD_OK/CMD_FAIL 62 */ 63 cmd_result_t 64 cmd_pemode(int u, args_t *a) 65 { 66 67 #ifdef PE_MODE_CMD 68 pemode_init(u); 69 pe_mode_enable(u); 70 #else 71 cli_out("cmd_pemode() API calls commented out\n"); 72 #endif /* PE_MODE_CMD */ 73 74 return CMD_OK; 75 } 76 77 78 #ifdef PE_MODE_CMD 79 80 int pe_port_enable(int unit, int pe_port) 81 { 82 int rv; 83 /* extender ports */ 84 bcm_extender_port_t extender_port[4]; 85 int pe_etag_pcp[4] = {0, 2, 4, 6}; 86 int pe_etag_vid[4] = {0x11, 0x22, 0x33, 0x44}; 87 int i, num_pe_port = 4; 88 89 bcm_gport_t pe_gport; /* gport to access the extended ports */ 90 int val; 91 bcm_vlan_t vlan_id, def_vlan_id; 92 int flags, mgroup; 93 bcm_vlan_control_vlan_t vlan_ctrl; 94 95 /* get the gport of pe_gport */ 96 rv = bcm_port_gport_get(unit, pe_port, &pe_gport); 97 if (BCM_FAILURE(rv)) { 98 cli_out("bcm_port_gport_get() failed (%s)\n", bcm_errmsg(rv)); 99 return rv; 100 } 101 102 rv = bcm_port_control_get(unit, pe_port, bcmPortControlExtenderType, &val) ; 103 if (BCM_FAILURE(rv)) { 104 cli_out("Failed to get port %d extender type: %s\n", pe_port, bcm_errmsg(rv)); 105 return rv; 106 } 107 if (BCM_PORT_EXTENDER_TYPE_SWITCH == val) { 108 /* cli_out("Port is in PE MODE already, return without any operations.\n"); */ 109 return BCM_E_NONE; 110 } 111 112 /* set port type for the pe_port */ 113 rv = bcm_port_control_set(unit, pe_gport, bcmPortControlExtenderType, BCM_PORT_EXTENDER_TYPE_SWITCH); 114 if (BCM_FAILURE(rv)) { 115 cli_out("bcm_port_control_set() failed (%s)\n", bcm_errmsg(rv)); 116 return rv; 117 } 118 119 /* add pe port */ 120 for (i=0; i<num_pe_port; i++) { 121 /* initialize each of the port extender port structures */ 122 bcm_extender_port_t_init(&extender_port[i]); 123 extender_port[i].flags = 0; 124 extender_port[i].port = pe_gport; 125 extender_port[i].extended_port_vid = pe_etag_vid[i]; 126 extender_port[i].pcp_de_select = BCM_EXTENDER_PCP_DE_SELECT_DEFAULT; 127 extender_port[i].pcp = pe_etag_pcp[i]; 128 extender_port[i].de = 0; 129 rv = bcm_extender_port_add(unit, &extender_port[i]); 130 if (BCM_FAILURE(rv)) { 131 cli_out("bcm_extender_port_add(%d) failed (%s)\n", i, bcm_errmsg(rv)); 132 return rv; 133 } 134 /* 135 cli_out("bcm_extender_port_add(%d) ep_vid: 0x%x; ep_id: 0x%x\n", i, 136 extender_port[i].extended_port_vid, extender_port[i].extender_port_id); 137 */ 138 } 139 140 /* get default vlan */ 141 bcm_vlan_default_get(unit, &def_vlan_id); 142 vlan_id = def_vlan_id; 143 144 /* don't remove the Etag on egress of pe port */ 145 rv = bcm_port_control_set(unit, pe_gport, bcmPortControlEtagEgressDelete, 0); 146 if (BCM_FAILURE(rv)) { 147 cli_out("bcm_port_control_set(%d) failed (%s)\n", pe_port, bcm_errmsg(rv)); 148 return rv; 149 } 150 151 /* configure entender port vlans for untagged packets */ 152 for (i=0; i<num_pe_port; i++) { 153 rv = bcm_port_untagged_vlan_set(unit, extender_port[i].extender_port_id, vlan_id); 154 if (BCM_FAILURE(rv)) { 155 cli_out("Set default vlan on extender port %#x failed: %s \n", 156 extender_port[i].extender_port_id, bcm_errmsg(rv)); 157 return rv; 158 } 159 } 160 161 bcm_vlan_control_vlan_t_init(&vlan_ctrl); 162 rv = bcm_vlan_control_vlan_get(unit, vlan_id, &vlan_ctrl); 163 if(BCM_FAILURE(rv)) { 164 cli_out("Failed to get vlan %d information: %s\n", vlan_id, bcm_errmsg(rv)); 165 return rv; 166 } 167 mgroup = vlan_ctrl.broadcast_group; 168 169 /* Multicast is not created */ 170 if (!mgroup) { 171 /* create multicast group */ 172 flags = BCM_MULTICAST_TYPE_EXTENDER; 173 rv = bcm_multicast_create(unit, flags, &mgroup); 174 if (BCM_FAILURE(rv)) { 175 cli_out("Create multigroup failed: %s\n", bcm_errmsg(rv)); 176 return rv; 177 } 178 179 /* configure multicast group vlans */ 180 bcm_vlan_control_vlan_t_init(&vlan_ctrl); 181 vlan_ctrl.broadcast_group = mgroup; 182 vlan_ctrl.unknown_multicast_group = mgroup; 183 vlan_ctrl.unknown_unicast_group = mgroup; 184 rv = bcm_vlan_control_vlan_set(unit, vlan_id, vlan_ctrl); 185 if(BCM_FAILURE(rv)) { 186 cli_out("Failed to configure multicast group on vlan %d: %s\n", vlan_id, bcm_errmsg(rv)); 187 return rv; 188 } 189 } 190 191 /* add pe_port to new vlan */ 192 flags = BCM_VLAN_PORT_UNTAGGED; 193 rv = bcm_vlan_gport_add(unit, vlan_id, pe_gport, flags); 194 if (BCM_FAILURE(rv)) { 195 cli_out("Failed to configure port %#x as untagged port: %s\n", pe_gport, bcm_errmsg(rv)); 196 return rv; 197 } 198 for (i=0; i<num_pe_port; i++) { 199 rv = bcm_vlan_gport_add(unit, vlan_id, extender_port[i].extender_port_id, flags); 200 if (BCM_FAILURE(rv)) { 201 cli_out("Error to add extender port %#x to vlan %d: %s\n", 202 extender_port[i].extender_port_id, vlan_id, bcm_errmsg(rv)); 203 return rv; 204 } 205 } 206 207 cli_out("Port %d has been configured as PE mode\n", pe_port); 208 return BCM_E_NONE; 209 } 210 211 212 int extender_port_traverse_cb(int unit, bcm_extender_port_t *extender_port, void *user_data) 213 { 214 pe_port_info_t pe_port_info; 215 sal_memcpy(&pe_port_info, user_data, sizeof(pe_port_info)); 216 if (extender_port->port == pe_port_info.pe_gport) { 217 if (pe_port_info.count >= 4) { 218 cli_out("Error, number of extender port of port %#x is bigger than 4\n", 219 pe_port_info.pe_gport); 220 return BCM_E_NONE; 221 } 222 pe_port_info.extender_port_id[pe_port_info.count] = extender_port->extender_port_id; 223 pe_port_info.count ++; 224 } 225 sal_memcpy(user_data, &pe_port_info, sizeof(pe_port_info)); 226 return BCM_E_NONE; 227 } 228 229 230 int pe_port_disable(int unit, int pe_port) 231 { 232 int rv; 233 int i; 234 bcm_gport_t pe_gport; /* gport to access the extended ports */ 235 int flags, val; 236 bcm_vlan_t vlan_id, def_vlan_id; 237 pe_port_info_t pe_port_info; 238 239 /* get the gport of pe_gport */ 240 rv = bcm_port_gport_get(unit, pe_port, &pe_gport); 241 if (BCM_FAILURE(rv)) { 242 cli_out("bcm_port_gport_get() failed (%s)\n", bcm_errmsg(rv)); 243 return rv; 244 } 245 246 rv = bcm_port_control_get(unit, pe_port, bcmPortControlExtenderType, &val); 247 if (BCM_FAILURE(rv)) { 248 cli_out("Failed to get port %d extender type: %s\n", pe_port, bcm_errmsg(rv)); 249 return rv; 250 } 251 if (BCM_PORT_EXTENDER_TYPE_SWITCH != val) { 252 cli_out("Port %d is not in PE mode\n", pe_port); 253 return BCM_E_NONE; 254 } 255 256 /* get default vlan */ 257 bcm_vlan_default_get(unit, &def_vlan_id); 258 vlan_id = def_vlan_id; 259 260 sal_memset(&pe_port_info, 0, sizeof(pe_port_info)); 261 pe_port_info.pe_gport = pe_gport; 262 rv = bcm_extender_port_traverse(0, extender_port_traverse_cb, (void*)&pe_port_info); 263 if (BCM_FAILURE(rv)) { 264 cli_out("Failed to get extender ports of port 0x%x: %s\n", pe_gport, bcm_errmsg(rv)); 265 return rv; 266 } 267 for (i=0; i<pe_port_info.count; i++) 268 { 269 rv = bcm_vlan_gport_delete(unit, vlan_id, pe_port_info.extender_port_id[i]); 270 if (BCM_FAILURE(rv)) { 271 cli_out("Error in deleting extender port 0x%x from vlan %d: %s\n", 272 pe_port_info.extender_port_id[i], vlan_id, bcm_errmsg(rv)); 273 return rv; 274 } 275 rv = bcm_extender_port_delete(unit, pe_port_info.extender_port_id[i]); 276 if (BCM_FAILURE(rv)) { 277 cli_out("bcm_extender_port_delete(0x%x) failed (%s)\n", 278 pe_port_info.extender_port_id[i], bcm_errmsg(rv)); 279 return rv; 280 } 281 } 282 283 /* set port to ethernet port */ 284 rv = bcm_port_control_set(unit, pe_gport, bcmPortControlExtenderType, BCM_PORT_EXTENDER_TYPE_NONE); 285 if (BCM_FAILURE(rv)) { 286 cli_out("bcm_port_control_set() failed (%s)\n", bcm_errmsg(rv)); 287 return rv; 288 } 289 290 rv = bcm_port_control_set(unit, pe_gport, bcmPortControlEtagEgressDelete, 1); 291 if (BCM_FAILURE(rv)) { 292 cli_out("Configure port(%d) to delete ETAG failed (%s)\n", pe_port, bcm_errmsg(rv)); 293 return rv; 294 } 295 /* add pe_port to new vlan */ 296 flags = BCM_VLAN_PORT_UNTAGGED; 297 rv = bcm_vlan_gport_add(unit, vlan_id, pe_gport, flags); 298 if (BCM_FAILURE(rv)) { 299 cli_out("Failed to configure port 0x%x as untagged port: %s\n", pe_gport, bcm_errmsg(rv)); 300 return rv; 301 } 302 303 cli_out("PE mode on port %d has been disabled\n", pe_port); 304 305 return BCM_E_NONE; 306 } 307 308 309 void dump_pkt(bcm_pkt_t *pkt) 310 { 311 int i; 312 cli_out("Packet from port %d", pkt->src_port); 313 cli_out("\n"); 314 for (i=0; i<16; i++) { 315 cli_out("==="); 316 } 317 cli_out("\n"); 318 for (i=0; i<16; i++) { 319 cli_out("%02d ", i); 320 } 321 cli_out("\n"); 322 for (i=0; i<16; i++) { 323 cli_out("==="); 324 } 325 326 cli_out("\n%02x ", pkt->pkt_data->data[0]); 327 for (i=1; i<pkt->pkt_len; i++) { 328 if (i%16) { 329 cli_out("%02x ", pkt->pkt_data->data[i]); 330 } else { 331 cli_out("\n%02x ", pkt->pkt_data->data[i]); 332 } 333 } 334 cli_out("\n"); 335 } 336 337 338 int brcm_tlv_parser(char *data, int *length) 339 { 340 int subtype, tlv_hdr, tlv_len, tlv_type; 341 int tlv_oui; 342 /*int op_mode, tag_mode;*/ 343 uint16 *tlvptr=(uint16*)data; 344 uint8 *uint8ptr; 345 uint8 ecid[4]; 346 347 tlv_hdr = ntohs(*tlvptr); 348 tlv_len = tlv_hdr & LLDP_TLV_HDR_LEN_MASK; 349 tlv_type = (tlv_hdr & LLDP_TLV_HDR_TYPE_MASK) >> LLDP_TLV_HDR_TYPE_SHIFT; 350 /* cli_out("%s() tlv_hdr 0x%x; tlv_len 0x%x; tlv type 0x%x\n", __FUNCTION__, tlv_hdr, tlv_len, tlv_type); */ 351 *length = tlv_len + LLDP_TLV_HDR_LEN; 352 if (!tlv_type) 353 { 354 return LLDP_TLV_PARSER_DONE; 355 } 356 /* Parser BRCM OUI only */ 357 if (BCM_TLV_TYPE == tlv_type) { 358 uint8ptr = (uint8*)(data+LLDP_TLV_HDR_LEN); 359 tlv_oui = *(uint8ptr++)<<16; 360 tlv_oui += *(uint8ptr++)<<8; 361 tlv_oui += *(uint8ptr++); 362 /*cli_out("BCM TLV OUI value: 0x%x\n", tlv_oui);*/ 363 if (tlv_oui == BCM_TLV_OUI_VAL) { 364 subtype = data[LLDP_TLV_HDR_LEN+LLDP_TLV_OUI_LEN]; 365 /*cli_out("BCM TLV subtype value: 0x%x\n", subtype);*/ 366 switch(subtype) { 367 case BCM_MODE_TLV_SUBTYPE_VAL: 368 /*op_mode = data[LLDP_TLV_HDR_LEN+LLDP_TLV_OUI_LEN+1];*/ 369 /*cli_out("Opertaion mode: %d\n", op_mode);*/ 370 return BCM_MODE_TLV_DETECTED_FLAG; 371 break; 372 case BCM_TAG_TLV_SUBTYPE_VAL: 373 /*tag_mode = data[LLDP_TLV_HDR_LEN+LLDP_TLV_OUI_LEN+1];*/ 374 /*cli_out("Tag mode: %d\n", tag_mode);*/ 375 return BCM_TAG_TLV_DETECTED_FLAG; 376 break; 377 case BCM_ECID_TLV_SUBTYPE_VAL: 378 uint8ptr = (uint8*)(data+LLDP_TLV_HDR_LEN+LLDP_TLV_OUI_LEN+1); 379 ecid[0] = *(uint8ptr++); 380 ecid[1] = *(uint8ptr++); 381 ecid[2] = *(uint8ptr++); 382 ecid[3] = *(uint8ptr++); 383 if (BCM_PRE_DEFINED_ECID0 != ecid[0]) { 384 cli_out("Unexpected TLV ECID0 value: 0x%x\n", ecid[0]); 385 return 0; 386 } else if (BCM_PRE_DEFINED_ECID1 != ecid[1]) { 387 cli_out("Unexpected TLV ECID1 value: 0x%x\n", ecid[1]); 388 return 0; 389 } else if (BCM_PRE_DEFINED_ECID2 != ecid[2]) { 390 cli_out("Unexpected TLV ECID2 value: 0x%x\n", ecid[2]); 391 return 0; 392 } else if (BCM_PRE_DEFINED_ECID3 != ecid[3]) { 393 cli_out("Unexpected TLV ECID3 value: 0x%x\n", ecid[3]); 394 return 0; 395 } else { 396 /*cli_out("ECID TLV is detected: 0x%x, 0x%x, 0x%x, 0x%x\n", ecid[0], ecid[1], ecid[2], ecid[3]);*/ 397 } 398 return BCM_ECID_TLV_DETECTED_FLAG; 399 break; 400 default: 401 cli_out("Unknown subtype in BRCM TLV: %d\n", subtype); 402 return 0; 403 } 404 } 405 } 406 return 0; 407 } 408 409 410 int is_brcm_pe_pkt(bcm_pkt_t* pkt) 411 { 412 int offset, length; 413 int ether_type, rv, flags; 414 uint16 *ethtype; 415 416 /* Check ether type: offset:16, length: 2*/ 417 offset = 16; 418 length = 2; 419 ethtype = (uint16*)(pkt->pkt_data->data+offset); 420 ether_type = ntohs(*ethtype); 421 /* cli_out("%s() ethtype 0x%x\n", __FUNCTION__, ether_type); */ 422 if (ether_type != PE_MODE_ID_ETHERTYPE) { 423 return 0; 424 } 425 426 /* TLV parser started */ 427 offset += length; 428 flags = 0; 429 do { 430 rv = brcm_tlv_parser((char*)(pkt->pkt_data->data+offset), &length); 431 if (rv == LLDP_TLV_PARSER_DONE) { 432 break; 433 } else { 434 flags |= rv; 435 } 436 437 /* dead loop protection */ 438 if (length <= LLDP_TLV_HDR_LEN) { 439 break; 440 } 441 442 offset += length; 443 if (offset >= pkt->pkt_len - 4) { 444 break; 445 } 446 } while (1); 447 /* print flags; */ 448 if (flags == (BCM_MODE_TLV_DETECTED_FLAG | BCM_TAG_TLV_DETECTED_FLAG | BCM_ECID_TLV_DETECTED_FLAG)) { 449 return 1; 450 } else { 451 cli_out("Failed to parser packet\n"); 452 dump_pkt(pkt); 453 } 454 return 0; 455 } 456 457 458 bcm_rx_t pe_protocol_parser(int unit, bcm_pkt_t* pkt, void* cookie) 459 { 460 int rv; 461 462 if (NULL == pkt) { 463 cli_out("pkt is NULL\n"); 464 return BCM_RX_HANDLED; 465 } 466 467 rv = is_brcm_pe_pkt(pkt); 468 if (rv) { 469 if (pe_port_enable(unit, pkt->src_port)) { 470 cli_out("\nFailed to configure port %d into PE mdoe\n", pkt->src_port); 471 } 472 return BCM_RX_HANDLED; 473 } 474 else { 475 return BCM_RX_NOT_HANDLED; 476 } 477 } 478 479 480 void pe_disable_on_linkdown(int unit, bcm_port_t port, bcm_port_info_t *info) 481 { 482 if (info->linkstatus == 0) { 483 pe_port_disable(unit, port); 484 } 485 return; 486 } 487 488 489 int pemode_init(int unit) 490 { 491 int rv; 492 int port; 493 bcm_gport_t gport; 494 bcm_port_config_t port_config; 495 int val; 496 497 bcm_port_config_t_init(&port_config); 498 499 /* bcmSwitchL3EgressMode must be set */ 500 rv = bcm_switch_control_set(unit, bcmSwitchL3EgressMode, 1); 501 if (BCM_FAILURE(rv)) { 502 cli_out("Error to configure device into L3EgressMode: %s\n", bcm_errmsg(rv)); 503 return rv; 504 } 505 506 /* initialize the port extender module */ 507 rv = bcm_extender_init(unit); 508 if (BCM_FAILURE(rv)) { 509 cli_out("bcm_extender_init() failed: %s\n", bcm_errmsg(rv)); 510 return rv; 511 } 512 513 /* set extender ethertype to 0x893f (BR tag) */ 514 rv = bcm_switch_control_set(unit, bcmSwitchEtagEthertype, 0x893f); 515 if (BCM_FAILURE(rv)) { 516 cli_out("Error to configure extender more ether type: %s\n", bcm_errmsg(rv)); 517 return rv; 518 } 519 520 rv = bcm_port_config_get(unit, &port_config); 521 if (BCM_FAILURE(rv)) { 522 cli_out("Failed to get port config: %s\n", bcm_errmsg(rv)); 523 return rv; 524 } 525 526 cli_out("Initializing PE Mode\n"); 527 /* configure the switch to remove the Etag on egress of ethernet port */ 528 BCM_PBMP_ITER(port_config.all, port){ 529 rv = bcm_port_gport_get(unit, port, &gport); 530 if (BCM_FAILURE(rv)) { 531 cli_out("bcm_port_gport_get(%d) failed (%s)\n", port, bcm_errmsg(rv)); 532 return rv; 533 } 534 535 rv = bcm_port_control_get(unit, port, bcmPortControlExtenderType, &val); 536 if (BCM_FAILURE(rv)) { 537 cli_out("Failed to get port %d extender type: %s\n", port, bcm_errmsg(rv)); 538 return rv; 539 } 540 541 /* If the port is in PE mode, then next*/ 542 if (BCM_PORT_EXTENDER_TYPE_NONE != val) { 543 cli_out("Port %d is Port Extender, do not reconfigure\n", port); 544 continue; 545 } 546 547 /* Ethernet port, delete ETAG on egress */ 548 rv = bcm_port_control_set(unit, gport, bcmPortControlEtagEgressDelete, 1); 549 if (BCM_FAILURE(rv)) { 550 cli_out("Configure port(%d) to delete ETAG failed (%s)\n", port, bcm_errmsg(rv)); 551 return rv; 552 } 553 } 554 555 return BCM_E_NONE; 556 } 557 558 559 int pe_mode_enable(int unit) 560 { 561 bcm_rx_cfg_t rx_cfg; 562 563 cli_out("Initializing PE-ID packet and Link-Down Callbacks\n"); 564 bcm_rx_cfg_t_init(&rx_cfg); 565 bcm_rx_cfg_get(unit, &rx_cfg); 566 567 bcm_rx_register(unit, "PE PACKET PARSER", pe_protocol_parser, BCM_RX_PRIO_MAX, NULL, BCM_RCO_F_ALL_COS); 568 bcm_linkscan_register(unit, pe_disable_on_linkdown); 569 if (!bcm_rx_active(unit)) { 570 cli_out("Starting RX process\n"); 571 bcm_rx_start(unit, &rx_cfg); 572 } else { 573 cli_out("RX process is already started\n"); 574 } 575 return CMD_OK; 576 } 577 578 #endif /* PE_MODE_CMD */ 579