LCOV - code coverage report
Current view: top level - plugins/nat/nat44-ed - nat44_ed.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 1427 1808 78.9 %
Date: 2023-07-05 22:20:52 Functions: 109 123 88.6 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : 
      16             : #include <vpp/app/version.h>
      17             : 
      18             : #include <vnet/vnet.h>
      19             : #include <vnet/ip/ip.h>
      20             : #include <vnet/ip/ip4.h>
      21             : #include <vnet/ip/ip_table.h>
      22             : #include <vnet/ip/reass/ip4_sv_reass.h>
      23             : #include <vnet/fib/fib_table.h>
      24             : #include <vnet/fib/ip4_fib.h>
      25             : #include <vnet/plugin/plugin.h>
      26             : #include <vppinfra/bihash_16_8.h>
      27             : 
      28             : #include <nat/lib/log.h>
      29             : #include <nat/lib/nat_inlines.h>
      30             : #include <nat/lib/ipfix_logging.h>
      31             : #include <vnet/syslog/syslog.h>
      32             : #include <nat/lib/nat_syslog_constants.h>
      33             : #include <nat/lib/nat_syslog.h>
      34             : 
      35             : #include <nat/nat44-ed/nat44_ed.h>
      36             : #include <nat/nat44-ed/nat44_ed_affinity.h>
      37             : #include <nat/nat44-ed/nat44_ed_inlines.h>
      38             : 
      39             : #include <vlib/stats/stats.h>
      40             : 
      41             : snat_main_t snat_main;
      42             : 
      43             : static_always_inline void nat_validate_interface_counters (snat_main_t *sm,
      44             :                                                            u32 sw_if_index);
      45             : 
      46             : #define skip_if_disabled()                                                    \
      47             :   do                                                                          \
      48             :     {                                                                         \
      49             :       snat_main_t *sm = &snat_main;                                           \
      50             :       if (PREDICT_FALSE (!sm->enabled))                                       \
      51             :         return;                                                               \
      52             :     }                                                                         \
      53             :   while (0)
      54             : 
      55             : #define fail_if_enabled()                                                     \
      56             :   do                                                                          \
      57             :     {                                                                         \
      58             :       snat_main_t *sm = &snat_main;                                           \
      59             :       if (PREDICT_FALSE (sm->enabled))                                        \
      60             :         {                                                                     \
      61             :           nat_log_err ("plugin enabled");                                     \
      62             :           return VNET_API_ERROR_FEATURE_ALREADY_ENABLED;                      \
      63             :         }                                                                     \
      64             :     }                                                                         \
      65             :   while (0)
      66             : 
      67             : #define fail_if_disabled()                                                    \
      68             :   do                                                                          \
      69             :     {                                                                         \
      70             :       snat_main_t *sm = &snat_main;                                           \
      71             :       if (PREDICT_FALSE (!sm->enabled))                                       \
      72             :         {                                                                     \
      73             :           nat_log_err ("plugin disabled");                                    \
      74             :           return VNET_API_ERROR_FEATURE_ALREADY_DISABLED;                     \
      75             :         }                                                                     \
      76             :     }                                                                         \
      77             :   while (0)
      78             : 
      79       24639 : VNET_FEATURE_INIT (nat_pre_in2out, static) = {
      80             :   .arc_name = "ip4-unicast",
      81             :   .node_name = "nat-pre-in2out",
      82             :   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
      83             :                                "ip4-sv-reassembly-feature"),
      84             : };
      85       24639 : VNET_FEATURE_INIT (nat_pre_out2in, static) = {
      86             :   .arc_name = "ip4-unicast",
      87             :   .node_name = "nat-pre-out2in",
      88             :   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
      89             :                                "ip4-dhcp-client-detect",
      90             :                                "ip4-sv-reassembly-feature"),
      91             : };
      92       24639 : VNET_FEATURE_INIT (ip4_nat44_ed_classify, static) = {
      93             :   .arc_name = "ip4-unicast",
      94             :   .node_name = "nat44-ed-classify",
      95             :   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
      96             :                                "ip4-sv-reassembly-feature"),
      97             : };
      98       24639 : VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = {
      99             :   .arc_name = "ip4-unicast",
     100             :   .node_name = "nat44-handoff-classify",
     101             :   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
     102             :                                "ip4-sv-reassembly-feature"),
     103             : };
     104       24639 : VNET_FEATURE_INIT (snat_in2out_worker_handoff, static) = {
     105             :   .arc_name = "ip4-unicast",
     106             :   .node_name = "nat44-in2out-worker-handoff",
     107             :   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
     108             : };
     109       24639 : VNET_FEATURE_INIT (snat_out2in_worker_handoff, static) = {
     110             :   .arc_name = "ip4-unicast",
     111             :   .node_name = "nat44-out2in-worker-handoff",
     112             :   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
     113             :                                "ip4-dhcp-client-detect"),
     114             : };
     115       24639 : VNET_FEATURE_INIT (ip4_nat44_ed_in2out, static) = {
     116             :   .arc_name = "ip4-unicast",
     117             :   .node_name = "nat44-ed-in2out",
     118             :   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"),
     119             : };
     120       24639 : VNET_FEATURE_INIT (ip4_nat44_ed_out2in, static) = {
     121             :   .arc_name = "ip4-unicast",
     122             :   .node_name = "nat44-ed-out2in",
     123             :   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature",
     124             :                                "ip4-dhcp-client-detect"),
     125             : };
     126       24639 : VNET_FEATURE_INIT (nat_pre_in2out_output, static) = {
     127             :   .arc_name = "ip4-output",
     128             :   .node_name = "nat-pre-in2out-output",
     129             :   .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"),
     130             :   .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
     131             : };
     132       24639 : VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = {
     133             :   .arc_name = "ip4-output",
     134             :   .node_name = "nat44-in2out-output-worker-handoff",
     135             :   .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"),
     136             :   .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
     137             : };
     138       24639 : VNET_FEATURE_INIT (ip4_nat44_ed_in2out_output, static) = {
     139             :   .arc_name = "ip4-output",
     140             :   .node_name = "nat44-ed-in2out-output",
     141             :   .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"),
     142             :   .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
     143             : };
     144             : 
     145             : VLIB_PLUGIN_REGISTER () = {
     146             :     .version = VPP_BUILD_VER,
     147             :     .description = "Network Address Translation (NAT)",
     148             : };
     149             : 
     150             : static void nat44_ed_db_init ();
     151             : static void nat44_ed_db_free ();
     152             : static void nat44_ed_worker_db_free (snat_main_per_thread_data_t *tsm);
     153             : 
     154             : static int nat44_ed_add_static_mapping_internal (
     155             :   ip4_address_t l_addr, ip4_address_t e_addr, u16 l_port, u16 e_port,
     156             :   ip_protocol_t proto, u32 vrf_id, u32 sw_if_index, u32 flags,
     157             :   ip4_address_t pool_addr, u8 *tag);
     158             : static int nat44_ed_del_static_mapping_internal (ip4_address_t l_addr,
     159             :                                                  ip4_address_t e_addr,
     160             :                                                  u16 l_port, u16 e_port,
     161             :                                                  ip_protocol_t proto,
     162             :                                                  u32 vrf_id, u32 flags);
     163             : 
     164             : u32 nat_calc_bihash_buckets (u32 n_elts);
     165             : 
     166             : static_always_inline int
     167          27 : nat44_ed_sm_i2o_add (snat_main_t *sm, snat_static_mapping_t *m,
     168             :                      ip4_address_t addr, u16 port, u32 fib_index, u8 proto)
     169             : {
     170          27 :   ASSERT (!pool_is_free (sm->static_mappings, m));
     171             :   clib_bihash_kv_16_8_t kv;
     172          27 :   nat44_ed_sm_init_i2o_kv (&kv, addr.as_u32, port, fib_index, proto,
     173          27 :                            m - sm->static_mappings);
     174          27 :   return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, 1 /*is_add*/);
     175             : }
     176             : 
     177             : static_always_inline int
     178          37 : nat44_ed_sm_i2o_del (snat_main_t *sm, ip4_address_t addr, u16 port,
     179             :                      u32 fib_index, u8 proto)
     180             : {
     181             :   clib_bihash_kv_16_8_t kv;
     182          37 :   nat44_ed_sm_init_i2o_k (&kv, addr.as_u32, port, fib_index, proto);
     183          37 :   return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, 0 /*is_add*/);
     184             : }
     185             : 
     186             : static_always_inline int
     187          78 : nat44_ed_sm_o2i_add (snat_main_t *sm, snat_static_mapping_t *m,
     188             :                      ip4_address_t addr, u16 port, u32 fib_index, u8 proto)
     189             : {
     190          78 :   ASSERT (!pool_is_free (sm->static_mappings, m));
     191             :   clib_bihash_kv_16_8_t kv;
     192          78 :   nat44_ed_sm_init_o2i_kv (&kv, addr.as_u32, port, fib_index, proto,
     193          78 :                            m - sm->static_mappings);
     194          78 :   return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, 1 /*is_add*/);
     195             : }
     196             : 
     197             : static_always_inline int
     198          46 : nat44_ed_sm_o2i_del (snat_main_t *sm, ip4_address_t addr, u16 port,
     199             :                      u32 fib_index, u8 proto)
     200             : {
     201             :   clib_bihash_kv_16_8_t kv;
     202          46 :   nat44_ed_sm_init_o2i_k (&kv, addr.as_u32, port, fib_index, proto);
     203          46 :   return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, 0 /*is_add*/);
     204             : }
     205             : 
     206             : void
     207       28305 : nat44_ed_free_session_data (snat_main_t *sm, snat_session_t *s,
     208             :                             u32 thread_index, u8 is_ha)
     209             : {
     210       28305 :   per_vrf_sessions_unregister_session (s, thread_index);
     211             : 
     212       28305 :   if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 0))
     213           9 :     nat_elog_warn (sm, "flow hash del failed");
     214             : 
     215       28305 :   if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0))
     216           8 :     nat_elog_warn (sm, "flow hash del failed");
     217             : 
     218       28305 :   if (na44_ed_is_fwd_bypass_session (s))
     219             :     {
     220           6 :       return;
     221             :     }
     222             : 
     223       28299 :   if (nat44_ed_is_affinity_session (s))
     224           2 :     nat_affinity_unlock (s->ext_host_addr, s->out2in.addr, s->proto,
     225           2 :                          s->out2in.port);
     226             : 
     227       28299 :   if (!is_ha)
     228       28299 :     nat_syslog_nat44_sdel (0, s->in2out.fib_index, &s->in2out.addr,
     229       28299 :                            s->in2out.port, &s->ext_host_nat_addr,
     230       28299 :                            s->ext_host_nat_port, &s->out2in.addr,
     231       28299 :                            s->out2in.port, &s->ext_host_addr, s->ext_host_port,
     232       28299 :                            s->proto, nat44_ed_is_twice_nat_session (s));
     233             : 
     234       28299 :   if (!is_ha)
     235             :     {
     236             :       /* log NAT event */
     237       28299 :       nat_ipfix_logging_nat44_ses_delete (
     238       28299 :         thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32, s->proto,
     239       28299 :         s->in2out.port, s->out2in.port, s->in2out.fib_index);
     240             :     }
     241             : }
     242             : 
     243             : static ip_interface_address_t *
     244           0 : nat44_ed_get_ip_interface_address (u32 sw_if_index, ip4_address_t addr)
     245             : {
     246           0 :   snat_main_t *sm = &snat_main;
     247             : 
     248           0 :   ip_lookup_main_t *lm = &sm->ip4_main->lookup_main;
     249             :   ip_interface_address_t *ia;
     250             :   ip4_address_t *ip4a;
     251             : 
     252           0 :   foreach_ip_interface_address (
     253             :     lm, ia, sw_if_index, 1, ({
     254             :       ip4a = ip_interface_address_get_address (lm, ia);
     255             :       nat_log_debug ("sw_if_idx: %u addr: %U ? %U", sw_if_index,
     256             :                      format_ip4_address, ip4a, format_ip4_address, &addr);
     257             :       if (ip4a->as_u32 == addr.as_u32)
     258             :         {
     259             :           return ia;
     260             :         }
     261             :     }));
     262           0 :   return NULL;
     263             : }
     264             : 
     265             : static int
     266         524 : nat44_ed_resolve_nat_addr_len (snat_address_t *ap,
     267             :                                snat_interface_t *interfaces)
     268             : {
     269             :   ip_interface_address_t *ia;
     270             :   snat_interface_t *i;
     271             :   u32 fib_index;
     272             : 
     273         929 :   pool_foreach (i, interfaces)
     274             :     {
     275         405 :       if (!nat44_ed_is_interface_outside (i))
     276             :         {
     277         203 :           continue;
     278             :         }
     279             : 
     280         202 :       fib_index = ip4_fib_table_get_index_for_sw_if_index (i->sw_if_index);
     281         202 :       if (fib_index != ap->fib_index)
     282             :         {
     283         202 :           continue;
     284             :         }
     285             : 
     286           0 :       if ((ia = nat44_ed_get_ip_interface_address (i->sw_if_index, ap->addr)))
     287             :         {
     288           0 :           ap->addr_len = ia->address_length;
     289           0 :           ap->sw_if_index = i->sw_if_index;
     290           0 :           ap->net.as_u32 = ap->addr.as_u32 & ip4_main.fib_masks[ap->addr_len];
     291             : 
     292           0 :           nat_log_debug ("pool addr %U binds to -> sw_if_idx: %u net: %U/%u",
     293             :                          format_ip4_address, &ap->addr, ap->sw_if_index,
     294             :                          format_ip4_address, &ap->net, ap->addr_len);
     295           0 :           return 0;
     296             :         }
     297             :     }
     298         524 :   return 1;
     299             : }
     300             : 
     301             : static void
     302         262 : nat44_ed_update_outside_if_addresses (snat_address_t *ap)
     303             : {
     304         262 :   snat_main_t *sm = &snat_main;
     305             : 
     306         262 :   if (!nat44_ed_resolve_nat_addr_len (ap, sm->interfaces))
     307             :     {
     308           0 :       return;
     309             :     }
     310             : 
     311         262 :   if (!nat44_ed_resolve_nat_addr_len (ap, sm->output_feature_interfaces))
     312             :     {
     313           0 :       return;
     314             :     }
     315             : }
     316             : 
     317             : static void
     318         108 : nat44_ed_bind_if_addr_to_nat_addr (u32 sw_if_index)
     319             : {
     320         108 :   snat_main_t *sm = &snat_main;
     321             :   ip_interface_address_t *ia;
     322             :   snat_address_t *ap;
     323             : 
     324         108 :   u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
     325             : 
     326         182 :   vec_foreach (ap, sm->addresses)
     327             :     {
     328          74 :       if (fib_index != ap->fib_index)
     329             :         {
     330          74 :           continue;
     331             :         }
     332             : 
     333           0 :       if ((ia = nat44_ed_get_ip_interface_address (sw_if_index, ap->addr)))
     334             :         {
     335           0 :           ap->addr_len = ia->address_length;
     336           0 :           ap->sw_if_index = sw_if_index;
     337           0 :           ap->net.as_u32 = ap->addr.as_u32 & ip4_main.fib_masks[ap->addr_len];
     338             : 
     339           0 :           nat_log_debug ("pool addr %U binds to -> sw_if_idx: %u net: %U/%u",
     340             :                          format_ip4_address, &ap->addr, ap->sw_if_index,
     341             :                          format_ip4_address, &ap->net, ap->addr_len);
     342           0 :           return;
     343             :         }
     344             :     }
     345             : }
     346             : 
     347             : static_always_inline snat_fib_entry_reg_t *
     348         709 : nat44_ed_get_fib_entry_reg (ip4_address_t addr, u32 sw_if_index, int *out_idx)
     349             : {
     350         709 :   snat_main_t *sm = &snat_main;
     351             :   snat_fib_entry_reg_t *fe;
     352             :   int i;
     353             : 
     354       15847 :   for (i = 0; i < vec_len (sm->fib_entry_reg); i++)
     355             :     {
     356       15567 :       fe = sm->fib_entry_reg + i;
     357       15567 :       if ((addr.as_u32 == fe->addr.as_u32) && (sw_if_index == fe->sw_if_index))
     358             :         {
     359         429 :           if (out_idx)
     360             :             {
     361         349 :               *out_idx = i;
     362             :             }
     363         429 :           return fe;
     364             :         }
     365             :     }
     366         280 :   return NULL;
     367             : }
     368             : 
     369             : static void
     370         360 : nat44_ed_add_fib_entry_reg (ip4_address_t addr, u32 sw_if_index)
     371             : {
     372             :   // Add the external NAT address to the FIB as receive entries. This ensures
     373             :   // that VPP will reply to ARP for this address and we don't need to enable
     374             :   // proxy ARP on the outside interface.
     375         360 :   snat_main_t *sm = &snat_main;
     376             :   snat_fib_entry_reg_t *fe;
     377             : 
     378         360 :   if (!(fe = nat44_ed_get_fib_entry_reg (addr, sw_if_index, 0)))
     379             :     {
     380         280 :       fib_prefix_t prefix = {
     381             :         .fp_len = 32,
     382             :         .fp_proto = FIB_PROTOCOL_IP4,
     383             :         .fp_addr = {
     384         280 :                   .ip4.as_u32 = addr.as_u32,
     385             :                 },
     386             :       };
     387         280 :       u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
     388         280 :       fib_table_entry_update_one_path (fib_index, &prefix, sm->fib_src_low,
     389             :                                        (FIB_ENTRY_FLAG_CONNECTED |
     390             :                                         FIB_ENTRY_FLAG_LOCAL |
     391             :                                         FIB_ENTRY_FLAG_EXCLUSIVE),
     392             :                                        DPO_PROTO_IP4, NULL, sw_if_index, ~0, 1,
     393             :                                        NULL, FIB_ROUTE_PATH_FLAG_NONE);
     394             : 
     395         280 :       vec_add2 (sm->fib_entry_reg, fe, 1);
     396         280 :       clib_memset (fe, 0, sizeof (*fe));
     397         280 :       fe->addr.as_u32 = addr.as_u32;
     398         280 :       fe->sw_if_index = sw_if_index;
     399             :     }
     400         360 :   fe->count++;
     401         360 : }
     402             : 
     403             : static void
     404         349 : nat44_ed_del_fib_entry_reg (ip4_address_t addr, u32 sw_if_index)
     405             : {
     406         349 :   snat_main_t *sm = &snat_main;
     407             :   snat_fib_entry_reg_t *fe;
     408             :   int i;
     409             : 
     410         349 :   if ((fe = nat44_ed_get_fib_entry_reg (addr, sw_if_index, &i)))
     411             :     {
     412         349 :       fe->count--;
     413         349 :       if (0 == fe->count)
     414             :         {
     415         276 :           fib_prefix_t prefix = {
     416             :             .fp_len = 32,
     417             :             .fp_proto = FIB_PROTOCOL_IP4,
     418             :             .fp_addr = {
     419         276 :               .ip4.as_u32 = addr.as_u32,
     420             :                     },
     421             :           };
     422             :           u32 fib_index =
     423         276 :             ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
     424         276 :           fib_table_entry_delete (fib_index, &prefix, sm->fib_src_low);
     425         276 :           vec_del1 (sm->fib_entry_reg, i);
     426             :         }
     427             :     }
     428         349 : }
     429             : 
     430             : static void
     431         616 : nat44_ed_add_del_interface_fib_reg_entries (ip4_address_t addr, u8 is_add)
     432             : {
     433         616 :   snat_main_t *sm = &snat_main;
     434             :   snat_interface_t *i;
     435             : 
     436        1674 :   pool_foreach (i, sm->interfaces)
     437             :     {
     438        1058 :       if (nat44_ed_is_interface_outside (i))
     439             :         {
     440         580 :           if (is_add)
     441             :             {
     442         256 :               nat44_ed_add_fib_entry_reg (addr, i->sw_if_index);
     443             :             }
     444             :           else
     445             :             {
     446         324 :               nat44_ed_del_fib_entry_reg (addr, i->sw_if_index);
     447             :             }
     448             :         }
     449             :     }
     450         639 :   pool_foreach (i, sm->output_feature_interfaces)
     451             :     {
     452          23 :       if (nat44_ed_is_interface_outside (i))
     453             :         {
     454          23 :           if (is_add)
     455             :             {
     456           4 :               nat44_ed_add_fib_entry_reg (addr, i->sw_if_index);
     457             :             }
     458             :           else
     459             :             {
     460          19 :               nat44_ed_del_fib_entry_reg (addr, i->sw_if_index);
     461             :             }
     462             :         }
     463             :     }
     464         616 : }
     465             : 
     466             : static_always_inline void
     467         216 : nat44_ed_add_del_nat_addr_fib_reg_entries (u32 sw_if_index, u8 is_add)
     468             : {
     469         216 :   snat_main_t *sm = &snat_main;
     470             :   snat_address_t *ap;
     471             : 
     472         296 :   vec_foreach (ap, sm->addresses)
     473             :     {
     474          80 :       if (is_add)
     475             :         {
     476          74 :           nat44_ed_add_fib_entry_reg (ap->addr, sw_if_index);
     477             :         }
     478             :       else
     479             :         {
     480           6 :           nat44_ed_del_fib_entry_reg (ap->addr, sw_if_index);
     481             :         }
     482             :     }
     483         216 : }
     484             : 
     485             : static_always_inline void
     486         216 : nat44_ed_add_del_sm_fib_reg_entries (u32 sw_if_index, u8 is_add)
     487             : {
     488         216 :   snat_main_t *sm = &snat_main;
     489             :   snat_static_mapping_t *m;
     490             : 
     491         242 :   pool_foreach (m, sm->static_mappings)
     492             :     {
     493          26 :       if (is_add)
     494             :         {
     495          26 :           nat44_ed_add_fib_entry_reg (m->external_addr, sw_if_index);
     496             :         }
     497             :       else
     498             :         {
     499           0 :           nat44_ed_del_fib_entry_reg (m->external_addr, sw_if_index);
     500             :         }
     501             :     }
     502         216 : }
     503             : 
     504             : int
     505         276 : nat44_ed_add_address (ip4_address_t *addr, u32 vrf_id, u8 twice_nat)
     506             : {
     507         276 :   snat_main_t *sm = &snat_main;
     508             :   snat_address_t *ap, *addresses;
     509             : 
     510         276 :   addresses = twice_nat ? sm->twice_nat_addresses : sm->addresses;
     511             : 
     512         276 :   if (!sm->enabled)
     513             :     {
     514           0 :       return VNET_API_ERROR_UNSUPPORTED;
     515             :     }
     516             : 
     517             :   // check if address already exists
     518        9978 :   vec_foreach (ap, addresses)
     519             :     {
     520        9702 :       if (ap->addr.as_u32 == addr->as_u32)
     521             :         {
     522           0 :           nat_log_err ("address exist");
     523           0 :           return VNET_API_ERROR_VALUE_EXIST;
     524             :         }
     525             :     }
     526             : 
     527         276 :   if (twice_nat)
     528             :     {
     529          14 :       vec_add2 (sm->twice_nat_addresses, ap, 1);
     530             :     }
     531             :   else
     532             :     {
     533         262 :       vec_add2 (sm->addresses, ap, 1);
     534             :     }
     535             : 
     536         276 :   ap->addr_len = ~0;
     537         276 :   ap->fib_index = ~0;
     538         276 :   ap->addr = *addr;
     539             : 
     540         276 :   if (vrf_id != ~0)
     541             :     {
     542           1 :       ap->fib_index = fib_table_find_or_create_and_lock (
     543           1 :         FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
     544             :     }
     545             : 
     546         276 :   if (!twice_nat)
     547             :     {
     548             :       // if we don't have enabled interface we don't add address
     549             :       // to fib
     550         262 :       nat44_ed_add_del_interface_fib_reg_entries (*addr, 1);
     551         262 :       nat44_ed_update_outside_if_addresses (ap);
     552             :     }
     553         276 :   return 0;
     554             : }
     555             : 
     556             : int
     557         276 : nat44_ed_del_address (ip4_address_t addr, u8 twice_nat)
     558             : {
     559         276 :   snat_main_t *sm = &snat_main;
     560         276 :   snat_address_t *a = 0, *addresses;
     561             :   snat_session_t *ses;
     562         276 :   u32 *ses_to_be_removed = 0, *ses_index;
     563             :   snat_main_per_thread_data_t *tsm;
     564             :   int j;
     565             : 
     566         276 :   addresses = twice_nat ? sm->twice_nat_addresses : sm->addresses;
     567             : 
     568        5078 :   for (j = 0; j < vec_len (addresses); j++)
     569             :     {
     570        5078 :       if (addresses[j].addr.as_u32 == addr.as_u32)
     571             :         {
     572         276 :           a = addresses + j;
     573         276 :           break;
     574             :         }
     575             :     }
     576         276 :   if (!a)
     577             :     {
     578           0 :       nat_log_err ("no such address");
     579           0 :       return VNET_API_ERROR_NO_SUCH_ENTRY;
     580             :     }
     581             : 
     582             :   // delete dynamic sessions only
     583        1184 :   vec_foreach (tsm, sm->per_thread_data)
     584             :     {
     585       34208 :       pool_foreach (ses, tsm->sessions)
     586             :         {
     587       33300 :           if (ses->flags & SNAT_SESSION_FLAG_STATIC_MAPPING)
     588             :             {
     589         218 :               continue;
     590             :             }
     591       33082 :           if (ses->out2in.addr.as_u32 == addr.as_u32)
     592             :             {
     593       23214 :               nat44_ed_free_session_data (sm, ses, tsm - sm->per_thread_data,
     594             :                                           0);
     595       23214 :               vec_add1 (ses_to_be_removed, ses - tsm->sessions);
     596             :             }
     597             :         }
     598       24122 :       vec_foreach (ses_index, ses_to_be_removed)
     599             :         {
     600       23214 :           ses = pool_elt_at_index (tsm->sessions, ses_index[0]);
     601       23214 :           nat_ed_session_delete (sm, ses, tsm - sm->per_thread_data, 1);
     602             :         }
     603         908 :       vec_free (ses_to_be_removed);
     604             :     }
     605             : 
     606         276 :   if (!twice_nat)
     607             :     {
     608         262 :       nat44_ed_add_del_interface_fib_reg_entries (addr, 0);
     609             :     }
     610             : 
     611         276 :   if (a->fib_index != ~0)
     612             :     {
     613           1 :       fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
     614             :     }
     615             : 
     616         276 :   if (!twice_nat)
     617             :     {
     618         262 :       vec_del1 (sm->addresses, j);
     619             :     }
     620             :   else
     621             :     {
     622          14 :       vec_del1 (sm->twice_nat_addresses, j);
     623             :     }
     624             : 
     625         276 :   return 0;
     626             : }
     627             : 
     628             : vrf_table_t *
     629           0 : nat44_ed_get_vrf_table (u32 table_vrf_id)
     630             : {
     631           0 :   snat_main_t *sm = &snat_main;
     632             :   vrf_table_t *t;
     633             : 
     634           0 :   pool_foreach (t, sm->vrf_tables)
     635             :     {
     636           0 :       if (table_vrf_id == t->table_vrf_id)
     637             :         {
     638           0 :           return t;
     639             :         }
     640             :     }
     641           0 :   return NULL;
     642             : }
     643             : 
     644             : vrf_route_t *
     645           0 : nat44_ed_get_vrf_route (vrf_table_t *t, u32 vrf_id)
     646             : {
     647             :   vrf_route_t *r;
     648             : 
     649           0 :   pool_foreach (r, t->routes)
     650             :     {
     651           0 :       if (vrf_id == r->vrf_id)
     652             :         {
     653           0 :           return r;
     654             :         }
     655             :     }
     656           0 :   return NULL;
     657             : }
     658             : 
     659             : int
     660           0 : nat44_ed_add_del_vrf_table (u32 table_vrf_id, bool is_add)
     661             : {
     662           0 :   snat_main_t *sm = &snat_main;
     663             :   vrf_table_t *t;
     664             :   vrf_route_t *r;
     665             : 
     666           0 :   t = nat44_ed_get_vrf_table (table_vrf_id);
     667           0 :   if (t)
     668             :     {
     669           0 :       if (is_add)
     670             :         {
     671           0 :           return VNET_API_ERROR_VALUE_EXIST;
     672             :         }
     673           0 :       pool_foreach (r, t->routes)
     674             :         {
     675           0 :           fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
     676             :         }
     677           0 :       fib_table_unlock (t->table_fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
     678             : 
     679           0 :       pool_free (t->routes);
     680           0 :       pool_put (sm->vrf_tables, t);
     681             :     }
     682             :   else
     683             :     {
     684           0 :       if (!is_add)
     685             :         {
     686           0 :           return VNET_API_ERROR_NO_SUCH_ENTRY;
     687             :         }
     688           0 :       pool_get (sm->vrf_tables, t);
     689           0 :       clib_memset (t, 0, sizeof (*t));
     690           0 :       t->table_vrf_id = table_vrf_id;
     691           0 :       t->table_fib_index = fib_table_find_or_create_and_lock (
     692           0 :         FIB_PROTOCOL_IP4, table_vrf_id, sm->fib_src_low);
     693             :     }
     694             : 
     695           0 :   return 0;
     696             : }
     697             : 
     698             : void
     699          87 : nat44_ed_del_vrf_tables ()
     700             : {
     701          87 :   snat_main_t *sm = &snat_main;
     702             :   vrf_table_t *t;
     703             :   vrf_route_t *r;
     704             : 
     705          87 :   pool_foreach (t, sm->vrf_tables)
     706             :     {
     707           0 :       pool_foreach (r, t->routes)
     708             :         {
     709           0 :           fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
     710             :         }
     711           0 :       fib_table_unlock (t->table_fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
     712           0 :       pool_free (t->routes);
     713             :     }
     714          87 :   pool_free (sm->vrf_tables);
     715          87 : }
     716             : 
     717             : int
     718           0 : nat44_ed_add_del_vrf_route (u32 table_vrf_id, u32 vrf_id, bool is_add)
     719             : {
     720           0 :   snat_main_t *sm = &snat_main;
     721             :   vrf_table_t *t;
     722             :   vrf_route_t *r;
     723             : 
     724           0 :   t = nat44_ed_get_vrf_table (table_vrf_id);
     725           0 :   if (!t)
     726             :     {
     727           0 :       return VNET_API_ERROR_NO_SUCH_ENTRY;
     728             :     }
     729             : 
     730           0 :   r = nat44_ed_get_vrf_route (t, vrf_id);
     731           0 :   if (r)
     732             :     {
     733           0 :       if (is_add)
     734             :         {
     735           0 :           return VNET_API_ERROR_VALUE_EXIST;
     736             :         }
     737           0 :       fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
     738           0 :       pool_put (t->routes, r);
     739             :     }
     740             :   else
     741             :     {
     742           0 :       if (!is_add)
     743             :         {
     744           0 :           return VNET_API_ERROR_NO_SUCH_ENTRY;
     745             :         }
     746           0 :       pool_get (t->routes, r);
     747           0 :       clib_memset (r, 0, sizeof (*r));
     748           0 :       r->vrf_id = vrf_id;
     749           0 :       r->fib_index = fib_table_find_or_create_and_lock (
     750           0 :         FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
     751             :     }
     752             : 
     753           0 :   return 0;
     754             : }
     755             : 
     756             : u32
     757          71 : get_thread_idx_by_port (u16 e_port)
     758             : {
     759          71 :   snat_main_t *sm = &snat_main;
     760          71 :   u32 thread_idx = sm->num_workers;
     761          71 :   if (sm->num_workers > 1)
     762             :     {
     763         142 :       thread_idx = sm->first_worker_index +
     764         142 :                    sm->workers[(e_port - ED_USER_PORT_OFFSET) /
     765          71 :                                sm->port_per_thread % _vec_len (sm->workers)];
     766             :     }
     767          71 :   return thread_idx;
     768             : }
     769             : 
     770             : void
     771          58 : nat_ed_static_mapping_del_sessions (snat_main_t * sm,
     772             :                                     snat_main_per_thread_data_t * tsm,
     773             :                                     ip4_address_t l_addr,
     774             :                                     u16 l_port,
     775             :                                     u8 protocol,
     776             :                                     u32 fib_index, int addr_only,
     777             :                                     ip4_address_t e_addr, u16 e_port)
     778             : {
     779             :   snat_session_t *s;
     780          58 :   u32 *indexes_to_free = NULL;
     781         247 :   pool_foreach (s, tsm->sessions) {
     782         208 :     if (s->in2out.fib_index != fib_index ||
     783         207 :         s->in2out.addr.as_u32 != l_addr.as_u32)
     784             :       {
     785         175 :         continue;
     786             :       }
     787          33 :     if (!addr_only)
     788             :       {
     789          24 :         if ((s->out2in.addr.as_u32 != e_addr.as_u32) ||
     790          20 :             s->out2in.port != e_port || s->in2out.port != l_port ||
     791          19 :             s->proto != protocol)
     792           5 :           continue;
     793             :       }
     794             : 
     795          28 :     if (nat44_ed_is_lb_session (s))
     796           0 :       continue;
     797          28 :     if (!nat44_ed_is_session_static (s))
     798           0 :       continue;
     799          28 :     nat44_ed_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
     800          28 :     vec_add1 (indexes_to_free, s - tsm->sessions);
     801          28 :     if (!addr_only)
     802          19 :       break;
     803             :   }
     804             :   u32 *ses_index;
     805          86 :   vec_foreach (ses_index, indexes_to_free)
     806             :   {
     807          28 :     s = pool_elt_at_index (tsm->sessions, *ses_index);
     808          28 :     nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
     809             :   }
     810          58 :   vec_free (indexes_to_free);
     811          58 : }
     812             : 
     813             : static_always_inline snat_static_mapping_t *
     814      214155 : nat44_ed_sm_lookup (snat_main_t *sm, clib_bihash_kv_16_8_t *kv)
     815             : {
     816             :   clib_bihash_kv_16_8_t v;
     817      214155 :   int rc = clib_bihash_search_16_8 (&sm->flow_hash, kv, &v);
     818      214142 :   if (!rc)
     819             :     {
     820         612 :       ASSERT (0 == ed_value_get_thread_index (&v));
     821         612 :       return pool_elt_at_index (sm->static_mappings,
     822             :                                 ed_value_get_session_index (&v));
     823             :     }
     824      213530 :   return NULL;
     825             : }
     826             : 
     827             : snat_static_mapping_t *
     828      157368 : nat44_ed_sm_o2i_lookup (snat_main_t *sm, ip4_address_t addr, u16 port,
     829             :                         u32 fib_index, u8 proto)
     830             : {
     831             :   clib_bihash_kv_16_8_t kv;
     832      157368 :   nat44_ed_sm_init_o2i_k (&kv, addr.as_u32, port, fib_index, proto);
     833      157377 :   return nat44_ed_sm_lookup (sm, &kv);
     834             : }
     835             : 
     836             : snat_static_mapping_t *
     837       56789 : nat44_ed_sm_i2o_lookup (snat_main_t *sm, ip4_address_t addr, u16 port,
     838             :                         u32 fib_index, u8 proto)
     839             : {
     840             :   clib_bihash_kv_16_8_t kv;
     841       56789 :   nat44_ed_sm_init_i2o_k (&kv, addr.as_u32, port, fib_index, proto);
     842       56791 :   return nat44_ed_sm_lookup (sm, &kv);
     843             : }
     844             : 
     845             : static snat_static_mapping_resolve_t *
     846           4 : nat44_ed_get_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port,
     847             :                              ip_protocol_t proto, u32 vrf_id, u32 sw_if_index,
     848             :                              u32 flags, int *out_idx)
     849             : {
     850             :   snat_static_mapping_resolve_t *rp;
     851           4 :   snat_main_t *sm = &snat_main;
     852             :   int i;
     853             : 
     854           4 :   for (i = 0; i < vec_len (sm->sm_to_resolve); i++)
     855             :     {
     856           0 :       rp = sm->sm_to_resolve + i;
     857             : 
     858           0 :       if (rp->sw_if_index == sw_if_index && rp->vrf_id == vrf_id)
     859             :         {
     860           0 :           if (is_sm_identity_nat (rp->flags) && is_sm_identity_nat (flags))
     861             :             {
     862           0 :               if (!(is_sm_addr_only (rp->flags) && is_sm_addr_only (flags)))
     863             :                 {
     864           0 :                   if (rp->e_port != e_port || rp->proto != proto)
     865             :                     {
     866           0 :                       continue;
     867             :                     }
     868             :                 }
     869             :             }
     870           0 :           else if (rp->l_addr.as_u32 == l_addr.as_u32)
     871             :             {
     872           0 :               if (!(is_sm_addr_only (rp->flags) && is_sm_addr_only (flags)))
     873             :                 {
     874           0 :                   if (rp->l_port != l_port || rp->e_port != e_port ||
     875           0 :                       rp->proto != proto)
     876             :                     {
     877           0 :                       continue;
     878             :                     }
     879             :                 }
     880             :             }
     881             :           else
     882             :             {
     883           0 :               continue;
     884             :             }
     885           0 :           if (out_idx)
     886             :             {
     887           0 :               *out_idx = i;
     888             :             }
     889           0 :           return rp;
     890             :         }
     891             :     }
     892           4 :   return NULL;
     893             : }
     894             : 
     895             : static int
     896           0 : nat44_ed_del_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port,
     897             :                              ip_protocol_t proto, u32 vrf_id, u32 sw_if_index,
     898             :                              u32 flags)
     899             : {
     900           0 :   snat_main_t *sm = &snat_main;
     901             :   int i;
     902           0 :   if (nat44_ed_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
     903             :                                    sw_if_index, flags, &i))
     904             :     {
     905           0 :       vec_del1 (sm->sm_to_resolve, i);
     906           0 :       return 0;
     907             :     }
     908           0 :   return 1;
     909             : }
     910             : 
     911             : static_always_inline int
     912          48 : nat44_ed_validate_sm_input (u32 flags)
     913             : {
     914             :   // identity nat can be initiated only from inside interface
     915          48 :   if (is_sm_identity_nat (flags) && is_sm_out2in_only (flags))
     916             :     {
     917           0 :       return VNET_API_ERROR_UNSUPPORTED;
     918             :     }
     919             : 
     920          48 :   if (is_sm_twice_nat (flags) || is_sm_self_twice_nat (flags))
     921             :     {
     922          14 :       if (is_sm_addr_only (flags) || is_sm_identity_nat (flags))
     923             :         {
     924           0 :           return VNET_API_ERROR_UNSUPPORTED;
     925             :         }
     926             :     }
     927          48 :   return 0;
     928             : }
     929             : 
     930             : int
     931          46 : nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
     932             :                              u16 l_port, u16 e_port, ip_protocol_t proto,
     933             :                              u32 vrf_id, u32 sw_if_index, u32 flags,
     934             :                              ip4_address_t pool_addr, u8 *tag)
     935             : {
     936             :   snat_static_mapping_resolve_t *rp;
     937          46 :   snat_main_t *sm = &snat_main;
     938             :   int rv;
     939             : 
     940          46 :   if (!sm->enabled)
     941             :     {
     942           0 :       return VNET_API_ERROR_UNSUPPORTED;
     943             :     }
     944             : 
     945          46 :   rv = nat44_ed_validate_sm_input (flags);
     946          46 :   if (rv != 0)
     947             :     {
     948           0 :       return rv;
     949             :     }
     950             : 
     951             :   // interface bound mapping
     952          46 :   if (is_sm_switch_address (flags))
     953             :     {
     954           4 :       if (nat44_ed_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
     955             :                                        sw_if_index, flags, 0))
     956             :         {
     957           0 :           return VNET_API_ERROR_VALUE_EXIST;
     958             :         }
     959             : 
     960           4 :       vec_add2 (sm->sm_to_resolve, rp, 1);
     961           4 :       rp->l_addr.as_u32 = l_addr.as_u32;
     962           4 :       rp->l_port = l_port;
     963           4 :       rp->e_port = e_port;
     964           4 :       rp->sw_if_index = sw_if_index;
     965           4 :       rp->vrf_id = vrf_id;
     966           4 :       rp->proto = proto;
     967           4 :       rp->flags = flags;
     968           4 :       rp->pool_addr = pool_addr;
     969           4 :       rp->tag = vec_dup (tag);
     970           4 :       rp->is_resolved = 0;
     971             : 
     972             :       ip4_address_t *first_int_addr =
     973           4 :         ip4_interface_first_address (sm->ip4_main, sw_if_index, 0);
     974           4 :       if (!first_int_addr)
     975             :         {
     976           0 :           return 0;
     977             :         }
     978             : 
     979           4 :       e_addr.as_u32 = first_int_addr->as_u32;
     980           4 :       rp->is_resolved = 1;
     981             :     }
     982             : 
     983          46 :   rv = nat44_ed_add_static_mapping_internal (l_addr, e_addr, l_port, e_port,
     984             :                                              proto, vrf_id, sw_if_index, flags,
     985             :                                              pool_addr, tag);
     986          46 :   if ((0 != rv) && is_sm_switch_address (flags))
     987             :     {
     988           0 :       nat44_ed_del_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
     989             :                                    sw_if_index, flags);
     990             :     }
     991             : 
     992          46 :   return rv;
     993             : }
     994             : 
     995             : int
     996           2 : nat44_ed_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
     997             :                              u16 l_port, u16 e_port, ip_protocol_t proto,
     998             :                              u32 vrf_id, u32 sw_if_index, u32 flags)
     999             : {
    1000           2 :   snat_main_t *sm = &snat_main;
    1001             :   int rv;
    1002             : 
    1003           2 :   if (!sm->enabled)
    1004             :     {
    1005           0 :       return VNET_API_ERROR_UNSUPPORTED;
    1006             :     }
    1007             : 
    1008           2 :   rv = nat44_ed_validate_sm_input (flags);
    1009           2 :   if (rv != 0)
    1010             :     {
    1011           0 :       return rv;
    1012             :     }
    1013             : 
    1014             :   // interface bound mapping
    1015           2 :   if (is_sm_switch_address (flags))
    1016             :     {
    1017           0 :       if (nat44_ed_del_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
    1018             :                                        sw_if_index, flags))
    1019             :         {
    1020           0 :           return VNET_API_ERROR_NO_SUCH_ENTRY;
    1021             :         }
    1022             : 
    1023             :       ip4_address_t *first_int_addr =
    1024           0 :         ip4_interface_first_address (sm->ip4_main, sw_if_index, 0);
    1025           0 :       if (!first_int_addr)
    1026             :         {
    1027             :           // dhcp resolution required
    1028           0 :           return 0;
    1029             :         }
    1030             : 
    1031           0 :       e_addr.as_u32 = first_int_addr->as_u32;
    1032             :     }
    1033             : 
    1034           2 :   return nat44_ed_del_static_mapping_internal (l_addr, e_addr, l_port, e_port,
    1035             :                                                proto, vrf_id, flags);
    1036             : }
    1037             : 
    1038             : static int
    1039          46 : nat44_ed_add_static_mapping_internal (ip4_address_t l_addr,
    1040             :                                       ip4_address_t e_addr, u16 l_port,
    1041             :                                       u16 e_port, ip_protocol_t proto,
    1042             :                                       u32 vrf_id, u32 sw_if_index, u32 flags,
    1043             :                                       ip4_address_t pool_addr, u8 *tag)
    1044             : {
    1045          46 :   snat_main_t *sm = &snat_main;
    1046             :   nat44_lb_addr_port_t *local;
    1047             :   snat_static_mapping_t *m;
    1048          46 :   u32 fib_index = ~0;
    1049             : 
    1050          46 :   if (is_sm_addr_only (flags))
    1051             :     {
    1052           9 :       e_port = l_port = proto = 0;
    1053             :     }
    1054             : 
    1055          46 :   if (is_sm_identity_nat (flags))
    1056             :     {
    1057           1 :       l_port = e_port;
    1058           1 :       l_addr.as_u32 = e_addr.as_u32;
    1059             :     }
    1060             : 
    1061          46 :   m = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto);
    1062          46 :   if (m)
    1063             :     {
    1064             :       // case:
    1065             :       // adding local identity nat record for different vrf table
    1066             : 
    1067           0 :       if (!is_sm_identity_nat (m->flags))
    1068             :         {
    1069           0 :           return VNET_API_ERROR_VALUE_EXIST;
    1070             :         }
    1071             : 
    1072           0 :       pool_foreach (local, m->locals)
    1073             :         {
    1074           0 :           if (local->vrf_id == vrf_id)
    1075             :             {
    1076           0 :               return VNET_API_ERROR_VALUE_EXIST;
    1077             :             }
    1078             :         }
    1079             : 
    1080           0 :       pool_get (m->locals, local);
    1081             : 
    1082           0 :       local->vrf_id = vrf_id;
    1083           0 :       local->fib_index = fib_table_find_or_create_and_lock (
    1084           0 :         FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
    1085             : 
    1086           0 :       nat44_ed_sm_i2o_add (sm, m, m->local_addr, m->local_port,
    1087           0 :                            local->fib_index, m->proto);
    1088             : 
    1089           0 :       return 0;
    1090             :     }
    1091             : 
    1092          46 :   if (vrf_id != ~0)
    1093             :     {
    1094          46 :       fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
    1095          46 :                                                      sm->fib_src_low);
    1096             :     }
    1097             :   else
    1098             :     {
    1099             :       // fallback to default vrf
    1100           0 :       vrf_id = sm->inside_vrf_id;
    1101           0 :       fib_index = sm->inside_fib_index;
    1102           0 :       fib_table_lock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
    1103             :     }
    1104             : 
    1105             :   // test if local mapping record doesn't exist
    1106             :   // identity nat supports multiple records in local mapping
    1107          46 :   if (!(is_sm_out2in_only (flags) || is_sm_identity_nat (flags)))
    1108             :     {
    1109          25 :       if (nat44_ed_sm_i2o_lookup (sm, l_addr, l_port, fib_index, proto))
    1110             :         {
    1111           0 :           return VNET_API_ERROR_VALUE_EXIST;
    1112             :         }
    1113             :     }
    1114             : 
    1115          46 :   pool_get (sm->static_mappings, m);
    1116          46 :   clib_memset (m, 0, sizeof (*m));
    1117             : 
    1118          46 :   m->flags = flags;
    1119          46 :   m->local_addr = l_addr;
    1120          46 :   m->external_addr = e_addr;
    1121             : 
    1122          46 :   m->pool_addr = pool_addr;
    1123          46 :   m->tag = vec_dup (tag);
    1124             : 
    1125          46 :   if (!is_sm_addr_only (flags))
    1126             :     {
    1127          37 :       m->local_port = l_port;
    1128          37 :       m->external_port = e_port;
    1129          37 :       m->proto = proto;
    1130             :     }
    1131             : 
    1132          46 :   if (is_sm_identity_nat (flags))
    1133             :     {
    1134           1 :       pool_get (m->locals, local);
    1135             : 
    1136           1 :       local->vrf_id = vrf_id;
    1137           1 :       local->fib_index = fib_index;
    1138             :     }
    1139             :   else
    1140             :     {
    1141          45 :       m->vrf_id = vrf_id;
    1142          45 :       m->fib_index = fib_index;
    1143             :     }
    1144             : 
    1145          46 :   if (!is_sm_out2in_only (flags))
    1146             :     {
    1147          26 :       nat44_ed_sm_i2o_add (sm, m, m->local_addr, m->local_port, fib_index,
    1148          26 :                            m->proto);
    1149             :     }
    1150             : 
    1151          46 :   nat44_ed_sm_o2i_add (sm, m, m->external_addr, m->external_port, 0, m->proto);
    1152             : 
    1153          46 :   if (sm->num_workers > 1)
    1154             :     {
    1155             :       // store worker index for this record
    1156          34 :       ip4_header_t ip = {
    1157          34 :         .src_address = m->local_addr,
    1158             :       };
    1159             :       u32 worker_index;
    1160             :       worker_index =
    1161          34 :         nat44_ed_get_in2out_worker_index (0, &ip, m->fib_index, 0);
    1162          34 :       vec_add1 (m->workers, worker_index);
    1163             :     }
    1164             : 
    1165          46 :   nat44_ed_add_del_interface_fib_reg_entries (e_addr, 1);
    1166             : 
    1167          46 :   return 0;
    1168             : }
    1169             : 
    1170             : static int
    1171          58 : nat44_ed_del_static_mapping_internal (ip4_address_t l_addr,
    1172             :                                       ip4_address_t e_addr, u16 l_port,
    1173             :                                       u16 e_port, ip_protocol_t proto,
    1174             :                                       u32 vrf_id, u32 flags)
    1175             : {
    1176             :   snat_main_per_thread_data_t *tsm;
    1177          58 :   snat_main_t *sm = &snat_main;
    1178             : 
    1179             :   nat44_lb_addr_port_t *local;
    1180             :   snat_static_mapping_t *m;
    1181          58 :   u32 fib_index = ~0;
    1182             : 
    1183          58 :   if (is_sm_addr_only (flags))
    1184             :     {
    1185           9 :       e_port = l_port = proto = 0;
    1186             :     }
    1187             : 
    1188          58 :   if (is_sm_identity_nat (flags))
    1189             :     {
    1190           1 :       l_port = e_port;
    1191           1 :       l_addr.as_u32 = e_addr.as_u32;
    1192             :     }
    1193             : 
    1194             :   // fib index 0
    1195          58 :   m = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto);
    1196          58 :   if (!m)
    1197             :     {
    1198           0 :       return VNET_API_ERROR_NO_SUCH_ENTRY;
    1199             :     }
    1200             : 
    1201          58 :   if (is_sm_identity_nat (flags))
    1202             :     {
    1203           1 :       u8 found = 0;
    1204             : 
    1205           1 :       if (vrf_id == ~0)
    1206             :         {
    1207           0 :           vrf_id = sm->inside_vrf_id;
    1208             :         }
    1209             : 
    1210           2 :       pool_foreach (local, m->locals)
    1211             :         {
    1212           1 :           if (local->vrf_id == vrf_id)
    1213             :             {
    1214           1 :               local = pool_elt_at_index (m->locals, local - m->locals);
    1215           1 :               fib_index = local->fib_index;
    1216           1 :               pool_put (m->locals, local);
    1217           1 :               found = 1;
    1218             :             }
    1219             :         }
    1220             : 
    1221           1 :       if (!found)
    1222             :         {
    1223           0 :           return VNET_API_ERROR_NO_SUCH_ENTRY;
    1224             :         }
    1225             :     }
    1226             :   else
    1227             :     {
    1228          57 :       fib_index = m->fib_index;
    1229             :     }
    1230             : 
    1231          58 :   if (!is_sm_out2in_only (flags))
    1232             :     {
    1233          36 :       nat44_ed_sm_i2o_del (sm, l_addr, l_port, fib_index, proto);
    1234             :     }
    1235             : 
    1236             :   // delete sessions for static mapping
    1237          58 :   if (sm->num_workers > 1)
    1238             :     {
    1239          41 :       tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]);
    1240             :     }
    1241             :   else
    1242             :     {
    1243          17 :       tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
    1244             :     }
    1245             : 
    1246         116 :   nat_ed_static_mapping_del_sessions (sm, tsm, m->local_addr, m->local_port,
    1247          58 :                                       m->proto, fib_index,
    1248          58 :                                       is_sm_addr_only (flags), e_addr, e_port);
    1249             : 
    1250          58 :   fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
    1251             : 
    1252          58 :   if (!pool_elts (m->locals))
    1253             :     {
    1254             :       // this is last record remove all required stuff
    1255             :       // fib_index 0
    1256          46 :       nat44_ed_sm_o2i_del (sm, e_addr, e_port, 0, proto);
    1257             : 
    1258          46 :       vec_free (m->tag);
    1259          46 :       vec_free (m->workers);
    1260          46 :       pool_put (sm->static_mappings, m);
    1261             : 
    1262          46 :       nat44_ed_add_del_interface_fib_reg_entries (e_addr, 0);
    1263             :     }
    1264             : 
    1265          58 :   return 0;
    1266             : }
    1267             : 
    1268             : int
    1269          12 : nat44_ed_add_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
    1270             :                                 ip_protocol_t proto,
    1271             :                                 nat44_lb_addr_port_t *locals, u32 flags,
    1272             :                                 u8 *tag, u32 affinity)
    1273             : {
    1274          12 :   snat_main_t *sm = &snat_main;
    1275             :   snat_static_mapping_t *m;
    1276          12 :   snat_address_t *a = 0;
    1277             : 
    1278             :   nat44_lb_addr_port_t *local;
    1279          12 :   uword *bitmap = 0;
    1280          12 :   int rc = 0;
    1281             : 
    1282             :   int i;
    1283             : 
    1284          12 :   if (!sm->enabled)
    1285             :     {
    1286           0 :       return VNET_API_ERROR_UNSUPPORTED;
    1287             :     }
    1288             : 
    1289          12 :   m = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto);
    1290             : 
    1291          12 :   if (m)
    1292             :     {
    1293           0 :       return VNET_API_ERROR_VALUE_EXIST;
    1294             :     }
    1295             : 
    1296          12 :   if (vec_len (locals) < 2)
    1297             :     {
    1298           0 :       return VNET_API_ERROR_INVALID_VALUE;
    1299             :     }
    1300             : 
    1301          12 :   if (!is_sm_out2in_only (flags))
    1302             :     {
    1303             :       /* Find external address in allocated addresses and reserve port for
    1304             :          address and port pair mapping when dynamic translations enabled */
    1305          10 :       for (i = 0; i < vec_len (sm->addresses); i++)
    1306             :         {
    1307          10 :           if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
    1308             :             {
    1309             :               /* External port must be unused */
    1310          10 :               a = sm->addresses + i;
    1311          10 :               if (nat44_ed_sm_o2i_lookup (sm, a->addr, e_port, 0, proto))
    1312             :                 {
    1313           0 :                   return VNET_API_ERROR_VALUE_EXIST;
    1314             :                 }
    1315          10 :               break;
    1316             :             }
    1317             :         }
    1318             :       // external address must be allocated
    1319          10 :       if (!a)
    1320             :         {
    1321           0 :           return VNET_API_ERROR_NO_SUCH_ENTRY;
    1322             :         }
    1323             :     }
    1324             : 
    1325          12 :   pool_get (sm->static_mappings, m);
    1326          12 :   clib_memset (m, 0, sizeof (*m));
    1327          12 :   m->tag = vec_dup (tag);
    1328          12 :   m->external_addr = e_addr;
    1329          12 :   m->external_port = e_port;
    1330          12 :   m->affinity = affinity;
    1331          12 :   m->proto = proto;
    1332             : 
    1333          12 :   m->flags = flags;
    1334          12 :   m->flags |= NAT_SM_FLAG_LB;
    1335             : 
    1336          12 :   if (affinity)
    1337           2 :     m->affinity_per_service_list_head_index =
    1338           2 :       nat_affinity_get_per_service_list_head_index ();
    1339             :   else
    1340          10 :     m->affinity_per_service_list_head_index = ~0;
    1341             : 
    1342          12 :   if (nat44_ed_sm_o2i_add (sm, m, m->external_addr, m->external_port, 0,
    1343          12 :                            m->proto))
    1344             :     {
    1345           0 :       nat_log_err ("sm o2i key add failed");
    1346           0 :       return VNET_API_ERROR_UNSPECIFIED;
    1347             :     }
    1348             : 
    1349          36 :   for (i = 0; i < vec_len (locals); i++)
    1350             :     {
    1351          48 :       locals[i].fib_index = fib_table_find_or_create_and_lock (
    1352          24 :         FIB_PROTOCOL_IP4, locals[i].vrf_id, sm->fib_src_low);
    1353          24 :       if (!is_sm_out2in_only (flags))
    1354             :         {
    1355          20 :           if (nat44_ed_sm_o2i_add (sm, m, e_addr, e_port, 0, proto))
    1356             :             {
    1357           0 :               nat_log_err ("sm o2i key add failed");
    1358           0 :               rc = VNET_API_ERROR_UNSPECIFIED;
    1359             :               // here we continue with add operation so that it can be safely
    1360             :               // reversed in delete path - otherwise we'd have to track what
    1361             :               // we've done and deal with partial cleanups and since bihash
    1362             :               // adds are (extremely improbable) the only points of failure,
    1363             :               // it's easier to just do it this way
    1364             :             }
    1365             :         }
    1366          24 :       locals[i].prefix = (i == 0) ?
    1367          12 :                            locals[i].probability :
    1368          12 :                            (locals[i - 1].prefix + locals[i].probability);
    1369          24 :       pool_get (m->locals, local);
    1370          24 :       *local = locals[i];
    1371          24 :       if (sm->num_workers > 1)
    1372             :         {
    1373          14 :           ip4_header_t ip = {
    1374          14 :             .src_address = locals[i].addr,
    1375             :           };
    1376          14 :           bitmap = clib_bitmap_set (
    1377          14 :             bitmap, nat44_ed_get_in2out_worker_index (0, &ip, m->fib_index, 0),
    1378             :             1);
    1379             :         }
    1380             :     }
    1381             : 
    1382             :   /* Assign workers */
    1383          12 :   if (sm->num_workers > 1)
    1384             :     {
    1385          19 :       clib_bitmap_foreach (i, bitmap)
    1386             :         {
    1387          12 :           vec_add1 (m->workers, i);
    1388             :         }
    1389             :     }
    1390             : 
    1391          12 :   return rc;
    1392             : }
    1393             : 
    1394             : int
    1395           0 : nat44_ed_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
    1396             :                                 ip_protocol_t proto, u32 flags)
    1397             : {
    1398           0 :   snat_main_t *sm = &snat_main;
    1399             :   snat_static_mapping_t *m;
    1400             : 
    1401             :   nat44_lb_addr_port_t *local;
    1402             :   snat_main_per_thread_data_t *tsm;
    1403             :   snat_session_t *s;
    1404             : 
    1405           0 :   if (!sm->enabled)
    1406             :     {
    1407           0 :       return VNET_API_ERROR_UNSUPPORTED;
    1408             :     }
    1409             : 
    1410           0 :   m = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto);
    1411           0 :   if (!m)
    1412           0 :     return VNET_API_ERROR_NO_SUCH_ENTRY;
    1413             : 
    1414           0 :   if (!is_sm_lb (m->flags))
    1415           0 :     return VNET_API_ERROR_INVALID_VALUE;
    1416             : 
    1417           0 :   if (nat44_ed_sm_o2i_del (sm, m->external_addr, m->external_port, 0,
    1418           0 :                            m->proto))
    1419             :     {
    1420           0 :       nat_log_err ("sm o2i key del failed");
    1421           0 :       return VNET_API_ERROR_UNSPECIFIED;
    1422             :     }
    1423             : 
    1424           0 :   pool_foreach (local, m->locals)
    1425             :     {
    1426           0 :       fib_table_unlock (local->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
    1427           0 :       if (!is_sm_out2in_only (flags))
    1428             :         {
    1429           0 :           if (nat44_ed_sm_i2o_del (sm, local->addr, local->port,
    1430           0 :                                    local->fib_index, m->proto))
    1431             :             {
    1432           0 :               nat_log_err ("sm i2o key del failed");
    1433           0 :               return VNET_API_ERROR_UNSPECIFIED;
    1434             :             }
    1435             :         }
    1436             : 
    1437           0 :       if (sm->num_workers > 1)
    1438             :         {
    1439           0 :           ip4_header_t ip = {
    1440             :             .src_address = local->addr,
    1441             :           };
    1442           0 :           tsm = vec_elt_at_index (
    1443             :             sm->per_thread_data,
    1444             :             nat44_ed_get_in2out_worker_index (0, &ip, m->fib_index, 0));
    1445             :         }
    1446             :       else
    1447           0 :         tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
    1448             : 
    1449             :       /* Delete sessions */
    1450           0 :       pool_foreach (s, tsm->sessions)
    1451             :         {
    1452           0 :           if (!(nat44_ed_is_lb_session (s)))
    1453           0 :             continue;
    1454             : 
    1455           0 :           if ((s->in2out.addr.as_u32 != local->addr.as_u32) ||
    1456           0 :               s->in2out.port != local->port)
    1457           0 :             continue;
    1458             : 
    1459           0 :           nat44_ed_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
    1460           0 :           nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
    1461             :         }
    1462             :     }
    1463             : 
    1464           0 :   if (m->affinity)
    1465             :     {
    1466           0 :       nat_affinity_flush_service (m->affinity_per_service_list_head_index);
    1467             :     }
    1468             : 
    1469           0 :   pool_free (m->locals);
    1470           0 :   vec_free (m->tag);
    1471           0 :   vec_free (m->workers);
    1472           0 :   pool_put (sm->static_mappings, m);
    1473             : 
    1474           0 :   return 0;
    1475             : }
    1476             : 
    1477             : int
    1478           2 : nat44_ed_add_del_lb_static_mapping_local (ip4_address_t e_addr, u16 e_port,
    1479             :                                           ip4_address_t l_addr, u16 l_port,
    1480             :                                           ip_protocol_t proto, u32 vrf_id,
    1481             :                                           u8 probability, u8 is_add)
    1482             : {
    1483           2 :   snat_main_t *sm = &snat_main;
    1484           2 :   snat_static_mapping_t *m = 0;
    1485           2 :   nat44_lb_addr_port_t *local, *prev_local, *match_local = 0;
    1486             :   snat_main_per_thread_data_t *tsm;
    1487             :   snat_session_t *s;
    1488           2 :   u32 *locals = 0;
    1489           2 :   uword *bitmap = 0;
    1490             :   int i;
    1491             : 
    1492           2 :   if (!sm->enabled)
    1493             :     {
    1494           0 :       return VNET_API_ERROR_UNSUPPORTED;
    1495             :     }
    1496             : 
    1497           2 :   m = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto);
    1498             : 
    1499           2 :   if (!m)
    1500             :     {
    1501           0 :       return VNET_API_ERROR_NO_SUCH_ENTRY;
    1502             :     }
    1503             : 
    1504           2 :   if (!is_sm_lb (m->flags))
    1505             :     {
    1506           0 :       return VNET_API_ERROR_INVALID_VALUE;
    1507             :     }
    1508             : 
    1509           5 :   pool_foreach (local, m->locals)
    1510             :    {
    1511           4 :     if ((local->addr.as_u32 == l_addr.as_u32) && (local->port == l_port) &&
    1512           1 :         (local->vrf_id == vrf_id))
    1513             :       {
    1514           1 :         match_local = local;
    1515           1 :         break;
    1516             :       }
    1517             :   }
    1518             : 
    1519           2 :   if (is_add)
    1520             :     {
    1521           1 :       if (match_local)
    1522             :         {
    1523           0 :           return VNET_API_ERROR_VALUE_EXIST;
    1524             :         }
    1525             : 
    1526           1 :       pool_get (m->locals, local);
    1527           1 :       clib_memset (local, 0, sizeof (*local));
    1528           1 :       local->addr.as_u32 = l_addr.as_u32;
    1529           1 :       local->port = l_port;
    1530           1 :       local->probability = probability;
    1531           1 :       local->vrf_id = vrf_id;
    1532           2 :       local->fib_index =
    1533           1 :         fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
    1534           1 :                                            sm->fib_src_low);
    1535             : 
    1536           1 :       if (!is_sm_out2in_only (m->flags))
    1537             :         {
    1538           1 :           if (nat44_ed_sm_i2o_add (sm, m, l_addr, l_port, local->fib_index,
    1539             :                                    proto))
    1540             :             {
    1541           0 :               nat_log_err ("sm i2o key add failed");
    1542           0 :               pool_put (m->locals, local);
    1543           0 :               return VNET_API_ERROR_UNSPECIFIED;
    1544             :             }
    1545             :         }
    1546             :     }
    1547             :   else
    1548             :     {
    1549           1 :       if (!match_local)
    1550           0 :         return VNET_API_ERROR_NO_SUCH_ENTRY;
    1551             : 
    1552           1 :       if (pool_elts (m->locals) < 3)
    1553           0 :         return VNET_API_ERROR_UNSPECIFIED;
    1554             : 
    1555           1 :       fib_table_unlock (match_local->fib_index, FIB_PROTOCOL_IP4,
    1556           1 :                         sm->fib_src_low);
    1557             : 
    1558           1 :       if (!is_sm_out2in_only (m->flags))
    1559             :         {
    1560           1 :           if (nat44_ed_sm_i2o_del (sm, l_addr, l_port, match_local->fib_index,
    1561             :                                    proto))
    1562           1 :             nat_log_err ("sm i2o key del failed");
    1563             :         }
    1564             : 
    1565           1 :       if (sm->num_workers > 1)
    1566             :         {
    1567           1 :           ip4_header_t ip = {
    1568           1 :             .src_address = local->addr,
    1569             :           };
    1570           1 :           tsm = vec_elt_at_index (
    1571             :             sm->per_thread_data,
    1572             :             nat44_ed_get_in2out_worker_index (0, &ip, m->fib_index, 0));
    1573             :         }
    1574             :       else
    1575           0 :         tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
    1576             : 
    1577             :       /* Delete sessions */
    1578          38 :       pool_foreach (s, tsm->sessions) {
    1579          37 :           if (!(nat44_ed_is_lb_session (s)))
    1580           0 :             continue;
    1581             : 
    1582          37 :           if ((s->in2out.addr.as_u32 != match_local->addr.as_u32) ||
    1583          37 :               s->in2out.port != match_local->port)
    1584           0 :             continue;
    1585             : 
    1586          37 :           nat44_ed_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
    1587          37 :           nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
    1588             :       }
    1589             : 
    1590           1 :       pool_put (m->locals, match_local);
    1591             :     }
    1592             : 
    1593           2 :   vec_free (m->workers);
    1594             : 
    1595           7 :   pool_foreach (local, m->locals)
    1596             :    {
    1597           5 :     vec_add1 (locals, local - m->locals);
    1598           5 :     if (sm->num_workers > 1)
    1599             :       {
    1600             :         ip4_header_t ip;
    1601           5 :         ip.src_address.as_u32 = local->addr.as_u32,
    1602           5 :         bitmap = clib_bitmap_set (
    1603             :           bitmap,
    1604           5 :           nat44_ed_get_in2out_worker_index (0, &ip, local->fib_index, 0), 1);
    1605             :       }
    1606             :   }
    1607             : 
    1608           2 :   ASSERT (vec_len (locals) > 1);
    1609             : 
    1610           2 :   local = pool_elt_at_index (m->locals, locals[0]);
    1611           2 :   local->prefix = local->probability;
    1612           5 :   for (i = 1; i < vec_len (locals); i++)
    1613             :     {
    1614           3 :       local = pool_elt_at_index (m->locals, locals[i]);
    1615           3 :       prev_local = pool_elt_at_index (m->locals, locals[i - 1]);
    1616           3 :       local->prefix = local->probability + prev_local->prefix;
    1617             :     }
    1618             : 
    1619             :   /* Assign workers */
    1620           2 :   if (sm->num_workers > 1)
    1621             :     {
    1622           7 :       clib_bitmap_foreach (i, bitmap)  { vec_add1(m->workers, i); }
    1623             :     }
    1624             : 
    1625           2 :   return 0;
    1626             : }
    1627             : 
    1628             : void
    1629         358 : expire_per_vrf_sessions (u32 fib_index)
    1630             : {
    1631             :   per_vrf_sessions_t *per_vrf_sessions;
    1632             :   snat_main_per_thread_data_t *tsm;
    1633         358 :   snat_main_t *sm = &snat_main;
    1634             : 
    1635         752 :   vec_foreach (tsm, sm->per_thread_data)
    1636             :     {
    1637         404 :       pool_foreach (per_vrf_sessions, tsm->per_vrf_sessions_pool)
    1638             :         {
    1639          10 :           if ((per_vrf_sessions->rx_fib_index == fib_index) ||
    1640           3 :               (per_vrf_sessions->tx_fib_index == fib_index))
    1641             :             {
    1642           9 :               per_vrf_sessions->expired = 1;
    1643             :             }
    1644             :         }
    1645             :     }
    1646         358 : }
    1647             : 
    1648             : void
    1649         388 : update_per_vrf_sessions_pool (u32 fib_index, int is_del)
    1650             : {
    1651         388 :   snat_main_t *sm = &snat_main;
    1652             :   nat_fib_t *fib;
    1653             : 
    1654             :   // we don't care if it is outside/inside fib
    1655             :   // we just care about their ref_count
    1656             :   // if it reaches 0 sessions should expire
    1657             :   // because the fib isn't valid for NAT anymore
    1658             : 
    1659       11207 :   vec_foreach (fib, sm->fibs)
    1660             :   {
    1661       11012 :     if (fib->fib_index == fib_index)
    1662             :       {
    1663        8275 :         if (is_del)
    1664             :           {
    1665         193 :             fib->ref_count--;
    1666         193 :             if (!fib->ref_count)
    1667             :               {
    1668           8 :                 vec_del1 (sm->fibs, fib - sm->fibs);
    1669           8 :                 expire_per_vrf_sessions (fib_index);
    1670             :               }
    1671         193 :             return;
    1672             :           }
    1673             :         else
    1674        8082 :           fib->ref_count++;
    1675             :       }
    1676             :   }
    1677         195 :   if (!is_del)
    1678             :     {
    1679         194 :       vec_add2 (sm->fibs, fib, 1);
    1680         194 :       fib->ref_count = 1;
    1681         194 :       fib->fib_index = fib_index;
    1682             :     }
    1683             : }
    1684             : 
    1685             : static_always_inline nat_fib_t *
    1686         216 : nat44_ed_get_outside_fib (nat_fib_t *outside_fibs, u32 fib_index)
    1687             : {
    1688             :   nat_fib_t *f;
    1689         228 :   vec_foreach (f, outside_fibs)
    1690             :     {
    1691         136 :       if (f->fib_index == fib_index)
    1692             :         {
    1693         124 :           return f;
    1694             :         }
    1695             :     }
    1696          92 :   return 0;
    1697             : }
    1698             : 
    1699             : static_always_inline snat_interface_t *
    1700         614 : nat44_ed_get_interface (snat_interface_t *interfaces, u32 sw_if_index)
    1701             : {
    1702             :   snat_interface_t *i;
    1703         721 :   pool_foreach (i, interfaces)
    1704             :     {
    1705         339 :       if (i->sw_if_index == sw_if_index)
    1706             :         {
    1707         232 :           return i;
    1708             :         }
    1709             :     }
    1710         382 :   return 0;
    1711             : }
    1712             : 
    1713             : int
    1714         180 : nat44_ed_add_interface (u32 sw_if_index, u8 is_inside)
    1715             : {
    1716             :   const char *del_feature_name, *feature_name;
    1717         180 :   snat_main_t *sm = &snat_main;
    1718             : 
    1719             :   nat_fib_t *outside_fib;
    1720             :   snat_interface_t *i;
    1721             :   u32 fib_index;
    1722             :   int rv;
    1723             : 
    1724         180 :   if (!sm->enabled)
    1725             :     {
    1726           0 :       nat_log_err ("nat44 is disabled");
    1727           0 :       return VNET_API_ERROR_UNSUPPORTED;
    1728             :     }
    1729             : 
    1730         180 :   if (nat44_ed_get_interface (sm->output_feature_interfaces, sw_if_index))
    1731             :     {
    1732           0 :       nat_log_err ("error interface already configured");
    1733           0 :       return VNET_API_ERROR_VALUE_EXIST;
    1734             :     }
    1735             : 
    1736         180 :   i = nat44_ed_get_interface (sm->interfaces, sw_if_index);
    1737         180 :   if (i)
    1738             :     {
    1739          60 :       if ((nat44_ed_is_interface_inside (i) && is_inside) ||
    1740          49 :           (nat44_ed_is_interface_outside (i) && !is_inside))
    1741             :         {
    1742           0 :           return 0;
    1743             :         }
    1744          30 :       if (sm->num_workers > 1)
    1745             :         {
    1746          19 :           del_feature_name = !is_inside ? "nat44-in2out-worker-handoff" :
    1747             :                                           "nat44-out2in-worker-handoff";
    1748          19 :           feature_name = "nat44-handoff-classify";
    1749             :         }
    1750             :       else
    1751             :         {
    1752          11 :           del_feature_name = !is_inside ? "nat-pre-in2out" : "nat-pre-out2in";
    1753             : 
    1754          11 :           feature_name = "nat44-ed-classify";
    1755             :         }
    1756             : 
    1757          30 :       rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
    1758          30 :       if (rv)
    1759           0 :         return rv;
    1760          30 :       vnet_feature_enable_disable ("ip4-unicast", del_feature_name,
    1761             :                                    sw_if_index, 0, 0, 0);
    1762          30 :       vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1,
    1763             :                                    0, 0);
    1764             :     }
    1765             :   else
    1766             :     {
    1767         150 :       if (sm->num_workers > 1)
    1768             :         {
    1769         107 :           feature_name = is_inside ? "nat44-in2out-worker-handoff" :
    1770             :                                      "nat44-out2in-worker-handoff";
    1771             :         }
    1772             :       else
    1773             :         {
    1774          43 :           feature_name = is_inside ? "nat-pre-in2out" : "nat-pre-out2in";
    1775             :         }
    1776             : 
    1777         150 :       nat_validate_interface_counters (sm, sw_if_index);
    1778         150 :       rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
    1779         150 :       if (rv)
    1780           0 :         return rv;
    1781         150 :       vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1,
    1782             :                                    0, 0);
    1783             : 
    1784         150 :       pool_get (sm->interfaces, i);
    1785         150 :       i->sw_if_index = sw_if_index;
    1786         150 :       i->flags = 0;
    1787             :     }
    1788             : 
    1789             :   fib_index =
    1790         180 :     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
    1791             : 
    1792         180 :   update_per_vrf_sessions_pool (fib_index, 0 /*is_del*/);
    1793             : 
    1794         180 :   if (!is_inside)
    1795             :     {
    1796          94 :       i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE;
    1797             : 
    1798          94 :       outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
    1799          94 :       if (outside_fib)
    1800             :         {
    1801           8 :           outside_fib->ref_count++;
    1802             :         }
    1803             :       else
    1804             :         {
    1805          86 :           vec_add2 (sm->outside_fibs, outside_fib, 1);
    1806          86 :           outside_fib->fib_index = fib_index;
    1807          86 :           outside_fib->ref_count = 1;
    1808             :         }
    1809             : 
    1810          94 :       nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 1);
    1811          94 :       nat44_ed_add_del_sm_fib_reg_entries (sw_if_index, 1);
    1812             : 
    1813          94 :       nat44_ed_bind_if_addr_to_nat_addr (sw_if_index);
    1814             :     }
    1815             :   else
    1816             :     {
    1817          86 :       i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE;
    1818             :     }
    1819             : 
    1820         180 :   return 0;
    1821             : }
    1822             : 
    1823             : int
    1824         180 : nat44_ed_del_interface (u32 sw_if_index, u8 is_inside)
    1825             : {
    1826             :   const char *del_feature_name, *feature_name;
    1827         180 :   snat_main_t *sm = &snat_main;
    1828             : 
    1829             :   nat_fib_t *outside_fib;
    1830             :   snat_interface_t *i;
    1831             :   u32 fib_index;
    1832             :   int rv;
    1833             : 
    1834         180 :   if (!sm->enabled)
    1835             :     {
    1836           0 :       nat_log_err ("nat44 is disabled");
    1837           0 :       return VNET_API_ERROR_UNSUPPORTED;
    1838             :     }
    1839             : 
    1840         180 :   i = nat44_ed_get_interface (sm->interfaces, sw_if_index);
    1841         180 :   if (i == 0)
    1842             :     {
    1843           0 :       nat_log_err ("error interface couldn't be found");
    1844           0 :       return VNET_API_ERROR_NO_SUCH_ENTRY;
    1845             :     }
    1846             : 
    1847         180 :   if (nat44_ed_is_interface_inside (i) && nat44_ed_is_interface_outside (i))
    1848             :     {
    1849          30 :       if (sm->num_workers > 1)
    1850             :         {
    1851          19 :           del_feature_name = "nat44-handoff-classify";
    1852          19 :           feature_name = !is_inside ? "nat44-in2out-worker-handoff" :
    1853             :                                       "nat44-out2in-worker-handoff";
    1854             :         }
    1855             :       else
    1856             :         {
    1857          11 :           del_feature_name = "nat44-ed-classify";
    1858          11 :           feature_name = !is_inside ? "nat-pre-in2out" : "nat-pre-out2in";
    1859             :         }
    1860             : 
    1861          30 :       rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
    1862          30 :       if (rv)
    1863             :         {
    1864           0 :           return rv;
    1865             :         }
    1866          30 :       vnet_feature_enable_disable ("ip4-unicast", del_feature_name,
    1867             :                                    sw_if_index, 0, 0, 0);
    1868          30 :       vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1,
    1869             :                                    0, 0);
    1870             : 
    1871          30 :       if (is_inside)
    1872             :         {
    1873          30 :           i->flags &= ~NAT_INTERFACE_FLAG_IS_INSIDE;
    1874             :         }
    1875             :       else
    1876             :         {
    1877           0 :           i->flags &= ~NAT_INTERFACE_FLAG_IS_OUTSIDE;
    1878             :         }
    1879             :     }
    1880             :   else
    1881             :     {
    1882         150 :       if (sm->num_workers > 1)
    1883             :         {
    1884         107 :           feature_name = is_inside ? "nat44-in2out-worker-handoff" :
    1885             :                                      "nat44-out2in-worker-handoff";
    1886             :         }
    1887             :       else
    1888             :         {
    1889          43 :           feature_name = is_inside ? "nat-pre-in2out" : "nat-pre-out2in";
    1890             :         }
    1891             : 
    1892         150 :       rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
    1893         150 :       if (rv)
    1894             :         {
    1895           0 :           return rv;
    1896             :         }
    1897         150 :       vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 0,
    1898             :                                    0, 0);
    1899             : 
    1900             :       // remove interface
    1901         150 :       pool_put (sm->interfaces, i);
    1902             :     }
    1903             : 
    1904             :   fib_index =
    1905         180 :     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
    1906             : 
    1907         180 :   update_per_vrf_sessions_pool (fib_index, 1 /*is_del*/);
    1908             : 
    1909         180 :   if (!is_inside)
    1910             :     {
    1911          94 :       outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
    1912          94 :       if (outside_fib)
    1913             :         {
    1914          94 :           outside_fib->ref_count--;
    1915          94 :           if (!outside_fib->ref_count)
    1916             :             {
    1917          78 :               vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
    1918             :             }
    1919             :         }
    1920             : 
    1921          94 :       nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 0);
    1922          94 :       nat44_ed_add_del_sm_fib_reg_entries (sw_if_index, 0);
    1923             :     }
    1924             : 
    1925         180 :   return 0;
    1926             : }
    1927             : 
    1928             : int
    1929          14 : nat44_ed_add_output_interface (u32 sw_if_index)
    1930             : {
    1931          14 :   snat_main_t *sm = &snat_main;
    1932             : 
    1933             :   nat_fib_t *outside_fib;
    1934             :   snat_interface_t *i;
    1935             :   u32 fib_index;
    1936             :   int rv;
    1937             : 
    1938          14 :   if (!sm->enabled)
    1939             :     {
    1940           0 :       nat_log_err ("nat44 is disabled");
    1941           0 :       return VNET_API_ERROR_UNSUPPORTED;
    1942             :     }
    1943             : 
    1944          14 :   if (nat44_ed_get_interface (sm->interfaces, sw_if_index))
    1945             :     {
    1946           0 :       nat_log_err ("error interface already configured");
    1947           0 :       return VNET_API_ERROR_VALUE_EXIST;
    1948             :     }
    1949             : 
    1950          14 :   if (nat44_ed_get_interface (sm->output_feature_interfaces, sw_if_index))
    1951             :     {
    1952           0 :       nat_log_err ("error interface already configured");
    1953           0 :       return VNET_API_ERROR_VALUE_EXIST;
    1954             :     }
    1955             : 
    1956          14 :   if (sm->num_workers > 1)
    1957             :     {
    1958           9 :       rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
    1959           9 :       if (rv)
    1960             :         {
    1961           0 :           return rv;
    1962             :         }
    1963             : 
    1964           9 :       rv = ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, 1);
    1965           9 :       if (rv)
    1966             :         {
    1967           0 :           return rv;
    1968             :         }
    1969             : 
    1970           9 :       vnet_feature_enable_disable (
    1971             :         "ip4-unicast", "nat44-out2in-worker-handoff", sw_if_index, 1, 0, 0);
    1972           9 :       vnet_feature_enable_disable ("ip4-output",
    1973             :                                    "nat44-in2out-output-worker-handoff",
    1974             :                                    sw_if_index, 1, 0, 0);
    1975             :     }
    1976             :   else
    1977             :     {
    1978           5 :       rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
    1979           5 :       if (rv)
    1980             :         {
    1981           0 :           return rv;
    1982             :         }
    1983             : 
    1984           5 :       rv = ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, 1);
    1985           5 :       if (rv)
    1986             :         {
    1987           0 :           return rv;
    1988             :         }
    1989             : 
    1990           5 :       vnet_feature_enable_disable ("ip4-unicast", "nat-pre-out2in",
    1991             :                                    sw_if_index, 1, 0, 0);
    1992           5 :       vnet_feature_enable_disable ("ip4-output", "nat-pre-in2out-output",
    1993             :                                    sw_if_index, 1, 0, 0);
    1994             :     }
    1995             : 
    1996          14 :   nat_validate_interface_counters (sm, sw_if_index);
    1997             : 
    1998          14 :   pool_get (sm->output_feature_interfaces, i);
    1999          14 :   i->sw_if_index = sw_if_index;
    2000          14 :   i->flags = 0;
    2001          14 :   i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE;
    2002          14 :   i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE;
    2003             : 
    2004             :   fib_index =
    2005          14 :     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
    2006          14 :   update_per_vrf_sessions_pool (fib_index, 0 /*is_del*/);
    2007             : 
    2008          14 :   outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
    2009          14 :   if (outside_fib)
    2010             :     {
    2011           8 :       outside_fib->ref_count++;
    2012             :     }
    2013             :   else
    2014             :     {
    2015           6 :       vec_add2 (sm->outside_fibs, outside_fib, 1);
    2016           6 :       outside_fib->fib_index = fib_index;
    2017           6 :       outside_fib->ref_count = 1;
    2018             :     }
    2019             : 
    2020          14 :   nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 1);
    2021          14 :   nat44_ed_add_del_sm_fib_reg_entries (sw_if_index, 1);
    2022             : 
    2023          14 :   nat44_ed_bind_if_addr_to_nat_addr (sw_if_index);
    2024             : 
    2025          14 :   return 0;
    2026             : }
    2027             : 
    2028             : int
    2029          14 : nat44_ed_del_output_interface (u32 sw_if_index)
    2030             : {
    2031          14 :   snat_main_t *sm = &snat_main;
    2032             : 
    2033             :   nat_fib_t *outside_fib;
    2034             :   snat_interface_t *i;
    2035             :   u32 fib_index;
    2036             :   int rv;
    2037             : 
    2038          14 :   if (!sm->enabled)
    2039             :     {
    2040           0 :       nat_log_err ("nat44 is disabled");
    2041           0 :       return VNET_API_ERROR_UNSUPPORTED;
    2042             :     }
    2043             : 
    2044          14 :   i = nat44_ed_get_interface (sm->output_feature_interfaces, sw_if_index);
    2045          14 :   if (!i)
    2046             :     {
    2047           0 :       nat_log_err ("error interface couldn't be found");
    2048           0 :       return VNET_API_ERROR_NO_SUCH_ENTRY;
    2049             :     }
    2050             : 
    2051          14 :   if (sm->num_workers > 1)
    2052             :     {
    2053           9 :       rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
    2054           9 :       if (rv)
    2055             :         {
    2056           0 :           return rv;
    2057             :         }
    2058             : 
    2059           9 :       rv = ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, 0);
    2060           9 :       if (rv)
    2061             :         {
    2062           0 :           return rv;
    2063             :         }
    2064             : 
    2065           9 :       vnet_feature_enable_disable (
    2066             :         "ip4-unicast", "nat44-out2in-worker-handoff", sw_if_index, 0, 0, 0);
    2067           9 :       vnet_feature_enable_disable ("ip4-output",
    2068             :                                    "nat44-in2out-output-worker-handoff",
    2069             :                                    sw_if_index, 0, 0, 0);
    2070             :     }
    2071             :   else
    2072             :     {
    2073           5 :       rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
    2074           5 :       if (rv)
    2075             :         {
    2076           0 :           return rv;
    2077             :         }
    2078             : 
    2079           5 :       rv = ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, 0);
    2080           5 :       if (rv)
    2081             :         {
    2082           0 :           return rv;
    2083             :         }
    2084             : 
    2085           5 :       vnet_feature_enable_disable ("ip4-unicast", "nat-pre-out2in",
    2086             :                                    sw_if_index, 0, 0, 0);
    2087           5 :       vnet_feature_enable_disable ("ip4-output", "nat-pre-in2out-output",
    2088             :                                    sw_if_index, 0, 0, 0);
    2089             :     }
    2090             : 
    2091             :   // remove interface
    2092          14 :   pool_put (sm->output_feature_interfaces, i);
    2093             : 
    2094             :   fib_index =
    2095          14 :     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
    2096          14 :   update_per_vrf_sessions_pool (fib_index, 1 /*is_del*/);
    2097             : 
    2098          14 :   outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
    2099          14 :   if (outside_fib)
    2100             :     {
    2101          14 :       outside_fib->ref_count--;
    2102          14 :       if (!outside_fib->ref_count)
    2103             :         {
    2104          14 :           vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
    2105             :         }
    2106             :     }
    2107             : 
    2108          14 :   nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 0);
    2109          14 :   nat44_ed_add_del_sm_fib_reg_entries (sw_if_index, 0);
    2110             : 
    2111          14 :   return 0;
    2112             : }
    2113             : 
    2114             : int
    2115          14 : snat_set_workers (uword * bitmap)
    2116             : {
    2117          14 :   snat_main_t *sm = &snat_main;
    2118          14 :   int i, j = 0;
    2119             : 
    2120          14 :   if (sm->num_workers < 2)
    2121           0 :     return VNET_API_ERROR_FEATURE_DISABLED;
    2122             : 
    2123          14 :   if (clib_bitmap_last_set (bitmap) >= sm->num_workers)
    2124           0 :     return VNET_API_ERROR_INVALID_WORKER;
    2125             : 
    2126          14 :   vec_free (sm->workers);
    2127          46 :   clib_bitmap_foreach (i, bitmap)
    2128             :     {
    2129          32 :       vec_add1(sm->workers, i);
    2130          32 :       sm->per_thread_data[sm->first_worker_index + i].snat_thread_index = j;
    2131          32 :       sm->per_thread_data[sm->first_worker_index + i].thread_index = i;
    2132          32 :       j++;
    2133             :     }
    2134             : 
    2135          14 :   sm->port_per_thread = (65536 - ED_USER_PORT_OFFSET) / _vec_len (sm->workers);
    2136             : 
    2137          14 :   return 0;
    2138             : }
    2139             : 
    2140             : int
    2141           0 : nat44_ed_set_frame_queue_nelts (u32 frame_queue_nelts)
    2142             : {
    2143           0 :   fail_if_enabled ();
    2144           0 :   snat_main_t *sm = &snat_main;
    2145             : 
    2146           0 :   if ((sm->fq_in2out_index != ~0) || (sm->fq_out2in_index != ~0) ||
    2147           0 :       (sm->fq_in2out_output_index != ~0))
    2148             :     {
    2149             :       // frame queu nelts can be set only before first
    2150             :       // call to nat44_plugin_enable after that it
    2151             :       // doesn't make sense
    2152           0 :       nat_log_err ("Frame queue was already initialized. "
    2153             :                    "Change is not possible");
    2154           0 :       return 1;
    2155             :     }
    2156             : 
    2157           0 :   sm->frame_queue_nelts = frame_queue_nelts;
    2158           0 :   return 0;
    2159             : }
    2160             : 
    2161             : static void
    2162         896 : nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque, u32 sw_if_index,
    2163             :                                 u32 new_fib_index, u32 old_fib_index)
    2164             : {
    2165         896 :   snat_main_t *sm = &snat_main;
    2166             :   nat_fib_t *outside_fib;
    2167             :   snat_interface_t *i;
    2168         896 :   u8 is_add = 1;
    2169         896 :   u8 match = 0;
    2170             : 
    2171         896 :   if (!sm->enabled || (new_fib_index == old_fib_index)
    2172           4 :       || (!vec_len (sm->outside_fibs)))
    2173             :     {
    2174         895 :       return;
    2175             :     }
    2176             : 
    2177           2 :   pool_foreach (i, sm->interfaces)
    2178             :     {
    2179           0 :       if (i->sw_if_index == sw_if_index)
    2180             :         {
    2181           0 :           if (!(nat44_ed_is_interface_outside (i)))
    2182           0 :             return;
    2183           0 :           match = 1;
    2184             :         }
    2185             :     }
    2186             : 
    2187           4 :   pool_foreach (i, sm->output_feature_interfaces)
    2188             :     {
    2189           2 :       if (i->sw_if_index == sw_if_index)
    2190             :         {
    2191           1 :           if (!(nat44_ed_is_interface_outside (i)))
    2192           0 :             return;
    2193           1 :           match = 1;
    2194             :         }
    2195             :     }
    2196             : 
    2197           2 :   if (!match)
    2198           1 :     return;
    2199             : 
    2200           1 :   vec_foreach (outside_fib, sm->outside_fibs)
    2201             :     {
    2202           1 :       if (outside_fib->fib_index == old_fib_index)
    2203             :         {
    2204           1 :           outside_fib->ref_count--;
    2205           1 :           if (!outside_fib->ref_count)
    2206           1 :             vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
    2207           1 :           break;
    2208             :         }
    2209             :     }
    2210             : 
    2211           1 :   vec_foreach (outside_fib, sm->outside_fibs)
    2212             :     {
    2213           0 :       if (outside_fib->fib_index == new_fib_index)
    2214             :         {
    2215           0 :           outside_fib->ref_count++;
    2216           0 :           is_add = 0;
    2217           0 :           break;
    2218             :         }
    2219             :     }
    2220             : 
    2221           1 :   if (is_add)
    2222             :     {
    2223           1 :       vec_add2 (sm->outside_fibs, outside_fib, 1);
    2224           1 :       outside_fib->ref_count = 1;
    2225           1 :       outside_fib->fib_index = new_fib_index;
    2226             :     }
    2227             : }
    2228             : 
    2229             : static void nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque,
    2230             :                                             u32 sw_if_index, u32 new_fib_index,
    2231             :                                             u32 old_fib_index);
    2232             : 
    2233             : static void nat44_ed_add_del_interface_address_cb (
    2234             :   ip4_main_t *im, uword opaque, u32 sw_if_index, ip4_address_t *address,
    2235             :   u32 address_length, u32 if_address_index, u32 is_delete);
    2236             : 
    2237             : static void nat44_ed_add_del_static_mapping_cb (
    2238             :   ip4_main_t *im, uword opaque, u32 sw_if_index, ip4_address_t *address,
    2239             :   u32 address_length, u32 if_address_index, u32 is_delete);
    2240             : 
    2241             : void
    2242         559 : test_key_calc_split ()
    2243             : {
    2244             :   ip4_address_t l_addr;
    2245         559 :   l_addr.as_u8[0] = 1;
    2246         559 :   l_addr.as_u8[1] = 1;
    2247         559 :   l_addr.as_u8[2] = 1;
    2248         559 :   l_addr.as_u8[3] = 1;
    2249             :   ip4_address_t r_addr;
    2250         559 :   r_addr.as_u8[0] = 2;
    2251         559 :   r_addr.as_u8[1] = 2;
    2252         559 :   r_addr.as_u8[2] = 2;
    2253         559 :   r_addr.as_u8[3] = 2;
    2254         559 :   u16 l_port = 40001;
    2255         559 :   u16 r_port = 40301;
    2256         559 :   u8 proto = 9;
    2257         559 :   u32 fib_index = 9000001;
    2258         559 :   u32 thread_index = 3000000001;
    2259         559 :   u32 session_index = 3000000221;
    2260             :   clib_bihash_kv_16_8_t kv;
    2261         559 :   init_ed_kv (&kv, l_addr.as_u32, l_port, r_addr.as_u32, r_port, fib_index,
    2262             :               proto, thread_index, session_index);
    2263             :   ip4_address_t l_addr2;
    2264             :   ip4_address_t r_addr2;
    2265         559 :   clib_memset (&l_addr2, 0, sizeof (l_addr2));
    2266         559 :   clib_memset (&r_addr2, 0, sizeof (r_addr2));
    2267         559 :   u16 l_port2 = 0;
    2268         559 :   u16 r_port2 = 0;
    2269         559 :   u8 proto2 = 0;
    2270         559 :   u32 fib_index2 = 0;
    2271         559 :   split_ed_kv (&kv, &l_addr2, &r_addr2, &proto2, &fib_index2, &l_port2,
    2272             :                &r_port2);
    2273         559 :   ASSERT (l_addr.as_u32 == l_addr2.as_u32);
    2274         559 :   ASSERT (r_addr.as_u32 == r_addr2.as_u32);
    2275         559 :   ASSERT (l_port == l_port2);
    2276         559 :   ASSERT (r_port == r_port2);
    2277         559 :   ASSERT (proto == proto2);
    2278         559 :   ASSERT (fib_index == fib_index2);
    2279         559 :   ASSERT (thread_index == ed_value_get_thread_index (&kv));
    2280         559 :   ASSERT (session_index == ed_value_get_session_index (&kv));
    2281         559 : }
    2282             : 
    2283             : static clib_error_t *
    2284         762 : nat_ip_table_add_del (vnet_main_t * vnm, u32 table_id, u32 is_add)
    2285             : {
    2286             :   u32 fib_index;
    2287         762 :   if (!is_add)
    2288             :     {
    2289         373 :       fib_index = ip4_fib_index_from_table_id (table_id);
    2290         373 :       if (fib_index != ~0)
    2291             :         {
    2292         350 :           expire_per_vrf_sessions (fib_index);
    2293             :         }
    2294             :     }
    2295         762 :   return 0;
    2296             : }
    2297             : 
    2298        1119 : VNET_IP_TABLE_ADD_DEL_FUNCTION (nat_ip_table_add_del);
    2299             : 
    2300             : #define nat_validate_simple_counter(c, i)                                     \
    2301             :   do                                                                          \
    2302             :     {                                                                         \
    2303             :       vlib_validate_simple_counter (&c, i);                                   \
    2304             :       vlib_zero_simple_counter (&c, i);                                       \
    2305             :     }                                                                         \
    2306             :   while (0);
    2307             : 
    2308             : #define nat_init_simple_counter(c, n, sn)                                     \
    2309             :   do                                                                          \
    2310             :     {                                                                         \
    2311             :       c.name = n;                                                             \
    2312             :       c.stat_segment_name = sn;                                               \
    2313             :       nat_validate_simple_counter (c, 0);                                     \
    2314             :     }                                                                         \
    2315             :   while (0);
    2316             : 
    2317             : static_always_inline void
    2318         164 : nat_validate_interface_counters (snat_main_t *sm, u32 sw_if_index)
    2319             : {
    2320             : #define _(x)                                                                  \
    2321             :   nat_validate_simple_counter (sm->counters.fastpath.in2out.x, sw_if_index);  \
    2322             :   nat_validate_simple_counter (sm->counters.fastpath.out2in.x, sw_if_index);  \
    2323             :   nat_validate_simple_counter (sm->counters.slowpath.in2out.x, sw_if_index);  \
    2324             :   nat_validate_simple_counter (sm->counters.slowpath.out2in.x, sw_if_index);
    2325         164 :   foreach_nat_counter;
    2326             : #undef _
    2327         164 :   nat_validate_simple_counter (sm->counters.hairpinning, sw_if_index);
    2328         164 : }
    2329             : 
    2330             : static clib_error_t *
    2331         559 : nat_init (vlib_main_t * vm)
    2332             : {
    2333         559 :   snat_main_t *sm = &snat_main;
    2334         559 :   vlib_thread_main_t *tm = vlib_get_thread_main ();
    2335             :   vlib_thread_registration_t *tr;
    2336         559 :   ip4_add_del_interface_address_callback_t cbi = { 0 };
    2337         559 :   ip4_table_bind_callback_t cbt = { 0 };
    2338         559 :   u32 i, num_threads = 0;
    2339         559 :   uword *p, *bitmap = 0;
    2340             : 
    2341         559 :   clib_memset (sm, 0, sizeof (*sm));
    2342             : 
    2343             :   // convenience
    2344         559 :   sm->ip4_main = &ip4_main;
    2345             : 
    2346             :   // frame queue indices used for handoff
    2347         559 :   sm->fq_out2in_index = ~0;
    2348         559 :   sm->fq_in2out_index = ~0;
    2349         559 :   sm->fq_in2out_output_index = ~0;
    2350             : 
    2351         559 :   sm->log_level = NAT_LOG_ERROR;
    2352             : 
    2353         559 :   sm->log_class = vlib_log_register_class ("nat", 0);
    2354         559 :   nat_ipfix_logging_init (vm);
    2355             : 
    2356         559 :   nat_init_simple_counter (sm->total_sessions, "total-sessions",
    2357             :                            "/nat44-ed/total-sessions");
    2358         559 :   sm->max_cfg_sessions_gauge =
    2359         559 :     vlib_stats_add_gauge ("/nat44-ed/max-cfg-sessions");
    2360             : 
    2361             : #define _(x)                                                                  \
    2362             :   nat_init_simple_counter (sm->counters.fastpath.in2out.x, #x,                \
    2363             :                            "/nat44-ed/in2out/fastpath/" #x);                  \
    2364             :   nat_init_simple_counter (sm->counters.fastpath.out2in.x, #x,                \
    2365             :                            "/nat44-ed/out2in/fastpath/" #x);                  \
    2366             :   nat_init_simple_counter (sm->counters.slowpath.in2out.x, #x,                \
    2367             :                            "/nat44-ed/in2out/slowpath/" #x);                  \
    2368             :   nat_init_simple_counter (sm->counters.slowpath.out2in.x, #x,                \
    2369             :                            "/nat44-ed/out2in/slowpath/" #x);
    2370         559 :   foreach_nat_counter;
    2371             : #undef _
    2372         559 :   nat_init_simple_counter (sm->counters.hairpinning, "hairpinning",
    2373             :                            "/nat44-ed/hairpinning");
    2374             : 
    2375         559 :   p = hash_get_mem (tm->thread_registrations_by_name, "workers");
    2376         559 :   if (p)
    2377             :     {
    2378         559 :       tr = (vlib_thread_registration_t *) p[0];
    2379         559 :       if (tr)
    2380             :         {
    2381         559 :           sm->num_workers = tr->count;
    2382         559 :           sm->first_worker_index = tr->first_index;
    2383             :         }
    2384             :     }
    2385         559 :   num_threads = tm->n_vlib_mains - 1;
    2386         559 :   sm->port_per_thread = 65536 - ED_USER_PORT_OFFSET;
    2387         559 :   vec_validate (sm->per_thread_data, num_threads);
    2388             : 
    2389             :   /* Use all available workers by default */
    2390         559 :   if (sm->num_workers > 1)
    2391             :     {
    2392          46 :       for (i = 0; i < sm->num_workers; i++)
    2393          32 :         bitmap = clib_bitmap_set (bitmap, i, 1);
    2394          14 :       snat_set_workers (bitmap);
    2395          14 :       clib_bitmap_free (bitmap);
    2396             :     }
    2397             :   else
    2398             :     {
    2399         545 :       sm->per_thread_data[0].snat_thread_index = 0;
    2400             :     }
    2401             : 
    2402             :   /* callbacks to call when interface address changes. */
    2403         559 :   cbi.function = nat44_ed_add_del_interface_address_cb;
    2404         559 :   vec_add1 (sm->ip4_main->add_del_interface_address_callbacks, cbi);
    2405         559 :   cbi.function = nat44_ed_add_del_static_mapping_cb;
    2406         559 :   vec_add1 (sm->ip4_main->add_del_interface_address_callbacks, cbi);
    2407             : 
    2408             :   /* callbacks to call when interface to table biding changes */
    2409         559 :   cbt.function = nat44_ed_update_outside_fib_cb;
    2410         559 :   vec_add1 (sm->ip4_main->table_bind_callbacks, cbt);
    2411             : 
    2412         559 :   sm->fib_src_low =
    2413         559 :     fib_source_allocate ("nat-low", FIB_SOURCE_PRIORITY_LOW,
    2414             :                          FIB_SOURCE_BH_SIMPLE);
    2415         559 :   sm->fib_src_hi =
    2416         559 :     fib_source_allocate ("nat-hi", FIB_SOURCE_PRIORITY_HI,
    2417             :                          FIB_SOURCE_BH_SIMPLE);
    2418             : 
    2419         559 :   nat_affinity_init (vm);
    2420         559 :   test_key_calc_split ();
    2421             : 
    2422         559 :   return nat44_api_hookup (vm);
    2423             : }
    2424             : 
    2425        1119 : VLIB_INIT_FUNCTION (nat_init);
    2426             : 
    2427             : int
    2428          87 : nat44_plugin_enable (nat44_config_t c)
    2429             : {
    2430          87 :   snat_main_t *sm = &snat_main;
    2431             : 
    2432          87 :   fail_if_enabled ();
    2433             : 
    2434          87 :   sm->forwarding_enabled = 0;
    2435          87 :   sm->mss_clamping = 0;
    2436             : 
    2437          87 :   if (!c.sessions)
    2438           0 :     c.sessions = 63 * 1024;
    2439             : 
    2440          87 :   sm->max_translations_per_thread = c.sessions;
    2441          87 :   vlib_stats_set_gauge (sm->max_cfg_sessions_gauge,
    2442          87 :                         sm->max_translations_per_thread);
    2443          87 :   sm->translation_buckets = nat_calc_bihash_buckets (c.sessions);
    2444             : 
    2445          87 :   vec_add1 (sm->max_translations_per_fib, sm->max_translations_per_thread);
    2446             : 
    2447          87 :   sm->inside_vrf_id = c.inside_vrf;
    2448          87 :   sm->inside_fib_index =
    2449          87 :     fib_table_find_or_create_and_lock
    2450          87 :     (FIB_PROTOCOL_IP4, c.inside_vrf, sm->fib_src_hi);
    2451             : 
    2452          87 :   sm->outside_vrf_id = c.outside_vrf;
    2453         174 :   sm->outside_fib_index = fib_table_find_or_create_and_lock (
    2454          87 :     FIB_PROTOCOL_IP4, c.outside_vrf, sm->fib_src_hi);
    2455             : 
    2456          87 :   nat44_ed_db_init ();
    2457             : 
    2458          87 :   nat_affinity_enable ();
    2459             : 
    2460          87 :   nat_reset_timeouts (&sm->timeouts);
    2461             : 
    2462          87 :   vlib_zero_simple_counter (&sm->total_sessions, 0);
    2463             : 
    2464          87 :   if (!sm->frame_queue_nelts)
    2465             :     {
    2466           3 :       sm->frame_queue_nelts = NAT_FQ_NELTS_DEFAULT;
    2467             :     }
    2468             : 
    2469          87 :   if (sm->num_workers > 1)
    2470             :     {
    2471          63 :       vlib_main_t *vm = vlib_get_main ();
    2472             :       vlib_node_t *node;
    2473             : 
    2474          63 :       if (sm->fq_in2out_index == ~0)
    2475             :         {
    2476           1 :           node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out");
    2477           1 :           sm->fq_in2out_index =
    2478           1 :             vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts);
    2479             :         }
    2480          63 :       if (sm->fq_out2in_index == ~0)
    2481             :         {
    2482           1 :           node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in");
    2483           1 :           sm->fq_out2in_index =
    2484           1 :             vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts);
    2485             :         }
    2486          63 :       if (sm->fq_in2out_output_index == ~0)
    2487             :         {
    2488           1 :           node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-output");
    2489           1 :           sm->fq_in2out_output_index =
    2490           1 :             vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts);
    2491             :         }
    2492             :     }
    2493             : 
    2494          87 :   sm->enabled = 1;
    2495          87 :   sm->rconfig = c;
    2496             : 
    2497          87 :   return 0;
    2498             : }
    2499             : 
    2500             : int
    2501          87 : nat44_ed_del_addresses ()
    2502             : {
    2503          87 :   snat_main_t *sm = &snat_main;
    2504             :   snat_address_t *a, *vec;
    2505          87 :   int error = 0;
    2506             : 
    2507          87 :   vec = vec_dup (sm->addresses);
    2508         348 :   vec_foreach (a, vec)
    2509             :     {
    2510         261 :       error = nat44_ed_del_address (a->addr, 0);
    2511         261 :       if (error)
    2512             :         {
    2513           0 :           nat_log_err ("error occurred while removing adderess");
    2514             :         }
    2515             :     }
    2516          87 :   vec_free (vec);
    2517          87 :   vec_free (sm->addresses);
    2518          87 :   sm->addresses = 0;
    2519             : 
    2520          87 :   vec = vec_dup (sm->twice_nat_addresses);
    2521         100 :   vec_foreach (a, vec)
    2522             :     {
    2523          13 :       error = nat44_ed_del_address (a->addr, 1);
    2524          13 :       if (error)
    2525             :         {
    2526           0 :           nat_log_err ("error occurred while removing adderess");
    2527             :         }
    2528             :     }
    2529          87 :   vec_free (vec);
    2530          87 :   vec_free (sm->twice_nat_addresses);
    2531          87 :   sm->twice_nat_addresses = 0;
    2532             : 
    2533          87 :   vec_free (sm->addr_to_resolve);
    2534          87 :   sm->addr_to_resolve = 0;
    2535             : 
    2536          87 :   return error;
    2537             : }
    2538             : 
    2539             : int
    2540          87 : nat44_ed_del_interfaces ()
    2541             : {
    2542          87 :   snat_main_t *sm = &snat_main;
    2543             :   snat_interface_t *i, *pool;
    2544          87 :   int error = 0;
    2545             : 
    2546          87 :   pool = pool_dup (sm->interfaces);
    2547         231 :   pool_foreach (i, pool)
    2548             :     {
    2549         144 :       if (nat44_ed_is_interface_inside (i))
    2550             :         {
    2551          82 :           error = nat44_ed_del_interface (i->sw_if_index, 1);
    2552             :         }
    2553         144 :       if (nat44_ed_is_interface_outside (i))
    2554             :         {
    2555          90 :           error = nat44_ed_del_interface (i->sw_if_index, 0);
    2556             :         }
    2557             : 
    2558         144 :       if (error)
    2559             :         {
    2560           0 :           nat_log_err ("error occurred while removing interface");
    2561             :         }
    2562             :     }
    2563          87 :   pool_free (pool);
    2564          87 :   pool_free (sm->interfaces);
    2565          87 :   sm->interfaces = 0;
    2566          87 :   return error;
    2567             : }
    2568             : 
    2569             : int
    2570          87 : nat44_ed_del_output_interfaces ()
    2571             : {
    2572          87 :   snat_main_t *sm = &snat_main;
    2573             :   snat_interface_t *i, *pool;
    2574          87 :   int error = 0;
    2575             : 
    2576          87 :   pool = pool_dup (sm->output_feature_interfaces);
    2577          99 :   pool_foreach (i, pool)
    2578             :     {
    2579          12 :       error = nat44_ed_del_output_interface (i->sw_if_index);
    2580          12 :       if (error)
    2581             :         {
    2582           0 :           nat_log_err ("error occurred while removing output interface");
    2583             :         }
    2584             :     }
    2585          87 :   pool_free (pool);
    2586          87 :   pool_free (sm->output_feature_interfaces);
    2587          87 :   sm->output_feature_interfaces = 0;
    2588          87 :   return error;
    2589             : }
    2590             : 
    2591             : static clib_error_t *
    2592       11597 : nat44_ed_sw_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_add)
    2593             : {
    2594       11597 :   snat_main_t *sm = &snat_main;
    2595             :   snat_interface_t *i;
    2596       11597 :   int error = 0;
    2597             : 
    2598       11597 :   if (is_add)
    2599        7418 :     return 0;
    2600             : 
    2601        4179 :   if (!sm->enabled)
    2602        4163 :     return 0;
    2603             : 
    2604          16 :   i = nat44_ed_get_interface (sm->interfaces, sw_if_index);
    2605          16 :   if (i)
    2606             :     {
    2607           6 :       bool is_inside = nat44_ed_is_interface_inside (i);
    2608           6 :       bool is_outside = nat44_ed_is_interface_outside (i);
    2609             : 
    2610           6 :       if (is_inside)
    2611             :         {
    2612           4 :           error |= nat44_ed_del_interface (sw_if_index, 1);
    2613             :         }
    2614           6 :       if (is_outside)
    2615             :         {
    2616           4 :           error |= nat44_ed_del_interface (sw_if_index, 0);
    2617             :         }
    2618             : 
    2619           6 :       if (error)
    2620             :         {
    2621           0 :           nat_log_err ("error occurred while removing interface");
    2622             :         }
    2623             :     }
    2624             : 
    2625          16 :   i = nat44_ed_get_interface (sm->output_feature_interfaces, sw_if_index);
    2626          16 :   if (i)
    2627             :     {
    2628           2 :       error = nat44_ed_del_output_interface (sw_if_index);
    2629           2 :       if (error)
    2630             :         {
    2631           0 :           nat_log_err ("error occurred while removing output interface");
    2632             :         }
    2633             :     }
    2634             : 
    2635          16 :   return 0;
    2636             : }
    2637             : 
    2638        1119 : VNET_SW_INTERFACE_ADD_DEL_FUNCTION (nat44_ed_sw_interface_add_del);
    2639             : 
    2640             : int
    2641          87 : nat44_ed_del_static_mappings ()
    2642             : {
    2643          87 :   snat_main_t *sm = &snat_main;
    2644             :   snat_static_mapping_t *m, *pool;
    2645          87 :   int error = 0;
    2646             : 
    2647          87 :   pool = pool_dup (sm->static_mappings);
    2648         143 :   pool_foreach (m, pool)
    2649             :     {
    2650          56 :       error = nat44_ed_del_static_mapping_internal (
    2651          56 :         m->local_addr, m->external_addr, m->local_port, m->external_port,
    2652          56 :         m->proto, m->vrf_id, m->flags);
    2653          56 :       if (error)
    2654             :         {
    2655           0 :           nat_log_err ("error occurred while removing mapping");
    2656             :         }
    2657             :     }
    2658          87 :   pool_free (pool);
    2659          87 :   pool_free (sm->static_mappings);
    2660          87 :   sm->static_mappings = 0;
    2661             : 
    2662          87 :   vec_free (sm->sm_to_resolve);
    2663          87 :   sm->sm_to_resolve = 0;
    2664             : 
    2665          87 :   return error;
    2666             : }
    2667             : 
    2668             : int
    2669          87 : nat44_plugin_disable ()
    2670             : {
    2671          87 :   snat_main_t *sm = &snat_main;
    2672          87 :   int rc, error = 0;
    2673             : 
    2674          87 :   fail_if_disabled ();
    2675             : 
    2676          87 :   rc = nat44_ed_del_static_mappings ();
    2677          87 :   if (rc)
    2678           0 :     error = VNET_API_ERROR_BUG;
    2679             : 
    2680          87 :   rc = nat44_ed_del_addresses ();
    2681          87 :   if (rc)
    2682           0 :     error = VNET_API_ERROR_BUG;
    2683             : 
    2684          87 :   rc = nat44_ed_del_interfaces ();
    2685          87 :   if (rc)
    2686           0 :     error = VNET_API_ERROR_BUG;
    2687             : 
    2688          87 :   rc = nat44_ed_del_output_interfaces ();
    2689          87 :   if (rc)
    2690           0 :     error = VNET_API_ERROR_BUG;
    2691             : 
    2692          87 :   nat44_ed_del_vrf_tables ();
    2693             : 
    2694          87 :   vec_free (sm->max_translations_per_fib);
    2695          87 :   sm->max_translations_per_fib = 0;
    2696             : 
    2697          87 :   nat44_ed_db_free ();
    2698             : 
    2699          87 :   clib_memset (&sm->rconfig, 0, sizeof (sm->rconfig));
    2700             : 
    2701          87 :   nat_affinity_disable ();
    2702             : 
    2703          87 :   sm->forwarding_enabled = 0;
    2704          87 :   sm->enabled = 0;
    2705             : 
    2706          87 :   return error;
    2707             : }
    2708             : 
    2709             : void
    2710          25 : nat44_ed_forwarding_enable_disable (u8 is_enable)
    2711             : {
    2712             :   snat_main_per_thread_data_t *tsm;
    2713          25 :   snat_main_t *sm = &snat_main;
    2714             :   snat_session_t *s;
    2715             : 
    2716          25 :   u32 *ses_to_be_removed = 0, *ses_index;
    2717             : 
    2718          25 :   sm->forwarding_enabled = is_enable != 0;
    2719             : 
    2720          25 :   if (!sm->enabled || is_enable)
    2721             :     {
    2722          23 :       return;
    2723             :     }
    2724             : 
    2725           8 :   vec_foreach (tsm, sm->per_thread_data)
    2726             :     {
    2727          16 :       pool_foreach (s, tsm->sessions)
    2728             :         {
    2729          10 :           if (na44_ed_is_fwd_bypass_session (s))
    2730             :             {
    2731           4 :               vec_add1 (ses_to_be_removed, s - tsm->sessions);
    2732             :             }
    2733             :         }
    2734          10 :       vec_foreach (ses_index, ses_to_be_removed)
    2735             :         {
    2736           4 :           s = pool_elt_at_index (tsm->sessions, ses_index[0]);
    2737           4 :           nat44_ed_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
    2738           4 :           nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
    2739             :         }
    2740             : 
    2741           6 :       vec_free (ses_to_be_removed);
    2742             :     }
    2743             : }
    2744             : 
    2745             : static_always_inline snat_static_mapping_t *
    2746       57120 : nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
    2747             :                    u32 match_fib_index, ip_protocol_t match_protocol,
    2748             :                    int by_external)
    2749             : {
    2750             :   snat_static_mapping_t *m;
    2751       57120 :   if (!by_external)
    2752             :     {
    2753       28362 :       m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port, match_fib_index,
    2754             :                                   match_protocol);
    2755       28362 :       if (m)
    2756          12 :         return m;
    2757             : 
    2758             :       // try address only mapping
    2759       28350 :       m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, match_fib_index, 0);
    2760       28350 :       if (m)
    2761           0 :         return m;
    2762             : 
    2763             :       // default static mapping fib index (based on configuration)
    2764       28350 :       if (sm->inside_fib_index != match_fib_index)
    2765             :         {
    2766          13 :           m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port,
    2767             :                                       sm->inside_fib_index, match_protocol);
    2768          13 :           if (m)
    2769           0 :             return m;
    2770             : 
    2771             :           // try address only mapping
    2772          13 :           m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, sm->inside_fib_index,
    2773             :                                       0);
    2774          13 :           if (m)
    2775           0 :             return m;
    2776             :         }
    2777             :       // TODO: this specific use case may be deprecated (needs testing)
    2778       28350 :       if (sm->outside_fib_index != match_fib_index)
    2779             :         {
    2780          13 :           m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port,
    2781             :                                       sm->outside_fib_index, match_protocol);
    2782          13 :           if (m)
    2783           0 :             return m;
    2784             : 
    2785             :           // try address only mapping
    2786          13 :           m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, sm->outside_fib_index,
    2787             :                                       0);
    2788          13 :           if (m)
    2789           0 :             return m;
    2790             :         }
    2791             :     }
    2792             :   else
    2793             :     {
    2794             :       m =
    2795       28758 :         nat44_ed_sm_o2i_lookup (sm, match_addr, match_port, 0, match_protocol);
    2796       28758 :       if (m)
    2797         284 :         return m;
    2798             : 
    2799             :       // try address only mapping
    2800       28474 :       m = nat44_ed_sm_o2i_lookup (sm, match_addr, 0, 0, 0);
    2801       28474 :       if (m)
    2802          25 :         return m;
    2803             :     }
    2804       56799 :   return 0;
    2805             : }
    2806             : 
    2807             : int
    2808       57121 : snat_static_mapping_match (vlib_main_t *vm, ip4_address_t match_addr,
    2809             :                            u16 match_port, u32 match_fib_index,
    2810             :                            ip_protocol_t match_protocol,
    2811             :                            ip4_address_t *mapping_addr, u16 *mapping_port,
    2812             :                            u32 *mapping_fib_index, int by_external,
    2813             :                            u8 *is_addr_only, twice_nat_type_t *twice_nat,
    2814             :                            lb_nat_type_t *lb, ip4_address_t *ext_host_addr,
    2815             :                            u8 *is_identity_nat, snat_static_mapping_t **out)
    2816             : {
    2817       57121 :   snat_main_t *sm = &snat_main;
    2818             :   snat_static_mapping_t *m;
    2819       57121 :   u32 rand, lo = 0, hi, mid, *tmp = 0, i;
    2820             :   nat44_lb_addr_port_t *local;
    2821             :   u8 backend_index;
    2822             : 
    2823       57121 :   m = nat44_ed_sm_match (sm, match_addr, match_port, match_fib_index,
    2824             :                          match_protocol, by_external);
    2825       57119 :   if (!m)
    2826             :     {
    2827       56798 :       return 1;
    2828             :     }
    2829             : 
    2830         321 :   if (by_external)
    2831             :     {
    2832         309 :       if (is_sm_lb (m->flags))
    2833             :         {
    2834         258 :           if (PREDICT_FALSE (lb != 0))
    2835         258 :             *lb = m->affinity ? AFFINITY_LB_NAT : LB_NAT;
    2836         258 :           if (m->affinity && !nat_affinity_find_and_lock (
    2837             :                                vm, ext_host_addr[0], match_addr,
    2838             :                                match_protocol, match_port, &backend_index))
    2839             :             {
    2840         140 :               local = pool_elt_at_index (m->locals, backend_index);
    2841         140 :               *mapping_addr = local->addr;
    2842         140 :               *mapping_port = local->port;
    2843         140 :               *mapping_fib_index = local->fib_index;
    2844         140 :               goto end;
    2845             :             }
    2846             :           // pick locals matching this worker
    2847         118 :           if (PREDICT_FALSE (sm->num_workers > 1))
    2848             :             {
    2849         113 :               u32 thread_index = vlib_get_thread_index ();
    2850         385 :               pool_foreach_index (i, m->locals)
    2851             :                {
    2852         273 :                 local = pool_elt_at_index (m->locals, i);
    2853             : 
    2854         272 :                 ip4_header_t ip = {
    2855             :                   .src_address = local->addr,
    2856             :                 };
    2857             : 
    2858         272 :                 if (nat44_ed_get_in2out_worker_index (0, &ip, m->fib_index,
    2859             :                                                       0) == thread_index)
    2860             :                   {
    2861         115 :                     vec_add1 (tmp, i);
    2862             :                   }
    2863             :                }
    2864         110 :               ASSERT (vec_len (tmp) != 0);
    2865             :             }
    2866             :           else
    2867             :             {
    2868          15 :               pool_foreach_index (i, m->locals)
    2869             :                {
    2870          10 :                 vec_add1 (tmp, i);
    2871             :               }
    2872             :             }
    2873         116 :           hi = vec_len (tmp) - 1;
    2874         116 :           local = pool_elt_at_index (m->locals, tmp[hi]);
    2875         116 :           rand = 1 + (random_u32 (&sm->random_seed) % local->prefix);
    2876         123 :           while (lo < hi)
    2877             :             {
    2878           7 :               mid = ((hi - lo) >> 1) + lo;
    2879           7 :               local = pool_elt_at_index (m->locals, tmp[mid]);
    2880           7 :               (rand > local->prefix) ? (lo = mid + 1) : (hi = mid);
    2881             :             }
    2882         116 :           local = pool_elt_at_index (m->locals, tmp[lo]);
    2883         116 :           if (!(local->prefix >= rand))
    2884           0 :             return 1;
    2885         116 :           *mapping_addr = local->addr;
    2886         116 :           *mapping_port = local->port;
    2887         116 :           *mapping_fib_index = local->fib_index;
    2888         116 :           if (m->affinity)
    2889             :             {
    2890           2 :               if (nat_affinity_create_and_lock (ext_host_addr[0], match_addr,
    2891             :                                                 match_protocol, match_port,
    2892           2 :                                                 tmp[lo], m->affinity,
    2893             :                                                 m->affinity_per_service_list_head_index))
    2894           0 :                 nat_elog_info (sm, "create affinity record failed");
    2895             :             }
    2896         116 :           vec_free (tmp);
    2897             :         }
    2898             :       else
    2899             :         {
    2900          51 :           if (PREDICT_FALSE (lb != 0))
    2901          32 :             *lb = NO_LB_NAT;
    2902          51 :           *mapping_fib_index = m->fib_index;
    2903          51 :           *mapping_addr = m->local_addr;
    2904             :           /* Address only mapping doesn't change port */
    2905          51 :           *mapping_port =
    2906          51 :             is_sm_addr_only (m->flags) ? match_port : m->local_port;
    2907             :         }
    2908             :     }
    2909             :   else
    2910             :     {
    2911          12 :       *mapping_addr = m->external_addr;
    2912             :       /* Address only mapping doesn't change port */
    2913          12 :       *mapping_port =
    2914          12 :         is_sm_addr_only (m->flags) ? match_port : m->external_port;
    2915          12 :       *mapping_fib_index = sm->outside_fib_index;
    2916             :     }
    2917             : 
    2918         321 : end:
    2919         321 :   if (PREDICT_FALSE (is_addr_only != 0))
    2920          10 :     *is_addr_only = is_sm_addr_only (m->flags);
    2921             : 
    2922         321 :   if (PREDICT_FALSE (twice_nat != 0))
    2923             :     {
    2924         290 :       *twice_nat = TWICE_NAT_DISABLED;
    2925             : 
    2926         290 :       if (is_sm_twice_nat (m->flags))
    2927             :         {
    2928           7 :           *twice_nat = TWICE_NAT;
    2929             :         }
    2930         283 :       else if (is_sm_self_twice_nat (m->flags))
    2931             :         {
    2932           7 :           *twice_nat = TWICE_NAT_SELF;
    2933             :         }
    2934             :     }
    2935             : 
    2936         321 :   if (PREDICT_FALSE (is_identity_nat != 0))
    2937         312 :     *is_identity_nat = is_sm_identity_nat (m->flags);
    2938             : 
    2939         321 :   if (out != 0)
    2940         300 :     *out = m;
    2941             : 
    2942         321 :   return 0;
    2943             : }
    2944             : 
    2945             : u32
    2946       28683 : nat44_ed_get_in2out_worker_index (vlib_buffer_t *b, ip4_header_t *ip,
    2947             :                                   u32 rx_fib_index, u8 is_output)
    2948             : {
    2949       28683 :   snat_main_t *sm = &snat_main;
    2950       28683 :   u32 next_worker_index = sm->first_worker_index;
    2951             :   u32 hash;
    2952             : 
    2953             :   clib_bihash_kv_16_8_t kv16, value16;
    2954             : 
    2955       28683 :   u32 fib_index = rx_fib_index;
    2956       28683 :   if (b)
    2957             :     {
    2958       28275 :       if (PREDICT_FALSE (is_output))
    2959             :         {
    2960          42 :           fib_index = sm->outside_fib_index;
    2961             :           nat_fib_t *outside_fib;
    2962          42 :           fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
    2963          42 :           fib_prefix_t pfx = {
    2964             :                   .fp_proto = FIB_PROTOCOL_IP4,
    2965             :                   .fp_len = 32,
    2966             :                   .fp_addr = {
    2967          42 :                           .ip4.as_u32 = ip->dst_address.as_u32,
    2968             :                   } ,
    2969             :           };
    2970             : 
    2971          42 :           switch (vec_len (sm->outside_fibs))
    2972             :             {
    2973           0 :             case 0:
    2974           0 :               fib_index = sm->outside_fib_index;
    2975           0 :               break;
    2976          41 :             case 1:
    2977          41 :               fib_index = sm->outside_fibs[0].fib_index;
    2978          41 :               break;
    2979           1 :             default:
    2980           1 :               vec_foreach (outside_fib, sm->outside_fibs)
    2981             :                 {
    2982           1 :                   fei = fib_table_lookup (outside_fib->fib_index, &pfx);
    2983           1 :                   if (FIB_NODE_INDEX_INVALID != fei)
    2984             :                     {
    2985           1 :                       if (fib_entry_get_resolving_interface (fei) != ~0)
    2986             :                         {
    2987           1 :                           fib_index = outside_fib->fib_index;
    2988           1 :                           break;
    2989             :                         }
    2990             :                     }
    2991             :                 }
    2992           1 :               break;
    2993             :             }
    2994             :         }
    2995             : 
    2996       28275 :       if (PREDICT_FALSE (ip->protocol == IP_PROTOCOL_ICMP))
    2997             :         {
    2998             :           ip4_address_t lookup_saddr, lookup_daddr;
    2999             :           u16 lookup_sport, lookup_dport;
    3000             :           u8 lookup_protocol;
    3001             : 
    3002        6568 :           if (!nat_get_icmp_session_lookup_values (
    3003             :                 b, ip, &lookup_saddr, &lookup_sport, &lookup_daddr,
    3004             :                 &lookup_dport, &lookup_protocol))
    3005             :             {
    3006        6568 :               init_ed_k (&kv16, lookup_saddr.as_u32, lookup_sport,
    3007             :                          lookup_daddr.as_u32, lookup_dport, rx_fib_index,
    3008             :                          lookup_protocol);
    3009        6566 :               if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))
    3010             :                 {
    3011          25 :                   next_worker_index = ed_value_get_thread_index (&value16);
    3012          50 :                   vnet_buffer2 (b)->nat.cached_session_index =
    3013          25 :                     ed_value_get_session_index (&value16);
    3014          25 :                   goto out;
    3015             :                 }
    3016             :             }
    3017             :         }
    3018             : 
    3019       28255 :       init_ed_k (&kv16, ip->src_address.as_u32,
    3020       28255 :                  vnet_buffer (b)->ip.reass.l4_src_port, ip->dst_address.as_u32,
    3021       28255 :                  vnet_buffer (b)->ip.reass.l4_dst_port, fib_index,
    3022       28255 :                  ip->protocol);
    3023             : 
    3024       28283 :       if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))
    3025             :         {
    3026         119 :           next_worker_index = ed_value_get_thread_index (&value16);
    3027         238 :           vnet_buffer2 (b)->nat.cached_session_index =
    3028         119 :             ed_value_get_session_index (&value16);
    3029         119 :           goto out;
    3030             :         }
    3031             : 
    3032             :       // dst NAT
    3033       28178 :       init_ed_k (&kv16, ip->dst_address.as_u32,
    3034       28178 :                  vnet_buffer (b)->ip.reass.l4_dst_port, ip->src_address.as_u32,
    3035       28178 :                  vnet_buffer (b)->ip.reass.l4_src_port, rx_fib_index,
    3036       28178 :                  ip->protocol);
    3037       28170 :       if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))
    3038             :         {
    3039          20 :           next_worker_index = ed_value_get_thread_index (&value16);
    3040          54 :           vnet_buffer2 (b)->nat.cached_dst_nat_session_index =
    3041          20 :             ed_value_get_session_index (&value16);
    3042          34 :           goto out;
    3043             :         }
    3044             :     }
    3045             : 
    3046       28569 :   hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) +
    3047       28569 :          (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24) +
    3048       28569 :          rx_fib_index + (rx_fib_index >> 8) + (rx_fib_index >> 16) +
    3049       28569 :          (rx_fib_index >> 24);
    3050             : 
    3051       28569 :   if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
    3052       28561 :     next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
    3053             :   else
    3054           0 :     next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
    3055             : 
    3056       28703 : out:
    3057       28703 :   if (PREDICT_TRUE (!is_output))
    3058             :     {
    3059       28661 :       nat_elog_debug_handoff (sm, "HANDOFF IN2OUT", next_worker_index,
    3060             :                               rx_fib_index,
    3061             :                               clib_net_to_host_u32 (ip->src_address.as_u32),
    3062             :                               clib_net_to_host_u32 (ip->dst_address.as_u32));
    3063             :     }
    3064             :   else
    3065             :     {
    3066          42 :       nat_elog_debug_handoff (sm, "HANDOFF IN2OUT-OUTPUT-FEATURE",
    3067             :                               next_worker_index, rx_fib_index,
    3068             :                               clib_net_to_host_u32 (ip->src_address.as_u32),
    3069             :                               clib_net_to_host_u32 (ip->dst_address.as_u32));
    3070             :     }
    3071             : 
    3072       28703 :   return next_worker_index;
    3073             : }
    3074             : 
    3075             : u32
    3076        4736 : nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip,
    3077             :                                   u32 rx_fib_index, u8 is_output)
    3078             : {
    3079        4736 :   snat_main_t *sm = &snat_main;
    3080             :   clib_bihash_kv_16_8_t kv16, value16;
    3081             : 
    3082        4736 :   u8 proto, next_worker_index = 0;
    3083             :   u16 port;
    3084             :   snat_static_mapping_t *m;
    3085             :   u32 hash;
    3086             : 
    3087        4736 :   proto = ip->protocol;
    3088             : 
    3089        4736 :   if (PREDICT_FALSE (IP_PROTOCOL_ICMP == proto))
    3090             :     {
    3091             :       ip4_address_t lookup_saddr, lookup_daddr;
    3092             :       u16 lookup_sport, lookup_dport;
    3093             :       u8 lookup_protocol;
    3094        1556 :       if (!nat_get_icmp_session_lookup_values (
    3095             :             b, ip, &lookup_saddr, &lookup_sport, &lookup_daddr, &lookup_dport,
    3096             :             &lookup_protocol))
    3097             :         {
    3098        1553 :           init_ed_k (&kv16, lookup_saddr.as_u32, lookup_sport,
    3099             :                      lookup_daddr.as_u32, lookup_dport, rx_fib_index,
    3100             :                      lookup_protocol);
    3101        1553 :           if (PREDICT_TRUE (
    3102             :                 !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16)))
    3103             :             {
    3104        1527 :               next_worker_index = ed_value_get_thread_index (&value16);
    3105        1525 :               nat_elog_debug_handoff (
    3106             :                 sm, "HANDOFF OUT2IN (session)", next_worker_index,
    3107             :                 rx_fib_index, clib_net_to_host_u32 (ip->src_address.as_u32),
    3108             :                 clib_net_to_host_u32 (ip->dst_address.as_u32));
    3109        1525 :               return next_worker_index;
    3110             :             }
    3111             :         }
    3112             :     }
    3113             : 
    3114        3201 :   init_ed_k (&kv16, ip->src_address.as_u32,
    3115        3201 :              vnet_buffer (b)->ip.reass.l4_src_port, ip->dst_address.as_u32,
    3116        3201 :              vnet_buffer (b)->ip.reass.l4_dst_port, rx_fib_index,
    3117        3201 :              ip->protocol);
    3118             : 
    3119        3347 :   if (PREDICT_TRUE (
    3120             :         !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16)))
    3121             :     {
    3122        6111 :       vnet_buffer2 (b)->nat.cached_session_index =
    3123        3056 :         ed_value_get_session_index (&value16);
    3124        3055 :       next_worker_index = ed_value_get_thread_index (&value16);
    3125        3050 :       nat_elog_debug_handoff (sm, "HANDOFF OUT2IN (session)",
    3126             :                               next_worker_index, rx_fib_index,
    3127             :                               clib_net_to_host_u32 (ip->src_address.as_u32),
    3128             :                               clib_net_to_host_u32 (ip->dst_address.as_u32));
    3129        3049 :       return next_worker_index;
    3130             :     }
    3131             : 
    3132             :   /* first try static mappings without port */
    3133         278 :   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
    3134             :     {
    3135         232 :       m = nat44_ed_sm_o2i_lookup (sm, ip->dst_address, 0, 0, proto);
    3136         232 :       if (m)
    3137             :         {
    3138             :           {
    3139           0 :             next_worker_index = m->workers[0];
    3140           0 :             goto done;
    3141             :           }
    3142             :         }
    3143             :     }
    3144             : 
    3145             :   /* unknown protocol */
    3146         278 :   if (PREDICT_FALSE (nat44_ed_is_unk_proto (proto)))
    3147             :     {
    3148             :       /* use current thread */
    3149           0 :       next_worker_index = vlib_get_thread_index ();
    3150           0 :       goto done;
    3151             :     }
    3152             : 
    3153         278 :   port = vnet_buffer (b)->ip.reass.l4_dst_port;
    3154             : 
    3155         278 :   if (PREDICT_FALSE (ip->protocol == IP_PROTOCOL_ICMP))
    3156             :     {
    3157          21 :       udp_header_t *udp = ip4_next_header (ip);
    3158          21 :       icmp46_header_t *icmp = (icmp46_header_t *) udp;
    3159          21 :       nat_icmp_echo_header_t *echo = (nat_icmp_echo_header_t *) (icmp + 1);
    3160          21 :       if (!icmp_type_is_error_message
    3161          21 :           (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
    3162          21 :         port = vnet_buffer (b)->ip.reass.l4_src_port;
    3163             :       else
    3164             :         {
    3165             :           /* if error message, then it's not fragmented and we can access it */
    3166           0 :           ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
    3167           0 :           proto = inner_ip->protocol;
    3168           0 :           void *l4_header = ip4_next_header (inner_ip);
    3169           0 :           switch (proto)
    3170             :             {
    3171           0 :             case IP_PROTOCOL_ICMP:
    3172           0 :               icmp = (icmp46_header_t *) l4_header;
    3173           0 :               echo = (nat_icmp_echo_header_t *) (icmp + 1);
    3174           0 :               port = echo->identifier;
    3175           0 :               break;
    3176           0 :             case IP_PROTOCOL_UDP:
    3177             :               /* breakthrough */
    3178             :             case IP_PROTOCOL_TCP:
    3179           0 :               port = ((nat_tcp_udp_header_t *) l4_header)->src_port;
    3180           0 :               break;
    3181           0 :             default:
    3182           0 :               next_worker_index = vlib_get_thread_index ();
    3183           0 :               goto done;
    3184             :             }
    3185         257 :         }
    3186             :     }
    3187             : 
    3188             :   /* try static mappings with port */
    3189         278 :   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
    3190             :     {
    3191         232 :       m = nat44_ed_sm_o2i_lookup (sm, ip->dst_address, port, 0, proto);
    3192         232 :       if (m)
    3193             :         {
    3194         207 :           if (!is_sm_lb (m->flags))
    3195             :             {
    3196          24 :               next_worker_index = m->workers[0];
    3197          24 :               goto done;
    3198             :             }
    3199             : 
    3200         183 :           hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) +
    3201         183 :             (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24);
    3202             : 
    3203         183 :           if (PREDICT_TRUE (is_pow2 (_vec_len (m->workers))))
    3204         133 :             next_worker_index =
    3205         133 :               m->workers[hash & (_vec_len (m->workers) - 1)];
    3206             :           else
    3207          50 :             next_worker_index = m->workers[hash % _vec_len (m->workers)];
    3208         183 :           goto done;
    3209             :         }
    3210             :     }
    3211             : 
    3212             :   /* worker by outside port */
    3213          71 :   next_worker_index = get_thread_idx_by_port (clib_net_to_host_u16 (port));
    3214             : 
    3215         278 : done:
    3216         278 :   nat_elog_debug_handoff (sm, "HANDOFF OUT2IN", next_worker_index,
    3217             :                           rx_fib_index,
    3218             :                           clib_net_to_host_u32 (ip->src_address.as_u32),
    3219             :                           clib_net_to_host_u32 (ip->dst_address.as_u32));
    3220         278 :   return next_worker_index;
    3221             : }
    3222             : 
    3223             : u32
    3224           0 : nat44_get_max_session_limit ()
    3225             : {
    3226           0 :   snat_main_t *sm = &snat_main;
    3227           0 :   u32 max_limit = 0, len = 0;
    3228             : 
    3229           0 :   for (; len < vec_len (sm->max_translations_per_fib); len++)
    3230             :     {
    3231           0 :       if (max_limit < sm->max_translations_per_fib[len])
    3232           0 :         max_limit = sm->max_translations_per_fib[len];
    3233             :     }
    3234           0 :   return max_limit;
    3235             : }
    3236             : 
    3237             : int
    3238           1 : nat44_set_session_limit (u32 session_limit, u32 vrf_id)
    3239             : {
    3240           1 :   snat_main_t *sm = &snat_main;
    3241           1 :   u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
    3242           1 :   u32 len = vec_len (sm->max_translations_per_fib);
    3243             : 
    3244           1 :   if (len <= fib_index)
    3245             :     {
    3246           1 :       vec_validate (sm->max_translations_per_fib, fib_index + 1);
    3247             : 
    3248           3 :       for (; len < vec_len (sm->max_translations_per_fib); len++)
    3249           2 :         sm->max_translations_per_fib[len] = sm->max_translations_per_thread;
    3250             :     }
    3251             : 
    3252           1 :   sm->max_translations_per_fib[fib_index] = session_limit;
    3253           1 :   return 0;
    3254             : }
    3255             : 
    3256             : int
    3257           0 : nat44_update_session_limit (u32 session_limit, u32 vrf_id)
    3258             : {
    3259           0 :   snat_main_t *sm = &snat_main;
    3260             : 
    3261           0 :   if (nat44_set_session_limit (session_limit, vrf_id))
    3262           0 :     return 1;
    3263           0 :   sm->max_translations_per_thread = nat44_get_max_session_limit ();
    3264             : 
    3265           0 :   vlib_stats_set_gauge (sm->max_cfg_sessions_gauge,
    3266           0 :                         sm->max_translations_per_thread);
    3267             : 
    3268           0 :   sm->translation_buckets =
    3269           0 :     nat_calc_bihash_buckets (sm->max_translations_per_thread);
    3270             : 
    3271           0 :   nat44_ed_sessions_clear ();
    3272           0 :   return 0;
    3273             : }
    3274             : 
    3275             : static void
    3276         339 : nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations)
    3277             : {
    3278             :   dlist_elt_t *head;
    3279             : 
    3280         339 :   pool_alloc (tsm->per_vrf_sessions_pool, translations);
    3281         339 :   pool_alloc (tsm->sessions, translations);
    3282         339 :   pool_alloc (tsm->lru_pool, translations);
    3283             : 
    3284         339 :   pool_get (tsm->lru_pool, head);
    3285         339 :   tsm->tcp_trans_lru_head_index = head - tsm->lru_pool;
    3286         339 :   clib_dlist_init (tsm->lru_pool, tsm->tcp_trans_lru_head_index);
    3287             : 
    3288         339 :   pool_get (tsm->lru_pool, head);
    3289         339 :   tsm->tcp_estab_lru_head_index = head - tsm->lru_pool;
    3290         339 :   clib_dlist_init (tsm->lru_pool, tsm->tcp_estab_lru_head_index);
    3291             : 
    3292         339 :   pool_get (tsm->lru_pool, head);
    3293         339 :   tsm->udp_lru_head_index = head - tsm->lru_pool;
    3294         339 :   clib_dlist_init (tsm->lru_pool, tsm->udp_lru_head_index);
    3295             : 
    3296         339 :   pool_get (tsm->lru_pool, head);
    3297         339 :   tsm->icmp_lru_head_index = head - tsm->lru_pool;
    3298         339 :   clib_dlist_init (tsm->lru_pool, tsm->icmp_lru_head_index);
    3299             : 
    3300         339 :   pool_get (tsm->lru_pool, head);
    3301         339 :   tsm->unk_proto_lru_head_index = head - tsm->lru_pool;
    3302         339 :   clib_dlist_init (tsm->lru_pool, tsm->unk_proto_lru_head_index);
    3303         339 : }
    3304             : 
    3305             : static void
    3306          87 : nat44_ed_flow_hash_init ()
    3307             : {
    3308          87 :   snat_main_t *sm = &snat_main;
    3309             :   // we expect 2 flows per session, so multiply translation_buckets by 2
    3310          87 :   clib_bihash_init_16_8 (
    3311             :     &sm->flow_hash, "ed-flow-hash",
    3312          87 :     clib_max (1, sm->num_workers) * 2 * sm->translation_buckets, 0);
    3313          87 :   clib_bihash_set_kvp_format_fn_16_8 (&sm->flow_hash, format_ed_session_kvp);
    3314          87 : }
    3315             : 
    3316             : static void
    3317          87 : nat44_ed_db_init ()
    3318             : {
    3319          87 :   snat_main_t *sm = &snat_main;
    3320             :   snat_main_per_thread_data_t *tsm;
    3321             : 
    3322          87 :   nat44_ed_flow_hash_init ();
    3323             : 
    3324         426 :   vec_foreach (tsm, sm->per_thread_data)
    3325             :     {
    3326         339 :       nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread);
    3327             :     }
    3328          87 : }
    3329             : 
    3330             : static void
    3331         339 : nat44_ed_worker_db_free (snat_main_per_thread_data_t *tsm)
    3332             : {
    3333         339 :   pool_free (tsm->lru_pool);
    3334         339 :   pool_free (tsm->sessions);
    3335         339 :   pool_free (tsm->per_vrf_sessions_pool);
    3336         339 : }
    3337             : 
    3338             : static void
    3339          87 : nat44_ed_flow_hash_free ()
    3340             : {
    3341          87 :   snat_main_t *sm = &snat_main;
    3342             : 
    3343          87 :   clib_bihash_free_16_8 (&sm->flow_hash);
    3344          87 : }
    3345             : 
    3346             : static void
    3347          87 : nat44_ed_db_free ()
    3348             : {
    3349          87 :   snat_main_t *sm = &snat_main;
    3350             :   snat_main_per_thread_data_t *tsm;
    3351             : 
    3352         426 :   vec_foreach (tsm, sm->per_thread_data)
    3353             :     {
    3354         339 :       nat44_ed_worker_db_free (tsm);
    3355             :     }
    3356             : 
    3357          87 :   nat44_ed_flow_hash_free ();
    3358          87 : }
    3359             : 
    3360             : void
    3361           0 : nat44_ed_sessions_clear ()
    3362             : {
    3363           0 :   snat_main_t *sm = &snat_main;
    3364             : 
    3365           0 :   nat44_ed_db_free ();
    3366           0 :   nat44_ed_db_init ();
    3367           0 :   vlib_zero_simple_counter (&sm->total_sessions, 0);
    3368           0 : }
    3369             : 
    3370             : static void
    3371        4699 : nat44_ed_add_del_static_mapping_cb (ip4_main_t *im, uword opaque,
    3372             :                                     u32 sw_if_index, ip4_address_t *address,
    3373             :                                     u32 address_length, u32 if_address_index,
    3374             :                                     u32 is_delete)
    3375             : {
    3376             :   snat_static_mapping_resolve_t *rp;
    3377        4699 :   snat_main_t *sm = &snat_main;
    3378        4699 :   int rv = 0;
    3379             : 
    3380        4699 :   if (!sm->enabled)
    3381             :     {
    3382        4679 :       return;
    3383             :     }
    3384             : 
    3385          20 :   vec_foreach (rp, sm->sm_to_resolve)
    3386             :     {
    3387           0 :       if (sw_if_index == rp->sw_if_index)
    3388             :         {
    3389           0 :           if (is_delete)
    3390             :             {
    3391           0 :               if (rp->is_resolved)
    3392             :                 {
    3393           0 :                   rv = nat44_ed_del_static_mapping_internal (
    3394           0 :                     rp->l_addr, address[0], rp->l_port, rp->e_port, rp->proto,
    3395             :                     rp->vrf_id, rp->flags);
    3396           0 :                   if (rv)
    3397             :                     {
    3398           0 :                       nat_log_err ("ed del static mapping failed");
    3399             :                     }
    3400             :                   else
    3401             :                     {
    3402           0 :                       rp->is_resolved = 0;
    3403             :                     }
    3404             :                 }
    3405             :             }
    3406             :           else
    3407             :             {
    3408           0 :               if (!rp->is_resolved)
    3409             :                 {
    3410           0 :                   rv = nat44_ed_add_static_mapping_internal (
    3411           0 :                     rp->l_addr, address[0], rp->l_port, rp->e_port, rp->proto,
    3412             :                     rp->vrf_id, ~0, rp->flags, rp->pool_addr, rp->tag);
    3413           0 :                   if (rv)
    3414             :                     {
    3415           0 :                       nat_log_err ("ed add static mapping failed");
    3416             :                     }
    3417             :                   else
    3418             :                     {
    3419           0 :                       rp->is_resolved = 1;
    3420             :                     }
    3421             :                 }
    3422             :             }
    3423             :         }
    3424             :     }
    3425             : }
    3426             : 
    3427             : static int
    3428          42 : nat44_ed_get_addr_resolve_record (u32 sw_if_index, u8 twice_nat, int *out)
    3429             : {
    3430          42 :   snat_main_t *sm = &snat_main;
    3431             :   snat_address_resolve_t *rp;
    3432             :   int i;
    3433             : 
    3434          44 :   for (i = 0; i < vec_len (sm->addr_to_resolve); i++)
    3435             :     {
    3436           4 :       rp = sm->addr_to_resolve + i;
    3437             : 
    3438           4 :       if ((rp->sw_if_index == sw_if_index) && (rp->is_twice_nat == twice_nat))
    3439             :         {
    3440           2 :           if (out)
    3441             :             {
    3442           2 :               *out = i;
    3443             :             }
    3444           2 :           return 0;
    3445             :         }
    3446             :     }
    3447          40 :   return 1;
    3448             : }
    3449             : static int
    3450           0 : nat44_ed_del_addr_resolve_record (u32 sw_if_index, u8 twice_nat)
    3451             : {
    3452           0 :   snat_main_t *sm = &snat_main;
    3453             :   int i;
    3454           0 :   if (!nat44_ed_get_addr_resolve_record (sw_if_index, twice_nat, &i))
    3455             :     {
    3456           0 :       vec_del1 (sm->addr_to_resolve, i);
    3457           0 :       return 0;
    3458             :     }
    3459           0 :   return 1;
    3460             : }
    3461             : 
    3462             : static void
    3463        4699 : nat44_ed_add_del_interface_address_cb (ip4_main_t *im, uword opaque,
    3464             :                                        u32 sw_if_index, ip4_address_t *address,
    3465             :                                        u32 address_length,
    3466             :                                        u32 if_address_index, u32 is_delete)
    3467             : {
    3468        4699 :   snat_main_t *sm = &snat_main;
    3469             :   snat_address_resolve_t *arp;
    3470             :   snat_address_t *ap;
    3471        4699 :   u8 twice_nat = 0;
    3472             :   int i, rv;
    3473             : 
    3474        4699 :   if (!sm->enabled)
    3475             :     {
    3476        4697 :       return;
    3477             :     }
    3478             : 
    3479          20 :   if (nat44_ed_get_addr_resolve_record (sw_if_index, twice_nat, &i))
    3480             :     {
    3481          20 :       twice_nat = 1;
    3482          20 :       if (nat44_ed_get_addr_resolve_record (sw_if_index, twice_nat, &i))
    3483             :         {
    3484             :           u32 fib_index =
    3485          18 :             ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
    3486          36 :           vec_foreach (ap, sm->addresses)
    3487             :             {
    3488          18 :               if ((fib_index == ap->fib_index) &&
    3489           2 :                   (address->as_u32 == ap->addr.as_u32))
    3490             :                 {
    3491           0 :                   if (!is_delete)
    3492             :                     {
    3493           0 :                       ap->addr_len = address_length;
    3494           0 :                       ap->sw_if_index = sw_if_index;
    3495           0 :                       ap->net.as_u32 =
    3496           0 :                         ap->addr.as_u32 & ip4_main.fib_masks[ap->addr_len];
    3497             : 
    3498           0 :                       nat_log_debug (
    3499             :                         "pool addr %U binds to -> sw_if_idx: %u net: %U/%u",
    3500             :                         format_ip4_address, &ap->addr, ap->sw_if_index,
    3501             :                         format_ip4_address, &ap->net, ap->addr_len);
    3502             :                     }
    3503             :                   else
    3504             :                     {
    3505           0 :                       ap->addr_len = ~0;
    3506             :                     }
    3507           0 :                   break;
    3508             :                 }
    3509             :             }
    3510          18 :           return;
    3511             :         }
    3512             :     }
    3513             : 
    3514           2 :   arp = sm->addr_to_resolve + i;
    3515             : 
    3516           2 :   if (!is_delete)
    3517             :     {
    3518           1 :       if (arp->is_resolved)
    3519             :         {
    3520           0 :           return;
    3521             :         }
    3522             : 
    3523           1 :       rv = nat44_ed_add_address (address, ~0, arp->is_twice_nat);
    3524           1 :       if (0 == rv)
    3525             :         {
    3526           1 :           arp->is_resolved = 1;
    3527             :         }
    3528             :     }
    3529             :   else
    3530             :     {
    3531           1 :       if (!arp->is_resolved)
    3532             :         {
    3533           0 :           return;
    3534             :         }
    3535             : 
    3536           1 :       rv = nat44_ed_del_address (address[0], arp->is_twice_nat);
    3537           1 :       if (0 == rv)
    3538             :         {
    3539           1 :           arp->is_resolved = 0;
    3540             :         }
    3541             :     }
    3542             : }
    3543             : 
    3544             : int
    3545           2 : nat44_ed_add_interface_address (u32 sw_if_index, u8 twice_nat)
    3546             : {
    3547           2 :   snat_main_t *sm = &snat_main;
    3548           2 :   ip4_main_t *ip4_main = sm->ip4_main;
    3549             :   ip4_address_t *first_int_addr;
    3550             :   snat_address_resolve_t *ap;
    3551             :   int rv;
    3552             : 
    3553           2 :   if (!sm->enabled)
    3554             :     {
    3555           0 :       nat_log_err ("nat44 is disabled");
    3556           0 :       return VNET_API_ERROR_UNSUPPORTED;
    3557             :     }
    3558             : 
    3559           2 :   if (!nat44_ed_get_addr_resolve_record (sw_if_index, twice_nat, 0))
    3560             :     {
    3561           0 :       return VNET_API_ERROR_VALUE_EXIST;
    3562             :     }
    3563             : 
    3564           2 :   vec_add2 (sm->addr_to_resolve, ap, 1);
    3565           2 :   ap->sw_if_index = sw_if_index;
    3566           2 :   ap->is_twice_nat = twice_nat;
    3567           2 :   ap->is_resolved = 0;
    3568             : 
    3569           2 :   first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0);
    3570           2 :   if (first_int_addr)
    3571             :     {
    3572           1 :       rv = nat44_ed_add_address (first_int_addr, ~0, twice_nat);
    3573           1 :       if (0 != rv)
    3574             :         {
    3575           0 :           nat44_ed_del_addr_resolve_record (sw_if_index, twice_nat);
    3576           0 :           return rv;
    3577             :         }
    3578           1 :       ap->is_resolved = 1;
    3579             :     }
    3580             : 
    3581           2 :   return 0;
    3582             : }
    3583             : 
    3584             : int
    3585           0 : nat44_ed_del_interface_address (u32 sw_if_index, u8 twice_nat)
    3586             : {
    3587           0 :   snat_main_t *sm = &snat_main;
    3588           0 :   ip4_main_t *ip4_main = sm->ip4_main;
    3589             :   ip4_address_t *first_int_addr;
    3590             : 
    3591           0 :   if (!sm->enabled)
    3592             :     {
    3593           0 :       nat_log_err ("nat44 is disabled");
    3594           0 :       return VNET_API_ERROR_UNSUPPORTED;
    3595             :     }
    3596             : 
    3597           0 :   if (nat44_ed_del_addr_resolve_record (sw_if_index, twice_nat))
    3598             :     {
    3599           0 :       return VNET_API_ERROR_NO_SUCH_ENTRY;
    3600             :     }
    3601             : 
    3602           0 :   first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0);
    3603           0 :   if (first_int_addr)
    3604             :     {
    3605           0 :       return nat44_ed_del_address (first_int_addr[0], twice_nat);
    3606             :     }
    3607             : 
    3608           0 :   return 0;
    3609             : }
    3610             : 
    3611             : int
    3612          14 : nat44_ed_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
    3613             :                       ip4_address_t *eh_addr, u16 eh_port, u8 proto,
    3614             :                       u32 vrf_id, int is_in)
    3615             : {
    3616             :   ip4_header_t ip;
    3617             :   clib_bihash_kv_16_8_t kv, value;
    3618             :   u32 fib_index;
    3619             :   snat_session_t *s;
    3620             :   snat_main_per_thread_data_t *tsm;
    3621             : 
    3622          14 :   if (!sm->enabled)
    3623             :     {
    3624           0 :       return VNET_API_ERROR_UNSUPPORTED;
    3625             :     }
    3626             : 
    3627          14 :   fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
    3628          14 :   ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32;
    3629          14 :   if (sm->num_workers > 1)
    3630           7 :     tsm = vec_elt_at_index (
    3631             :       sm->per_thread_data,
    3632             :       nat44_ed_get_in2out_worker_index (0, &ip, fib_index, 0));
    3633             :   else
    3634           7 :     tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
    3635             : 
    3636          14 :   init_ed_k (&kv, addr->as_u32, port, eh_addr->as_u32, eh_port, fib_index,
    3637             :              proto);
    3638          14 :   if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value))
    3639             :     {
    3640           0 :       return VNET_API_ERROR_NO_SUCH_ENTRY;
    3641             :     }
    3642             : 
    3643          14 :   if (pool_is_free_index (tsm->sessions, ed_value_get_session_index (&value)))
    3644           0 :     return VNET_API_ERROR_UNSPECIFIED;
    3645          14 :   s = pool_elt_at_index (tsm->sessions, ed_value_get_session_index (&value));
    3646          14 :   nat44_ed_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
    3647          14 :   nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
    3648          14 :   return 0;
    3649             : }
    3650             : 
    3651         559 : VLIB_NODE_FN (nat_default_node) (vlib_main_t * vm,
    3652             :                                  vlib_node_runtime_t * node,
    3653             :                                  vlib_frame_t * frame)
    3654             : {
    3655           0 :   return 0;
    3656             : }
    3657             : 
    3658       49304 : VLIB_REGISTER_NODE (nat_default_node) = {
    3659             :   .name = "nat-default",
    3660             :   .vector_size = sizeof (u32),
    3661             :   .format_trace = 0,
    3662             :   .type = VLIB_NODE_TYPE_INTERNAL,
    3663             :   .n_errors = 0,
    3664             :   .n_next_nodes = NAT_N_NEXT,
    3665             :   .next_nodes = {
    3666             :     [NAT_NEXT_DROP] = "error-drop",
    3667             :     [NAT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
    3668             :     [NAT_NEXT_IN2OUT_ED_FAST_PATH] = "nat44-ed-in2out",
    3669             :     [NAT_NEXT_IN2OUT_ED_SLOW_PATH] = "nat44-ed-in2out-slowpath",
    3670             :     [NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH] = "nat44-ed-in2out-output",
    3671             :     [NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath",
    3672             :     [NAT_NEXT_OUT2IN_ED_FAST_PATH] = "nat44-ed-out2in",
    3673             :     [NAT_NEXT_OUT2IN_ED_SLOW_PATH] = "nat44-ed-out2in-slowpath",
    3674             :     [NAT_NEXT_IN2OUT_CLASSIFY] = "nat44-in2out-worker-handoff",
    3675             :     [NAT_NEXT_OUT2IN_CLASSIFY] = "nat44-out2in-worker-handoff",
    3676             :   },
    3677             : };
    3678             : 
    3679             : void
    3680       71609 : nat_6t_l3_l4_csum_calc (nat_6t_flow_t *f)
    3681             : {
    3682       71609 :   f->l3_csum_delta = 0;
    3683       71609 :   f->l4_csum_delta = 0;
    3684       71609 :   if (f->ops & NAT_FLOW_OP_SADDR_REWRITE &&
    3685       28529 :       f->rewrite.saddr.as_u32 != f->match.saddr.as_u32)
    3686             :     {
    3687       28528 :       f->l3_csum_delta =
    3688       28528 :         ip_csum_add_even (f->l3_csum_delta, f->rewrite.saddr.as_u32);
    3689       28531 :       f->l3_csum_delta =
    3690       28528 :         ip_csum_sub_even (f->l3_csum_delta, f->match.saddr.as_u32);
    3691             :     }
    3692             :   else
    3693             :     {
    3694       43081 :       f->rewrite.saddr.as_u32 = f->match.saddr.as_u32;
    3695             :     }
    3696       71612 :   if (f->ops & NAT_FLOW_OP_DADDR_REWRITE &&
    3697       71278 :       f->rewrite.daddr.as_u32 != f->match.daddr.as_u32)
    3698             :     {
    3699       43067 :       f->l3_csum_delta =
    3700       43068 :         ip_csum_add_even (f->l3_csum_delta, f->rewrite.daddr.as_u32);
    3701       43065 :       f->l3_csum_delta =
    3702       43067 :         ip_csum_sub_even (f->l3_csum_delta, f->match.daddr.as_u32);
    3703             :     }
    3704             :   else
    3705             :     {
    3706       28544 :       f->rewrite.daddr.as_u32 = f->match.daddr.as_u32;
    3707             :     }
    3708       71609 :   if (f->ops & NAT_FLOW_OP_SPORT_REWRITE && f->rewrite.sport != f->match.sport)
    3709             :     {
    3710       21787 :       f->l4_csum_delta = ip_csum_add_even (f->l4_csum_delta, f->rewrite.sport);
    3711       21787 :       f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.sport);
    3712             :     }
    3713             :   else
    3714             :     {
    3715       49822 :       f->rewrite.sport = f->match.sport;
    3716             :     }
    3717       71612 :   if (f->ops & NAT_FLOW_OP_DPORT_REWRITE && f->rewrite.dport != f->match.dport)
    3718             :     {
    3719       35351 :       f->l4_csum_delta = ip_csum_add_even (f->l4_csum_delta, f->rewrite.dport);
    3720       35351 :       f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.dport);
    3721             :     }
    3722             :   else
    3723             :     {
    3724       36261 :       f->rewrite.dport = f->match.dport;
    3725             :     }
    3726       71610 :   if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE &&
    3727       14116 :       f->rewrite.icmp_id != f->match.sport)
    3728             :     {
    3729       14062 :       f->l4_csum_delta =
    3730       14062 :         ip_csum_add_even (f->l4_csum_delta, f->rewrite.icmp_id);
    3731       14062 :       f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.sport);
    3732             :     }
    3733             :   else
    3734             :     {
    3735       57548 :       f->rewrite.icmp_id = f->match.sport;
    3736             :     }
    3737       71610 :   if (f->ops & NAT_FLOW_OP_TXFIB_REWRITE)
    3738             :     {
    3739             :     }
    3740             :   else
    3741             :     {
    3742         309 :       f->rewrite.fib_index = f->match.fib_index;
    3743             :     }
    3744       71610 : }
    3745             : 
    3746             : static_always_inline int
    3747             : nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
    3748             :                             ip4_header_t *ip, nat_6t_flow_t *f);
    3749             : 
    3750             : static_always_inline void
    3751       33513 : nat_6t_flow_ip4_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip,
    3752             :                            nat_6t_flow_t *f, ip_protocol_t proto,
    3753             :                            int is_icmp_inner_ip4, int skip_saddr_rewrite)
    3754             : {
    3755       33513 :   udp_header_t *udp = ip4_next_header (ip);
    3756       33512 :   tcp_header_t *tcp = (tcp_header_t *) udp;
    3757             : 
    3758       33512 :   if ((IP_PROTOCOL_TCP == proto || IP_PROTOCOL_UDP == proto) &&
    3759       25331 :       !vnet_buffer (b)->ip.reass.is_non_first_fragment)
    3760             :     {
    3761       25270 :       if (!is_icmp_inner_ip4)
    3762             :         { // regular case
    3763       25261 :           ip->src_address = f->rewrite.saddr;
    3764       25261 :           ip->dst_address = f->rewrite.daddr;
    3765       25261 :           udp->src_port = f->rewrite.sport;
    3766       25261 :           udp->dst_port = f->rewrite.dport;
    3767             :         }
    3768             :       else
    3769             :         { // icmp inner ip4 - reversed saddr/daddr
    3770           9 :           ip->src_address = f->rewrite.daddr;
    3771           9 :           ip->dst_address = f->rewrite.saddr;
    3772           9 :           udp->src_port = f->rewrite.dport;
    3773           9 :           udp->dst_port = f->rewrite.sport;
    3774             :         }
    3775             : 
    3776       25270 :       if (IP_PROTOCOL_TCP == proto)
    3777             :         {
    3778        3634 :           ip_csum_t tcp_sum = tcp->checksum;
    3779        3634 :           tcp_sum = ip_csum_sub_even (tcp_sum, f->l3_csum_delta);
    3780        3634 :           tcp_sum = ip_csum_sub_even (tcp_sum, f->l4_csum_delta);
    3781        3633 :           mss_clamping (sm->mss_clamping, tcp, &tcp_sum);
    3782        3633 :           tcp->checksum = ip_csum_fold (tcp_sum);
    3783             :         }
    3784       21636 :       else if (IP_PROTOCOL_UDP == proto && udp->checksum)
    3785             :         {
    3786       21638 :           ip_csum_t udp_sum = udp->checksum;
    3787       21638 :           udp_sum = ip_csum_sub_even (udp_sum, f->l3_csum_delta);
    3788       21638 :           udp_sum = ip_csum_sub_even (udp_sum, f->l4_csum_delta);
    3789       21638 :           udp->checksum = ip_csum_fold (udp_sum);
    3790             :         }
    3791             :     }
    3792             :   else
    3793             :     {
    3794        8242 :       if (!is_icmp_inner_ip4)
    3795             :         { // regular case
    3796        8244 :           if (!skip_saddr_rewrite)
    3797             :             {
    3798        8242 :               ip->src_address = f->rewrite.saddr;
    3799             :             }
    3800        8244 :           ip->dst_address = f->rewrite.daddr;
    3801             :         }
    3802             :       else
    3803             :         { // icmp inner ip4 - reversed saddr/daddr
    3804           0 :           ip->src_address = f->rewrite.daddr;
    3805           0 :           ip->dst_address = f->rewrite.saddr;
    3806             :         }
    3807             :     }
    3808             : 
    3809       33510 :   if (skip_saddr_rewrite)
    3810             :     {
    3811           2 :       ip->checksum = ip4_header_checksum (ip);
    3812             :     }
    3813             :   else
    3814             :     {
    3815       33508 :       ip_csum_t ip_sum = ip->checksum;
    3816       33508 :       ip_sum = ip_csum_sub_even (ip_sum, f->l3_csum_delta);
    3817       33509 :       ip->checksum = ip_csum_fold (ip_sum);
    3818             :     }
    3819       33510 :   if (0xffff == ip->checksum)
    3820           0 :     ip->checksum = 0;
    3821       33510 :   ASSERT (ip4_header_checksum_is_valid (ip));
    3822       33519 : }
    3823             : 
    3824             : static_always_inline int
    3825        8157 : it_fits (vlib_main_t *vm, vlib_buffer_t *b, void *object, size_t size)
    3826             : {
    3827       16314 :   int result = ((u8 *) object + size <=
    3828       16314 :                 (u8 *) vlib_buffer_get_current (b) + b->current_length) &&
    3829        8157 :                vlib_object_within_buffer_data (vm, b, object, size);
    3830        8157 :   return result;
    3831             : }
    3832             : 
    3833             : static_always_inline int
    3834        8176 : nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
    3835             :                             ip4_header_t *ip, nat_6t_flow_t *f)
    3836             : {
    3837        8176 :   if (IP_PROTOCOL_ICMP != ip->protocol)
    3838           0 :     return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED;
    3839             : 
    3840        8176 :   icmp46_header_t *icmp = ip4_next_header (ip);
    3841        8176 :   nat_icmp_echo_header_t *echo = (nat_icmp_echo_header_t *) (icmp + 1);
    3842             : 
    3843        8176 :   if ((!vnet_buffer (b)->ip.reass.is_non_first_fragment))
    3844             :     {
    3845        8146 :       if (!it_fits (vm, b, icmp, sizeof (*icmp)))
    3846             :         {
    3847           0 :           return NAT_ED_TRNSL_ERR_PACKET_TRUNCATED;
    3848             :         }
    3849             : 
    3850        8146 :       if (!icmp_type_is_error_message (icmp->type))
    3851             :         {
    3852        8135 :           if ((f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) &&
    3853        8131 :               (f->rewrite.icmp_id != echo->identifier))
    3854             :             {
    3855        8055 :               ip_csum_t sum = icmp->checksum;
    3856        8055 :               sum = ip_csum_update (sum, echo->identifier, f->rewrite.icmp_id,
    3857             :                                     nat_icmp_echo_header_t,
    3858             :                                     identifier /* changed member */);
    3859        8055 :               echo->identifier = f->rewrite.icmp_id;
    3860        8055 :               icmp->checksum = ip_csum_fold (sum);
    3861             :             }
    3862             :         }
    3863             :       else
    3864             :         {
    3865          11 :           ip_csum_t sum = ip_incremental_checksum (
    3866             :             0, icmp,
    3867          11 :             clib_net_to_host_u16 (ip->length) - ip4_header_bytes (ip));
    3868          11 :           sum = (u16) ~ip_csum_fold (sum);
    3869          11 :           if (sum != 0)
    3870             :             {
    3871           0 :               return NAT_ED_TRNSL_ERR_INVALID_CSUM;
    3872             :             }
    3873             : 
    3874             :           // errors are not fragmented
    3875          11 :           ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
    3876             : 
    3877          11 :           if (!ip4_header_checksum_is_valid (inner_ip))
    3878             :             {
    3879           0 :               return NAT_ED_TRNSL_ERR_INNER_IP_CORRUPT;
    3880             :             }
    3881             : 
    3882          11 :           ip_protocol_t inner_proto = inner_ip->protocol;
    3883             : 
    3884          11 :           ip_csum_t old_icmp_sum = icmp->checksum;
    3885          11 :           ip_csum_t old_inner_ip_sum = inner_ip->checksum;
    3886             :           ip_csum_t old_udp_sum;
    3887             :           ip_csum_t old_tcp_sum;
    3888             :           ip_csum_t new_icmp_sum;
    3889             :           udp_header_t *udp;
    3890             :           tcp_header_t *tcp;
    3891             : 
    3892          11 :           switch (inner_proto)
    3893             :             {
    3894           3 :             case IP_PROTOCOL_UDP:
    3895           3 :               udp = (udp_header_t *) (inner_ip + 1);
    3896           3 :               if (!it_fits (vm, b, udp, sizeof (*udp)))
    3897             :                 {
    3898           0 :                   return NAT_ED_TRNSL_ERR_PACKET_TRUNCATED;
    3899             :                 }
    3900           3 :               old_udp_sum = udp->checksum;
    3901           3 :               nat_6t_flow_ip4_translate (sm, b, inner_ip, f, inner_proto,
    3902             :                                          1 /* is_icmp_inner_ip4 */,
    3903             :                                          0 /* skip_saddr_rewrite */);
    3904           3 :               new_icmp_sum = ip_csum_sub_even (old_icmp_sum, f->l3_csum_delta);
    3905           3 :               new_icmp_sum = ip_csum_sub_even (new_icmp_sum, f->l4_csum_delta);
    3906             :               new_icmp_sum =
    3907           3 :                 ip_csum_update (new_icmp_sum, old_inner_ip_sum,
    3908             :                                 inner_ip->checksum, ip4_header_t, checksum);
    3909             :               new_icmp_sum =
    3910           3 :                 ip_csum_update (new_icmp_sum, old_udp_sum, udp->checksum,
    3911             :                                 udp_header_t, checksum);
    3912           3 :               new_icmp_sum = ip_csum_fold (new_icmp_sum);
    3913           3 :               icmp->checksum = new_icmp_sum;
    3914           3 :               break;
    3915           6 :             case IP_PROTOCOL_TCP:
    3916           6 :               tcp = (tcp_header_t *) (inner_ip + 1);
    3917           6 :               if (!it_fits (vm, b, tcp, sizeof (*tcp)))
    3918             :                 {
    3919           0 :                   return NAT_ED_TRNSL_ERR_PACKET_TRUNCATED;
    3920             :                 }
    3921           6 :               old_tcp_sum = tcp->checksum;
    3922           6 :               nat_6t_flow_ip4_translate (sm, b, inner_ip, f, inner_proto,
    3923             :                                          1 /* is_icmp_inner_ip4 */,
    3924             :                                          0 /* skip_saddr_rewrite */);
    3925           6 :               new_icmp_sum = ip_csum_sub_even (old_icmp_sum, f->l3_csum_delta);
    3926           6 :               new_icmp_sum = ip_csum_sub_even (new_icmp_sum, f->l4_csum_delta);
    3927             :               new_icmp_sum =
    3928           6 :                 ip_csum_update (new_icmp_sum, old_inner_ip_sum,
    3929             :                                 inner_ip->checksum, ip4_header_t, checksum);
    3930             :               new_icmp_sum =
    3931           6 :                 ip_csum_update (new_icmp_sum, old_tcp_sum, tcp->checksum,
    3932             :                                 tcp_header_t, checksum);
    3933           6 :               new_icmp_sum = ip_csum_fold (new_icmp_sum);
    3934           6 :               icmp->checksum = new_icmp_sum;
    3935           6 :               break;
    3936           2 :             case IP_PROTOCOL_ICMP:
    3937           2 :               nat_6t_flow_ip4_translate (sm, b, inner_ip, f, inner_proto,
    3938             :                                          1 /* is_icmp_inner_ip4 */,
    3939             :                                          0 /* skip_saddr_rewrite */);
    3940           2 :               if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE)
    3941             :                 {
    3942           2 :                   icmp46_header_t *inner_icmp = ip4_next_header (inner_ip);
    3943           2 :                   if (!it_fits (vm, b, inner_icmp, sizeof (*inner_icmp)))
    3944             :                     {
    3945           0 :                       return NAT_ED_TRNSL_ERR_PACKET_TRUNCATED;
    3946             :                     }
    3947           2 :                   nat_icmp_echo_header_t *inner_echo =
    3948             :                     (nat_icmp_echo_header_t *) (inner_icmp + 1);
    3949           2 :                   if (f->rewrite.icmp_id != inner_echo->identifier)
    3950             :                     {
    3951           1 :                       ip_csum_t sum = icmp->checksum;
    3952           1 :                       sum = ip_csum_update (sum, inner_echo->identifier,
    3953             :                                             f->rewrite.icmp_id,
    3954             :                                             nat_icmp_echo_header_t,
    3955             :                                             identifier /* changed member */);
    3956           1 :                       icmp->checksum = ip_csum_fold (sum);
    3957           1 :                       ip_csum_t inner_sum = inner_icmp->checksum;
    3958           1 :                       inner_sum = ip_csum_update (
    3959             :                         sum, inner_echo->identifier, f->rewrite.icmp_id,
    3960             :                         nat_icmp_echo_header_t,
    3961             :                         identifier /* changed member */);
    3962           1 :                       inner_icmp->checksum = ip_csum_fold (inner_sum);
    3963           1 :                       inner_echo->identifier = f->rewrite.icmp_id;
    3964             :                     }
    3965             :                 }
    3966           2 :               break;
    3967           0 :             default:
    3968           0 :               clib_warning ("unexpected NAT protocol value `%d'", inner_proto);
    3969           0 :               return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED;
    3970             :             }
    3971          30 :         }
    3972             :     }
    3973             : 
    3974        8177 :   return NAT_ED_TRNSL_ERR_SUCCESS;
    3975             : }
    3976             : 
    3977             : static_always_inline nat_translation_error_e
    3978       33501 : nat_6t_flow_buf_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
    3979             :                            ip4_header_t *ip, nat_6t_flow_t *f,
    3980             :                            ip_protocol_t proto, int is_output_feature,
    3981             :                            int is_i2o)
    3982             : {
    3983       33501 :   if (!is_output_feature && f->ops & NAT_FLOW_OP_TXFIB_REWRITE)
    3984             :     {
    3985       33384 :       vnet_buffer (b)->sw_if_index[VLIB_TX] = f->rewrite.fib_index;
    3986             :     }
    3987             : 
    3988       33501 :   if (IP_PROTOCOL_ICMP == proto)
    3989             :     {
    3990        8176 :       if (ip->src_address.as_u32 != f->rewrite.saddr.as_u32)
    3991             :         {
    3992             :           // packet is returned from a router, not from destination
    3993             :           // skip source address rewrite if in o2i path
    3994        6591 :           nat_6t_flow_ip4_translate (sm, b, ip, f, proto,
    3995             :                                      0 /* is_icmp_inner_ip4 */,
    3996             :                                      !is_i2o /* skip_saddr_rewrite */);
    3997             :         }
    3998             :       else
    3999             :         {
    4000        1585 :           nat_6t_flow_ip4_translate (sm, b, ip, f, proto,
    4001             :                                      0 /* is_icmp_inner_ip4 */,
    4002             :                                      0 /* skip_saddr_rewrite */);
    4003             :         }
    4004        8177 :       return nat_6t_flow_icmp_translate (vm, sm, b, ip, f);
    4005             :     }
    4006             : 
    4007       25325 :   nat_6t_flow_ip4_translate (sm, b, ip, f, proto, 0 /* is_icmp_inner_ip4 */,
    4008             :                              0 /* skip_saddr_rewrite */);
    4009             : 
    4010       25330 :   return NAT_ED_TRNSL_ERR_SUCCESS;
    4011             : }
    4012             : 
    4013             : nat_translation_error_e
    4014       28411 : nat_6t_flow_buf_translate_i2o (vlib_main_t *vm, snat_main_t *sm,
    4015             :                                vlib_buffer_t *b, ip4_header_t *ip,
    4016             :                                nat_6t_flow_t *f, ip_protocol_t proto,
    4017             :                                int is_output_feature)
    4018             : {
    4019       28411 :   return nat_6t_flow_buf_translate (vm, sm, b, ip, f, proto, is_output_feature,
    4020             :                                     1 /* is_i2o */);
    4021             : }
    4022             : 
    4023             : nat_translation_error_e
    4024        5088 : nat_6t_flow_buf_translate_o2i (vlib_main_t *vm, snat_main_t *sm,
    4025             :                                vlib_buffer_t *b, ip4_header_t *ip,
    4026             :                                nat_6t_flow_t *f, ip_protocol_t proto,
    4027             :                                int is_output_feature)
    4028             : {
    4029        5088 :   return nat_6t_flow_buf_translate (vm, sm, b, ip, f, proto, is_output_feature,
    4030             :                                     0 /* is_i2o */);
    4031             : }
    4032             : 
    4033             : static_always_inline void
    4034       56821 : nat_syslog_nat44_sess (u32 ssubix, u32 sfibix, ip4_address_t *isaddr,
    4035             :                        u16 isport, ip4_address_t *xsaddr, u16 xsport,
    4036             :                        ip4_address_t *idaddr, u16 idport,
    4037             :                        ip4_address_t *xdaddr, u16 xdport, u8 proto, u8 is_add,
    4038             :                        u8 is_twicenat)
    4039             : {
    4040             :   syslog_msg_t syslog_msg;
    4041             :   fib_table_t *fib;
    4042             : 
    4043       56821 :   if (!syslog_is_enabled ())
    4044       56814 :     return;
    4045             : 
    4046           6 :   if (syslog_severity_filter_block (SADD_SDEL_SEVERITY))
    4047           0 :     return;
    4048             : 
    4049           6 :   fib = fib_table_get (sfibix, FIB_PROTOCOL_IP4);
    4050             : 
    4051           6 :   syslog_msg_init (&syslog_msg, NAT_FACILITY, SADD_SDEL_SEVERITY, NAT_APPNAME,
    4052             :                    is_add ? SADD_MSGID : SDEL_MSGID);
    4053             : 
    4054           6 :   syslog_msg_sd_init (&syslog_msg, NSESS_SDID);
    4055           6 :   syslog_msg_add_sd_param (&syslog_msg, SSUBIX_SDPARAM_NAME, "%d", ssubix);
    4056           6 :   syslog_msg_add_sd_param (&syslog_msg, SVLAN_SDPARAM_NAME, "%d",
    4057             :                            fib->ft_table_id);
    4058           6 :   syslog_msg_add_sd_param (&syslog_msg, IATYP_SDPARAM_NAME, IATYP_IPV4);
    4059           6 :   syslog_msg_add_sd_param (&syslog_msg, ISADDR_SDPARAM_NAME, "%U",
    4060             :                            format_ip4_address, isaddr);
    4061           6 :   syslog_msg_add_sd_param (&syslog_msg, ISPORT_SDPARAM_NAME, "%d",
    4062           6 :                            clib_net_to_host_u16 (isport));
    4063           6 :   syslog_msg_add_sd_param (&syslog_msg, XATYP_SDPARAM_NAME, IATYP_IPV4);
    4064           6 :   syslog_msg_add_sd_param (&syslog_msg, XSADDR_SDPARAM_NAME, "%U",
    4065             :                            format_ip4_address, xsaddr);
    4066           6 :   syslog_msg_add_sd_param (&syslog_msg, XSPORT_SDPARAM_NAME, "%d",
    4067           6 :                            clib_net_to_host_u16 (xsport));
    4068           6 :   syslog_msg_add_sd_param (&syslog_msg, PROTO_SDPARAM_NAME, "%d", proto);
    4069           6 :   syslog_msg_add_sd_param (&syslog_msg, XDADDR_SDPARAM_NAME, "%U",
    4070             :                            format_ip4_address, xdaddr);
    4071           6 :   syslog_msg_add_sd_param (&syslog_msg, XDPORT_SDPARAM_NAME, "%d",
    4072           6 :                            clib_net_to_host_u16 (xdport));
    4073           6 :   if (is_twicenat)
    4074             :     {
    4075           0 :       syslog_msg_add_sd_param (&syslog_msg, IDADDR_SDPARAM_NAME, "%U",
    4076             :                                format_ip4_address, idaddr);
    4077           0 :       syslog_msg_add_sd_param (&syslog_msg, IDPORT_SDPARAM_NAME, "%d",
    4078           0 :                                clib_net_to_host_u16 (idport));
    4079             :     }
    4080             : 
    4081           6 :   syslog_msg_send (&syslog_msg);
    4082             : }
    4083             : 
    4084             : void
    4085       28520 : nat_syslog_nat44_sadd (u32 ssubix, u32 sfibix, ip4_address_t *isaddr,
    4086             :                        u16 isport, ip4_address_t *idaddr, u16 idport,
    4087             :                        ip4_address_t *xsaddr, u16 xsport,
    4088             :                        ip4_address_t *xdaddr, u16 xdport, u8 proto,
    4089             :                        u8 is_twicenat)
    4090             : {
    4091       28520 :   nat_syslog_nat44_sess (ssubix, sfibix, isaddr, isport, xsaddr, xsport,
    4092             :                          idaddr, idport, xdaddr, xdport, proto, 1,
    4093             :                          is_twicenat);
    4094       28519 : }
    4095             : 
    4096             : void
    4097       28301 : nat_syslog_nat44_sdel (u32 ssubix, u32 sfibix, ip4_address_t *isaddr,
    4098             :                        u16 isport, ip4_address_t *idaddr, u16 idport,
    4099             :                        ip4_address_t *xsaddr, u16 xsport,
    4100             :                        ip4_address_t *xdaddr, u16 xdport, u8 proto,
    4101             :                        u8 is_twicenat)
    4102             : {
    4103       28301 :   nat_syslog_nat44_sess (ssubix, sfibix, isaddr, isport, xsaddr, xsport,
    4104             :                          idaddr, idport, xdaddr, xdport, proto, 0,
    4105             :                          is_twicenat);
    4106       28301 : }
    4107             : 
    4108             : /*
    4109             :  * fd.io coding-style-patch-verification: ON
    4110             :  *
    4111             :  * Local Variables:
    4112             :  * eval: (c-set-style "gnu")
    4113             :  * End:
    4114             :  */

Generated by: LCOV version 1.14