+/*
+ * Test Path Preference
+ */
+static int
+fib_test_pref (void)
+{
+ test_main_t *tm = &test_main;
+
+ const fib_prefix_t pfx_1_1_1_1_s_32 = {
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr = {
+ .ip4 = {
+ .as_u32 = clib_host_to_net_u32(0x01010101),
+ },
+ },
+ };
+
+ /*
+ * 2 high, 2 medium and 2 low preference non-recursive paths
+ */
+ fib_route_path_t nr_path_hi_1 = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_sw_if_index = tm->hw[0]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 1,
+ .frp_preference = 0,
+ .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
+ .frp_addr = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
+ },
+ };
+ fib_route_path_t nr_path_hi_2 = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_sw_if_index = tm->hw[0]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 1,
+ .frp_preference = 0,
+ .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
+ .frp_addr = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02),
+ },
+ };
+ fib_route_path_t nr_path_med_1 = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_sw_if_index = tm->hw[1]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 1,
+ .frp_preference = 1,
+ .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
+ .frp_addr = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0c01),
+ },
+ };
+ fib_route_path_t nr_path_med_2 = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_sw_if_index = tm->hw[1]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 1,
+ .frp_preference = 1,
+ .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
+ .frp_addr = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0c01),
+ },
+ };
+ fib_route_path_t nr_path_low_1 = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_sw_if_index = tm->hw[2]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 1,
+ .frp_preference = 2,
+ .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
+ .frp_addr = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0b01),
+ },
+ };
+ fib_route_path_t nr_path_low_2 = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_sw_if_index = tm->hw[2]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 1,
+ .frp_preference = 2,
+ .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
+ .frp_addr = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0b02),
+ },
+ };
+ fib_route_path_t *nr_paths = NULL;
+
+ vec_add1(nr_paths, nr_path_hi_1);
+ vec_add1(nr_paths, nr_path_hi_2);
+ vec_add1(nr_paths, nr_path_med_1);
+ vec_add1(nr_paths, nr_path_med_2);
+ vec_add1(nr_paths, nr_path_low_1);
+ vec_add1(nr_paths, nr_path_low_2);
+
+ adj_index_t ai_hi_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &nr_path_hi_1.frp_addr,
+ nr_path_hi_1.frp_sw_if_index);
+ adj_index_t ai_hi_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &nr_path_hi_2.frp_addr,
+ nr_path_hi_2.frp_sw_if_index);
+ adj_index_t ai_med_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &nr_path_med_1.frp_addr,
+ nr_path_med_1.frp_sw_if_index);
+ adj_index_t ai_med_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &nr_path_med_2.frp_addr,
+ nr_path_med_2.frp_sw_if_index);
+ adj_index_t ai_low_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &nr_path_low_1.frp_addr,
+ nr_path_low_1.frp_sw_if_index);
+ adj_index_t ai_low_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &nr_path_low_2.frp_addr,
+ nr_path_low_2.frp_sw_if_index);
+
+ fib_test_lb_bucket_t ip_hi_1 = {
+ .type = FT_LB_ADJ,
+ .adj = {
+ .adj = ai_hi_1,
+ },
+ };
+ fib_test_lb_bucket_t ip_hi_2 = {
+ .type = FT_LB_ADJ,
+ .adj = {
+ .adj = ai_hi_2,
+ },
+ };
+ fib_test_lb_bucket_t ip_med_1 = {
+ .type = FT_LB_ADJ,
+ .adj = {
+ .adj = ai_med_1,
+ },
+ };
+ fib_test_lb_bucket_t ip_med_2 = {
+ .type = FT_LB_ADJ,
+ .adj = {
+ .adj = ai_med_2,
+ },
+ };
+ fib_test_lb_bucket_t ip_low_1 = {
+ .type = FT_LB_ADJ,
+ .adj = {
+ .adj = ai_low_1,
+ },
+ };
+ fib_test_lb_bucket_t ip_low_2 = {
+ .type = FT_LB_ADJ,
+ .adj = {
+ .adj = ai_low_2,
+ },
+ };
+
+ fib_node_index_t fei;
+
+ fei = fib_table_entry_path_add2(0,
+ &pfx_1_1_1_1_s_32,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ nr_paths);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 2,
+ &ip_hi_1,
+ &ip_hi_2),
+ "1.1.1.1/32 via high preference paths");
+
+ /*
+ * bring down the interface on which the high preference path lie
+ */
+ vnet_sw_interface_set_flags(vnet_get_main(),
+ tm->hw[0]->sw_if_index,
+ 0);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 2,
+ &ip_med_1,
+ &ip_med_2),
+ "1.1.1.1/32 via medium preference paths");
+
+ /*
+ * bring down the interface on which the medium preference path lie
+ */
+ vnet_sw_interface_set_flags(vnet_get_main(),
+ tm->hw[1]->sw_if_index,
+ 0);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 2,
+ &ip_low_1,
+ &ip_low_2),
+ "1.1.1.1/32 via low preference paths");
+
+ /*
+ * bring up the interface on which the high preference path lie
+ */
+ vnet_sw_interface_set_flags(vnet_get_main(),
+ tm->hw[0]->sw_if_index,
+ VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 2,
+ &ip_hi_1,
+ &ip_hi_2),
+ "1.1.1.1/32 via high preference paths");
+
+ /*
+ * bring up the interface on which the medium preference path lie
+ */
+ vnet_sw_interface_set_flags(vnet_get_main(),
+ tm->hw[1]->sw_if_index,
+ VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 2,
+ &ip_hi_1,
+ &ip_hi_2),
+ "1.1.1.1/32 via high preference paths");
+
+ dpo_id_t ip_1_1_1_1 = DPO_INVALID;
+ fib_entry_contribute_forwarding(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ &ip_1_1_1_1);
+
+ /*
+ * 3 recursive paths of different preference
+ */
+ const fib_prefix_t pfx_1_1_1_2_s_32 = {
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr = {
+ .ip4 = {
+ .as_u32 = clib_host_to_net_u32(0x01010102),
+ },
+ },
+ };
+ const fib_prefix_t pfx_1_1_1_3_s_32 = {
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr = {
+ .ip4 = {
+ .as_u32 = clib_host_to_net_u32(0x01010103),
+ },
+ },
+ };
+ fei = fib_table_entry_path_add2(0,
+ &pfx_1_1_1_2_s_32,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ nr_paths);
+ dpo_id_t ip_1_1_1_2 = DPO_INVALID;
+ fib_entry_contribute_forwarding(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ &ip_1_1_1_2);
+ fei = fib_table_entry_path_add2(0,
+ &pfx_1_1_1_3_s_32,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ nr_paths);
+ dpo_id_t ip_1_1_1_3 = DPO_INVALID;
+ fib_entry_contribute_forwarding(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ &ip_1_1_1_3);
+
+ fib_test_lb_bucket_t ip_o_1_1_1_1 = {
+ .type = FT_LB_O_LB,
+ .lb = {
+ .lb = ip_1_1_1_1.dpoi_index,
+ },
+ };
+ fib_test_lb_bucket_t ip_o_1_1_1_2 = {
+ .type = FT_LB_O_LB,
+ .lb = {
+ .lb = ip_1_1_1_2.dpoi_index,
+ },
+ };
+ fib_test_lb_bucket_t ip_o_1_1_1_3 = {
+ .type = FT_LB_O_LB,
+ .lb = {
+ .lb = ip_1_1_1_3.dpoi_index,
+ },
+ };
+ fib_route_path_t r_path_hi = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_sw_if_index = ~0,
+ .frp_fib_index = 0,
+ .frp_weight = 1,
+ .frp_preference = 0,
+ .frp_flags = FIB_ROUTE_PATH_RESOLVE_VIA_HOST,
+ .frp_addr = pfx_1_1_1_1_s_32.fp_addr,
+ };
+ fib_route_path_t r_path_med = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_sw_if_index = ~0,
+ .frp_fib_index = 0,
+ .frp_weight = 1,
+ .frp_preference = 10,
+ .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
+ .frp_addr = pfx_1_1_1_2_s_32.fp_addr,
+ };
+ fib_route_path_t r_path_low = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_sw_if_index = ~0,
+ .frp_fib_index = 0,
+ .frp_weight = 1,
+ .frp_preference = 255,
+ .frp_flags = FIB_ROUTE_PATH_RESOLVE_VIA_HOST,
+ .frp_addr = pfx_1_1_1_3_s_32.fp_addr,
+ };
+ fib_route_path_t *r_paths = NULL;
+
+ vec_add1(r_paths, r_path_hi);
+ vec_add1(r_paths, r_path_low);
+ vec_add1(r_paths, r_path_med);
+
+ /*
+ * add many recursive so we get the LB MAp created
+ */
+ #define N_PFXS 64
+ fib_prefix_t pfx_r[N_PFXS];
+ unsigned int n_pfxs;
+ for (n_pfxs = 0; n_pfxs < N_PFXS; n_pfxs++)
+ {
+ pfx_r[n_pfxs].fp_len = 32;
+ pfx_r[n_pfxs].fp_proto = FIB_PROTOCOL_IP4;
+ pfx_r[n_pfxs].fp_addr.ip4.as_u32 =
+ clib_host_to_net_u32(0x02000000 + n_pfxs);
+
+ fei = fib_table_entry_path_add2(0,
+ &pfx_r[n_pfxs],
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ r_paths);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &ip_o_1_1_1_1),
+ "recursive via high preference paths");
+
+ /*
+ * withdraw hig pref resolving entry
+ */
+ fib_table_entry_delete(0,
+ &pfx_1_1_1_1_s_32,
+ FIB_SOURCE_API);
+
+ /* suspend so the update walk kicks int */
+ vlib_process_suspend(vlib_get_main(), 1e-5);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &ip_o_1_1_1_2),
+ "recursive via medium preference paths");
+
+ /*
+ * withdraw medium pref resolving entry
+ */
+ fib_table_entry_delete(0,
+ &pfx_1_1_1_2_s_32,
+ FIB_SOURCE_API);
+
+ /* suspend so the update walk kicks int */
+ vlib_process_suspend(vlib_get_main(), 1e-5);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &ip_o_1_1_1_3),
+ "recursive via low preference paths");
+
+ /*
+ * add back paths for next iteration
+ */
+ fei = fib_table_entry_update(0,
+ &pfx_1_1_1_2_s_32,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ nr_paths);
+ fei = fib_table_entry_update(0,
+ &pfx_1_1_1_1_s_32,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ nr_paths);
+
+ /* suspend so the update walk kicks int */
+ vlib_process_suspend(vlib_get_main(), 1e-5);
+
+ fei = fib_table_lookup_exact_match(0, &pfx_r[n_pfxs]);
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &ip_o_1_1_1_1),
+ "recursive via high preference paths");
+ }
+
+
+ fib_table_entry_delete(0,
+ &pfx_1_1_1_1_s_32,
+ FIB_SOURCE_API);
+
+ /* suspend so the update walk kicks int */
+ vlib_process_suspend(vlib_get_main(), 1e-5);
+
+ for (n_pfxs = 0; n_pfxs < N_PFXS; n_pfxs++)
+ {
+ fei = fib_table_lookup_exact_match(0, &pfx_r[n_pfxs]);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &ip_o_1_1_1_2),
+ "recursive via medium preference paths");
+ }
+ for (n_pfxs = 0; n_pfxs < N_PFXS; n_pfxs++)
+ {
+ fib_table_entry_delete(0,
+ &pfx_r[n_pfxs],
+ FIB_SOURCE_API);
+ }
+
+ /*
+ * Cleanup
+ */
+ fib_table_entry_delete(0,
+ &pfx_1_1_1_2_s_32,
+ FIB_SOURCE_API);
+ fib_table_entry_delete(0,
+ &pfx_1_1_1_3_s_32,
+ FIB_SOURCE_API);
+
+ dpo_reset(&ip_1_1_1_1);
+ dpo_reset(&ip_1_1_1_2);
+ dpo_reset(&ip_1_1_1_3);
+ adj_unlock(ai_low_2);
+ adj_unlock(ai_low_1);
+ adj_unlock(ai_med_2);
+ adj_unlock(ai_med_1);
+ adj_unlock(ai_hi_2);
+ adj_unlock(ai_hi_1);
+ return (0);
+}