diff --git a/src/vnet/interface.h b/src/vnet/interface.h index f0cb540f979..eb557fdef8b 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -191,6 +191,22 @@ static __clib_unused void * __clib_unused_##f = f; #define VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION_PRIO(f,p) \ _VNET_INTERFACE_FUNCTION_DECL_PRIO(f,sw_interface_admin_up_down, p) +#define VNET_SW_INTERFACE_TABLE_BIND_V4_CB(f) \ + { \ + ip4_table_bind_callback_t cb = { \ + .function = f, \ + }; \ + vec_add1 (ip4_main.table_bind_callbacks, cb); \ + } + +#define VNET_SW_INTERFACE_TABLE_BIND_V6_CB(f) \ + { \ + ip6_table_bind_callback_t cb = { \ + .function = f, \ + }; \ + vec_add1 (ip6_main.table_bind_callbacks, cb); \ + } + /** * Tunnel description parameters */ diff --git a/src/vnet/session/application_namespace.c b/src/vnet/session/application_namespace.c index 5930c34dce5..c1c8527fb02 100644 --- a/src/vnet/session/application_namespace.c +++ b/src/vnet/session/application_namespace.c @@ -107,6 +107,7 @@ vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t *a) session_table_t *st; u32 ns_index; session_error_t rv; + u32 ip4_fib_index, ip6_fib_index; if (a->is_add) { @@ -115,6 +116,10 @@ vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t *a) a->sw_if_index)) return SESSION_E_INVALID; + /* + * sw_if_index takes precedence over fib_id's. + * When sw_if_index is provided, overwrite what's in fib_id's + */ if (a->sw_if_index != APP_NAMESPACE_INVALID_INDEX) { a->ip4_fib_id = @@ -124,8 +129,23 @@ vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t *a) fib_table_get_table_id_for_sw_if_index (FIB_PROTOCOL_IP6, a->sw_if_index); } - if (a->sw_if_index == APP_NAMESPACE_INVALID_INDEX - && a->ip4_fib_id == APP_NAMESPACE_INVALID_INDEX) + /* Either sw_if_index or one of 2 fib_id's must be provided */ + if (a->sw_if_index == APP_NAMESPACE_INVALID_INDEX && + a->ip4_fib_id == APP_NAMESPACE_INVALID_INDEX && + a->ip6_fib_id == APP_NAMESPACE_INVALID_INDEX) + return SESSION_E_INVALID; + + /* Validate the fib_id's */ + ip4_fib_index = fib_table_find (FIB_PROTOCOL_IP4, a->ip4_fib_id); + ip6_fib_index = fib_table_find (FIB_PROTOCOL_IP6, a->ip6_fib_id); + if ((ip4_fib_index == ~0) && (ip6_fib_index == ~0)) + return SESSION_E_INVALID; + + /* if fib_id entered, check fib_id exist */ + if (((a->ip4_fib_id != APP_NAMESPACE_INVALID_INDEX) && + (ip4_fib_index == ~0)) || + ((a->ip6_fib_id != APP_NAMESPACE_INVALID_INDEX) && + (ip6_fib_index == ~0))) return SESSION_E_INVALID; app_ns = app_namespace_get_from_id (a->ns_id); @@ -169,8 +189,8 @@ vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t *a) app_ns->ns_secret = a->secret; app_ns->sw_if_index = a->sw_if_index; - app_ns->ip4_fib_index = fib_table_find (FIB_PROTOCOL_IP4, a->ip4_fib_id); - app_ns->ip6_fib_index = fib_table_find (FIB_PROTOCOL_IP6, a->ip6_fib_id); + app_ns->ip4_fib_index = ip4_fib_index; + app_ns->ip6_fib_index = ip6_fib_index; session_lookup_set_tables_appns (app_ns); } else @@ -266,11 +286,98 @@ appns_sapi_enabled (void) return app_sapi_enabled; } +static void +app_namespace_table_bind_v4 (ip4_main_t *im, uword opaque, u32 sw_if_index, + u32 new_fib_index, u32 old_fib_index) +{ + app_namespace_t *app_ns; + + pool_foreach (app_ns, app_namespace_pool) + { + if (app_ns->sw_if_index == sw_if_index) + { + /* + * For add, call vnet_app_namespace_add_del without sw_if_index and + * keep the existing ip6_fib_index. + */ + vnet_app_namespace_add_del_args_t add = { + .ns_id = vec_dup (app_ns->ns_id), + .secret = app_ns->ns_secret, + .sw_if_index = APP_NAMESPACE_INVALID_INDEX, + .sock_name = vec_dup (app_ns->sock_name), + .ip4_fib_id = + fib_table_get_table_id (new_fib_index, FIB_PROTOCOL_IP4), + .ip6_fib_id = + fib_table_get_table_id (app_ns->ip6_fib_index, FIB_PROTOCOL_IP6), + .is_add = 1, + }; + vnet_app_namespace_add_del_args_t del = { + .ns_id = app_ns->ns_id, + .is_add = 0, + }; + vnet_app_namespace_add_del (&del); + + vnet_app_namespace_add_del (&add); + + /* keep the sw_if_index */ + app_ns->sw_if_index = sw_if_index; + + vec_free (add.ns_id); + vec_free (add.sock_name); + } + } +} + +static void +app_namespace_table_bind_v6 (ip6_main_t *im, uword opaque, u32 sw_if_index, + u32 new_fib_index, u32 old_fib_index) +{ + app_namespace_t *app_ns; + + pool_foreach (app_ns, app_namespace_pool) + { + if (app_ns->sw_if_index == sw_if_index) + { + /* + * For add, call vnet_app_namespace_add_del without sw_if_index and + * keep the existing ip4_fib_index. + */ + vnet_app_namespace_add_del_args_t add = { + .ns_id = vec_dup (app_ns->ns_id), + .secret = app_ns->ns_secret, + .sw_if_index = APP_NAMESPACE_INVALID_INDEX, + .sock_name = vec_dup (app_ns->sock_name), + .ip4_fib_id = + fib_table_get_table_id (app_ns->ip4_fib_index, FIB_PROTOCOL_IP4), + .ip6_fib_id = + fib_table_get_table_id (new_fib_index, FIB_PROTOCOL_IP6), + .is_add = 1, + }; + vnet_app_namespace_add_del_args_t del = { + .ns_id = app_ns->ns_id, + .is_add = 0, + }; + vnet_app_namespace_add_del (&del); + + vnet_app_namespace_add_del (&add); + + /* keep the sw_if_index */ + app_ns->sw_if_index = sw_if_index; + + vec_free (add.ns_id); + vec_free (add.sock_name); + } + } +} + void app_namespaces_init (void) { u8 *ns_id = format (0, "default"); + VNET_SW_INTERFACE_TABLE_BIND_V4_CB (app_namespace_table_bind_v4); + VNET_SW_INTERFACE_TABLE_BIND_V6_CB (app_namespace_table_bind_v6); + if (!app_namespace_lookup_table) app_namespace_lookup_table = hash_create_vec (0, sizeof (u8), sizeof (uword)); @@ -298,9 +405,11 @@ app_ns_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { u8 is_add = 0, *ns_id = 0, secret_set = 0, sw_if_index_set = 0; + u8 fib6_id_set = 0, fib4_id_set = 0; u8 *sock_name = 0; unformat_input_t _line_input, *line_input = &_line_input; - u32 sw_if_index, fib_id = APP_NAMESPACE_INVALID_INDEX; + u32 sw_if_index = APP_NAMESPACE_INVALID_INDEX; + u32 fib4_id = ~0, fib6_id = ~0; vnet_main_t *vnm = vnet_get_main (); u64 secret; clib_error_t *error = 0; @@ -326,8 +435,10 @@ app_ns_fn (vlib_main_t * vm, unformat_input_t * input, else if (unformat (line_input, "if %U", unformat_vnet_sw_interface, vnm, &sw_if_index)) sw_if_index_set = 1; - else if (unformat (line_input, "fib_id", &fib_id)) - ; + else if (unformat (line_input, "ip4-fib-id %u", &fib4_id)) + fib4_id_set = 1; + else if (unformat (line_input, "ip6-fib-id %u", &fib6_id)) + fib6_id_set = 1; else if (unformat (line_input, "sock-name %_%v%_", &sock_name)) ; else @@ -344,9 +455,23 @@ app_ns_fn (vlib_main_t * vm, unformat_input_t * input, goto done; } - if (is_add && (!secret_set || !sw_if_index_set)) + if (is_add && + (!secret_set || (!sw_if_index_set && !fib4_id_set && !fib6_id_set))) { - vlib_cli_output (vm, "secret and interface must be provided"); + vlib_cli_output (vm, "secret and interface or fib-id must be provided"); + goto done; + } + + /* Validate the fib-id is valid */ + if (fib4_id_set && (fib_table_find (FIB_PROTOCOL_IP4, fib4_id) == ~0)) + { + vlib_cli_output (vm, "ip4-fib-id %u does not exist", fib4_id); + goto done; + } + + if (fib6_id_set && (fib_table_find (FIB_PROTOCOL_IP6, fib6_id) == ~0)) + { + vlib_cli_output (vm, "ip6-fib-id %u does not exist", fib6_id); goto done; } @@ -356,7 +481,8 @@ app_ns_fn (vlib_main_t * vm, unformat_input_t * input, .secret = secret, .sw_if_index = sw_if_index, .sock_name = sock_name, - .ip4_fib_id = fib_id, + .ip4_fib_id = fib4_id, + .ip6_fib_id = fib6_id, .is_add = is_add, }; /* clang-format on */ @@ -376,7 +502,8 @@ done: VLIB_CLI_COMMAND (app_ns_command, static) = { .path = "app ns", .short_help = "app ns [add|del] id secret " - "sw_if_index if ", + "sw_if_index " + "{ip4-fib-id ip6-fib-id | if }", .function = app_ns_fn, }; diff --git a/src/vnet/session/session_lookup.c b/src/vnet/session/session_lookup.c index 3a99c0b5aaf..28a1feb1ed8 100644 --- a/src/vnet/session/session_lookup.c +++ b/src/vnet/session/session_lookup.c @@ -1454,6 +1454,8 @@ session_lookup_set_tables_appns (app_namespace_t * app_ns) for (fp = 0; fp < ARRAY_LEN (fib_index_to_table_index); fp++) { fib_index = app_namespace_get_fib_index (app_ns, fp); + if (fib_index == ~0) + continue; st = session_table_get_or_alloc (fp, fib_index); if (st) { @@ -1953,6 +1955,8 @@ session_lookup_table_cleanup (u32 fib_proto, u32 fib_index, u32 ns_index) u32 table_index, appns_index; int i; + if (fib_index == ~0) + return; session_lookup_fib_table_unlock (fib_index, fib_proto); table_index = session_lookup_get_index_for_fib (fib_proto, fib_index); st = session_table_get (table_index); diff --git a/test/asf/test_session.py b/test/asf/test_session.py index fe8da126195..79bbbd63b35 100644 --- a/test/asf/test_session.py +++ b/test/asf/test_session.py @@ -122,7 +122,6 @@ class TestSession(VppAsfTestCase): ip_t10.remove_vpp_config() -@tag_fixme_vpp_workers class TestApplicationNamespace(VppAsfTestCase): """Application Namespacee""" @@ -219,6 +218,160 @@ class TestApplicationNamespace(VppAsfTestCase): ) self.assertEqual(rv.retval, -1) + def test_application_namespace_binding(self): + """Application Namespace Interface Binding""" + + self.vapi.session_enable_disable_v2( + rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_RULE_TABLE + ) + + table_id = 99 + + # Bad ip4_fib_id + with self.vapi.assert_negative_api_retval(): + rv = self.vapi.app_namespace_add_del_v4( + is_add=1, namespace_id="2", ip4_fib_id=table_id, ip6_fib_id=0 + ) + self.assertEqual(rv.retval, -19) + + # Bad ip6_fib_id + with self.vapi.assert_negative_api_retval(): + rv = self.vapi.app_namespace_add_del_v4( + is_add=1, namespace_id="2", ip4_fib_id=0, ip6_fib_id=table_id + ) + self.assertEqual(rv.retval, -19) + + tbl = VppIpTable(self, table_id) + tbl.add_vpp_config() + + tbl6 = VppIpTable(self, table_id, is_ip6=1) + tbl6.add_vpp_config() + + # Not expecting an error with valid table_id's + self.vapi.app_namespace_add_del_v4( + is_add=1, namespace_id="2", ip4_fib_id=table_id, ip6_fib_id=table_id + ) + # delete + self.vapi.app_namespace_add_del_v4( + is_add=0, namespace_id="2", ip4_fib_id=table_id, ip6_fib_id=table_id + ) + + # ip4 only + self.vapi.app_namespace_add_del_v4( + is_add=1, namespace_id="2", ip4_fib_id=table_id, ip6_fib_id=0xFFFFFFFF + ) + # delete + self.vapi.app_namespace_add_del_v4( + is_add=0, namespace_id="2", ip4_fib_id=table_id, ip6_fib_id=0xFFFFFFFF + ) + + # ip6 only + self.vapi.app_namespace_add_del_v4( + is_add=1, namespace_id="2", ip4_fib_id=0xFFFFFFFF, ip6_fib_id=table_id + ) + # delete + self.vapi.app_namespace_add_del_v4( + is_add=0, namespace_id="2", ip4_fib_id=0xFFFFFFFF, ip6_fib_id=table_id + ) + + app0 = self.vapi.app_namespace_add_del_v4( + namespace_id="0", sw_if_index=self.loop0.sw_if_index, is_add=1 + ) + self.vapi.session_rule_add_del( + transport_proto=VppEnum.vl_api_transport_proto_t.TRANSPORT_PROTO_API_TCP, + lcl="172.100.1.1/32", + rmt="172.100.1.2/32", + lcl_port=5000, + rmt_port=5000, + action_index=1, + appns_index=app0.appns_index, + scope=VppEnum.vl_api_session_rule_scope_t.SESSION_RULE_SCOPE_API_GLOBAL, + is_add=1, + ) + dump = self.vapi.session_rules_v2_dump() + self.assertEqual(len(dump[1].appns_index), 2) + self.assertEqual(dump[1].count, 2) + + # move the interface to vrf 99 + self.vapi.sw_interface_set_table( + sw_if_index=self.loop0.sw_if_index, + is_ipv6=0, + vrf_id=table_id, + ) + dump = self.vapi.session_rules_v2_dump() + self.assertEqual(len(dump[1].appns_index), 1) + self.assertEqual(dump[1].count, 1) + + self.vapi.session_rule_add_del( + transport_proto=VppEnum.vl_api_transport_proto_t.TRANSPORT_PROTO_API_TCP, + lcl="172.100.1.1/32", + rmt="172.100.1.2/32", + lcl_port=5000, + rmt_port=5000, + action_index=1, + appns_index=0, + scope=VppEnum.vl_api_session_rule_scope_t.SESSION_RULE_SCOPE_API_GLOBAL, + is_add=0, + ) + + # move the interface to vrf 0 + self.vapi.sw_interface_set_table( + sw_if_index=self.loop0.sw_if_index, + is_ipv6=0, + vrf_id=0, + ) + + # try it with ip6 + self.vapi.session_rule_add_del( + transport_proto=VppEnum.vl_api_transport_proto_t.TRANSPORT_PROTO_API_TCP, + lcl="2001::1/128", + rmt="2002::1/128", + lcl_port=5000, + rmt_port=5000, + action_index=1, + appns_index=app0.appns_index, + scope=VppEnum.vl_api_session_rule_scope_t.SESSION_RULE_SCOPE_API_GLOBAL, + is_add=1, + ) + dump = self.vapi.session_rules_v2_dump() + self.assertEqual(len(dump[1].appns_index), 2) + self.assertEqual(dump[1].count, 2) + + self.vapi.sw_interface_set_table( + sw_if_index=self.loop0.sw_if_index, + is_ipv6=1, + vrf_id=table_id, + ) + dump = self.vapi.session_rules_v2_dump() + self.assertEqual(len(dump[1].appns_index), 2) + self.assertEqual(dump[1].count, 2) + + # move back to 0 + self.vapi.sw_interface_set_table( + sw_if_index=self.loop0.sw_if_index, + is_ipv6=1, + vrf_id=0, + ) + + self.vapi.session_rule_add_del( + transport_proto=VppEnum.vl_api_transport_proto_t.TRANSPORT_PROTO_API_TCP, + lcl="2001::1/128", + rmt="2002::1/128", + lcl_port=5000, + rmt_port=5000, + action_index=1, + appns_index=0, + scope=VppEnum.vl_api_session_rule_scope_t.SESSION_RULE_SCOPE_API_GLOBAL, + is_add=0, + ) + + self.vapi.app_namespace_add_del_v4( + namespace_id="0", sw_if_index=self.loop0.sw_if_index, is_add=0 + ) + + tbl.remove_vpp_config() + tbl6.remove_vpp_config() + @tag_fixme_vpp_workers class TestSessionUnitTests(VppAsfTestCase):