From e47dd2ee224936a197a6bf81e47db49537a13aa9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Sep 2021 07:48:43 +0200 Subject: [PATCH] l3cfg: configure dependent routes when creating combined config --- src/core/nm-l3-config-data.c | 282 +++++++++++++------------------ src/core/nm-l3-config-data.h | 12 +- src/core/nm-l3cfg.c | 33 ++++ src/core/nm-l3cfg.h | 2 + src/libnm-platform/nm-platform.c | 51 ++++++ src/libnm-platform/nm-platform.h | 8 + 6 files changed, 222 insertions(+), 166 deletions(-) diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index ca82cd1e31..0670912182 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -2100,8 +2100,8 @@ _data_get_direct_route_for_host(const NML3ConfigData *self, 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))) + 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) @@ -2159,9 +2159,6 @@ nm_l3_config_data_get_blacklisted_ip4_routes(const NML3ConfigData *self, gboolea in_addr_t network_4; NMPlatformIPXRoute rx; - if (my_addr->external) - continue; - nm_assert(my_addr->plen <= 32); if (my_addr->plen == 0) continue; @@ -2210,173 +2207,35 @@ nm_l3_config_data_get_blacklisted_ip4_routes(const NML3ConfigData *self, gboolea /*****************************************************************************/ void -nm_l3_config_data_add_dependent_routes(NML3ConfigData *self, - int addr_family, - guint32 route_table, - guint32 route_metric, - gboolean is_vrf) +nm_l3_config_data_add_dependent_onlink_routes(NML3ConfigData *self, int addr_family) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); gs_unref_ptrarray GPtrArray *extra_onlink_routes = NULL; - const NMPObject * my_addr_obj; - const NMPObject * my_route_obj; - NMPlatformIPXRoute rx; + const NMPObject * obj_src; NMDedupMultiIter iter; - in_addr_t network_4 = 0; + int IS_IPv4; guint i; + if (addr_family == AF_UNSPEC) { + nm_l3_config_data_add_dependent_onlink_routes(self, AF_INET); + nm_l3_config_data_add_dependent_onlink_routes(self, AF_INET6); + return; + } + + IS_IPv4 = NM_IS_IPv4(addr_family); + 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); - } 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); + nm_l3_config_data_iter_obj_for_each (&iter, self, &obj_src, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) { + const NMPlatformIPXRoute *route_src = NMP_OBJECT_CAST_IPX_ROUTE(obj_src); 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); + p_gateway = nm_platform_ip_route_get_gateway(addr_family, &route_src->rx); if (nm_ip_addr_is_null(addr_family, p_gateway)) continue; @@ -2385,17 +2244,17 @@ nm_l3_config_data_add_dependent_routes(NML3ConfigData *self, self, addr_family, p_gateway, - nm_platform_route_table_uncoerce(my_route->rx.table_coerced, TRUE))) + nm_platform_route_table_uncoerce(route_src->rx.table_coerced, TRUE))) continue; - new_route = nmp_object_clone(my_route_obj, FALSE); + new_route = nmp_object_clone(obj_src, FALSE); new_r = NMP_OBJECT_CAST_IPX_ROUTE(new_route); if (IS_IPv4) { - new_r->r4.network = my_route->r4.gateway; + new_r->r4.network = route_src->r4.gateway; new_r->r4.plen = 32; new_r->r4.gateway = 0; } else { - new_r->r6.network = my_route->r6.gateway; + new_r->r6.network = route_src->r6.gateway; new_r->r6.plen = 128; new_r->r6.gateway = in6addr_any; } @@ -2418,6 +2277,107 @@ nm_l3_config_data_add_dependent_routes(NML3ConfigData *self, } } +void +nm_l3_config_data_add_dependent_device_routes(NML3ConfigData * self, + int addr_family, + guint32 route_table, + guint32 route_metric, + const NML3ConfigData *source) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + const NMPObject * obj_src; + NMPlatformIPXRoute rx; + NMDedupMultiIter iter; + + nm_assert_addr_family(addr_family); + nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); + nm_assert(_NM_IS_L3_CONFIG_DATA(source, TRUE)); + nm_assert(self != source); + + /* For IPv6 slaac, we explicitly add the device-routes (onlink) and track them + * as regular routes in NML3ConfigData. + * + * For IPv4 and for manual IPv6 addresses we don't do that. Instead, add those + * routes automatically afterwards. + * + * As route-table/metric is associated with the source l3cd, we need to process + * all source l3cds separately. */ + + nm_l3_config_data_iter_obj_for_each (&iter, + source, + &obj_src, + NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) { + const NMPlatformIPXAddress *const addr_src = NMP_OBJECT_CAST_IPX_ADDRESS(obj_src); + const NMPlatformIPXAddress * addr_dst; + + addr_dst = NMP_OBJECT_CAST_IPX_ADDRESS( + nm_dedup_multi_entry_get_obj(nm_l3_config_data_lookup_obj(self, obj_src))); + if (!addr_dst) + continue; + + if (IS_IPv4) { + NMPlatformIPXRoute r_stack; + const NMPlatformIPRoute *r; + + if (addr_dst->a4.a_acd_not_ready) + continue; + + r = (NMPlatformIPRoute *) nm_platform_ip4_address_generate_device_route(&addr_src->a4, + self->ifindex, + route_table, + route_metric, + &r_stack.r4); + if (r) + nm_l3_config_data_add_route(self, addr_family, NULL, r); + } else { + const gboolean has_peer = !IN6_IS_ADDR_UNSPECIFIED(&addr_src->a6.peer_address); + int routes_i; + + if (addr_src->ax.plen == 0) + continue; + + if (NM_FLAGS_HAS(addr_src->a6.n_ifa_flags, IFA_F_NOPREFIXROUTE)) + continue; + + /* 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(&addr_src->a6.address, &addr_src->a6.peer_address)) + break; + + if (has_peer) { + if (routes_i == 0) + a6 = &addr_src->a6.address; + else + a6 = &addr_src->a6.peer_address; + plen = 128; + } else { + a6 = nm_utils_ip6_address_clear_host_address(&a6_stack, + &addr_src->a6.address, + addr_src->a6.plen); + plen = addr_src->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); + } + } + } +} + /*****************************************************************************/ static void diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index 35b316d162..523b9a0d35 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -158,11 +158,13 @@ void nm_l3_config_data_merge(NML3ConfigData * self, GPtrArray *nm_l3_config_data_get_blacklisted_ip4_routes(const NML3ConfigData *self, gboolean is_vrf); -void nm_l3_config_data_add_dependent_routes(NML3ConfigData *self, - int addr_family, - guint32 route_table, - guint32 route_metric, - gboolean is_vrf); +void nm_l3_config_data_add_dependent_onlink_routes(NML3ConfigData *self, int addr_family); + +void nm_l3_config_data_add_dependent_device_routes(NML3ConfigData * self, + int addr_family, + guint32 route_table, + guint32 route_metric, + const NML3ConfigData *source); /*****************************************************************************/ diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 3ef4a31125..76c296cc20 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -385,6 +385,17 @@ static NM_UTILS_LOOKUP_DEFINE(_l3_acd_addr_state_to_string, /*****************************************************************************/ +gboolean +nm_l3cfg_is_vrf(const NML3Cfg *self) +{ + const NMPlatformLink *pllink; + + pllink = nm_l3cfg_get_pllink(self, TRUE); + return pllink && pllink->type == NM_LINK_TYPE_VRF; +} + +/*****************************************************************************/ + static const char * _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data, char * sbuf, @@ -3510,6 +3521,28 @@ _l3cfg_update_combined_config(NML3Cfg * self, &hook_data); } + for (i = 0; i < l3_config_datas_len; i++) { + const L3ConfigData *l3cd_data = l3_config_datas_arr[i]; + int IS_IPv4; + + if (NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ONLY_FOR_ACD)) + continue; + + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + /* FIXME(l3cfg): VRF needs to be treated specially. */ + nm_assert(!nm_l3cfg_is_vrf(self)); + + nm_l3_config_data_add_dependent_device_routes( + l3cd, + IS_IPv4 ? AF_INET : AF_INET6, + l3cd_data->default_route_table_x[IS_IPv4], + l3cd_data->default_route_metric_x[IS_IPv4], + l3cd_data->l3cd); + } + } + + nm_l3_config_data_add_dependent_onlink_routes(l3cd, AF_UNSPEC); + nm_assert(l3cd); nm_assert(nm_l3_config_data_get_ifindex(l3cd) == self->priv.ifindex); diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index b5da926ece..c76e9271c1 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -269,6 +269,8 @@ nm_l3cfg_get_ifname(const NML3Cfg *self, gboolean get_next) return nmp_object_link_get_ifname(nm_l3cfg_get_plobj(self, get_next)); } +gboolean nm_l3cfg_is_vrf(const NML3Cfg *self); + static inline NMNetns * nm_l3cfg_get_netns(const NML3Cfg *self) { diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 0a7b4458b6..5d4ec037ec 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -8578,6 +8578,57 @@ nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex) /*****************************************************************************/ +NMPlatformIP4Route * +nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr, + int ifindex, + guint32 route_table, + guint32 route_metric, + NMPlatformIP4Route * dst) +{ + in_addr_t network_4; + + /* When you add an IPv4 address (without "noprefixroute" flag), then kernel will + * automatically add a device route for the IPv4 subnet. This function generates + * such a route for the given address. */ + + nm_assert(addr); + nm_assert(addr->plen <= 32); + if (addr->plen == 0) + return NULL; + + if (addr->plen == 0) + return NULL; + + network_4 = nm_utils_ip4_address_clear_host_address(addr->peer_address, addr->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. */ + return NULL; + } + + if (addr->plen == 32 && addr->address == addr->peer_address) { + /* Kernel doesn't add device-routes for /32 addresses unless + * they have a peer. */ + return NULL; + } + + *dst = (NMPlatformIP4Route){ + .ifindex = ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_KERNEL, + .network = network_4, + .plen = addr->plen, + .pref_src = addr->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(AF_INET, (NMPlatformIPRoute *) dst); + + return dst; +} + const char * nm_platform_signal_change_type_to_string(NMPlatformSignalChangeType change_type) { diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index a13f921dc7..116efbeec5 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -2422,6 +2422,14 @@ struct _NMDedupMultiIndex *nm_platform_get_multi_idx(NMPlatform *self); /*****************************************************************************/ +NMPlatformIP4Route *nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr, + int ifindex, + guint32 route_table, + guint32 route_metric, + NMPlatformIP4Route *dst); + +/*****************************************************************************/ + gboolean nm_platform_ip_address_match(int addr_family, const NMPlatformIPAddress *addr, NMPlatformMatchFlags match_flag);