+ node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in-slowpath");
+ sm->ed_out2in_slowpath_node_index = node->index;
+ node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpinning");
+ sm->hairpinning_node_index = node->index;
+ node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpin-dst");
+ sm->hairpin_dst_node_index = node->index;
+ node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpin-src");
+ sm->hairpin_src_node_index = node->index;
+ node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-hairpinning");
+ sm->ed_hairpinning_node_index = node->index;
+ node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-hairpin-dst");
+ sm->ed_hairpin_dst_node_index = node->index;
+ node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-hairpin-src");
+ sm->ed_hairpin_src_node_index = node->index;
+}
+
+#define nat_init_simple_counter(c, n, sn) \
+do \
+ { \
+ c.name = n; \
+ c.stat_segment_name = sn; \
+ vlib_validate_simple_counter (&c, 0); \
+ vlib_zero_simple_counter (&c, 0); \
+ } while (0);
+
+static clib_error_t *
+nat_init (vlib_main_t * vm)
+{
+ snat_main_t *sm = &snat_main;
+ vlib_thread_main_t *tm = vlib_get_thread_main ();
+ vlib_thread_registration_t *tr;
+ ip4_add_del_interface_address_callback_t cbi = { 0 };
+ ip4_table_bind_callback_t cbt = { 0 };
+ u32 i, num_threads = 0;
+ uword *p, *bitmap = 0;
+
+ clib_memset (sm, 0, sizeof (*sm));
+
+ // required
+ sm->vnet_main = vnet_get_main ();
+ // convenience
+ sm->ip4_main = &ip4_main;
+ sm->api_main = vlibapi_get_main ();
+ sm->ip4_lookup_main = &ip4_main.lookup_main;
+
+ // frame queue indices used for handoff
+ sm->fq_out2in_index = ~0;
+ sm->fq_in2out_index = ~0;
+ sm->fq_in2out_output_index = ~0;
+
+ sm->log_level = SNAT_LOG_ERROR;
+
+ nat44_set_node_indexes (sm, vm);
+ sm->log_class = vlib_log_register_class ("nat", 0);
+ nat_ipfix_logging_init (vm);
+
+ nat_init_simple_counter (sm->total_users, "total-users",
+ "/nat44/total-users");
+ nat_init_simple_counter (sm->total_sessions, "total-sessions",
+ "/nat44/total-sessions");
+ nat_init_simple_counter (sm->user_limit_reached, "user-limit-reached",
+ "/nat44/user-limit-reached");
+
+#define _(x) \
+ sm->counters.fastpath.in2out.x.name = #x; \
+ sm->counters.fastpath.in2out.x.stat_segment_name = \
+ "/nat44/in2out/fastpath/" #x; \
+ sm->counters.slowpath.in2out.x.name = #x; \
+ sm->counters.slowpath.in2out.x.stat_segment_name = \
+ "/nat44/in2out/slowpath/" #x; \
+ sm->counters.fastpath.out2in.x.name = #x; \
+ sm->counters.fastpath.out2in.x.stat_segment_name = \
+ "/nat44/out2in/fastpath/" #x; \
+ sm->counters.slowpath.out2in.x.name = #x; \
+ sm->counters.slowpath.out2in.x.stat_segment_name = \
+ "/nat44/out2in/slowpath/" #x; \
+ sm->counters.fastpath.in2out_ed.x.name = #x; \
+ sm->counters.fastpath.in2out_ed.x.stat_segment_name = \
+ "/nat44/ed/in2out/fastpath/" #x; \
+ sm->counters.slowpath.in2out_ed.x.name = #x; \
+ sm->counters.slowpath.in2out_ed.x.stat_segment_name = \
+ "/nat44/ed/in2out/slowpath/" #x; \
+ sm->counters.fastpath.out2in_ed.x.name = #x; \
+ sm->counters.fastpath.out2in_ed.x.stat_segment_name = \
+ "/nat44/ed/out2in/fastpath/" #x; \
+ sm->counters.slowpath.out2in_ed.x.name = #x; \
+ sm->counters.slowpath.out2in_ed.x.stat_segment_name = \
+ "/nat44/ed/out2in/slowpath/" #x;
+ foreach_nat_counter;
+#undef _
+ sm->counters.hairpinning.name = "hairpinning";
+ sm->counters.hairpinning.stat_segment_name = "/nat44/hairpinning";
+
+ p = hash_get_mem (tm->thread_registrations_by_name, "workers");
+ if (p)
+ {
+ tr = (vlib_thread_registration_t *) p[0];
+ if (tr)
+ {
+ sm->num_workers = tr->count;
+ sm->first_worker_index = tr->first_index;
+ }
+ }
+ num_threads = tm->n_vlib_mains - 1;
+ sm->port_per_thread = 0xffff - 1024;
+ vec_validate (sm->per_thread_data, num_threads);
+
+ /* Use all available workers by default */
+ if (sm->num_workers > 1)
+ {
+
+ for (i = 0; i < sm->num_workers; i++)
+ bitmap = clib_bitmap_set (bitmap, i, 1);
+ snat_set_workers (bitmap);
+ clib_bitmap_free (bitmap);
+ }
+ else
+ sm->per_thread_data[0].snat_thread_index = 0;
+
+ /* callbacks to call when interface address changes. */
+ cbi.function = snat_ip4_add_del_interface_address_cb;
+ vec_add1 (sm->ip4_main->add_del_interface_address_callbacks, cbi);
+ cbi.function = nat_ip4_add_del_addr_only_sm_cb;
+ vec_add1 (sm->ip4_main->add_del_interface_address_callbacks, cbi);
+
+ /* callbacks to call when interface to table biding changes */
+ cbt.function = snat_update_outside_fib;
+ vec_add1 (sm->ip4_main->table_bind_callbacks, cbt);
+
+ sm->fib_src_low =
+ fib_source_allocate ("nat-low", FIB_SOURCE_PRIORITY_LOW,
+ FIB_SOURCE_BH_SIMPLE);
+ sm->fib_src_hi =
+ fib_source_allocate ("nat-hi", FIB_SOURCE_PRIORITY_HI,
+ FIB_SOURCE_BH_SIMPLE);
+
+ /* used only by out2in-dpo feature */
+ nat_dpo_module_init ();
+
+ nat_affinity_init (vm);
+ nat_ha_init (vm, sm->num_workers, num_threads);
+
+ test_key_calc_split ();
+ return nat44_api_hookup (vm);
+}
+
+VLIB_INIT_FUNCTION (nat_init);
+
+int
+nat44_plugin_enable (nat44_config_t c)
+{
+ snat_main_t *sm = &snat_main;
+ u32 static_mapping_buckets = 1024;
+ u32 static_mapping_memory_size = 64 << 20;
+
+ if (sm->enabled)
+ {
+ nat_log_err ("nat44 is enabled");
+ return 1;
+ }
+
+ // c.static_mapping_only + c.connection_tracking
+ // - supported in NAT EI & NAT ED
+ // c.out2in_dpo, c.static_mapping_only
+ // - supported in NAT EI
+
+ if (c.endpoint_dependent)
+ {
+ if ((c.static_mapping_only && !c.connection_tracking) || c.out2in_dpo)
+ {
+ nat_log_err ("unsupported combination of configuration");
+ return 1;
+ }
+ if (c.users || c.user_sessions)
+ {
+ nat_log_err ("unsupported combination of configuration");
+ return 1;
+ }
+ }
+
+ // reset to defaults:
+ sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
+ sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
+ //
+ nat_reset_timeouts (&sm->timeouts);
+
+ // nat44 feature configuration
+ sm->endpoint_dependent = c.endpoint_dependent;
+ sm->static_mapping_only = c.static_mapping_only;
+ sm->static_mapping_connection_tracking = c.connection_tracking;
+ sm->forwarding_enabled = 0;
+ sm->mss_clamping = 0;
+
+ if (!c.users)
+ c.users = 1024;
+
+ sm->max_users_per_thread = c.users;
+ sm->user_buckets = nat_calc_bihash_buckets (c.users);
+
+ if (!c.sessions)
+ c.sessions = 10 * 1024;
+
+ sm->max_translations_per_thread = c.sessions;
+ sm->translation_buckets = nat_calc_bihash_buckets (c.sessions);
+
+ vec_add1 (sm->max_translations_per_fib, sm->max_translations_per_thread);
+ sm->max_translations_per_user
+ = c.user_sessions ? c.user_sessions : sm->max_translations_per_thread;
+
+ sm->outside_vrf_id = c.outside_vrf;
+ sm->outside_fib_index =
+ fib_table_find_or_create_and_lock
+ (FIB_PROTOCOL_IP4, c.outside_vrf, sm->fib_src_hi);
+
+ sm->inside_vrf_id = c.inside_vrf;
+ sm->inside_fib_index =
+ fib_table_find_or_create_and_lock
+ (FIB_PROTOCOL_IP4, c.inside_vrf, sm->fib_src_hi);
+
+ if (c.endpoint_dependent)
+ {
+ sm->worker_out2in_cb = nat44_ed_get_worker_out2in_cb;
+ sm->worker_in2out_cb = nat44_ed_get_worker_in2out_cb;
+ sm->out2in_node_index = sm->ed_out2in_node_index;
+ sm->in2out_node_index = sm->ed_in2out_node_index;
+ sm->in2out_output_node_index = sm->ed_in2out_output_node_index;
+ sm->icmp_match_out2in_cb = icmp_match_out2in_ed;
+ sm->icmp_match_in2out_cb = icmp_match_in2out_ed;
+
+ // try to move it into nat44_db_init,
+ // consider static mapping requirements
+ clib_bihash_init_16_8 (&sm->out2in_ed, "out2in-ed",
+ sm->translation_buckets, 0);
+ clib_bihash_set_kvp_format_fn_16_8 (&sm->out2in_ed,
+ format_ed_session_kvp);
+
+
+ nat_affinity_enable ();
+
+ nat_ha_enable (nat_ha_sadd_ed_cb, nat_ha_sdel_ed_cb, nat_ha_sref_ed_cb);
+ }
+ else
+ {
+ sm->worker_out2in_cb = snat_get_worker_out2in_cb;
+ sm->worker_in2out_cb = snat_get_worker_in2out_cb;
+ sm->out2in_node_index = sm->ei_out2in_node_index;
+ sm->in2out_node_index = sm->ei_in2out_node_index;
+ sm->in2out_output_node_index = sm->ei_in2out_output_node_index;
+ sm->icmp_match_out2in_cb = icmp_match_out2in_slow;
+ sm->icmp_match_in2out_cb = icmp_match_in2out_slow;
+
+ nat_ha_enable (nat_ha_sadd_cb, nat_ha_sdel_cb, nat_ha_sref_cb);
+ }
+
+ // c.static_mapping & c.connection_tracking require
+ // session database
+ if (!c.static_mapping_only
+ || (c.static_mapping_only && c.connection_tracking))
+ {
+ snat_main_per_thread_data_t *tsm;
+ /* *INDENT-OFF* */
+ vec_foreach (tsm, sm->per_thread_data)
+ {
+ nat44_db_init (tsm);
+ }
+ /* *INDENT-ON* */
+ }
+ else
+ {
+ sm->icmp_match_in2out_cb = icmp_match_in2out_fast;
+ sm->icmp_match_out2in_cb = icmp_match_out2in_fast;
+ }
+
+ clib_bihash_init_8_8 (&sm->static_mapping_by_local,
+ "static_mapping_by_local", static_mapping_buckets,
+ static_mapping_memory_size);
+ clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_local,
+ format_static_mapping_kvp);