platform: handle route table RT_TABLE_UNSPEC specially

Kernel does not allow to add a route with table 0 (RT_TABLE_UNSPEC). It
effectively is an alias for the main table. We must consider that when
comparing routes sementically.
This commit is contained in:
Thomas Haller
2017-09-22 15:26:56 +02:00
parent 4ca2e1b10c
commit 9acf80a979
3 changed files with 81 additions and 18 deletions

View File

@@ -2594,7 +2594,7 @@ _nl_msg_new_route (int nlmsg_type,
const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj);
gboolean is_v4 = klass->addr_family == AF_INET;
const guint32 lock = ip_route_get_lock_flag (NMP_OBJECT_CAST_IP_ROUTE (obj));
const guint32 table = nm_platform_route_table_coerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced);
const guint32 table = nm_platform_route_table_uncoerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced, TRUE);
struct rtmsg rtmsg = {
.rtm_family = klass->addr_family,
.rtm_tos = is_v4

View File

@@ -2904,7 +2904,7 @@ nm_platform_lookup_predicate_routes_main (const NMPObject *obj,
{
nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE,
NMP_OBJECT_TYPE_IP6_ROUTE));
return !obj->ip_route.table_coerced;
return nm_platform_route_table_is_main (obj->ip_route.table_coerced);
}
gboolean
@@ -2913,7 +2913,7 @@ nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *ob
{
nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE,
NMP_OBJECT_TYPE_IP6_ROUTE));
return !obj->ip_route.table_coerced
return nm_platform_route_table_is_main (obj->ip_route.table_coerced)
&& obj->ip_route.rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL;
}
@@ -3828,12 +3828,14 @@ nm_platform_ip_route_normalize (int addr_family,
switch (addr_family) {
case AF_INET:
r4 = (NMPlatformIP4Route *) route;
r4->table_coerced = nm_platform_route_table_coerce (nm_platform_route_table_uncoerce (r4->table_coerced, TRUE));
r4->network = nm_utils_ip4_address_clear_host_address (r4->network, r4->plen);
r4->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r4->rt_source);
r4->scope_inv = _ip_route_scope_inv_get_normalized (r4);
break;
case AF_INET6:
r6 = (NMPlatformIP6Route *) route;
r6->table_coerced = nm_platform_route_table_coerce (nm_platform_route_table_uncoerce (r6->table_coerced, TRUE));
nm_utils_ip6_address_clear_host_address (&r6->network, &r6->network, r6->plen);
r6->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r6->rt_source),
r6->metric = nm_utils_ip6_route_metric_normalize (r6->metric);
@@ -4942,7 +4944,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
"%s" /* initrwnd */
"%s" /* mtu */
"",
route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_coerce (route->table_coerced)) : "",
route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_uncoerce (route->table_coerced, FALSE)) : "",
s_network,
route->plen,
s_gateway,
@@ -5015,7 +5017,7 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
"%s" /* initrwnd */
"%s" /* mtu */
"",
route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_coerce (route->table_coerced)) : "",
route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_uncoerce (route->table_coerced, FALSE)) : "",
s_network,
route->plen,
s_gateway,
@@ -5458,7 +5460,7 @@ nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpT
switch (cmp_type) {
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
h = NM_HASH_COMBINE (h, obj->table_coerced);
h = NM_HASH_COMBINE (h, nm_platform_route_table_uncoerce (obj->table_coerced, TRUE));
h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen));
h = NM_HASH_COMBINE (h, obj->plen);
h = NM_HASH_COMBINE (h, obj->metric);
@@ -5484,7 +5486,10 @@ nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpT
break;
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
h = NM_HASH_COMBINE (h, obj->table_coerced);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
h = NM_HASH_COMBINE (h, nm_platform_route_table_uncoerce (obj->table_coerced, TRUE));
else
h = NM_HASH_COMBINE (h, obj->table_coerced);
h = NM_HASH_COMBINE (h, obj->ifindex);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen));
@@ -5527,7 +5532,8 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route
switch (cmp_type) {
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
NM_CMP_FIELD (a, b, table_coerced);
NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE),
nm_platform_route_table_uncoerce (b->table_coerced, TRUE));
NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen));
NM_CMP_FIELD (a, b, plen);
NM_CMP_FIELD (a, b, metric);
@@ -5555,7 +5561,11 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route
break;
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
NM_CMP_FIELD (a, b, table_coerced);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE),
nm_platform_route_table_uncoerce (b->table_coerced, TRUE));
} else
NM_CMP_FIELD (a, b, table_coerced);
NM_CMP_FIELD (a, b, ifindex);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen));
@@ -5601,7 +5611,7 @@ nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpT
switch (cmp_type) {
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
h = NM_HASH_COMBINE (h, obj->table_coerced);
h = NM_HASH_COMBINE (h, nm_platform_route_table_uncoerce (obj->table_coerced, TRUE));
h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->network, obj->plen);
h = NM_HASH_COMBINE (h, obj->plen);
h = NM_HASH_COMBINE (h, nm_utils_ip6_route_metric_normalize (obj->metric));
@@ -5614,7 +5624,10 @@ nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpT
break;
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
h = NM_HASH_COMBINE (h, obj->table_coerced);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
h = NM_HASH_COMBINE (h, nm_platform_route_table_uncoerce (obj->table_coerced, TRUE));
else
h = NM_HASH_COMBINE (h, obj->table_coerced);
h = NM_HASH_COMBINE (h, obj->ifindex);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->network, obj->plen);
@@ -5661,7 +5674,8 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route
switch (cmp_type) {
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
NM_CMP_FIELD (a, b, table_coerced);
NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE),
nm_platform_route_table_uncoerce (b->table_coerced, TRUE));
NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen));
NM_CMP_FIELD (a, b, plen);
NM_CMP_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric));
@@ -5674,7 +5688,11 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route
break;
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
NM_CMP_FIELD (a, b, table_coerced);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE),
nm_platform_route_table_uncoerce (b->table_coerced, TRUE));
} else
NM_CMP_FIELD (a, b, table_coerced);
NM_CMP_FIELD (a, b, ifindex);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen));

View File

@@ -426,7 +426,7 @@ typedef union {
*
* This is not the original table ID. Instead, 254 (RT_TABLE_MAIN) and
* zero (RT_TABLE_UNSPEC) are swapped, so that the default is the main
* table. Use nm_platform_route_table_coerce(). */ \
* table. Use nm_platform_route_table_coerce()/nm_platform_route_table_uncoerce(). */ \
guint32 table_coerced; \
\
/*end*/
@@ -860,15 +860,21 @@ NMPlatform *nm_platform_get (void);
/**
* nm_platform_route_table_coerce:
* @table: the route table, either its original value, or its coerced.
* @table: the route table, in its original value as received
* from rtm_table/RTA_TABLE.
*
* Returns: returns the coerced table id. If the table id is like
* RTA_TABLE, it returns a value for NMPlatformIPRoute.table_coerced
* and vice versa.
* Returns: returns the coerced table id, that can be stored in
* NMPlatformIPRoute.table_coerced.
*/
static inline guint32
nm_platform_route_table_coerce (guint32 table)
{
/* For kernel, the default table is RT_TABLE_MAIN (254).
* We want that in NMPlatformIPRoute.table_coerced a numeric
* zero is the default. Hence, @table_coerced swaps the
* value 0 and 254. Use nm_platform_route_table_coerce()
* and nm_platform_route_table_uncoerce() to convert between
* the two domains. */
switch (table) {
case 0 /* RT_TABLE_UNSPEC */:
return 254;
@@ -879,6 +885,45 @@ nm_platform_route_table_coerce (guint32 table)
}
}
/**
* nm_platform_route_table_uncoerce:
* @table: the route table, in its coerced value
* @normalize: whether to normalize RT_TABLE_UNSPEC to
* RT_TABLE_MAIN. For kernel, routes with a table id
* RT_TABLE_UNSPEC do not exist and are treated like
* RT_TABLE_MAIN.
*
* Returns: reverts the coerced table ID in NMPlatformIPRoute.table_coerced
* to the original value as kernel understands it.
*/
static inline guint32
nm_platform_route_table_uncoerce (guint32 table_coerced, gboolean normalize)
{
/* this undoes nm_platform_route_table_coerce(). */
switch (table_coerced) {
case 0 /* RT_TABLE_UNSPEC */:
return 254;
case 254 /* RT_TABLE_MAIN */:
return normalize ? 254 : 0;
default:
return table_coerced;
}
}
static inline gboolean
nm_platform_route_table_is_main (guint32 table)
{
/* same as
* nm_platform_route_table_uncoerce (table, TRUE) == RT_TABLE_MAIN
* and
* nm_platform_route_table_uncoerce (nm_platform_route_table_coerce (table), TRUE) == RT_TABLE_MAIN
*
* That is, the function operates the same on @table and its coerced
* form.
*/
return table == 0 || table == 254;
}
/**
* nm_platform_route_scope_inv:
* @scope: the route scope, either its original value, or its inverse.