ndisc: Accept routes from on-link prefixes form ra

It is possible that an ra leads to two routes having
the same prefix as well as the same prefix length.
One of them, however, refers to the on-link prefix,
and the other one to a route from the route information field.
(Moreover, they might have different route preferences.)
Hence, if both routes differ in the on-link property,
both are added, and the route from the route information
option receives a metric penalty.
Fixed #1163.
This commit is contained in:
Alexander Lochmann 2022-12-14 14:23:29 +01:00 committed by Alexander Lochmann
parent 4f719da32d
commit 11832e2ba3
3 changed files with 53 additions and 4 deletions

View file

@ -211,6 +211,7 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
const NMNDiscRoute route = {
.network = r_network,
.plen = r_plen,
.on_link = TRUE,
.expiry_msec =
_nm_ndisc_lifetime_to_expiry(now_msec,
ndp_msg_opt_prefix_valid_time(msg, offset)),
@ -249,6 +250,7 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
.network = network,
.gateway = gateway_addr,
.plen = plen,
.on_link = FALSE,
.expiry_msec =
_nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_route_lifetime(msg, offset)),
.preference = _route_preference_coerce(ndp_msg_opt_route_preference(msg, offset)),

View file

@ -161,8 +161,9 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx,
.table_any = TRUE,
.table_coerced = 0,
.metric_any = TRUE,
.metric = 0,
.rt_pref = ndisc_route->preference,
/* Non-on_link routes get a small penalty */
.metric = ndisc_route->duplicate && !ndisc_route->on_link ? 5 : 0,
.rt_pref = ndisc_route->preference,
};
nm_assert((NMIcmpv6RouterPref) r.rt_pref == ndisc_route->preference);
@ -349,12 +350,47 @@ _ASSERT_data_gateways(const NMNDiscDataInternal *data)
}
/*****************************************************************************/
static bool
is_duplicate_route(const NMNDiscRoute *r0, const NMNDiscRoute *r1)
{
return IN6_ARE_ADDR_EQUAL(&r0->network, &r1->network) && r0->plen == r1->plen;
}
static void
_data_complete_prepare_routes(GArray *routes)
{
guint i, j;
for (i = 0; i < routes->len; i++) {
NMNDiscRoute *r0 = &nm_g_array_index(routes, NMNDiscRoute, i);
r0->duplicate = FALSE;
}
for (i = 0; i < routes->len; i++) {
NMNDiscRoute *r0 = &nm_g_array_index(routes, NMNDiscRoute, i);
for (j = i + 1; j < routes->len; j++) {
NMNDiscRoute *r1 = &nm_g_array_index(routes, NMNDiscRoute, j);
if (!is_duplicate_route(r0, r1))
continue;
r0->duplicate = TRUE;
r1->duplicate = TRUE;
/* Maybe after index j, there is yet another duplicate. But we
* will find that later, when i becomes j. */
break;
}
}
}
static const NMNDiscData *
_data_complete(NMNDiscDataInternal *data)
{
_ASSERT_data_gateways(data);
_data_complete_prepare_routes(data->routes);
#define _SET(data, field) \
G_STMT_START \
{ \
@ -658,8 +694,17 @@ nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new_item, gint64 now_msec
for (i = 0; i < rdata->routes->len;) {
NMNDiscRoute *item = &nm_g_array_index(rdata->routes, NMNDiscRoute, i);
if (IN6_ARE_ADDR_EQUAL(&item->network, &new_item->network)
&& item->plen == new_item->plen) {
/*
* It is possible that two entries in rdata->routes have
* the same prefix as well as the same prefix length.
* One of them, however, refers to the on-link prefix,
* and the other one to a route from the route information field.
* Moreover, they might have different route preferences.
* Hence, if both routes differ in the on-link flag,
* comparison is aborted, and both routes are added.
*/
if (IN6_ARE_ADDR_EQUAL(&item->network, &new_item->network) && item->plen == new_item->plen
&& item->on_link == new_item->on_link) {
if (new_item->expiry_msec <= now_msec) {
g_array_remove_index(rdata->routes, i);
return TRUE;

View file

@ -115,6 +115,8 @@ typedef struct _NMNDiscRoute {
gint64 expiry_msec;
NMIcmpv6RouterPref preference;
guint8 plen;
bool on_link : 1;
bool duplicate : 1;
} NMNDiscRoute;
typedef struct {