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, ®_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, ®_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 */