summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/linux-cp/lcp_router.c90
1 files changed, 88 insertions, 2 deletions
diff --git a/src/plugins/linux-cp/lcp_router.c b/src/plugins/linux-cp/lcp_router.c
index 7542aa06614..34d0500b0c5 100644
--- a/src/plugins/linux-cp/lcp_router.c
+++ b/src/plugins/linux-cp/lcp_router.c
@@ -307,6 +307,9 @@ lcp_router_link_addr (struct rtnl_link *rl, lcp_itf_pair_t *lip)
lip->lip_phy_adjs.adj_index[AF_IP6]);
}
+static void lcp_router_table_flush (lcp_router_table_t *nlt, u32 sw_if_index,
+ fib_source_t source);
+
static void
lcp_router_link_add (struct rtnl_link *rl, void *ctx)
{
@@ -320,6 +323,8 @@ lcp_router_link_add (struct rtnl_link *rl, void *ctx)
if (INDEX_INVALID != lipi)
{
lcp_itf_pair_t *lip;
+ u32 sw_if_flags;
+ u32 sw_if_up;
lip = lcp_itf_pair_get (lipi);
if (!vnet_get_sw_interface (vnm, lip->lip_phy_sw_if_index))
@@ -328,16 +333,48 @@ lcp_router_link_add (struct rtnl_link *rl, void *ctx)
if (lcp_router_lip_ts_check ((nl_msg_info_t *) ctx, lip))
return;
- if (up)
+ sw_if_flags =
+ vnet_sw_interface_get_flags (vnm, lip->lip_phy_sw_if_index);
+ sw_if_up = (sw_if_flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+
+ if (!sw_if_up && up)
{
vnet_sw_interface_admin_up (vnet_get_main (),
lip->lip_phy_sw_if_index);
}
- else
+ else if (sw_if_up && !up)
{
vnet_sw_interface_admin_down (vnet_get_main (),
lip->lip_phy_sw_if_index);
+
+ /* When an interface is brought down administratively, the kernel
+ * removes routes which resolve through that interface. For IPv4
+ * routes, the kernel will not send any explicit RTM_DELROUTE
+ * messages about removing them. In order to synchronize with the
+ * kernel, affected IPv4 routes need to be manually removed from the
+ * FIB. The behavior is different for IPv6 routes. Explicit
+ * RTM_DELROUTE messages are sent about IPv6 routes being removed.
+ */
+ u32 fib_index;
+ lcp_router_table_t *nlt;
+
+ fib_index = fib_table_get_index_for_sw_if_index (
+ FIB_PROTOCOL_IP4, lip->lip_phy_sw_if_index);
+
+ pool_foreach (nlt, lcp_router_table_pool)
+ {
+ if (fib_index == nlt->nlt_fib_index &&
+ FIB_PROTOCOL_IP4 == nlt->nlt_proto)
+ {
+ lcp_router_table_flush (nlt, lip->lip_phy_sw_if_index,
+ lcp_rt_fib_src);
+ lcp_router_table_flush (nlt, lip->lip_phy_sw_if_index,
+ lcp_rt_fib_src_dynamic);
+ break;
+ }
+ }
}
+
LCP_ROUTER_DBG ("link: %s (%d) -> %U/%U %s", rtnl_link_get_name (rl),
rtnl_link_get_ifindex (rl), format_vnet_sw_if_index_name,
vnm, lip->lip_phy_sw_if_index,
@@ -1108,6 +1145,55 @@ lcp_router_route_sync_end (void)
}
}
+typedef struct lcp_router_table_flush_ctx_t_
+{
+ fib_node_index_t *lrtf_entries;
+ u32 lrtf_sw_if_index;
+ fib_source_t lrtf_source;
+} lcp_router_table_flush_ctx_t;
+
+static fib_table_walk_rc_t
+lcp_router_table_flush_cb (fib_node_index_t fib_entry_index, void *arg)
+{
+ lcp_router_table_flush_ctx_t *ctx = arg;
+
+ if (fib_entry_get_resolving_interface_for_source (
+ fib_entry_index, ctx->lrtf_source) == ctx->lrtf_sw_if_index)
+ {
+ vec_add1 (ctx->lrtf_entries, fib_entry_index);
+ }
+ return (FIB_TABLE_WALK_CONTINUE);
+}
+
+static void
+lcp_router_table_flush (lcp_router_table_t *nlt, u32 sw_if_index,
+ fib_source_t source)
+{
+ fib_node_index_t *fib_entry_index;
+ lcp_router_table_flush_ctx_t ctx = {
+ .lrtf_entries = NULL,
+ .lrtf_sw_if_index = sw_if_index,
+ .lrtf_source = source,
+ };
+
+ fib_table_walk (nlt->nlt_fib_index, nlt->nlt_proto,
+ lcp_router_table_flush_cb, &ctx);
+
+ LCP_ROUTER_DBG (
+ "Flush table: proto %U, fib-index %u, resolved via %U for source %U",
+ format_fib_protocol, nlt->nlt_proto, nlt->nlt_fib_index,
+ format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index,
+ format_fib_source, source);
+
+ vec_foreach (fib_entry_index, ctx.lrtf_entries)
+ {
+ fib_table_entry_delete_index (*fib_entry_index, source);
+ lcp_router_table_unlock (nlt);
+ }
+
+ vec_free (ctx.lrtf_entries);
+}
+
const nl_vft_t lcp_router_vft = {
.nvl_rt_link_add = { .is_mp_safe = 0, .cb = lcp_router_link_add },
.nvl_rt_link_del = { .is_mp_safe = 0, .cb = lcp_router_link_del },