linux-cp: Fix add vs update on routes

Linux uses NLM_F_REPLACE in the netlink message to signal a FIB update
The code invariably does a FIB update for IPv4 and a addition for IPv6.
Without this fix, the following:
 ip route add 2001:db8::/48 via 2001:db8::1
 ip route replace 2001:db8::/48 via 2001:db8::2

ends up as two separate FIB entries in VPP. With the fix, there will be one FIB entry (the second one with nexthop ::2).

Type: fix
Change-Id: I8f98d6ded52ae0c60bfddaa7fc39acbbaa19d34a
Signed-off-by: Pim van Pelt <pim@ipng.nl>
(cherry picked from commit af4fa965e9)
This commit is contained in:
Pim van Pelt
2023-05-21 08:35:26 +02:00
committed by Andrew Yourtchenko
parent 1a67c82c84
commit 63e76fad7e
3 changed files with 80 additions and 69 deletions

View File

@ -205,7 +205,10 @@ nl_route_del (struct rtnl_route *rr, void *arg)
static void
nl_route_add (struct rtnl_route *rr, void *arg)
{
FOREACH_VFT (nvl_rt_route_add, rr);
nl_msg_info_t *msg_info = (nl_msg_info_t *) arg;
struct nlmsghdr *nlh = nlmsg_hdr (msg_info->msg);
FOREACH_VFT_CTX (nvl_rt_route_add, rr, (nlh->nlmsg_flags & NLM_F_REPLACE));
}
static void

View File

@ -26,7 +26,8 @@ typedef void (*nl_rt_addr_cb_t) (struct rtnl_addr *ra);
typedef void (*nl_rt_addr_sync_cb_t) (void);
typedef void (*nl_rt_neigh_cb_t) (struct rtnl_neigh *rr);
typedef void (*nl_rt_neigh_sync_cb_t) (void);
typedef void (*nl_rt_route_cb_t) (struct rtnl_route *rn);
typedef void (*nl_rt_route_add_cb_t) (struct rtnl_route *rn, int is_replace);
typedef void (*nl_rt_route_del_cb_t) (struct rtnl_route *rn);
typedef void (*nl_rt_route_sync_cb_t) (void);
#define NL_RT_COMMON uword is_mp_safe
@ -73,12 +74,19 @@ typedef struct nl_rt_neigh_sync_t_
nl_rt_neigh_sync_cb_t cb;
} nl_rt_neigh_sync_t;
typedef struct nl_rt_route_t_
typedef struct nl_rt_route_add_t_
{
NL_RT_COMMON;
nl_rt_route_cb_t cb;
} nl_rt_route_t;
nl_rt_route_add_cb_t cb;
} nl_rt_route_add_t;
typedef struct nl_rt_route_del_t_
{
NL_RT_COMMON;
nl_rt_route_del_cb_t cb;
} nl_rt_route_del_t;
typedef struct nl_rt_route_sync_t_
{
@ -103,8 +111,8 @@ typedef struct nl_vft_t_
nl_rt_neigh_t nvl_rt_neigh_del;
nl_rt_neigh_sync_t nvl_rt_neigh_sync_begin;
nl_rt_neigh_sync_t nvl_rt_neigh_sync_end;
nl_rt_route_t nvl_rt_route_add;
nl_rt_route_t nvl_rt_route_del;
nl_rt_route_add_t nvl_rt_route_add;
nl_rt_route_del_t nvl_rt_route_del;
nl_rt_route_sync_t nvl_rt_route_sync_begin;
nl_rt_route_sync_t nvl_rt_route_sync_end;
} nl_vft_t;

View File

@ -1167,7 +1167,7 @@ lcp_router_route_del (struct rtnl_route *rr)
}
static void
lcp_router_route_add (struct rtnl_route *rr)
lcp_router_route_add (struct rtnl_route *rr, int is_replace)
{
fib_entry_flag_t entry_flags;
uint32_t table_id;
@ -1196,71 +1196,71 @@ lcp_router_route_add (struct rtnl_route *rr)
LCP_ROUTER_DBG ("route skip: %d:%U %U", rtnl_route_get_table (rr),
format_fib_prefix, &pfx, format_fib_entry_flags,
entry_flags);
return;
}
LCP_ROUTER_DBG ("route %s: %d:%U %U", is_replace ? "replace" : "add",
rtnl_route_get_table (rr), format_fib_prefix, &pfx,
format_fib_entry_flags, entry_flags);
lcp_router_route_path_parse_t np = {
.route_proto = pfx.fp_proto,
.is_mcast = (rtype == RTN_MULTICAST),
.type_flags = lcp_router_route_type_frpflags[rtype],
.preference = (u8) rtnl_route_get_priority (rr),
};
rtnl_route_foreach_nexthop (rr, lcp_router_route_path_parse, &np);
lcp_router_route_path_add_special (rr, &np);
if (0 != vec_len (np.paths))
{
if (rtype == RTN_MULTICAST)
{
/* it's not clear to me how linux expresses the RPF paramters
* so we'll allow from all interfaces and hope for the best */
mfib_prefix_t mpfx = {};
lcp_router_route_mk_mprefix (rr, &mpfx);
mfib_table_entry_update (nlt->nlt_mfib_index, &mpfx,
MFIB_SOURCE_PLUGIN_LOW, MFIB_RPF_ID_NONE,
MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
mfib_table_entry_paths_update (nlt->nlt_mfib_index, &mpfx,
MFIB_SOURCE_PLUGIN_LOW,
MFIB_ENTRY_FLAG_NONE, np.paths);
}
else
{
fib_source_t fib_src;
const fib_route_path_t *rpath;
vec_foreach (rpath, np.paths)
{
if (fib_route_path_is_attached (rpath))
{
entry_flags |= FIB_ENTRY_FLAG_ATTACHED;
break;
}
}
fib_src = lcp_router_proto_fib_source (rproto);
if (is_replace)
fib_table_entry_update (nlt->nlt_fib_index, &pfx, fib_src,
entry_flags, np.paths);
else
fib_table_entry_path_add2 (nlt->nlt_fib_index, &pfx, fib_src,
entry_flags, np.paths);
}
}
else
{
LCP_ROUTER_DBG ("route add: %d:%U %U", rtnl_route_get_table (rr),
format_fib_prefix, &pfx, format_fib_entry_flags,
entry_flags);
lcp_router_route_path_parse_t np = {
.route_proto = pfx.fp_proto,
.is_mcast = (rtype == RTN_MULTICAST),
.type_flags = lcp_router_route_type_frpflags[rtype],
.preference = (u8) rtnl_route_get_priority (rr),
};
rtnl_route_foreach_nexthop (rr, lcp_router_route_path_parse, &np);
lcp_router_route_path_add_special (rr, &np);
if (0 != vec_len (np.paths))
{
if (rtype == RTN_MULTICAST)
{
/* it's not clear to me how linux expresses the RPF paramters
* so we'll allow from all interfaces and hope for the best */
mfib_prefix_t mpfx = {};
lcp_router_route_mk_mprefix (rr, &mpfx);
mfib_table_entry_update (
nlt->nlt_mfib_index, &mpfx, MFIB_SOURCE_PLUGIN_LOW,
MFIB_RPF_ID_NONE, MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
mfib_table_entry_paths_update (nlt->nlt_mfib_index, &mpfx,
MFIB_SOURCE_PLUGIN_LOW,
MFIB_ENTRY_FLAG_NONE, np.paths);
}
else
{
fib_source_t fib_src;
const fib_route_path_t *rpath;
vec_foreach (rpath, np.paths)
{
if (fib_route_path_is_attached (rpath))
{
entry_flags |= FIB_ENTRY_FLAG_ATTACHED;
break;
}
}
fib_src = lcp_router_proto_fib_source (rproto);
if (pfx.fp_proto == FIB_PROTOCOL_IP6)
fib_table_entry_path_add2 (nlt->nlt_fib_index, &pfx, fib_src,
entry_flags, np.paths);
else
fib_table_entry_update (nlt->nlt_fib_index, &pfx, fib_src,
entry_flags, np.paths);
}
}
else
LCP_ROUTER_DBG ("no paths for route add: %d:%U %U",
rtnl_route_get_table (rr), format_fib_prefix, &pfx,
format_fib_entry_flags, entry_flags);
vec_free (np.paths);
LCP_ROUTER_DBG ("no paths for route: %d:%U %U",
rtnl_route_get_table (rr), format_fib_prefix, &pfx,
format_fib_entry_flags, entry_flags);
}
vec_free (np.paths);
}
static void