diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f2149a..4e47d1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ add_vpp_library(lcpng lcpng_interface.c lcpng_adj.c lcpng_if_sync.c + lcpng_mpls_sync.c lcpng.c LINK_LIBRARIES diff --git a/lcpng.c b/lcpng.c index 6432dd0..3b102b5 100644 --- a/lcpng.c +++ b/lcpng.c @@ -23,7 +23,9 @@ lcp_main_t lcp_main; -u8 *lcp_get_default_ns(void) { +u8 * +lcp_get_default_ns (void) +{ lcp_main_t *lcpm = &lcp_main; if (!lcpm->default_namespace || lcpm->default_namespace[0] == 0) @@ -31,7 +33,9 @@ u8 *lcp_get_default_ns(void) { return lcpm->default_namespace; } -int lcp_get_default_ns_fd(void) { +int +lcp_get_default_ns_fd (void) +{ lcp_main_t *lcpm = &lcp_main; return lcpm->default_ns_fd; @@ -40,7 +44,9 @@ int lcp_get_default_ns_fd(void) { /* * ns is expected to be or look like a NUL-terminated C string. */ -int lcp_set_default_ns(u8 *ns) { +int +lcp_set_default_ns (u8 *ns) +{ lcp_main_t *lcpm = &lcp_main; char *p; int len; diff --git a/lcpng.h b/lcpng.h index 8917db3..cf51411 100644 --- a/lcpng.h +++ b/lcpng.h @@ -21,8 +21,8 @@ typedef struct lcp_main_s { - u16 msg_id_base; /* API message ID base */ - u8 *default_namespace; /* default namespace if set */ + u16 msg_id_base; /* API message ID base */ + u8 *default_namespace; /* default namespace if set */ int default_ns_fd; u8 lcp_auto_subint; /* Automatically create/delete LCP sub-interfaces */ u8 lcp_sync; /* Automatically sync VPP changes to LCP */ @@ -35,9 +35,9 @@ extern lcp_main_t lcp_main; /** * Get/Set the default namespace for LCP host taps. */ -int lcp_set_default_ns(u8 *ns); -u8 *lcp_get_default_ns(void); /* Returns NULL or shared string */ -int lcp_get_default_ns_fd(void); +int lcp_set_default_ns (u8 *ns); +u8 *lcp_get_default_ns (void); /* Returns NULL or shared string */ +int lcp_get_default_ns_fd (void); /* * Sync state from VPP into all LCP devices diff --git a/lcpng_adj.c b/lcpng_adj.c index 0b8aa77..2d7bf6c 100644 --- a/lcpng_adj.c +++ b/lcpng_adj.c @@ -185,17 +185,17 @@ lcp_adj_show_cmd (vlib_main_t *vm, unformat_input_t *input, if (unformat (input, "verbose")) verbose = 1; - vlib_cli_output(vm, "lcp Adjs:\n%U", BV(format_bihash), &lcp_adj_tbl, - verbose); + vlib_cli_output (vm, "lcp Adjs:\n%U", BV (format_bihash), &lcp_adj_tbl, + verbose); return 0; } -VLIB_CLI_COMMAND(lcp_itf_pair_show_cmd_node, static) = { - .path = "show lcp adj", - .function = lcp_adj_show_cmd, - .short_help = "show lcp adj", - .is_mp_safe = 1, +VLIB_CLI_COMMAND (lcp_itf_pair_show_cmd_node, static) = { + .path = "show lcp adj", + .function = lcp_adj_show_cmd, + .short_help = "show lcp adj", + .is_mp_safe = 1, }; const adj_delegate_vft_t lcp_adj_vft = { @@ -210,7 +210,7 @@ lcp_adj_init (vlib_main_t *vm) { adj_type = adj_delegate_register_new_type (&lcp_adj_vft); - BV(clib_bihash_init)(&lcp_adj_tbl, "lcp ADJ table", 1024, 1 << 24); + BV (clib_bihash_init) (&lcp_adj_tbl, "lcp ADJ table", 1024, 1 << 24); BV (clib_bihash_set_kvp_format_fn) (&lcp_adj_tbl, format_lcp_adj_kvp); return (NULL); diff --git a/lcpng_if.api b/lcpng_if.api index 059ce64..ddd299b 100644 --- a/lcpng_if.api +++ b/lcpng_if.api @@ -30,7 +30,7 @@ autoreply define lcp_default_ns_set { u32 client_index; u32 context; - string netns[32]; /* LCP_NS_LEN */ + string netns[32]; /* LCP_NS_LEN */ }; /** \brief get the default Linux Control Plane netns @@ -51,7 +51,7 @@ define lcp_default_ns_get define lcp_default_ns_get_reply { u32 context; - string netns[32]; /* LCP_NS_LEN */ + string netns[32]; /* LCP_NS_LEN */ }; enum lcp_itf_host_type : u8 @@ -75,9 +75,9 @@ autoreply autoendian define lcp_itf_pair_add_del u32 context; bool is_add; vl_api_interface_index_t sw_if_index; - string host_if_name[16]; /* IFNAMSIZ */ + string host_if_name[16]; /* IFNAMSIZ */ vl_api_lcp_itf_host_type_t host_if_type; - string netns[32]; /* LCP_NS_LEN */ + string netns[32]; /* LCP_NS_LEN */ }; autoendian define lcp_itf_pair_add_del_v2 { @@ -85,9 +85,9 @@ autoendian define lcp_itf_pair_add_del_v2 u32 context; bool is_add; vl_api_interface_index_t sw_if_index; - string host_if_name[16]; /* IFNAMSIZ */ + string host_if_name[16]; /* IFNAMSIZ */ vl_api_lcp_itf_host_type_t host_if_type; - string netns[32]; /* LCP_NS_LEN */ + string netns[32]; /* LCP_NS_LEN */ }; define lcp_itf_pair_add_del_v2_reply { @@ -129,14 +129,15 @@ autoendian define lcp_itf_pair_details vl_api_interface_index_t phy_sw_if_index; vl_api_interface_index_t host_sw_if_index; u32 vif_index; - string host_if_name[16]; /* IFNAMSIZ */ + string host_if_name[16]; /* IFNAMSIZ */ vl_api_lcp_itf_host_type_t host_if_type; - string netns[32]; /* LCP_NS_LEN */ + string netns[32]; /* LCP_NS_LEN */ }; -service { - rpc lcp_itf_pair_get returns lcp_itf_pair_get_reply - stream lcp_itf_pair_details; +service +{ + rpc lcp_itf_pair_get returns lcp_itf_pair_get_reply stream + lcp_itf_pair_details; }; /** \brief Replace end/begin @@ -155,14 +156,17 @@ autoreply define lcp_itf_pair_replace_end /* * Linux-CP Error counters/messages */ -counters linuxcp { - packets { +counters linuxcp +{ + packets + { severity info; type counter64; units "packets"; description "ARP packets processed"; }; - copies { + copies + { severity info; type counter64; units "packets"; @@ -170,9 +174,12 @@ counters linuxcp { }; }; -paths { - "/err/linux-cp-arp-phy" "linuxcp"; - "/err/linux-cp-arp-host" "linuxcp"; +paths +{ + "/err/linux-cp-arp-phy" + "linuxcp"; + "/err/linux-cp-arp-host" + "linuxcp"; }; /* diff --git a/lcpng_if_api.c b/lcpng_if_api.c index 747cbee..58c46f6 100644 --- a/lcpng_if_api.c +++ b/lcpng_if_api.c @@ -162,18 +162,20 @@ vl_api_lcp_itf_pair_get_t_handler (vl_api_lcp_itf_pair_get_t *mp) } static void -vl_api_lcp_default_ns_set_t_handler(vl_api_lcp_default_ns_set_t *mp) { +vl_api_lcp_default_ns_set_t_handler (vl_api_lcp_default_ns_set_t *mp) +{ vl_api_lcp_default_ns_set_reply_t *rmp; int rv; mp->netns[LCP_NS_LEN - 1] = 0; rv = lcp_set_default_ns (mp->netns); - REPLY_MACRO(VL_API_LCP_DEFAULT_NS_SET_REPLY); + REPLY_MACRO (VL_API_LCP_DEFAULT_NS_SET_REPLY); } static void -vl_api_lcp_default_ns_get_t_handler(vl_api_lcp_default_ns_get_t *mp) { +vl_api_lcp_default_ns_get_t_handler (vl_api_lcp_default_ns_get_t *mp) +{ vl_api_lcp_default_ns_get_reply_t *rmp; vl_api_registration_t *reg; char *ns; diff --git a/lcpng_if_cli.c b/lcpng_if_cli.c index d237092..beab982 100644 --- a/lcpng_if_cli.c +++ b/lcpng_if_cli.c @@ -99,16 +99,16 @@ lcp_itf_pair_create_command_fn (vlib_main_t *vm, unformat_input_t *input, vec_free (ns); if (r) - return clib_error_return(0, "lcp pair creation failed (%d)", r); + return clib_error_return (0, "lcp pair creation failed (%d)", r); return 0; } -VLIB_CLI_COMMAND(lcp_itf_pair_create_command, static) = { - .path = "lcp create", - .short_help = "lcp create | host-if " - "netns [tun]", - .function = lcp_itf_pair_create_command_fn, +VLIB_CLI_COMMAND (lcp_itf_pair_create_command, static) = { + .path = "lcp create", + .short_help = "lcp create | host-if " + "netns [tun]", + .function = lcp_itf_pair_create_command_fn, }; static clib_error_t * @@ -173,39 +173,41 @@ VLIB_CLI_COMMAND (lcp_auto_subint_command, static) = { .function = lcp_auto_subint_command_fn, }; -static clib_error_t *lcp_default_netns_command_fn(vlib_main_t *vm, - unformat_input_t *input, - vlib_cli_command_t *cmd) { +static clib_error_t * +lcp_default_netns_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ unformat_input_t _line_input, *line_input = &_line_input; u8 *ns; int r; clib_error_t *error = NULL; - if (!unformat_user(input, unformat_line_input, line_input)) + if (!unformat_user (input, unformat_line_input, line_input)) return 0; ns = 0; - while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) { - if (unformat(line_input, "netns %s", &ns)) - ; - else if (unformat(line_input, "clear netns")) - ; - else - { - vec_free (ns); - error = clib_error_return (0, "unknown input `%U'", - format_unformat_error, line_input); - goto done; - } - } + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "netns %s", &ns)) + ; + else if (unformat (line_input, "clear netns")) + ; + else + { + vec_free (ns); + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + goto done; + } + } - vlib_cli_output(vm, "lcp set default netns %v\n", ns); + vlib_cli_output (vm, "lcp set default netns %v\n", ns); - r = lcp_set_default_ns(ns); + r = lcp_set_default_ns (ns); if (r) - return clib_error_return(0, "linux-cp set default netns failed (%d)", r); + return clib_error_return (0, "linux-cp set default netns failed (%d)", r); done: unformat_free (line_input); @@ -213,10 +215,10 @@ done: return error; } -VLIB_CLI_COMMAND(lcp_default_netns_command, static) = { - .path = "lcp default", - .short_help = "lcp default netns []", - .function = lcp_default_netns_command_fn, +VLIB_CLI_COMMAND (lcp_default_netns_command, static) = { + .path = "lcp default", + .short_help = "lcp default netns []", + .function = lcp_default_netns_command_fn, }; static clib_error_t * @@ -253,14 +255,14 @@ lcp_itf_pair_delete_command_fn (vlib_main_t *vm, unformat_input_t *input, r = lcp_itf_pair_delete (sw_if_index); if (r) - return clib_error_return(0, "lcp pair deletion failed (%d)", r); + return clib_error_return (0, "lcp pair deletion failed (%d)", r); return 0; } -VLIB_CLI_COMMAND(lcp_itf_pair_delete_command, static) = { - .path = "lcp delete", - .short_help = "lcp delete |", - .function = lcp_itf_pair_delete_command_fn, +VLIB_CLI_COMMAND (lcp_itf_pair_delete_command, static) = { + .path = "lcp delete", + .short_help = "lcp delete |", + .function = lcp_itf_pair_delete_command_fn, }; static clib_error_t * @@ -287,11 +289,11 @@ lcp_itf_pair_show_cmd (vlib_main_t *vm, unformat_input_t *input, return 0; } -VLIB_CLI_COMMAND(lcp_itf_pair_show_cmd_node, static) = { - .path = "show lcp", - .function = lcp_itf_pair_show_cmd, - .short_help = "show lcp [phy ]", - .is_mp_safe = 1, +VLIB_CLI_COMMAND (lcp_itf_pair_show_cmd_node, static) = { + .path = "show lcp", + .function = lcp_itf_pair_show_cmd, + .short_help = "show lcp [phy ]", + .is_mp_safe = 1, }; clib_error_t * diff --git a/lcpng_if_node.c b/lcpng_if_node.c index 1095576..e1d674b 100644 --- a/lcpng_if_node.c +++ b/lcpng_if_node.c @@ -181,7 +181,7 @@ format_lcp_punt_l3_trace (u8 *s, va_list *args) CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); lcp_punt_l3_trace_t *t = va_arg (*args, lcp_punt_l3_trace_t *); - s = format(s, "linux-cp-punt-l3: %u", t->phy_sw_if_index); + s = format (s, "linux-cp-punt-l3: %u", t->phy_sw_if_index); return s; } @@ -261,16 +261,16 @@ VLIB_REGISTER_NODE(lcp_punt_l3_node) = { }, }; -VNET_FEATURE_INIT(lcp_punt_l3_ip4, static) = { - .arc_name = "ip4-punt", - .node_name = "linux-cp-punt-l3", - .runs_before = VNET_FEATURES("ip4-punt-redirect"), +VNET_FEATURE_INIT (lcp_punt_l3_ip4, static) = { + .arc_name = "ip4-punt", + .node_name = "linux-cp-punt-l3", + .runs_before = VNET_FEATURES ("ip4-punt-redirect"), }; -VNET_FEATURE_INIT(lip_punt_l3_ip6, static) = { - .arc_name = "ip6-punt", - .node_name = "linux-cp-punt-l3", - .runs_before = VNET_FEATURES("ip6-punt-redirect"), +VNET_FEATURE_INIT (lip_punt_l3_ip6, static) = { + .arc_name = "ip6-punt", + .node_name = "linux-cp-punt-l3", + .runs_before = VNET_FEATURES ("ip6-punt-redirect"), }; #define foreach_lcp_xc \ @@ -408,34 +408,131 @@ VLIB_NODE_FN (lcp_xc_ip6) return (lcp_xc_inline (vm, node, frame, AF_IP6)); } -VLIB_REGISTER_NODE(lcp_xc_ip4) = {.name = "linux-cp-xc-ip4", - .vector_size = sizeof(u32), - .format_trace = format_lcp_xc_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .sibling_of = "ip4-rewrite"}; +VLIB_REGISTER_NODE (lcp_xc_ip4) = { .name = "linux-cp-xc-ip4", + .vector_size = sizeof (u32), + .format_trace = format_lcp_xc_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .sibling_of = "ip4-rewrite" }; -VNET_FEATURE_INIT(lcp_xc_ip4_ucast_node, static) = { - .arc_name = "ip4-unicast", - .node_name = "linux-cp-xc-ip4", +VNET_FEATURE_INIT (lcp_xc_ip4_ucast_node, static) = { + .arc_name = "ip4-unicast", + .node_name = "linux-cp-xc-ip4", }; -VNET_FEATURE_INIT(lcp_xc_ip4_mcast_node, static) = { - .arc_name = "ip4-multicast", - .node_name = "linux-cp-xc-ip4", +VNET_FEATURE_INIT (lcp_xc_ip4_mcast_node, static) = { + .arc_name = "ip4-multicast", + .node_name = "linux-cp-xc-ip4", }; -VLIB_REGISTER_NODE(lcp_xc_ip6) = {.name = "linux-cp-xc-ip6", - .vector_size = sizeof(u32), - .format_trace = format_lcp_xc_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .sibling_of = "ip6-rewrite"}; +VLIB_REGISTER_NODE (lcp_xc_ip6) = { .name = "linux-cp-xc-ip6", + .vector_size = sizeof (u32), + .format_trace = format_lcp_xc_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .sibling_of = "ip6-rewrite" }; -VNET_FEATURE_INIT(lcp_xc_ip6_ucast_node, static) = { - .arc_name = "ip6-unicast", - .node_name = "linux-cp-xc-ip6", +VNET_FEATURE_INIT (lcp_xc_ip6_ucast_node, static) = { + .arc_name = "ip6-unicast", + .node_name = "linux-cp-xc-ip6", }; -VNET_FEATURE_INIT(lcp_xc_ip6_mcast_node, static) = { - .arc_name = "ip6-multicast", - .node_name = "linux-cp-xc-ip6", +VNET_FEATURE_INIT (lcp_xc_ip6_mcast_node, static) = { + .arc_name = "ip6-multicast", + .node_name = "linux-cp-xc-ip6", +}; + +typedef enum +{ + LCP_XC_MPLS_NEXT_DROP, + LCP_XC_MPLS_NEXT_IO, + LCP_XC_MPLS_N_NEXT, +} lcp_xc_mpls_next_t; + +static_always_inline uword +lcp_xc_mpls_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + u32 n_left_from, *from, *to_next, n_left_to_next; + lcp_xc_next_t next_index; + + next_index = 0; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + const ethernet_header_t *eth; + const lcp_itf_pair_t *lip; + u32 next0, bi0, lipi, ai; + vlib_buffer_t *b0; + // const ip_adjacency_t *adj; + + bi0 = to_next[0] = from[0]; + + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + lipi = + lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]); + lip = lcp_itf_pair_get (lipi); + + vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip->lip_phy_sw_if_index; + vlib_buffer_advance (b0, -lip->lip_rewrite_len); + eth = vlib_buffer_get_current (b0); + + ai = ADJ_INDEX_INVALID; + next0 = LCP_XC_MPLS_NEXT_DROP; + if (!ethernet_address_cast (eth->dst_address)) + ai = lcp_adj_lkup ((u8 *) eth, lip->lip_rewrite_len, + vnet_buffer (b0)->sw_if_index[VLIB_TX]); + if (ai != ADJ_INDEX_INVALID) + { + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai; + next0 = LCP_XC_MPLS_NEXT_IO; + } + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + lcp_xc_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->phy_sw_if_index = lip->lip_phy_sw_if_index; + t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +VLIB_NODE_FN (lcp_xc_mpls) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return (lcp_xc_mpls_inline (vm, node, frame)); +} + +VLIB_REGISTER_NODE ( + lcp_xc_mpls) = { .name = "linux-cp-xc-mpls", + .vector_size = sizeof (u32), + .format_trace = format_lcp_xc_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_next_nodes = LCP_XC_MPLS_N_NEXT, + .next_nodes = { + [LCP_XC_MPLS_NEXT_DROP] = "error-drop", + [LCP_XC_MPLS_NEXT_IO] = "interface-output", + } }; + +VNET_FEATURE_INIT (lcp_xc_mpls_node, static) = { + .arc_name = "mpls-input", + .node_name = "linux-cp-xc-mpls", }; typedef enum @@ -557,14 +654,14 @@ VLIB_REGISTER_NODE(lcp_xc_l3_ip4_node) = { }, }; -VNET_FEATURE_INIT(lcp_xc_node_l3_ip4_unicast, static) = { - .arc_name = "ip4-unicast", - .node_name = "linux-cp-xc-l3-ip4", +VNET_FEATURE_INIT (lcp_xc_node_l3_ip4_unicast, static) = { + .arc_name = "ip4-unicast", + .node_name = "linux-cp-xc-l3-ip4", }; -VNET_FEATURE_INIT(lcp_xc_node_l3_ip4_multicaast, static) = { - .arc_name = "ip4-multicast", - .node_name = "linux-cp-xc-l3-ip4", +VNET_FEATURE_INIT (lcp_xc_node_l3_ip4_multicaast, static) = { + .arc_name = "ip4-multicast", + .node_name = "linux-cp-xc-l3-ip4", }; VLIB_REGISTER_NODE(lcp_xc_l3_ip6_node) = { @@ -581,14 +678,14 @@ VLIB_REGISTER_NODE(lcp_xc_l3_ip6_node) = { }, }; -VNET_FEATURE_INIT(lcp_xc_node_l3_ip6_unicast, static) = { - .arc_name = "ip6-unicast", - .node_name = "linux-cp-xc-l3-ip6", +VNET_FEATURE_INIT (lcp_xc_node_l3_ip6_unicast, static) = { + .arc_name = "ip6-unicast", + .node_name = "linux-cp-xc-l3-ip6", }; -VNET_FEATURE_INIT(lcp_xc_node_l3_ip6_multicast, static) = { - .arc_name = "ip6-multicast", - .node_name = "linux-cp-xc-l3-ip6", +VNET_FEATURE_INIT (lcp_xc_node_l3_ip6_multicast, static) = { + .arc_name = "ip6-multicast", + .node_name = "linux-cp-xc-l3-ip6", }; #define foreach_lcp_arp \ @@ -850,10 +947,10 @@ VLIB_REGISTER_NODE(lcp_arp_phy_node) = { }, }; -VNET_FEATURE_INIT(lcp_arp_phy_arp_feat, static) = { - .arc_name = "arp", - .node_name = "linux-cp-arp-phy", - .runs_before = VNET_FEATURES("arp-reply"), +VNET_FEATURE_INIT (lcp_arp_phy_arp_feat, static) = { + .arc_name = "arp", + .node_name = "linux-cp-arp-phy", + .runs_before = VNET_FEATURES ("arp-reply"), }; /** @@ -935,10 +1032,10 @@ VLIB_REGISTER_NODE(lcp_arp_host_node) = { }, }; -VNET_FEATURE_INIT(lcp_arp_host_arp_feat, static) = { - .arc_name = "arp", - .node_name = "linux-cp-arp-host", - .runs_before = VNET_FEATURES("arp-reply"), +VNET_FEATURE_INIT (lcp_arp_host_arp_feat, static) = { + .arc_name = "arp", + .node_name = "linux-cp-arp-host", + .runs_before = VNET_FEATURES ("arp-reply"), }; /* diff --git a/lcpng_if_sync.c b/lcpng_if_sync.c index 4d84570..43bbb0a 100644 --- a/lcpng_if_sync.c +++ b/lcpng_if_sync.c @@ -170,7 +170,7 @@ lcp_itf_pair_sync_state_hw (vnet_hw_interface_t *hi) } static clib_error_t * -lcp_itf_admin_state_change (vnet_main_t * vnm, u32 sw_if_index, u32 flags) +lcp_itf_admin_state_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags) { lcp_itf_pair_t *lip; vnet_hw_interface_t *hi; @@ -184,7 +184,8 @@ lcp_itf_admin_state_change (vnet_main_t * vnm, u32 sw_if_index, u32 flags) // Sync interface state changes into host lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index)); - if (!lip) return NULL; + if (!lip) + return NULL; LCP_IF_INFO ("admin_state_change: %U flags %u", format_lcp_itf_pair, lip, flags); @@ -213,9 +214,9 @@ lcp_itf_admin_state_change (vnet_main_t * vnm, u32 sw_if_index, u32 flags) lcp_itf_pair_sync_state_hw (hi); return NULL; -} +} -VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(lcp_itf_admin_state_change); +VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (lcp_itf_admin_state_change); static clib_error_t * lcp_itf_mtu_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags) diff --git a/lcpng_interface.c b/lcpng_interface.c index c910743..a93d770 100644 --- a/lcpng_interface.c +++ b/lcpng_interface.c @@ -128,7 +128,7 @@ lcp_itf_pair_show (u32 phy_sw_if_index) index_t api; vm = vlib_get_main (); - ns = lcp_get_default_ns(); + ns = lcp_get_default_ns (); vlib_cli_output (vm, "lcp default netns %v\n", ns); vlib_cli_output (vm, "lcp lcp-auto-subint %s\n", lcp_auto_subint () ? "on" : "off"); @@ -149,8 +149,10 @@ lcp_itf_pair_show (u32 phy_sw_if_index) lcp_itf_pair_t * lcp_itf_pair_get (u32 index) { - if (!lcp_itf_pair_pool) return NULL; - if (index == INDEX_INVALID) return NULL; + if (!lcp_itf_pair_pool) + return NULL; + if (index == INDEX_INVALID) + return NULL; return pool_elt_at_index (lcp_itf_pair_pool, index); } @@ -230,15 +232,17 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name, if (lipi != INDEX_INVALID) return VNET_API_ERROR_VALUE_EXIST; - if (host_sw_if_index == ~0) { + if (host_sw_if_index == ~0) + { LCP_IF_ERROR ("pair_add: Cannot add LIP - invalid host"); return VNET_API_ERROR_INVALID_SW_IF_INDEX; - } + } - if (phy_sw_if_index == ~0) { + if (phy_sw_if_index == ~0) + { LCP_IF_ERROR ("pair_add: Cannot add LIP - invalid phy"); return VNET_API_ERROR_INVALID_SW_IF_INDEX; - } + } LCP_IF_NOTICE ( "pair_add: Adding LIP for host:%U phy:%U host_if:%v vif:%d netns:%v", @@ -278,6 +282,10 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name, lcp_itf_l3_feat_names[lip->lip_host_type][af], lip->lip_host_sw_if_index, 1, NULL, 0); + /* Enable MPLS */ + vnet_feature_enable_disable ("mpls-input", "linux-cp-xc-mpls", + lip->lip_host_sw_if_index, 1, NULL, 0); + /* * Configure passive punt to the host interface. */ @@ -304,10 +312,10 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name, /* enable ARP feature node for broadcast interfaces */ if (lip->lip_host_type != LCP_ITF_HOST_TUN) { - vnet_feature_enable_disable("arp", "linux-cp-arp-phy", - lip->lip_phy_sw_if_index, 1, NULL, 0); - vnet_feature_enable_disable("arp", "linux-cp-arp-host", - lip->lip_host_sw_if_index, 1, NULL, 0); + vnet_feature_enable_disable ("arp", "linux-cp-arp-phy", + lip->lip_phy_sw_if_index, 1, NULL, 0); + vnet_feature_enable_disable ("arp", "linux-cp-arp-host", + lip->lip_host_sw_if_index, 1, NULL, 0); } else { @@ -343,11 +351,12 @@ lcp_netlink_add_link_vlan (int parent, u32 vlan, u16 proto, const char *name) int err; sk = nl_socket_alloc (); - if ((err = nl_connect (sk, NETLINK_ROUTE)) < 0) { + if ((err = nl_connect (sk, NETLINK_ROUTE)) < 0) + { LCP_IF_ERROR ("netlink_add_link_vlan: Netlink connect error: %s", nl_geterror (err)); return clib_error_return (NULL, "Unable to connect socket: %d", err); - } + } link = rtnl_link_vlan_alloc (); @@ -356,11 +365,12 @@ lcp_netlink_add_link_vlan (int parent, u32 vlan, u16 proto, const char *name) rtnl_link_vlan_set_id (link, vlan); rtnl_link_vlan_set_protocol (link, htons (proto)); - if ((err = rtnl_link_add (sk, link, NLM_F_CREATE)) < 0) { + if ((err = rtnl_link_add (sk, link, NLM_F_CREATE)) < 0) + { LCP_IF_ERROR ("netlink_add_link_vlan: Netlink link add error: %s", nl_geterror (err)); return clib_error_return (NULL, "Unable to add link %s: %d", name, err); - } + } rtnl_link_put (link); nl_close (sk); @@ -424,6 +434,9 @@ lcp_itf_pair_del (u32 phy_sw_if_index) lcp_itf_l3_feat_names[lip->lip_host_type][af], lip->lip_host_sw_if_index, 0, NULL, 0); + vnet_feature_enable_disable ("mpls-input", "linux-cp-xc-mpls", + lip->lip_host_sw_if_index, 0, NULL, 0); + lcp_itf_unset_adjs (lip); ip4_punt_redirect_del (lip->lip_phy_sw_if_index); @@ -432,10 +445,10 @@ lcp_itf_pair_del (u32 phy_sw_if_index) /* disable ARP feature node for broadcast interfaces */ if (lip->lip_host_type != LCP_ITF_HOST_TUN) { - vnet_feature_enable_disable("arp", "linux-cp-arp-phy", - lip->lip_phy_sw_if_index, 0, NULL, 0); - vnet_feature_enable_disable("arp", "linux-cp-arp-host", - lip->lip_host_sw_if_index, 0, NULL, 0); + vnet_feature_enable_disable ("arp", "linux-cp-arp-phy", + lip->lip_phy_sw_if_index, 0, NULL, 0); + vnet_feature_enable_disable ("arp", "linux-cp-arp-host", + lip->lip_host_sw_if_index, 0, NULL, 0); } else { @@ -611,7 +624,8 @@ lcp_itf_set_link_state (const lcp_itf_pair_t *lip, u8 state) vnet_main_t *vnm = vnet_get_main (); int curr_ns_fd, vif_ns_fd; - if (!lip) return; + if (!lip) + return; curr_ns_fd = vif_ns_fd = -1; @@ -769,37 +783,41 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name, const vnet_hw_interface_t *hw; lcp_itf_pair_t *lip; - if (!vnet_sw_if_index_is_api_valid (phy_sw_if_index)) { + if (!vnet_sw_if_index_is_api_valid (phy_sw_if_index)) + { LCP_IF_ERROR ("pair_create: Invalid phy index %u", phy_sw_if_index); return VNET_API_ERROR_INVALID_SW_IF_INDEX; - } + } - if (!lcp_validate_if_name (host_if_name)) { + if (!lcp_validate_if_name (host_if_name)) + { LCP_IF_ERROR ("pair_create: Invalid host-if-name '%s'", host_if_name); return VNET_API_ERROR_INVALID_ARGUMENT; - } + } vnm = vnet_get_main (); sw = vnet_get_sw_interface (vnm, phy_sw_if_index); hw = vnet_get_sup_hw_interface (vnm, phy_sw_if_index); - if (!sw && !hw) { + if (!sw && !hw) + { LCP_IF_ERROR ("pair_create: Invalid interface"); return VNET_API_ERROR_INVALID_SW_IF_INDEX; - } + } if (hw->hw_class_index != ethernet_hw_interface_class.index && - host_if_type == LCP_ITF_HOST_TAP) { + host_if_type == LCP_ITF_HOST_TAP) + { LCP_IF_ERROR ( "pair_create: don't create TAP for non-eth interface; use tun"); return VNET_API_ERROR_INVALID_ARGUMENT; - } + } /* * Use interface-specific netns if supplied. * Otherwise, use netns if defined, otherwise use the OS default. */ if (ns == 0 || ns[0] == 0) - ns = lcp_get_default_ns(); + ns = lcp_get_default_ns (); /* sub interfaces do not need a tap created */ if (vnet_sw_interface_is_sub (vnm, phy_sw_if_index)) @@ -812,16 +830,19 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name, u16 vlan, proto; u32 parent_vif_index; - if (sw->type == VNET_SW_INTERFACE_TYPE_SUB && sw->sub.eth.flags.exact_match == 0) { + if (sw->type == VNET_SW_INTERFACE_TYPE_SUB && + sw->sub.eth.flags.exact_match == 0) + { LCP_IF_ERROR ("pair_create: Cannot create LIP for a " "sub-interface without exact-match set"); return VNET_API_ERROR_INVALID_ARGUMENT; - } + } outer_vlan = sw->sub.eth.outer_vlan_id; inner_vlan = sw->sub.eth.inner_vlan_id; outer_proto = inner_proto = ETH_P_8021Q; - if (1 == sw->sub.eth.flags.dot1ad) outer_proto = ETH_P_8021AD; + if (1 == sw->sub.eth.flags.dot1ad) + outer_proto = ETH_P_8021AD; LCP_IF_INFO ("pair_create: creating dot1%s %d inner-dot1q %d on %U", sw->sub.eth.flags.dot1ad ? "ad" : "q", outer_vlan, @@ -891,7 +912,7 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name, "vlan %d proto %s on %U", outer_vlan, outer_proto == ETH_P_8021AD ? "dot1ad" : - "dot1q", + "dot1q", format_vnet_sw_if_index_name, vnet_get_main (), hw->sw_if_index); return VNET_API_ERROR_INVALID_SW_IF_INDEX; @@ -916,13 +937,14 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name, err = lcp_netlink_add_link_vlan (parent_vif_index, vlan, proto, (const char *) host_if_name); - if (err != 0) { + if (err != 0) + { LCP_IF_ERROR ("pair_create: Cannot create link " "outer(proto:0x%04x,vlan:%u).inner(proto:0x%" "04x,vlan:%u) name:'%s'", outer_proto, outer_vlan, inner_proto, inner_vlan, host_if_name); - } + } if (!err) vif_index = if_nametoindex ((char *) host_if_name); @@ -1190,12 +1212,12 @@ VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (lcp_itf_pair_link_up_down); static clib_error_t * lcp_itf_pair_init (vlib_main_t *vm) { - vlib_punt_hdl_t punt_hdl = vlib_punt_client_register("linux-cp"); + vlib_punt_hdl_t punt_hdl = vlib_punt_client_register ("linux-cp"); lcp_itf_pair_logger = vlib_log_register_class ("linux-cp", "if"); /* punt IKE */ - vlib_punt_register(punt_hdl, ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0], - "linux-cp-punt"); + vlib_punt_register (punt_hdl, ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0], + "linux-cp-punt"); /* punt all unknown ports */ udp_punt_unknown (vm, 0, 1); diff --git a/lcpng_mpls_sync.c b/lcpng_mpls_sync.c new file mode 100644 index 0000000..47fed5e --- /dev/null +++ b/lcpng_mpls_sync.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include + +#include + +void +lcp_mpls_sync_pair_add_cb (lcp_itf_pair_t *lip) +{ + u8 phy_is_enabled = mpls_sw_interface_is_enabled (lip->lip_phy_sw_if_index); + LCP_IF_DBG ("mpls_pair_add_cb: mpls enabled %u, parent %U", phy_is_enabled, + format_lcp_itf_pair, lip); + if (phy_is_enabled) + mpls_sw_interface_enable_disable (&mpls_main, lip->lip_host_sw_if_index, + 1); +} + +void +lcp_mpls_sync_state_cb (struct mpls_main_t *mm, uword opaque, u32 sw_if_index, + u32 is_enable) +{ + lcp_itf_pair_t *lip; + index_t lipi; + int curr_ns_fd = -1; + int vif_ns_fd = -1; + int ctl_fd = -1; + u8 *ctl_path = NULL; + + LCP_IF_DBG ("mpls_sync_state_cb: called for sw_if_index %u", sw_if_index); + + // If device is LCP PHY, sync state to host tap. + lipi = lcp_itf_pair_find_by_phy (sw_if_index); + if (INDEX_INVALID != lipi) + { + lip = lcp_itf_pair_get (lipi); + LCP_IF_DBG ("mpls_sync_state_cb: mpls enabled %u parent %U", is_enable, + format_lcp_itf_pair, lip); + mpls_sw_interface_enable_disable (&mpls_main, lip->lip_host_sw_if_index, + is_enable); + return; + } + + // If device is LCP host, sync state to linux. + lipi = lcp_itf_pair_find_by_host (sw_if_index); + if (INDEX_INVALID == lipi) + return; + + lip = lcp_itf_pair_get (lipi); + if (lip->lip_namespace) + { + curr_ns_fd = clib_netns_open (NULL /* self */); + vif_ns_fd = clib_netns_open (lip->lip_namespace); + if (vif_ns_fd != -1) + clib_setns (vif_ns_fd); + } + + ctl_path = format (NULL, "/proc/sys/net/mpls/conf/%s/input%c", + lip->lip_host_name, NULL); + if (NULL == ctl_path) + { + LCP_IF_DBG ("mpls_sync_state_cb: failed to format sysctl"); + goto SYNC_CLEANUP; + } + + ctl_fd = open ((char *) ctl_path, O_WRONLY); + if (ctl_fd < 0) + { + LCP_IF_DBG ("mpls_sync_state_cb: failed to open %s for writing", + ctl_path); + goto SYNC_CLEANUP; + } + + if (fdformat (ctl_fd, "%u", is_enable) < 1) + { + LCP_IF_DBG ("mpls_sync_state_cb: failed to write to %s", ctl_path); + goto SYNC_CLEANUP; + } + + LCP_IF_DBG ("mpls_sync_state_cb: set mpls input for %s", lip->lip_host_name); + +SYNC_CLEANUP: + if (ctl_fd < 0) + close (ctl_fd); + + if (NULL != ctl_path) + vec_free (ctl_path); + + if (vif_ns_fd != -1) + close (vif_ns_fd); + + if (curr_ns_fd != -1) + { + clib_setns (curr_ns_fd); + close (curr_ns_fd); + } +} + +static clib_error_t * +lcp_mpls_sync_init (vlib_main_t *vm) +{ + lcp_itf_pair_vft_t mpls_sync_itf_pair_vft = { + .pair_add_fn = lcp_mpls_sync_pair_add_cb, + }; + lcp_itf_pair_register_vft (&mpls_sync_itf_pair_vft); + + mpls_interface_state_change_add_callback (lcp_mpls_sync_state_cb, 0); + + return NULL; +} + +VLIB_INIT_FUNCTION (lcp_mpls_sync_init) = { + .runs_after = VLIB_INITS ("lcp_itf_pair_init", "mpls_init"), +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lcpng_netlink.c b/lcpng_netlink.c index 00fc86b..bd4ec1f 100644 --- a/lcpng_netlink.c +++ b/lcpng_netlink.c @@ -408,7 +408,7 @@ lcp_nl_pair_add_cb (lcp_itf_pair_t *lip) nm->nl_ns.clib_file_lcp_refcnt); if ((nm->nl_ns.clib_file_lcp_refcnt > 0) && - vec_cmp(nm->nl_ns.netns_name, lip->lip_namespace)) + vec_cmp (nm->nl_ns.netns_name, lip->lip_namespace)) { LCP_NL_WARN ( "pair_add_cb: Existing netlink listener for netns %v -- this " diff --git a/lcpng_netlink.h b/lcpng_netlink.h index c0efc2d..ab368af 100644 --- a/lcpng_netlink.h +++ b/lcpng_netlink.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/lcpng_nl_sync.c b/lcpng_nl_sync.c index 52d532d..0dcbfa1 100644 --- a/lcpng_nl_sync.c +++ b/lcpng_nl_sync.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -146,13 +147,31 @@ vnet_sw_interface_get_available_subid (vnet_main_t *vnm, u32 sw_if_index, return 1; } +static fib_protocol_t +lcp_nl_proto_k2f (uint32_t k) +{ + switch (k) + { + case AF_INET6: + return FIB_PROTOCOL_IP6; + case AF_INET: + return FIB_PROTOCOL_IP4; + case AF_MPLS: + return FIB_PROTOCOL_MPLS; + default: + ASSERT (0); + return FIB_PROTOCOL_NONE; + } +} + static fib_protocol_t lcp_nl_mk_addr46 (const struct nl_addr *rna, ip46_address_t *ia) { fib_protocol_t fproto; - fproto = - nl_addr_get_family (rna) == AF_INET6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4; + fproto = lcp_nl_proto_k2f (nl_addr_get_family (rna)); + ASSERT (FIB_PROTOCOL_MPLS != fproto); + ip46_address_reset (ia); if (FIB_PROTOCOL_IP4 == fproto) memcpy (&ia->ip4, nl_addr_get_binary_addr (rna), nl_addr_get_len (rna)); @@ -166,9 +185,31 @@ static void lcp_nl_mk_route_prefix (struct rtnl_route *r, fib_prefix_t *p) { const struct nl_addr *addr = rtnl_route_get_dst (r); + u32 *baddr = nl_addr_get_binary_addr (addr); + u32 blen = nl_addr_get_len (addr); + ip46_address_t *paddr = &p->fp_addr; + u32 entry; + + p->fp_proto = lcp_nl_proto_k2f (nl_addr_get_family (addr)); + + switch (p->fp_proto) + { + case FIB_PROTOCOL_MPLS: + entry = ntohl (*baddr); + p->fp_label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT; + p->fp_len = 21; + p->fp_eos = MPLS_NON_EOS; + return; + case FIB_PROTOCOL_IP4: + ip46_address_reset (paddr); + memcpy (&paddr->ip4, baddr, blen); + break; + case FIB_PROTOCOL_IP6: + memcpy (&paddr->ip6, baddr, blen); + break; + } p->fp_len = nl_addr_get_prefixlen (addr); - p->fp_proto = lcp_nl_mk_addr46 (addr, &p->fp_addr); } static void @@ -208,6 +249,37 @@ lcp_nl_mk_route_entry_flags (uint8_t rtype, int table_id, uint8_t rproto) return (fef); } +static int +lcp_router_mpls_nladdr_to_path (fib_route_path_t *path, struct nl_addr *addr) +{ + if (!addr) + return 0; + + struct mpls_label *stack = nl_addr_get_binary_addr (addr); + u32 entry, label; + u8 exp, ttl; + int label_count = 0; + + while (1) + { + entry = ntohl (stack[label_count++].entry); + label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT; + exp = (entry & MPLS_LS_TC_MASK) >> MPLS_LS_TC_SHIFT; + ttl = (entry & MPLS_LS_TTL_MASK) >> MPLS_LS_TTL_SHIFT; + + fib_mpls_label_t fml = { + .fml_value = label, + .fml_exp = exp, + .fml_ttl = ttl, + }; + vec_add1 (path->frp_label_stack, fml); + + if (entry & MPLS_LS_S_MASK) + break; + } + return label_count; +} + static void lcp_nl_route_path_parse (struct rtnl_nexthop *rnh, void *arg) { @@ -216,6 +288,7 @@ lcp_nl_route_path_parse (struct rtnl_nexthop *rnh, void *arg) lcp_itf_pair_t *lip; fib_protocol_t fproto; struct nl_addr *addr; + int label_count = 0; /* We do not log a warning/error here, because some routes (like * blackhole/unreach) don't have an interface associated with them. @@ -230,9 +303,16 @@ lcp_nl_route_path_parse (struct rtnl_nexthop *rnh, void *arg) path->frp_flags = FIB_ROUTE_PATH_FLAG_NONE | ctx->type_flags; path->frp_sw_if_index = lip->lip_phy_sw_if_index; - path->frp_weight = rtnl_route_nh_get_weight (rnh); path->frp_preference = ctx->preference; + /* + * FIB Path Weight of 0 is meaningless and replaced with 1 further along. + * See fib_path_create. fib_path_cmp_w_route_path would fail to match + * such a fib_route_path_t with any fib_path_t, because a fib_path_t's + * fp_weight can never be 0. + */ + path->frp_weight = clib_max (1, rtnl_route_nh_get_weight (rnh)); + addr = rtnl_route_nh_get_gateway (rnh); if (!addr) addr = rtnl_route_nh_get_via (rnh); @@ -244,6 +324,32 @@ lcp_nl_route_path_parse (struct rtnl_nexthop *rnh, void *arg) path->frp_proto = fib_proto_to_dpo (fproto); + if (ctx->route_proto == FIB_PROTOCOL_MPLS) + { + addr = rtnl_route_nh_get_newdst (rnh); + label_count = lcp_router_mpls_nladdr_to_path (path, addr); + if (label_count) + { + LCP_NL_DBG ("router_path_parse: is label swap to %u", + path->frp_label_stack[0].fml_value); + } + else + { + fib_mpls_label_t fml = { + .fml_value = MPLS_LABEL_POP, + }; + vec_add1 (path->frp_label_stack, fml); + LCP_NL_DBG ("router_path_parse: is label pop"); + } + } + +#ifdef NL_CAPABILITY_VERSION_3_6_0 + addr = rtnl_route_nh_get_encap_mpls_dst (rnh); + label_count = lcp_router_mpls_nladdr_to_path (path, addr); + if (label_count) + LCP_NL_DBG ("router_path_parse: has encap mpls, %d labels", label_count); +#endif + if (ctx->is_mcast) path->frp_mitf_flags = MFIB_ITF_FLAG_FORWARD; @@ -439,11 +545,21 @@ lcp_nl_route_del (struct rtnl_route *rr) LCP_NL_DBG ("route_del: table %d prefix %U flags %U", rtnl_route_get_table (rr), format_fib_prefix, &pfx, format_fib_entry_flags, entry_flags); - if (pfx.fp_proto == FIB_PROTOCOL_IP6) - fib_table_entry_delete (nlt->nlt_fib_index, &pfx, fib_src); - else - fib_table_entry_path_remove2 (nlt->nlt_fib_index, &pfx, fib_src, - np.paths); + + switch (pfx.fp_proto) + { + case FIB_PROTOCOL_IP6: + fib_table_entry_delete (nlt->nlt_fib_index, &pfx, fib_src); + break; + case FIB_PROTOCOL_MPLS: + fib_table_entry_path_remove2 (nlt->nlt_fib_index, &pfx, fib_src, + np.paths); + /* delete the EOS route in addition to NEOS - fallthrough */ + pfx.fp_eos = MPLS_EOS; + default: + fib_table_entry_path_remove2 (nlt->nlt_fib_index, &pfx, fib_src, + np.paths); + } } vec_free (np.paths); @@ -451,6 +567,26 @@ lcp_nl_route_del (struct rtnl_route *rr) lcp_nl_table_unlock (nlt); } +static fib_route_path_t * +lcp_router_fib_route_path_dup (fib_route_path_t *old) +{ + int idx; + fib_route_path_t *p; + + fib_route_path_t *new = vec_dup (old); + if (!new) + return NULL; + + for (idx = 0; idx < vec_len (new); idx++) + { + p = &new[idx]; + if (p->frp_label_stack) + p->frp_label_stack = vec_dup (p->frp_label_stack); + } + + return new; +} + void lcp_nl_route_add (struct rtnl_route *rr, int is_replace) { @@ -461,7 +597,7 @@ lcp_nl_route_add (struct rtnl_route *rr, int is_replace) uint8_t rtype, rproto; LCP_NL_DBG ("route_add: netlink %U %s", format_nl_object, rr, - is_replace?"replace":""); + is_replace ? "replace" : ""); rtype = rtnl_route_get_type (rr); table_id = rtnl_route_get_table (rr); @@ -536,6 +672,24 @@ lcp_nl_route_add (struct rtnl_route *rr, int is_replace) rtnl_route_get_table (rr), format_fib_prefix, &pfx, format_fib_entry_flags, entry_flags); + if (pfx.fp_proto == FIB_PROTOCOL_MPLS) + { + /* in order to avoid double-frees, we duplicate the paths. */ + fib_route_path_t *pathdup = + lcp_router_fib_route_path_dup (np.paths); + if (is_replace) + fib_table_entry_update (nlt->nlt_fib_index, &pfx, fib_src, + entry_flags, pathdup); + else + fib_table_entry_path_add2 (nlt->nlt_fib_index, &pfx, fib_src, + entry_flags, pathdup); + vec_free (pathdup); + + /* install EOS route in addition to NEOS */ + pfx.fp_eos = MPLS_EOS; + pfx.fp_payload_proto = np.paths[0].frp_proto; + } + if (is_replace) fib_table_entry_update (nlt->nlt_fib_index, &pfx, fib_src, entry_flags, np.paths);