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

nat.c (15151B)


      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:    nat.c
      8  * Purpose: Implement Trident3 specific Large Scale NAT APIs.
      9  */
     10 
     11 #include <shared/bsl.h>
     12 #include <bcm/error.h>
     13 #include <soc/defs.h>
     14 #include <soc/mem.h>
     15 
     16 #if defined(INCLUDE_L3) 
     17 #include <bcm/l3.h>
     18 #include <bcm/nat.h>
     19 
     20 #include <bcm_int/esw/nat.h>
     21 
     22 #include <soc/trident3.h>
     23 
     24 #if defined (BCM_TRIDENT3_SUPPORT)
     25 
     26 STATIC soc_field_t sip_f[3] = {
     27     LS_NAT_L3UC_DNAT__INNER_IPV4_SIPf,
     28     LS_NAT_L3UC_SNAT__INNER_IPV4_SIPf,
     29     LS_NAT_L3MC__INNER_IPV4_SIPf};
     30 
     31 STATIC soc_field_t dip_31_17_f[3] = {
     32     LS_NAT_L3UC_DNAT__INNER_IPV4_DIP_31_17f,
     33     LS_NAT_L3UC_SNAT__INNER_IPV4_DIP_31_17f,
     34     LS_NAT_L3MC__INNER_IPV4_DIP_31_17f};
     35 
     36 STATIC soc_field_t dip_16_0_f[3] = {
     37     LS_NAT_L3UC_DNAT__INNER_IPV4_DIP_16_0f,
     38     LS_NAT_L3UC_SNAT__INNER_IPV4_DIP_16_0f,
     39     LS_NAT_L3MC__INNER_IPV4_DIP_16_0f};
     40 
     41 STATIC soc_field_t l4src_f[3] = {
     42     LS_NAT_L3UC_DNAT__L4_SRC_PORTf,
     43     LS_NAT_L3UC_SNAT__L4_SRC_PORTf,
     44     LS_NAT_L3MC__L4_SRC_PORTf};
     45 
     46 STATIC soc_field_t l4dst_f[3] = {
     47     LS_NAT_L3UC_DNAT__L4_DST_PORTf,
     48     LS_NAT_L3UC_SNAT__L4_DST_PORTf,
     49     LS_NAT_L3MC__L4_DST_PORTf};
     50 
     51 STATIC soc_field_t edit_f[3] = {
     52     LS_NAT_L3UC_DNAT__EDIT_CTRL_IDf,
     53     LS_NAT_L3UC_SNAT__EDIT_CTRL_IDf,
     54     LS_NAT_L3MC__EDIT_CTRL_IDf};
     55 
     56 STATIC soc_field_t key_type_v[3] = {
     57     BCM_TD3_L3UC_DNAT_KEY_TYPE,
     58     BCM_TD3_L3UC_SNAT_KEY_TYPE,
     59     BCM_TD3_L3MC_NAT_KEY_TYPE};
     60 
     61 STATIC soc_field_t opaque_f[3] = {
     62     LS_NAT_L3UC_DNAT__LARGE_SCALE_NAT_DST_EDIT_INDEXf,
     63     LS_NAT_L3UC_SNAT__LARGE_SCALE_NAT_SRC_EDIT_INDEXf,
     64     LS_NAT_L3MC__LARGE_SCALE_NAT_DST_EDIT_INDEXf};
     65 
     66 STATIC soc_field_t rw_type_f[3] = {
     67     L3MC_RW_TYPE_0_SELf,
     68     L3MC_RW_TYPE_1_SELf,
     69     L3MC_RW_TYPE_2_SELf};
     70 
     71 STATIC soc_field_t da_update_f[3] = {
     72     L3MC_RW_TYPE_0_MAC_DA_UPDATEf,
     73     L3MC_RW_TYPE_1_MAC_DA_UPDATEf,
     74     L3MC_RW_TYPE_2_MAC_DA_UPDATEf};
     75 
     76 int
     77 _bcm_td3_large_nat_egress_key_set(
     78     int unit,
     79     soc_mem_t mem,
     80     bcm_l3_large_nat_egress_t *nat_info,
     81     int *nat_key,
     82     uint32 *entry)
     83 {
     84     int rv = BCM_E_NONE;
     85     int nat_lookup;
     86     uint32 data_type = BCM_TD3_L3_NAT_DATA_TYPE;
     87 
     88     if (soc_mem_field_valid(unit, mem, BASE_VALIDf)) {
     89         soc_mem_field32_set(unit, mem, entry, BASE_VALIDf, 1);
     90     } else {
     91         soc_mem_field32_set(unit, mem, entry, BASE_VALID_0f, 3);
     92         soc_mem_field32_set(unit, mem, entry, BASE_VALID_1f, 7);
     93     }
     94 
     95     if (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_MULTICAST)  {
     96         soc_mem_field32_set(unit, mem, entry,
     97              LS_NAT_L3MC__REPLICATION_IDf,
     98              nat_info->replication_id);
     99         nat_lookup = BCM_TD3_L3MC_FIELDS;
    100     }
    101     else if (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_SNAT) {
    102         soc_mem_field32_set(unit, mem, entry,
    103              LS_NAT_L3UC_SNAT__EGR_L3_INTF_CLASS_IDf,
    104              nat_info->intf_class_id);
    105         nat_lookup = BCM_TD3_L3UC_SNAT_FIELDS;
    106     }
    107     else {
    108         nat_lookup = BCM_TD3_L3UC_DNAT_FIELDS;
    109         nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_DNAT;
    110     }
    111 
    112     soc_mem_field32_set(unit, mem, entry,
    113              opaque_f[nat_lookup], nat_info->nat_class_id);
    114 
    115     soc_mem_field32_set(unit, mem, entry, KEY_TYPEf,  key_type_v[nat_lookup]);
    116     soc_mem_field32_set(unit, mem, entry, DATA_TYPEf, data_type);
    117     *nat_key = nat_lookup;
    118 
    119     return rv;
    120 }
    121 
    122 STATIC int
    123 _bcm_td3_nat_egress_ls_find_mc_edit_type(
    124     int unit,
    125     bcm_l3_large_nat_egress_t *nat_info,
    126     int *rw_type) {
    127 
    128     int num_rw_types;
    129     int i, exp_rw_type;
    130     uint32   reg_val;
    131 
    132     if (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_DNAT &&
    133         nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_SNAT) {
    134         exp_rw_type = (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_NAPT) ? 6 : 3;
    135     }
    136     else if (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_DNAT) {
    137         exp_rw_type = (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_NAPT) ? 4 : 1;
    138     }
    139     else if (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_SNAT) {
    140         exp_rw_type = (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_NAPT) ? 5 : 2;
    141     }
    142     else {
    143         exp_rw_type = 0;
    144     }
    145 
    146    /* find IPMC RW register */
    147    READ_LARGE_SCALE_NAT_CONTROLr(unit, &reg_val);
    148 
    149    num_rw_types = sizeof(rw_type_f)/sizeof(rw_type_f[0]);
    150 
    151    for (i=0; i<num_rw_types; i++) {
    152        int field_val = soc_reg_field_get(unit,
    153                             LARGE_SCALE_NAT_CONTROLr, reg_val,
    154                             rw_type_f[i]);
    155        int da_update = soc_reg_field_get(unit,
    156                             LARGE_SCALE_NAT_CONTROLr, reg_val,
    157                             da_update_f[i]);
    158        if ((field_val == exp_rw_type) &&
    159            (da_update == ((nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_MACDA_UPDATE)!=0))) {
    160             break;
    161         }
    162    }
    163 
    164    if (i==num_rw_types) return BCM_E_CONFIG;
    165    /* found a matching type */
    166    *rw_type = i+1;
    167 
    168    return BCM_E_NONE;
    169 }
    170 
    171 STATIC int
    172 _bcm_td3_large_nat_egress_entry_set(
    173     int unit,
    174     soc_mem_t mem,
    175     bcm_l3_large_nat_egress_t *nat_info,
    176     uint32 nat_key,
    177     uint32 *entry)
    178 {
    179     int rv = BCM_E_NONE;
    180     int index = -1;
    181     uint32 return_entry[SOC_MAX_MEM_WORDS];
    182     int new_entry_add = FALSE;
    183     int edit_type = 0x1;
    184     rv = soc_mem_search(unit, mem, MEM_BLOCK_ANY, &index, entry,
    185                         return_entry, 0);
    186 
    187     if (BCM_E_NONE == rv) {
    188         /* found memory entry */
    189         if (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_REPLACE) {
    190             sal_memcpy(entry,return_entry,SOC_MAX_MEM_WORDS * 4);
    191         } else {
    192             return BCM_E_EXISTS;
    193         }
    194     } else if (BCM_E_NOT_FOUND == rv) {
    195         if (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_REPLACE) {
    196             return BCM_E_NOT_FOUND;
    197         } else {
    198             new_entry_add = TRUE;
    199         }
    200     } else {
    201         return rv;
    202     }
    203 
    204     if (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_MULTICAST) {
    205         BCM_IF_ERROR_RETURN(
    206             _bcm_td3_nat_egress_ls_find_mc_edit_type(unit, nat_info, &edit_type));
    207     }
    208 
    209     soc_mem_field32_set(unit, mem, entry, edit_f[nat_key],
    210         edit_type);
    211 
    212     soc_mem_field32_set(unit, mem, entry, sip_f[nat_key],
    213         nat_info->sip_addr);
    214 
    215     soc_mem_field32_set(unit, mem, entry, l4src_f[nat_key],
    216         nat_info->src_port);
    217 
    218     soc_mem_field32_set(unit, mem, entry, dip_16_0_f[nat_key],
    219         nat_info->dip_addr & 0x1ffff);
    220 
    221     soc_mem_field32_set(unit, mem, entry, dip_31_17_f[nat_key],
    222         nat_info->dip_addr>>17);
    223 
    224     soc_mem_field32_set(unit, mem, entry, l4dst_f[nat_key],
    225         nat_info->dst_port);
    226 
    227     if (new_entry_add) {
    228         rv = soc_mem_insert(unit, mem, MEM_BLOCK_ALL, entry);
    229     } else { /* replace action */
    230         rv = soc_mem_write(unit, mem, MEM_BLOCK_ALL, index, entry);
    231     }
    232 
    233     BCM_IF_ERROR_RETURN(rv);
    234     return rv;
    235 }
    236 
    237 int
    238 _bcm_td3_large_nat_egress_add(
    239     int unit,
    240     bcm_l3_large_nat_egress_t *nat_info)
    241 {
    242     soc_mem_t mem = EGR_VLAN_XLATE_2_DOUBLEm;
    243     uint32 entry[SOC_MAX_MEM_WORDS];
    244     int rv, nat_key;
    245 
    246     sal_memset(entry, 0, sizeof(entry));
    247 
    248     BCM_IF_ERROR_RETURN(
    249         _bcm_td3_large_nat_egress_key_set(unit,
    250              mem, nat_info, &nat_key, entry));
    251 
    252     soc_mem_lock(unit,mem);
    253 
    254     /* set the match object and attributes and write to memory */
    255     rv = _bcm_td3_large_nat_egress_entry_set(unit, mem,
    256                         nat_info, nat_key, entry);
    257 
    258     soc_mem_unlock(unit,mem);
    259 
    260     return rv;
    261 }
    262 
    263 STATIC void
    264 _bcm_td3_nat_egress_ls_get_mc_edit_type(
    265     int unit,
    266     int rw_type,
    267     bcm_l3_large_nat_egress_t *nat_info)
    268 {
    269     uint32 reg_val;
    270     uint32 field_val;
    271 
    272     if (rw_type >= 3) return;
    273 
    274     READ_LARGE_SCALE_NAT_CONTROLr(unit, &reg_val);
    275 
    276     if (soc_reg_field_get(unit,
    277         LARGE_SCALE_NAT_CONTROLr, reg_val, da_update_f[rw_type])) {
    278         nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_MACDA_UPDATE;
    279     }
    280 
    281     field_val = soc_reg_field_get(unit,
    282         LARGE_SCALE_NAT_CONTROLr, reg_val, rw_type_f[rw_type]);
    283 
    284     switch (field_val) {
    285         case 1:
    286             nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_DNAT;
    287             break;
    288         case 2:
    289             nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_SNAT;
    290             break;
    291         case 3:
    292             nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_DNAT |
    293                                BCM_L3_LARGE_NAT_EGRESS_SNAT;
    294             break;
    295         case 4:
    296             nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_DNAT |
    297                                BCM_L3_LARGE_NAT_EGRESS_NAPT;
    298             break;
    299         case 5:
    300             nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_SNAT |
    301                                BCM_L3_LARGE_NAT_EGRESS_NAPT;
    302             break;
    303         case 6:
    304             nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_DNAT |
    305                                BCM_L3_LARGE_NAT_EGRESS_SNAT |
    306                                BCM_L3_LARGE_NAT_EGRESS_NAPT;
    307             break;
    308         default:
    309             break;
    310    }
    311 }
    312 
    313 STATIC void
    314 _bcm_td3_large_nat_egress_data_get(
    315     int unit,
    316     soc_mem_t mem,
    317     uint32* entry,
    318     uint32 nat_lookup,
    319     bcm_l3_large_nat_egress_t *nat_info)
    320 {
    321     uint32 rw_type;
    322     uint32 edit_type;
    323 
    324     if (nat_info->flags & BCM_L3_LARGE_NAT_EGRESS_MULTICAST) {
    325         edit_type = soc_mem_field32_get(unit, mem, entry, edit_f[nat_lookup]);
    326         rw_type = edit_type - 1;
    327 
    328         (void) _bcm_td3_nat_egress_ls_get_mc_edit_type(
    329             unit, rw_type, nat_info);
    330     }
    331 
    332     nat_info->sip_addr = soc_mem_field32_get(unit, mem, entry,
    333         sip_f[nat_lookup]);
    334 
    335     nat_info->dip_addr = soc_mem_field32_get(unit, mem, entry,
    336         dip_16_0_f[nat_lookup]);
    337 
    338     nat_info->dip_addr |= soc_mem_field32_get(unit, mem, entry,
    339         dip_31_17_f[nat_lookup])<<17;
    340 
    341     nat_info->src_port = soc_mem_field32_get(unit, mem, entry,
    342         l4src_f[nat_lookup]);
    343 
    344     nat_info->dst_port = soc_mem_field32_get(unit, mem, entry,
    345         l4dst_f[nat_lookup]);
    346 }
    347 int
    348 _bcm_td3_large_nat_egress_entry_get(
    349     int unit,
    350     soc_mem_t mem,
    351     uint32* entry,
    352     uint32 nat_lookup,
    353     bcm_l3_large_nat_egress_t *nat_info)
    354 {
    355     int index = -1;
    356     uint32 return_entry[SOC_MAX_MEM_WORDS];
    357 
    358     SOC_IF_ERROR_RETURN(soc_mem_search(unit, mem, MEM_BLOCK_ANY, &index,
    359         entry, return_entry, 0));
    360 
    361     _bcm_td3_large_nat_egress_data_get(unit, mem, return_entry,
    362         nat_lookup, nat_info);
    363 
    364     return BCM_E_NONE;
    365 }
    366 
    367 int
    368 _bcm_td3_evxlt_entry_valid(
    369     int unit,
    370     soc_mem_t mem,
    371     uint32 *entry,
    372     int *valid)
    373 {
    374     uint32 base_valid_0 = 0;
    375     uint32 base_valid_1 = 0;
    376     uint32 base_valid = 0;
    377 
    378     if (INVALIDm == mem) {
    379         return BCM_E_MEMORY;
    380     }
    381 
    382     if ((NULL == entry) || (NULL == valid)) {
    383         return BCM_E_PARAM;
    384     }
    385     if (soc_mem_field_valid(unit, mem, BASE_VALID_0f)) {
    386         base_valid_0 = soc_mem_field32_get(unit, mem, entry, BASE_VALID_0f);
    387     }
    388     if (soc_mem_field_valid(unit, mem, BASE_VALID_1f)) {
    389         base_valid_1 = soc_mem_field32_get(unit, mem, entry, BASE_VALID_1f);
    390     }
    391     if (soc_mem_field_valid(unit, mem, BASE_VALIDf)) {
    392         base_valid = soc_mem_field32_get(unit, mem, entry, BASE_VALIDf);
    393     }
    394 
    395     if ((base_valid != 0x1) && ((base_valid_0 == 0) || (base_valid_1 == 0)) ) {
    396         *valid = FALSE;
    397     } else {
    398         *valid = TRUE;
    399     }
    400     return BCM_E_NONE;
    401 }
    402 
    403 STATIC
    404 int _bcm_td3_large_nat_egress_entry_parse(
    405     int unit,
    406     soc_mem_t mem,
    407     uint32 *entry,
    408     bcm_l3_large_nat_egress_t *nat_info)
    409 {
    410     uint32 key_type;
    411     uint32 nat_lookup;
    412 
    413     key_type = soc_mem_field32_get(unit, mem, entry, KEY_TYPEf);
    414 
    415     if (key_type == BCM_TD3_L3MC_NAT_KEY_TYPE) {
    416         nat_info->replication_id = soc_mem_field32_get(unit,
    417                                        mem, entry, LS_NAT_L3MC__REPLICATION_IDf);
    418         nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_MULTICAST;
    419         nat_lookup = BCM_TD3_L3MC_FIELDS;
    420     }
    421     else if (key_type == BCM_TD3_L3UC_SNAT_KEY_TYPE) {
    422         nat_info->intf_class_id = soc_mem_field32_get(unit,
    423                                    mem, entry,
    424                                    LS_NAT_L3UC_SNAT__EGR_L3_INTF_CLASS_IDf);
    425         nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_SNAT;
    426         nat_lookup = BCM_TD3_L3UC_SNAT_FIELDS;
    427     }
    428     else if (key_type == BCM_TD3_L3UC_DNAT_KEY_TYPE) {
    429         nat_info->flags |= BCM_L3_LARGE_NAT_EGRESS_DNAT;
    430         nat_lookup = BCM_TD3_L3UC_DNAT_FIELDS;
    431     }
    432     else {
    433         return BCM_E_NOT_FOUND;
    434     }
    435 
    436     nat_info->nat_class_id = soc_mem_field32_get(unit,
    437                                 mem, entry, opaque_f[nat_lookup]);
    438 
    439     _bcm_td3_large_nat_egress_data_get(unit,
    440                                 mem, entry, nat_lookup, nat_info);
    441 
    442     return BCM_E_NONE;
    443 }
    444 
    445 int _bcm_td3_large_nat_egress_traverse(
    446     int unit,
    447     soc_mem_t mem,
    448     bcm_l3_large_nat_egress_traverse_cb cb,
    449     void *user_data)
    450 {
    451 
    452     /* Indexes to iterate over memories, chunks and entries */
    453     int             chnk_idx, ent_idx, chnk_idx_max, mem_idx_max;
    454     int             buf_size, chunksize, chnk_end;
    455     /* Buffer to store chunk of table we currently work on */
    456     uint32          *ls_tbl_chnk;
    457     uint32          *entry;
    458     int             valid, rv = BCM_E_NONE;
    459     bcm_l3_large_nat_egress_t nat_info;
    460 
    461     if (INVALIDm == mem) {
    462         return BCM_E_MEMORY;
    463     }
    464     if (!soc_mem_index_count(unit, mem)) {
    465         return BCM_E_NONE;
    466     }
    467 
    468     chunksize = 256;
    469 
    470     buf_size = 4 * SOC_MAX_MEM_FIELD_WORDS * chunksize;
    471     ls_tbl_chnk = soc_cm_salloc(unit, buf_size, "large scale nat traverse");
    472     if (NULL == ls_tbl_chnk) {
    473         return BCM_E_MEMORY;
    474     }
    475 
    476     mem_idx_max = soc_mem_index_max(unit, mem);
    477     for (chnk_idx = soc_mem_index_min(unit, mem);
    478          chnk_idx <= mem_idx_max;
    479          chnk_idx += chunksize) {
    480         sal_memset((void *)ls_tbl_chnk, 0, buf_size);
    481 
    482         chnk_idx_max =
    483             ((chnk_idx + chunksize) <= mem_idx_max) ?
    484             chnk_idx + chunksize - 1: mem_idx_max;
    485 
    486         rv = soc_mem_read_range(unit, mem, MEM_BLOCK_ANY,
    487                                 chnk_idx, chnk_idx_max, ls_tbl_chnk);
    488         if (SOC_FAILURE(rv)) {
    489             break;
    490         }
    491         chnk_end = (chnk_idx_max - chnk_idx);
    492         for (ent_idx = 0 ; ent_idx <= chnk_end; ent_idx ++) {
    493             entry =
    494                 soc_mem_table_idx_to_pointer(unit, mem, uint32 *,
    495                                              ls_tbl_chnk, ent_idx);
    496             rv = _bcm_td3_evxlt_entry_valid(unit, mem, entry, &valid);
    497             if (BCM_FAILURE(rv)) {
    498                 break;
    499             }
    500             if (!valid) {
    501                 continue;
    502             }
    503 
    504             bcm_l3_large_nat_egress_t_init(&nat_info);
    505 
    506             rv = _bcm_td3_large_nat_egress_entry_parse(unit, mem, entry, &nat_info);
    507             if (rv == BCM_E_NOT_FOUND) { /* not a large scale nat entry */
    508                 continue;
    509             } else if (BCM_FAILURE(rv)) {
    510                 break;
    511             }
    512             rv = cb(unit, &nat_info, user_data);
    513             if (BCM_FAILURE(rv)) {
    514                 break;
    515             }
    516         }
    517     }
    518     soc_cm_sfree(unit, ls_tbl_chnk);
    519     return rv;
    520 }
    521 
    522 #endif  /* BCM_TRIDENT3_SUPPORT */
    523 
    524 #endif /* INCLUDE_L3 */