diff options
author | Thomas Haller <thaller@redhat.com> | 2020-07-27 16:43:36 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2020-07-31 08:53:06 +0200 |
commit | 695ce608bafb8a05d704b6705150cce10e869fe9 (patch) | |
tree | 06af8bb0e01446c69bf9fc6b0f8fe78f216dc0bb | |
parent | 81cf493b96fb2138e046b00fdfbebe95294b459b (diff) | |
download | NetworkManager-695ce608bafb8a05d704b6705150cce10e869fe9.tar.gz |
l3cfg: add nm_l3_config_data_add_dependent_routes()
-rw-r--r-- | src/nm-l3-config-data.c | 334 | ||||
-rw-r--r-- | src/nm-l3-config-data.h | 14 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 2 |
3 files changed, 350 insertions, 0 deletions
diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index 1dec11ed89..7adcf15ed7 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -6,6 +6,7 @@ #include <linux/if.h> #include <linux/if_addr.h> +#include <linux/rtnetlink.h> #include "nm-core-internal.h" #include "platform/nm-platform.h" @@ -460,6 +461,63 @@ nm_l3_config_data_unref (const NML3ConfigData *self) /*****************************************************************************/ +static const NMDedupMultiEntry * +_lookup_route (const NMDedupMultiIndex *multi_idx, + const DedupMultiIdxType *idx_type, + const NMPObject *needle) +{ + const NMDedupMultiEntry *entry; + + nm_assert (multi_idx); + nm_assert (idx_type); + nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == idx_type->obj_type); + + entry = nm_dedup_multi_index_lookup_obj (multi_idx, + &idx_type->parent, + needle); + nm_assert ( !entry + || ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE + && nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), NMP_OBJECT_CAST_IP4_ROUTE (needle), NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) + || ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP6_ROUTE + && nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (entry->obj), NMP_OBJECT_CAST_IP6_ROUTE (needle), NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0)); + + return entry; +} + +const NMDedupMultiEntry * +nm_l3_config_data_lookup_route_obj (const NML3ConfigData *self, + const NMPObject *needle) +{ + gboolean IS_IPv4; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (needle), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + IS_IPv4 = NM_IS_IPv4 (NMP_OBJECT_GET_ADDR_FAMILY (needle)); + return _lookup_route (self->multi_idx, + &self->idx_routes_x[IS_IPv4], + needle); +} + +const NMDedupMultiEntry * +nm_l3_config_data_lookup_route (const NML3ConfigData *self, + int addr_family, + const NMPlatformIPRoute *needle) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + NMPObject obj_stack; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert_addr_family (addr_family); + nm_assert (needle); + + return _lookup_route (self->multi_idx, + &self->idx_routes_x[IS_IPv4], + nmp_object_stackinit (&obj_stack, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4), needle)); +} + const NMDedupMultiHeadEntry * nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_type) { @@ -1088,6 +1146,282 @@ nm_l3_config_data_cmp (const NML3ConfigData *a, const NML3ConfigData *b) /*****************************************************************************/ +static const NMPObject * +_data_get_direct_route_for_host (const NML3ConfigData *self, + int addr_family, + gconstpointer host, + guint32 route_table) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + const NMPObject *best_route_obj = NULL; + const NMPlatformIPXRoute *best_route = NULL; + const NMPObject *item_obj; + NMDedupMultiIter ipconf_iter; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert_addr_family (addr_family); + nm_assert (host); + + if (nm_ip_addr_is_null (addr_family, host)) + return NULL; + + nm_l3_config_data_iter_obj_for_each (ipconf_iter, self, item_obj, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)) { + const NMPlatformIPXRoute *item = NMP_OBJECT_CAST_IPX_ROUTE (item_obj); + + if (nm_ip_addr_is_null (addr_family, + nm_platform_ip_route_get_gateway (addr_family, &item->rx))) + continue; + + if ( best_route + && best_route->rx.plen > item->rx.plen) + continue; + + if (nm_platform_route_table_uncoerce (item->rx.table_coerced, TRUE) != route_table) + continue; + + if (!nm_utils_ip_address_same_prefix (addr_family, host, item->rx.network_ptr, item->rx.plen)) + continue; + + if ( best_route + && best_route->rx.metric <= item->rx.metric) + continue; + + best_route_obj = item_obj; + best_route = item; + } + return best_route_obj; +} + +/*****************************************************************************/ + +void +nm_l3_config_data_add_dependent_routes (NML3ConfigData *self, + int addr_family, + guint32 route_table, + guint32 route_metric, + gboolean is_vrf, + GPtrArray **out_ip4_dev_route_blacklist) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; + gs_unref_ptrarray GPtrArray *extra_onlink_routes = NULL; + const NMPObject *my_addr_obj; + const NMPObject *my_route_obj; + NMPlatformIPXRoute rx; + NMDedupMultiIter iter; + in_addr_t network_4 = 0; + guint i; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + + /* For IPv6 slaac, we explicitly add the device-routes (onlink). + * As we don't do that for IPv4 and manual IPv6 addresses. Add them here + * as dependent routes. */ + + if (!IS_IPv4) { + /* Pre-generate multicast route */ + rx.r6 = (NMPlatformIP6Route) { + .ifindex = self->ifindex, + .network.s6_addr[0] = 0xffu, + .plen = 8, + .table_coerced = nm_platform_route_table_coerce (RT_TABLE_LOCAL), + .type_coerced = nm_platform_route_type_coerce (RTN_UNICAST), + .metric = 256, + }; + nm_l3_config_data_add_route (self, addr_family, NULL, &rx.rx); + } + + nm_l3_config_data_iter_obj_for_each (iter, self, my_addr_obj, NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4)) { + const NMPlatformIPXAddress *const my_addr = NMP_OBJECT_CAST_IPX_ADDRESS (my_addr_obj); + + if (my_addr->ax.external) + continue; + + if (IS_IPv4) { + nm_assert (my_addr->a4.plen <= 32); + if (my_addr->a4.plen == 0) + continue; + } + + if (IS_IPv4) { + rx.r4 = (NMPlatformIP4Route) { + .ifindex = self->ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_KERNEL, + .network = my_addr->a4.address, + .plen = 32, + .pref_src = my_addr->a4.address, + .type_coerced = nm_platform_route_type_coerce (RTN_LOCAL), + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_HOST), + .table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL), + }; + } else { + rx.r6 = (NMPlatformIP6Route) { + .ifindex = self->ifindex, + .network = my_addr->a6.address, + .plen = 128, + .type_coerced = nm_platform_route_type_coerce (RTN_LOCAL), + .metric = 0, + .table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL), + }; + } + nm_l3_config_data_add_route (self, addr_family, NULL, &rx.rx); + + if (my_addr->ax.plen == 0) + continue; + + if (IS_IPv4) { + network_4 = nm_utils_ip4_address_clear_host_address (my_addr->a4.peer_address, + my_addr->a4.plen); + + if (nm_utils_ip4_address_is_zeronet (network_4)) { + /* Kernel doesn't add device-routes for destinations that + * start with 0.x.y.z. Skip them. */ + continue; + } + + if ( my_addr->a4.plen == 32 + && my_addr->a4.address == my_addr->a4.peer_address) { + /* Kernel doesn't add device-routes for /32 addresses unless + * they have a peer. */ + continue; + } + } else { + if (NM_FLAGS_HAS (my_addr->a6.n_ifa_flags, IFA_F_NOPREFIXROUTE)) + continue; + } + + if (IS_IPv4) { + rx.r4 = (NMPlatformIP4Route) { + .ifindex = self->ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_KERNEL, + .network = network_4, + .plen = my_addr->a4.plen, + .pref_src = my_addr->a4.address, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + .scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK), + }; + nm_platform_ip_route_normalize (addr_family, &rx.rx); + nm_l3_config_data_add_route (self, addr_family, NULL, &rx.rx); + + if ( IS_IPv4 + && out_ip4_dev_route_blacklist + && ( route_table != RT_TABLE_MAIN + || route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE)) { + + rx.r4.table_coerced = nm_platform_route_table_coerce (RT_TABLE_MAIN); + rx.r4.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; + nm_platform_ip_route_normalize (addr_family, &rx.rx); + + if (nm_l3_config_data_lookup_route (self, + addr_family, + &rx.rx)) { + /* we track such a route explicitly. Don't blacklist it. */ + } else { + if (!ip4_dev_route_blacklist) + ip4_dev_route_blacklist = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (ip4_dev_route_blacklist, + nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, &rx)); + } + } + } else { + const gboolean has_peer = !IN6_IS_ADDR_UNSPECIFIED (&my_addr->a6.peer_address); + int routes_i; + + /* If we have an IPv6 peer, we add two /128 routes + * (unless, both addresses are identical). */ + for (routes_i = 0; routes_i < 2; routes_i++) { + struct in6_addr a6_stack; + const struct in6_addr *a6; + guint8 plen; + + if ( routes_i == 1 + && has_peer + && IN6_ARE_ADDR_EQUAL (&my_addr->a6.address, &my_addr->a6.peer_address)) + break; + + if (has_peer) { + if (routes_i == 0) + a6 = &my_addr->a6.address; + else + a6 = &my_addr->a6.peer_address; + plen = 128; + } else { + a6 = nm_utils_ip6_address_clear_host_address (&a6_stack, &my_addr->a6.address, my_addr->a6.plen); + plen = my_addr->a6.plen; + } + + rx.r6 = (NMPlatformIP6Route) { + .ifindex = self->ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_KERNEL, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + .network = *a6, + .plen = plen, + }; + nm_platform_ip_route_normalize (addr_family, &rx.rx); + nm_l3_config_data_add_route (self, addr_family, NULL, &rx.rx); + } + } + } + + nm_l3_config_data_iter_obj_for_each (iter, self, my_route_obj, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)) { + const NMPlatformIPXRoute *my_route = NMP_OBJECT_CAST_IPX_ROUTE (my_route_obj); + NMPObject *new_route; + NMPlatformIPXRoute *new_r; + const NMIPAddr *p_gateway; + + if ( !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (my_route) + || NM_IS_IP_CONFIG_SOURCE_RTPROT (my_route->rx.rt_source)) + continue; + + p_gateway = nm_platform_ip_route_get_gateway (addr_family, &my_route->rx); + + if (nm_ip_addr_is_null (addr_family, p_gateway)) + continue; + + if (_data_get_direct_route_for_host (self, + addr_family, + p_gateway, + nm_platform_route_table_uncoerce (my_route->rx.table_coerced, TRUE))) + continue; + + new_route = nmp_object_clone (my_route_obj, FALSE); + new_r = NMP_OBJECT_CAST_IPX_ROUTE (new_route); + if (IS_IPv4) { + new_r->r4.network = my_route->r4.gateway; + new_r->r4.plen = 32; + new_r->r4.gateway = 0; + } else { + new_r->r6.network = my_route->r6.gateway; + new_r->r6.plen = 128; + new_r->r6.gateway = in6addr_any; + } + + /* we cannot add the route right away, because that invalidates the iteration. */ + if (!extra_onlink_routes) + extra_onlink_routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + g_ptr_array_add (extra_onlink_routes, new_route); + } + if (extra_onlink_routes) { + for (i = 0; i < extra_onlink_routes->len; i++) { + nm_l3_config_data_add_route_full (self, + addr_family, + extra_onlink_routes->pdata[i], + NULL, + NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE, + NULL, + NULL); + } + } + + NM_SET_OUT (out_ip4_dev_route_blacklist, g_steal_pointer (&ip4_dev_route_blacklist)); +} + +/*****************************************************************************/ + static void _init_from_connection_ip (NML3ConfigData *self, int addr_family, diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 09b46a2ce4..84c33db163 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -111,6 +111,13 @@ NML3ConfigData *nm_l3_config_data_new_combined (NMDedupMultiIndex *multi_idx, const NML3ConfigDatMergeInfo *const*merge_infos, guint merge_infos_len); +void nm_l3_config_data_add_dependent_routes (NML3ConfigData *self, + int addr_family, + guint32 route_table, + guint32 route_metric, + gboolean is_vrf, + GPtrArray **out_ip4_dev_route_blacklist); + /*****************************************************************************/ int nm_l3_config_data_get_ifindex (const NML3ConfigData *self); @@ -140,6 +147,13 @@ nm_l3_config_data_equal (const NML3ConfigData *a, const NML3ConfigData *b) /*****************************************************************************/ +const NMDedupMultiEntry *nm_l3_config_data_lookup_route_obj (const NML3ConfigData *self, + const NMPObject *needle); + +const NMDedupMultiEntry *nm_l3_config_data_lookup_route (const NML3ConfigData *self, + int addr_family, + const NMPlatformIPRoute *needle); + const NMDedupMultiHeadEntry *nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_type); static inline const NMDedupMultiHeadEntry * diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index f50d272746..5c041cf8f3 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -4936,6 +4936,8 @@ nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, nm_assert (NM_IS_PLATFORM (self)); nm_assert (ifindex > 0); + /* TODO: the blacklist should be maintained by NML3Cfg. */ + priv = NM_PLATFORM_GET_PRIVATE (self); /* first, expire all for current ifindex... */ |