openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

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