ipmc.c (16278B)
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: bcmx/ipmc.c 8 * Purpose: BCMX IP Multicast APIs 9 */ 10 11 #ifdef INCLUDE_L3 12 13 #include <sal/core/libc.h> 14 15 #include <bcm/types.h> 16 17 #include <bcmx/ipmc.h> 18 #include <bcmx/lport.h> 19 #include <bcmx/bcmx.h> 20 #include <bcmx/lplist.h> 21 #include <bcmx/multicast.h> 22 23 #include "bcmx_int.h" 24 25 #define BCMX_IPMC_INIT_CHECK BCMX_READY_CHECK 26 27 #define BCMX_IPMC_ERROR_CHECK(_unit, _check, _rv) \ 28 BCMX_ERROR_CHECK(_unit, _check, _rv) 29 30 #define BCMX_IPMC_SET_ERROR_CHECK(_unit, _check, _rv) \ 31 BCMX_SET_ERROR_CHECK(_unit, _check, _rv) 32 33 #define BCMX_IPMC_DELETE_ERROR_CHECK(_unit, _check, _rv) \ 34 BCMX_DELETE_ERROR_CHECK(_unit, _check, _rv) 35 36 #define BCMX_IPMC_GET_IS_VALID(_unit, _rv) \ 37 BCMX_ERROR_IS_VALID(_unit, _rv) 38 39 /* 40 * Function: 41 * bcmx_ipmc_init 42 */ 43 44 int 45 bcmx_ipmc_init(void) 46 { 47 int rv = BCM_E_UNAVAIL, tmp_rv; 48 int i, bcm_unit; 49 50 BCMX_IPMC_INIT_CHECK; 51 52 BCMX_UNIT_ITER(bcm_unit, i) { 53 tmp_rv = bcm_ipmc_init(bcm_unit); 54 BCM_IF_ERROR_RETURN(BCMX_IPMC_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 55 } 56 57 return rv; 58 } 59 60 61 /* 62 * Function: 63 * bcmx_ipmc_detach 64 */ 65 66 int 67 bcmx_ipmc_detach(void) 68 { 69 int rv = BCM_E_UNAVAIL, tmp_rv; 70 int i, bcm_unit; 71 72 BCMX_IPMC_INIT_CHECK; 73 74 BCMX_UNIT_ITER(bcm_unit, i) { 75 tmp_rv = bcm_ipmc_detach(bcm_unit); 76 BCM_IF_ERROR_RETURN(BCMX_IPMC_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 77 } 78 79 return rv; 80 } 81 82 83 /* 84 * Function: 85 * bcmx_ipmc_enable 86 */ 87 88 int 89 bcmx_ipmc_enable(int enable) 90 { 91 int rv = BCM_E_UNAVAIL, tmp_rv; 92 int i, bcm_unit; 93 94 BCMX_IPMC_INIT_CHECK; 95 96 BCMX_UNIT_ITER(bcm_unit, i) { 97 tmp_rv = bcm_ipmc_enable(bcm_unit, enable); 98 BCM_IF_ERROR_RETURN(BCMX_IPMC_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 99 } 100 101 return rv; 102 } 103 104 105 /* 106 * Function: 107 * bcmx_ipmc_addr_t_init 108 */ 109 110 void 111 bcmx_ipmc_addr_t_init(bcmx_ipmc_addr_t *data) 112 { 113 sal_memset(data, 0, sizeof(*data)); 114 bcmx_lplist_init(&data->l2_ports, 0, 0); 115 bcmx_lplist_init(&data->l2_untag_ports, 0, 0); 116 bcmx_lplist_init(&data->l3_ports, 0, 0); 117 } 118 119 void 120 bcmx_ipmc_addr_t_free(bcmx_ipmc_addr_t *data) 121 { 122 bcmx_lplist_free(&data->l2_ports); 123 bcmx_lplist_free(&data->l2_untag_ports); 124 bcmx_lplist_free(&data->l3_ports); 125 } 126 127 128 /* 129 * Function: 130 * _bcmx_ipmc_addr_t_to_bcm 131 * Purpose: 132 * Assigns BCMX src IP, mc IP, Vlan, and VRF to BCM IPMC; 133 * IP version agnostic 134 * Parameters: 135 * data - BCMX IPMC struct 136 * ipmc - (OUT) BCM IPMC struct 137 * Returns: 138 * None 139 */ 140 void 141 _bcmx_ipmc_addr_t_to_bcm(bcmx_ipmc_addr_t *data, bcm_ipmc_addr_t *ipmc) 142 { 143 bcm_ipmc_addr_t_init(ipmc); 144 ipmc->vid = data->vid; 145 ipmc->vrf = data->vrf; 146 147 if (data->flags & BCM_IPMC_IP6) { 148 sal_memcpy(ipmc->s_ip6_addr, data->s_ip6_addr, BCM_IP6_ADDRLEN); 149 sal_memcpy(ipmc->mc_ip6_addr, data->mc_ip6_addr, BCM_IP6_ADDRLEN); 150 ipmc->flags |= BCM_IPMC_IP6; 151 } else { 152 ipmc->s_ip_addr = data->s_ip_addr; 153 ipmc->mc_ip_addr = data->mc_ip_addr; 154 } 155 } 156 157 /* 158 * Function: 159 * _bcm_ipmc_addr_t_to_bcmx 160 * Purpose: 161 * Assigns BCM src IP, mc IP, Vlan, and VRF to BCMX IPMC; 162 * IP version agnostic 163 * Parameters: 164 * data - BCM IPMC struct 165 * ipmc - (OUT) BCMX IPMC struct 166 * Returns: 167 * None 168 */ 169 void 170 _bcm_ipmc_addr_t_to_bcmx(bcm_ipmc_addr_t *data, bcmx_ipmc_addr_t *ipmc) 171 { 172 bcmx_ipmc_addr_t_init(ipmc); 173 ipmc->vid = data->vid; 174 ipmc->vrf = data->vrf; 175 176 if (data->flags & BCM_IPMC_IP6) { 177 sal_memcpy(ipmc->s_ip6_addr, data->s_ip6_addr, BCM_IP6_ADDRLEN); 178 sal_memcpy(ipmc->mc_ip6_addr, data->mc_ip6_addr, BCM_IP6_ADDRLEN); 179 ipmc->flags |= BCM_IPMC_IP6; 180 } else { 181 ipmc->s_ip_addr = data->s_ip_addr; 182 ipmc->mc_ip_addr = data->mc_ip_addr; 183 } 184 } 185 186 /* 187 * Function: 188 * bcmx_ipmc_add 189 */ 190 191 int 192 bcmx_ipmc_add(bcmx_ipmc_addr_t *data) 193 { 194 int rv = BCM_E_UNAVAIL;/* Function return value. */ 195 int tmp_rv; /* Single op return value. */ 196 int bcm_unit; /* BCM device number. */ 197 int mcindex; /* Multicast table index. */ 198 int port; /* Port iterator. */ 199 int unit_idx; /* BCM devices iterator. */ 200 bcm_ipmc_addr_t bcm_ipmc; /* Added entry buffer. */ 201 bcmx_lport_t lport; /* Logical port number. */ 202 bcm_pbmp_t pbmp; /* L3 forwarding port bit map. */ 203 int lport_unit; 204 bcm_port_t lport_port; 205 bcm_module_t modid; 206 bcm_port_t modport; 207 208 BCMX_IPMC_INIT_CHECK; 209 210 _bcmx_ipmc_addr_t_to_bcm(data, &bcm_ipmc); 211 bcm_ipmc.lookup_class = data->lookup_class; 212 bcm_ipmc.cos = data->cos; 213 bcm_ipmc.ts = data->ts; 214 215 if (!(data->flags & BCM_IPMC_SOURCE_PORT_NOCHECK)) { 216 if (data->ts) { 217 bcm_ipmc.port_tgid = data->port_tgid; 218 } else { 219 BCM_IF_ERROR_RETURN 220 (_bcmx_dest_to_modid_port(data->port_tgid, 221 &modid, &modport, 222 BCMX_DEST_CONVERT_DEFAULT)); 223 bcm_ipmc.mod_id = (int)modid; 224 bcm_ipmc.port_tgid = (int)modport; 225 } 226 } 227 bcm_ipmc.v = 1; /* VALID */ 228 bcm_ipmc.flags = data->flags; 229 230 /* 231 * Find ipmc index already in use for this address (if any) 232 */ 233 mcindex = data->ipmc_index; 234 bcm_ipmc.group = data->ipmc_index; 235 236 /* 237 * Add ipmc entry into non-fabric switches. 238 * First one might allocate an ipmc index if needed. 239 */ 240 BCMX_UNIT_ITER(bcm_unit, unit_idx) { 241 if (BCM_IS_FABRIC(bcm_unit)) { 242 continue; 243 } 244 tmp_rv = bcm_ipmc_add(bcm_unit, &bcm_ipmc); 245 BCM_IF_ERROR_RETURN(BCMX_IPMC_SET_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 246 } 247 248 if (mcindex < 0) { 249 return rv; 250 } 251 252 /* 253 * Add ipmc entry into fabric switches. 254 */ 255 BCMX_UNIT_ITER(bcm_unit, unit_idx) { 256 if (!BCM_IS_FABRIC(bcm_unit)) { 257 continue; 258 } 259 BCM_PBMP_CLEAR(pbmp); 260 BCMX_FOREACH_QUALIFIED_LPORT(lport, 261 BCMX_PORT_F_HG| 262 BCMX_PORT_F_STACK_INT| 263 BCMX_PORT_F_STACK_EXT) { 264 if (BCM_SUCCESS 265 (_bcmx_dest_to_unit_port(lport, &lport_unit, &lport_port, 266 BCMX_DEST_CONVERT_NON_GPORT))) { 267 if (lport_unit == bcm_unit) { 268 BCM_PBMP_PORT_ADD(pbmp, lport_port); 269 } 270 } 271 } 272 BCM_PBMP_ITER(pbmp, port) { 273 tmp_rv = bcm_ipmc_bitmap_set(bcm_unit, mcindex, port, pbmp); 274 BCM_IF_ERROR_RETURN 275 (BCMX_IPMC_SET_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 276 } 277 } 278 279 return rv; 280 } 281 282 283 284 285 STATIC int 286 _ipmc_bcmx_to_bcm_addr(int unit, 287 bcmx_ipmc_addr_t *src, 288 bcm_ipmc_addr_t *dest, 289 int do_ports) 290 { 291 _bcmx_ipmc_addr_t_to_bcm(src, dest); 292 dest->flags = src->flags; 293 294 /* 295 * Convert destination data 296 */ 297 if (!(src->flags & BCM_IPMC_SOURCE_PORT_NOCHECK)) { 298 uint32 flags = BCMX_DEST_CONVERT_DEFAULT; 299 _bcmx_dest_bcm_t to_bcm; 300 _bcmx_dest_bcmx_t from_bcmx; 301 302 _bcmx_dest_bcmx_t_init(&from_bcmx); 303 304 /* Set flags and data to convert */ 305 if (src->ts) { 306 flags |= BCMX_DEST_TRUNK; 307 } 308 from_bcmx.port = src->port_tgid; 309 from_bcmx.trunk = src->port_tgid; 310 311 /* Convert */ 312 BCM_IF_ERROR_RETURN(_bcmx_dest_to_bcm(&from_bcmx, &to_bcm, &flags)); 313 314 /* Set converted flags and data */ 315 if (flags & BCMX_DEST_TRUNK) { 316 dest->ts = src->ts; 317 dest->port_tgid = to_bcm.trunk; 318 } else { 319 dest->mod_id = (int)to_bcm.module_id; 320 dest->port_tgid = (int)to_bcm.module_port; 321 } 322 } 323 324 /* Set remaining fields */ 325 dest->lookup_class = src->lookup_class; 326 dest->cos = src->cos; 327 dest->v = src->v; 328 dest->group = src->ipmc_index; 329 330 return BCM_E_NONE; 331 } 332 333 /* 334 * Function: 335 * bcmx_ipmc_remove 336 */ 337 338 int 339 bcmx_ipmc_remove(bcmx_ipmc_addr_t *data) 340 { 341 int rv = BCM_E_UNAVAIL, tmp_rv; 342 uint32 flags; 343 int i, bcm_unit; 344 bcm_ipmc_addr_t bcm_data; 345 bcm_ipmc_addr_t bcm_data_temp; 346 int mcindex; 347 bcm_pbmp_t empty; 348 bcm_port_t bcm_port; 349 bcmx_lport_t lport; 350 351 352 BCMX_IPMC_INIT_CHECK; 353 354 if (bcmx_multicast_group_get(data->ipmc_index, &flags) > 0) { 355 mcindex = data->ipmc_index; 356 } else { 357 mcindex = -1; 358 } 359 360 BCMX_UNIT_ITER(bcm_unit, i) { 361 if (BCM_IS_FABRIC(bcm_unit)) { 362 continue; 363 } 364 365 BCM_IF_ERROR_RETURN 366 (_ipmc_bcmx_to_bcm_addr(bcm_unit, data, &bcm_data, FALSE)); 367 368 if (mcindex < 0) { 369 bcm_data_temp = bcm_data; 370 tmp_rv = bcm_ipmc_find(bcm_unit, &bcm_data_temp); 371 if (BCM_SUCCESS(tmp_rv)) { 372 mcindex = bcm_data_temp.group; 373 } 374 } 375 376 tmp_rv = bcm_ipmc_remove(bcm_unit, &bcm_data); 377 BCM_IF_ERROR_RETURN 378 (BCMX_IPMC_DELETE_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 379 } 380 381 if (mcindex < 0) { 382 return rv; 383 } 384 385 /* 386 * Delete ipmc index from fabric 387 */ 388 BCM_PBMP_CLEAR(empty); 389 BCMX_FOREACH_QUALIFIED_LPORT(lport, 390 BCMX_PORT_F_HG| 391 BCMX_PORT_F_STACK_INT| 392 BCMX_PORT_F_STACK_EXT) { 393 if (BCM_SUCCESS 394 (_bcmx_dest_to_unit_port(lport, &bcm_unit, &bcm_port, 395 BCMX_DEST_CONVERT_NON_GPORT))) { 396 if (!BCM_IS_FABRIC(bcm_unit)) { 397 continue; 398 } 399 tmp_rv = bcm_ipmc_bitmap_set(bcm_unit, mcindex, bcm_port, empty); 400 BCM_IF_ERROR_RETURN 401 (BCMX_IPMC_DELETE_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 402 } 403 } 404 405 return rv; 406 } 407 408 409 /* 410 * Function: 411 * bcmx_ipmc_remove_all 412 */ 413 414 int 415 bcmx_ipmc_remove_all(void) 416 { 417 int rv = BCM_E_UNAVAIL, tmp_rv; 418 int i, bcm_unit; 419 420 BCMX_IPMC_INIT_CHECK; 421 422 BCMX_UNIT_ITER(bcm_unit, i) { 423 tmp_rv = bcm_ipmc_remove_all(bcm_unit); 424 BCM_IF_ERROR_RETURN 425 (BCMX_IPMC_DELETE_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 426 } 427 428 return rv; 429 } 430 431 /* 432 * Function: 433 * bcmx_ipmc_find 434 * Purpose: 435 * Given a SrcIP, McIP and Vlan, returns matching entry 436 * Parameters: 437 * data - (IN) accepts SrcIP, McIP, Vlan 438 * (OUT) will have MCIndex, L2_Ports, L3_Ports, Untag_Ports 439 * filled in the same structure 440 */ 441 int 442 bcmx_ipmc_find(bcmx_ipmc_addr_t *data) 443 { 444 int rv = BCM_E_UNAVAIL, tmp_rv; 445 int i, bcm_unit; 446 bcm_ipmc_addr_t bcm_ipmc; 447 int convert_rv = BCM_E_PORT; 448 int successes = 0; 449 450 BCMX_IPMC_INIT_CHECK; 451 452 BCMX_PARAM_NULL_CHECK(data); 453 454 _bcmx_ipmc_addr_t_to_bcm(data, &bcm_ipmc); 455 456 BCMX_UNIT_ITER(bcm_unit, i) { 457 tmp_rv = bcm_ipmc_find(bcm_unit, &bcm_ipmc); 458 459 if (BCMX_IPMC_GET_IS_VALID(bcm_unit, tmp_rv)) { 460 rv = tmp_rv; 461 462 if (BCM_FAILURE(tmp_rv)) { 463 break; 464 } 465 466 successes++; 467 data->flags |= bcm_ipmc.flags; 468 469 if (BCM_FAILURE(convert_rv)) { 470 /* The following data items will be gathered from the 471 first device found. The initial value for 472 convert_rv forces this to be called at least once. 473 This will continue to be called until a trunk is 474 found or a port is successfully converted. */ 475 476 if (!(bcm_ipmc.flags & BCM_IPMC_SOURCE_PORT_NOCHECK)) { 477 if (bcm_ipmc.ts) { 478 /* trunk */ 479 data->port_tgid = bcm_ipmc.port_tgid; 480 convert_rv = BCM_E_NONE; 481 } else { 482 /* port */ 483 convert_rv = 484 _bcmx_dest_from_modid_port(&data->port_tgid, 485 bcm_ipmc.mod_id, 486 bcm_ipmc.port_tgid, 487 BCMX_DEST_CONVERT_DEFAULT); 488 if (BCM_FAILURE(convert_rv)) { 489 /* If there was an error, see if there's a 490 subsequent unit with a convertible port. */ 491 continue; 492 } 493 } 494 } 495 data->cos = bcm_ipmc.cos; 496 data->ts = bcm_ipmc.ts; 497 data->v = bcm_ipmc.v; 498 data->ipmc_index = bcm_ipmc.group; 499 data->lookup_class = bcm_ipmc.lookup_class; 500 } 501 } 502 } 503 504 if (BCM_SUCCESS(rv) && successes > 0) { 505 /* If there have been otherwise no failures, and there was 506 more than one attempt to convert returned IPMC data, then 507 the final return value is the IPMC port converion 508 result. */ 509 rv = convert_rv; 510 } 511 512 return rv; 513 } 514 515 /* 516 * Function: 517 * bcmx_ipmc_egress_port_init 518 */ 519 520 int 521 bcmx_ipmc_egress_port_init(void) 522 { 523 int rv = BCM_E_UNAVAIL, tmp_rv; 524 int i, bcm_unit; 525 526 BCMX_IPMC_INIT_CHECK; 527 528 BCMX_UNIT_ITER(bcm_unit, i) { 529 tmp_rv = bcm_ipmc_egress_port_init(bcm_unit); 530 BCM_IF_ERROR_RETURN(BCMX_IPMC_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 531 } 532 533 return rv; 534 } 535 536 537 /* 538 * Function: 539 * bcmx_ipmc_egress_port_set 540 */ 541 542 int 543 bcmx_ipmc_egress_port_set(bcmx_lport_t port, 544 const bcm_mac_t mac, 545 int untag, 546 bcm_vlan_t vid, 547 int ttl_threshold) 548 { 549 int bcm_unit; 550 bcm_port_t bcm_port; 551 552 BCMX_IPMC_INIT_CHECK; 553 554 BCM_IF_ERROR_RETURN(_bcmx_dest_to_unit_port(port, &bcm_unit, &bcm_port, 555 BCMX_DEST_CONVERT_DEFAULT)); 556 557 return bcm_ipmc_egress_port_set(bcm_unit, bcm_port, 558 mac, untag, vid, ttl_threshold); 559 } 560 561 562 /* 563 * Function: 564 * bcmx_ipmc_egress_port_get 565 */ 566 567 int 568 bcmx_ipmc_egress_port_get(bcmx_lport_t port, 569 bcm_mac_t mac, 570 int *untag, 571 bcm_vlan_t *vid, 572 int *ttl_threshold) 573 { 574 int bcm_unit; 575 bcm_port_t bcm_port; 576 577 BCMX_IPMC_INIT_CHECK; 578 579 BCMX_PARAM_NULL_CHECK(untag); 580 BCMX_PARAM_NULL_CHECK(vid); 581 BCMX_PARAM_NULL_CHECK(ttl_threshold); 582 583 BCM_IF_ERROR_RETURN(_bcmx_dest_to_unit_port(port, &bcm_unit, &bcm_port, 584 BCMX_DEST_CONVERT_DEFAULT)); 585 586 return bcm_ipmc_egress_port_get(bcm_unit, bcm_port, 587 mac, untag, vid, ttl_threshold); 588 } 589 590 591 /* 592 * Function: 593 * bcmx_ipmc_counters_get 594 */ 595 596 int 597 bcmx_ipmc_counters_get(bcmx_lport_t port, 598 bcm_ipmc_counters_t *counters) 599 { 600 int bcm_unit; 601 bcm_port_t bcm_port; 602 603 BCMX_IPMC_INIT_CHECK; 604 605 BCMX_PARAM_NULL_CHECK(counters); 606 607 BCM_IF_ERROR_RETURN(_bcmx_dest_to_unit_port(port, &bcm_unit, &bcm_port, 608 BCMX_DEST_CONVERT_DEFAULT)); 609 610 return bcm_ipmc_counters_get(bcm_unit, bcm_port, counters); 611 } 612 613 614 /* 615 * Function: 616 * bcmx_ipmc_age 617 */ 618 619 int 620 bcmx_ipmc_age(uint32 flags, 621 bcm_ipmc_traverse_cb age_cb, 622 void *user_data) 623 { 624 return BCM_E_UNAVAIL; 625 } 626 627 /* 628 * Function: 629 * bcmx_ipmc_traverse 630 */ 631 632 int 633 bcmx_ipmc_traverse(uint32 flags, 634 bcm_ipmc_traverse_cb cb, 635 void *user_data) 636 { 637 int rv; 638 int i, bcm_unit; 639 640 BCMX_IPMC_INIT_CHECK; 641 642 BCMX_LOCAL_UNIT_ITER(bcm_unit, i) { 643 rv = bcm_ipmc_traverse(bcm_unit, flags, cb, user_data); 644 if (BCMX_IPMC_GET_IS_VALID(bcm_unit, rv)) { 645 return rv; 646 } 647 } 648 649 return BCM_E_UNAVAIL; 650 } 651 652 #else 653 int _bcmx_ipmc_not_empty; 654 #endif /* INCLUDE_L3 */ 655