diff options
| author | Michael Biebl <biebl@debian.org> | 2024-12-25 20:36:29 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2024-12-25 20:36:29 +0100 |
| commit | e465722b908aa870bdc293b9a417d3c15294aa6d (patch) | |
| tree | 7c959d3b73f427d6c40522a7464eaa4531ef2f05 /src | |
| parent | 56928734cbcf1d3a02fae4f152a541df1b04e04a (diff) | |
New upstream version 1.50.1 upstream/1.50.1
Diffstat (limited to 'src')
33 files changed, 819 insertions, 158 deletions
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index b60dd3f1..3ab17aff 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -923,6 +923,91 @@ deactivate(NMDevice *device) /*****************************************************************************/ +gboolean +nm_device_bond_is_slb(NMDevice *device) +{ + NMConnection *connection; + NMSettingBond *s_bond; + + connection = nm_device_get_applied_connection(device); + if (!connection) + return FALSE; + + s_bond = nm_connection_get_setting_bond(connection); + if (!s_bond) + return FALSE; + + if (!_nm_setting_bond_opt_value_as_intbool(s_bond, NM_SETTING_BOND_OPTION_BALANCE_SLB)) + return FALSE; + + return TRUE; +} + +gboolean +nm_device_bond_announce_ports_on_slb(NMDevice *controller, NMDevice *port) +{ + NMDeviceBond *self = NM_DEVICE_BOND(controller); + int port_ifindex = nm_device_get_ifindex(port); + int controller_ifindex = nm_device_get_ifindex(controller); + NML3Cfg *l3cfg = nm_device_get_l3cfg(controller); + NMDevice *bond_controller = nm_device_get_controller(controller); + NML3Cfg *bridge_l3cfg; + gs_free in_addr_t *addrs_array = NULL; + gsize addrs_len; + + addrs_array = nm_l3cfg_get_configured_ip4_addresses(l3cfg, &addrs_len); + + if (addrs_len > 0) { + /* the bond has IPs configured, it is not attached to a + * bridge then. */ + if (!nm_bond_manager_send_arp(controller_ifindex, + -1, + nm_device_get_platform(port), + addrs_array, + addrs_len)) { + _LOGT(LOGD_BOND, + "failed to send gARP on port %s (ifindex %d)", + nm_device_get_iface(port), + port_ifindex); + return FALSE; + } + } else if (bond_controller + && nm_device_get_device_type(bond_controller) == NM_DEVICE_TYPE_BRIDGE) { + /* the bond is attached to a bridge, firts let's check if the bridge has IP + * configuration. */ + bridge_l3cfg = nm_device_get_l3cfg(bond_controller); + addrs_array = nm_l3cfg_get_configured_ip4_addresses(bridge_l3cfg, &addrs_len); + if (addrs_len > 0) { + /* the bridge has IPs configured, announcing them on the bond */ + if (!nm_bond_manager_send_arp(controller_ifindex, + -1, + nm_device_get_platform(port), + addrs_array, + addrs_len)) { + _LOGT(LOGD_BOND, + "failed to send gARP on port %s (ifindex %d) on behalf of bridge", + nm_device_get_iface(port), + port_ifindex); + return FALSE; + } + } + + /* we are going to ARP probe the content of the FDB table */ + if (!nm_bond_manager_send_arp(controller_ifindex, + nm_device_get_ifindex(bond_controller), + nm_device_get_platform(port), + NULL, + 0)) { + _LOGT(LOGD_BOND, "failed to send ARP probing with content of FDB table"); + return FALSE; + } + } + + return TRUE; +} + +/*****************************************************************************/ + static void nm_device_bond_init(NMDeviceBond *self) { diff --git a/src/core/devices/nm-device-bond.h b/src/core/devices/nm-device-bond.h index 083189bb..2a415843 100644 --- a/src/core/devices/nm-device-bond.h +++ b/src/core/devices/nm-device-bond.h @@ -23,4 +23,7 @@ typedef struct _NMDeviceBondClass NMDeviceBondClass; GType nm_device_bond_get_type(void); +gboolean nm_device_bond_is_slb(NMDevice *device); +gboolean nm_device_bond_announce_ports_on_slb(NMDevice *controller, NMDevice *port); + #endif /* NM_DEVICE_BOND_H */ diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 82c2d6b8..516e13df 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -78,6 +78,7 @@ #include "nm-hostname-manager.h" #include "nm-device-generic.h" +#include "nm-device-bond.h" #include "nm-device-bridge.h" #include "nm-device-loopback.h" #include "nm-device-vlan.h" @@ -5403,6 +5404,7 @@ get_ip_iface_identifier(NMDevice *self, NMUtilsIPv6IfaceId *out_iid) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NMPlatform *platform = nm_device_get_platform(self); const NMPlatformLink *pllink; + NMPLinkAddress permanent_hwaddr; NMLinkType link_type; const guint8 *hwaddr; guint8 pseudo_hwaddr[ETH_ALEN]; @@ -5446,6 +5448,21 @@ get_ip_iface_identifier(NMDevice *self, NMUtilsIPv6IfaceId *out_iid) hwaddr_len = G_N_ELEMENTS(pseudo_hwaddr); link_type = NM_LINK_TYPE_ETHERNET; } + } else if (NM_IN_SET(pllink->type, + NM_LINK_TYPE_VTI6, + NM_LINK_TYPE_IP6TNL, + NM_LINK_TYPE_IP6GRE)) { + /* Use the "permanent" 48-bit address to construct a EUI64 + * according to RFC 4291 Appendix A. */ + if (!nm_platform_link_get_permanent_address(platform, pllink, &permanent_hwaddr)) + return FALSE; + if (permanent_hwaddr.len < ETH_ALEN) + return FALSE; + + memcpy(pseudo_hwaddr, permanent_hwaddr.data, ETH_ALEN); + hwaddr = pseudo_hwaddr; + hwaddr_len = ETH_ALEN; + link_type = NM_LINK_TYPE_ETHERNET; } success = nm_utils_get_ipv6_interface_identifier(link_type, @@ -7339,10 +7356,12 @@ device_link_changed(gpointer user_data) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); gboolean ip_ifname_changed = FALSE; nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL; + NMDevice *controller; const NMPlatformLink *pllink; const char *str; int ifindex; gboolean was_up; + gboolean carrier_was_up; gboolean update_unmanaged_specs = FALSE; gboolean got_hw_addr = FALSE, had_hw_addr; gboolean seen_down = priv->device_link_changed_down; @@ -7425,6 +7444,8 @@ device_link_changed(gpointer user_data) _LOGD(LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); } + carrier_was_up = priv->carrier; + /* Update carrier from link event if applicable. */ if (nm_device_has_capability(self, NM_DEVICE_CAP_CARRIER_DETECT) && !nm_device_has_capability(self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) @@ -7441,6 +7462,35 @@ device_link_changed(gpointer user_data) was_up = priv->up; priv->up = NM_FLAGS_HAS(pllink->n_ifi_flags, IFF_UP); + if ((was_up && !priv->up) || (carrier_was_up && !priv->carrier)) { + /* the link was up and now is down, or the carrier was up and now is down. We must + * check if this is a port of a bond and if that bond is in balance-slb mode to perform + * gARP on the controller's port. + */ + controller = nm_device_get_controller(self); + if (controller && nm_device_get_device_type(controller) == NM_DEVICE_TYPE_BOND + && nm_device_bond_is_slb(controller)) { + NMDevicePrivate *controller_priv = NM_DEVICE_GET_PRIVATE(controller); + PortInfo *info; + + _LOGT( + LOGD_CORE, + "controller %s is a bond in bonding-slb mode, redirecting traffic to another port", + nm_device_get_iface(controller)); + + c_list_for_each_entry (info, &controller_priv->ports, lst_port) { + if (info->port != self && NM_DEVICE_GET_PRIVATE(info->port)->carrier) { + _LOGT(LOGD_CORE, + "sending gARP on port %s (ifindex %d)", + nm_device_get_iface(info->port), + nm_device_get_ifindex(info->port)); + if (nm_device_bond_announce_ports_on_slb(controller, info->port)) + break; + } + } + } + } + if (pllink->initialized && nm_device_get_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT)) { nm_device_set_unmanaged_by_user_udev(self); nm_device_set_unmanaged_by_user_conf(self); @@ -9485,6 +9535,7 @@ check_connection_compatible(NMDevice *self, NMSettingMatch *s_match; const GSList *specs; gboolean has_match = FALSE; + NMSettingSriov *s_sriov = NULL; klass = NM_DEVICE_GET_CLASS(self); if (klass->connection_type_check_compatible) { @@ -9502,12 +9553,14 @@ check_connection_compatible(NMDevice *self, return FALSE; } - if (!nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV) - && nm_connection_get_setting(connection, NM_TYPE_SETTING_SRIOV)) { - nm_utils_error_set_literal(error, - NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "device does not support SR-IOV"); - return FALSE; + if (!nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)) { + s_sriov = (NMSettingSriov *) nm_connection_get_setting(connection, NM_TYPE_SETTING_SRIOV); + if (s_sriov && nm_setting_sriov_get_total_vfs(s_sriov)) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "device does not support SR-IOV"); + return FALSE; + } } conn_iface = nm_manager_get_connection_iface(NM_MANAGER_GET, connection, NULL, NULL, &local); @@ -10118,7 +10171,7 @@ activate_stage1_device_prepare(NMDevice *self) s_sriov = nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV); } - if (s_sriov) { + if (s_sriov && nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)) { nm_auto_freev NMPlatformVF **plat_vfs = NULL; gs_free_error GError *error = NULL; NMSriovVF *vf; @@ -10126,8 +10179,6 @@ activate_stage1_device_prepare(NMDevice *self) guint num; guint i; - nm_assert(nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)); - autoprobe = nm_setting_sriov_get_autoprobe_drivers(s_sriov); if (autoprobe == NM_TERNARY_DEFAULT) { autoprobe = nm_config_data_get_connection_default_int64( diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index f24b9733..ea65499e 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -327,7 +327,7 @@ _scan_request_ssids_track(NMDeviceWifiPrivate *priv, const GPtrArray *ssids) priv->scan_request_ssids_hash = g_hash_table_new(nm_pg_bytes_hash, nm_pg_bytes_equal); /* Do a little dance. New elements shall keep their order as in @ssids, but all - * new elements should be sorted in the list preexisting elements of the list. + * new elements should be sorted before preexisting elements of the list. * First move the old elements away, and splice them back afterwards. */ c_list_init(&old_lst_head); c_list_splice(&old_lst_head, &priv->scan_request_ssids_lst_head); @@ -348,6 +348,8 @@ _scan_request_ssids_track(NMDeviceWifiPrivate *priv, const GPtrArray *ssids) g_hash_table_add(priv->scan_request_ssids_hash, d); } else d->timestamp_msec = now_msec; + + c_list_unlink_stale(&d->lst); c_list_link_tail(&priv->scan_request_ssids_lst_head, &d->lst); } diff --git a/src/core/devices/wwan/nm-modem.c b/src/core/devices/wwan/nm-modem.c index ea0fa7aa..23e7de4a 100644 --- a/src/core/devices/wwan/nm-modem.c +++ b/src/core/devices/wwan/nm-modem.c @@ -206,7 +206,6 @@ nm_modem_emit_signal_new_config(NMModem *self, nm_assert(NM_IS_MODEM(self)); nm_assert_addr_family(addr_family); nm_assert(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd)); - nm_assert(!do_auto || addr_family == AF_INET6); nm_assert(!iid || addr_family == AF_INET6); nm_assert(!error || (!l3cd && !do_auto && !iid)); diff --git a/src/core/ndisc/nm-lndp-ndisc.c b/src/core/ndisc/nm-lndp-ndisc.c index 932366ff..eea79373 100644 --- a/src/core/ndisc/nm-lndp-ndisc.c +++ b/src/core/ndisc/nm-lndp-ndisc.c @@ -115,7 +115,8 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) NMNDisc *ndisc = (NMNDisc *) user_data; NMNDiscDataInternal *rdata = ndisc->rdata; NMNDiscConfigMap changed = 0; - struct ndp_msgra *msgra = ndp_msgra(msg); + NMNDiscGateway gateway; + struct ndp_msgra *msgra = ndp_msgra(msg); struct in6_addr gateway_addr; const gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); int offset; @@ -174,23 +175,17 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) * Subsequent router advertisements can represent new default gateways * on the network. We should present all of them in router preference * order. - */ - { - const NMNDiscGateway gateway = { - .address = gateway_addr, - .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, ndp_msgra_router_lifetime(msgra)), - .preference = _route_preference_coerce(ndp_msgra_route_preference(msgra)), - }; - - /* https://tools.ietf.org/html/rfc2461#section-4.2 - * > A Lifetime of 0 indicates that the router is not a - * > default router and SHOULD NOT appear on the default - * > router list. - * We handle that by tracking a gateway that expires right now. */ - - if (nm_ndisc_add_gateway(ndisc, &gateway, now_msec)) - changed |= NM_NDISC_CONFIG_GATEWAYS; - } + * + * https://tools.ietf.org/html/rfc2461#section-4.2 : + * A Lifetime of 0 indicates that the router is not a default router and + * SHOULD NOT appear on the default router list. + * + * We handle that by tracking a gateway that expires right now. */ + gateway = (NMNDiscGateway){ + .address = gateway_addr, + .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, ndp_msgra_router_lifetime(msgra)), + .preference = _route_preference_coerce(ndp_msgra_route_preference(msgra)), + }; /* Addresses & Routes */ ndp_msg_opt_for_each_offset (offset, msg, NDP_MSG_OPT_PREFIX) { @@ -240,9 +235,24 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) guint8 plen = ndp_msg_opt_route_prefix_len(msg, offset); struct in6_addr network; - if (plen == 0 || plen > 128) + if (plen > 128) continue; + if (plen == 0) { + /* https://tools.ietf.org/html/rfc4191#section-3.1 : + * When processing a Router Advertisement, a type C host first updates a + * ::/0 route based on the Router Lifetime and Default Router Preference + * in the Router Advertisement message header. [...] The Router Preference + * and Lifetime values in a ::/0 Route Information Option override the + * preference and lifetime values in the Router Advertisement header. + */ + gateway.preference = + _route_preference_coerce(ndp_msg_opt_route_preference(msg, offset)); + gateway.expiry_msec = + _nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_route_lifetime(msg, offset)); + continue; + } + nm_ip6_addr_clear_host_address(&network, ndp_msg_opt_route_prefix(msg, offset), plen); { @@ -262,6 +272,9 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) } } + if (nm_ndisc_add_gateway(ndisc, &gateway, now_msec)) + changed |= NM_NDISC_CONFIG_GATEWAYS; + ndp_msg_opt_for_each_offset (offset, msg, NDP_MSG_OPT_RDNSS) { struct in6_addr *addr; int addr_index; diff --git a/src/core/nm-bond-manager.c b/src/core/nm-bond-manager.c index 71cd4ee7..f24c8163 100644 --- a/src/core/nm-bond-manager.c +++ b/src/core/nm-bond-manager.c @@ -6,8 +6,13 @@ #include <linux/if.h> +#include <linux/if_ether.h> +#include <linux/if_packet.h> +#include <sys/socket.h> + #include "NetworkManagerUtils.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" +#include "libnm-platform/nm-linux-platform.h" #include "libnm-glib-aux/nm-str-buf.h" #include "libnm-platform/nm-platform.h" #include "libnm-platform/nmp-object.h" @@ -94,6 +99,32 @@ struct _NMBondManager { /*****************************************************************************/ +#define IP_ADDR_LEN 4 + +#define ARP_OP_GARP 0x0001 +#define ARP_OP_RARP 0x0003 + +#define ARP_HW_TYPE_ETH 0x0001 + +#define ARP_PROTOCOL_IPV4 0x0800 + +typedef struct _nm_packed { + char s_addr[ETH_ALEN]; + char d_addr[ETH_ALEN]; + guint16 eth_type; + guint16 hw_type; + guint16 protocol; + guint8 addr_len; + guint8 ip_len; + guint16 op; + char s_hw_addr[ETH_ALEN]; + char s_ip_addr[IP_ADDR_LEN]; + char d_hw_addr[ETH_ALEN]; + char d_ip_addr[IP_ADDR_LEN]; +} ARPPacket; + +/*****************************************************************************/ + static void _nft_call(NMBondManager *self, gboolean up, const char *bond_ifname, @@ -839,6 +870,89 @@ nm_bond_manager_reapply(NMBondManager *self) _reconfigure_check(self, TRUE); } +gboolean +nm_bond_manager_send_arp(int bond_ifindex, + int bridge_ifindex, + struct _NMPlatform *platform, + in_addr_t *addrs_array, + gsize addrs_len) +{ + struct sockaddr_ll addr = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_ARP), + .sll_ifindex = bond_ifindex, + }; + ARPPacket data; + const guint8 *hwaddr; + gsize hwaddrlen = 0; + nm_auto_close int sockfd = -1; + bool announce_fdb = FALSE; + + nm_assert(NM_IS_PLATFORM(platform)); + nm_assert(bond_ifindex); + + /* if the bridge_ifindex is specified is because we want to + * announce the FDB table content from the bridge */ + if (bridge_ifindex > 0) + announce_fdb = TRUE; + + sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); + if (sockfd < 0) + return FALSE; + + hwaddr = nm_platform_link_get_address(platform, bond_ifindex, &hwaddrlen); + /* infiniband interfaces not supported */ + if (hwaddrlen > ETH_ALEN) + return FALSE; + + /* common ARP options to be configured */ + memset(data.d_addr, 0xff, ETH_ALEN); + data.eth_type = htons(ETH_P_ARP); + data.hw_type = htons(ARP_HW_TYPE_ETH); + data.protocol = htons(ARP_PROTOCOL_IPV4); + data.addr_len = ETH_ALEN; + data.ip_len = IP_ADDR_LEN; + + if (announce_fdb) { + /* if we are announcing the FDB we do a RARP, we don't set the + * source/dest IPv4 address */ + int ifindexes[] = {bridge_ifindex, bond_ifindex}; + int i; + gs_free NMEtherAddr **fdb_addrs = NULL; + + fdb_addrs = nm_linux_platform_get_link_fdb_table(platform, ifindexes, 2); + /* we want to send a Reverse ARP (RARP) packet */ + data.op = htons(ARP_OP_RARP); + + i = 0; + while (fdb_addrs[i] != NULL) { + NMEtherAddr *tmp_hwaddr = fdb_addrs[i]; + memcpy(data.s_hw_addr, tmp_hwaddr, ETH_ALEN); + memcpy(data.d_hw_addr, tmp_hwaddr, ETH_ALEN); + memcpy(data.s_addr, tmp_hwaddr, ETH_ALEN); + g_free(tmp_hwaddr); + if (sendto(sockfd, &data, sizeof(data), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) + return FALSE; + i++; + } + } else { + /* we want to send a Gratuitous ARP (GARP) packet */ + data.op = htons(ARP_OP_GARP); + memcpy(data.s_addr, hwaddr, hwaddrlen); + memcpy(data.s_hw_addr, hwaddr, hwaddrlen); + for (int i = 0; i < addrs_len; i++) { + const in_addr_t tmp_addr = addrs_array[i]; + + unaligned_write_ne32(data.s_ip_addr, tmp_addr); + unaligned_write_ne32(data.d_ip_addr, tmp_addr); + if (sendto(sockfd, &data, sizeof(data), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) + return FALSE; + } + } + + return TRUE; +} + /*****************************************************************************/ int diff --git a/src/core/nm-bond-manager.h b/src/core/nm-bond-manager.h index 92a89f0b..78ada15b 100644 --- a/src/core/nm-bond-manager.h +++ b/src/core/nm-bond-manager.h @@ -23,6 +23,12 @@ NMBondManager *nm_bond_manager_new(struct _NMPlatform *platform, void nm_bond_manager_reapply(NMBondManager *self); +gboolean nm_bond_manager_send_arp(int bond_ifindex, + int bridge_ifindex, + struct _NMPlatform *platform, + in_addr_t *addrs_array, + gsize addrs_len); + void nm_bond_manager_destroy(NMBondManager *self); int nm_bond_manager_get_ifindex(NMBondManager *self); diff --git a/src/core/nm-config.c b/src/core/nm-config.c index 878f343a..cc3d1135 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -1558,6 +1558,7 @@ intern_config_read(const char *filename, gs_strfreev char **groups = NULL; guint g, k; gboolean has_intern = FALSE; + gboolean has_global_dns; g_return_val_if_fail(filename, NULL); @@ -1575,6 +1576,8 @@ intern_config_read(const char *filename, goto out; } + has_global_dns = nm_config_keyfile_has_global_dns_config(keyfile_conf, FALSE); + groups = g_key_file_get_groups(keyfile, NULL); for (g = 0; groups && groups[g]; g++) { gs_strfreev char **keys = NULL; @@ -1591,6 +1594,21 @@ intern_config_read(const char *filename, is_intern = NM_STR_HAS_PREFIX(group, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); is_atomic = !is_intern && _is_atomic_section(atomic_section_prefixes, group); + if (has_global_dns + && (nm_streq0(group, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS) + || NM_STR_HAS_PREFIX_WITH_MORE( + group, + NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN))) { + /* + * If user configuration specifies global DNS options, the DNS + * options in internal configuration must be deleted. Otherwise, a + * deletion of options from user configuration may cause the + * internal options to appear again. + */ + needs_rewrite = TRUE; + continue; + } + if (is_atomic) { gs_free char *conf_section_was = NULL; gs_free char *conf_section_is = NULL; @@ -1684,26 +1702,6 @@ intern_config_read(const char *filename, } out: - /* - * If user configuration specifies global DNS options, the DNS - * options in internal configuration must be deleted. Otherwise, a - * deletion of options from user configuration may cause the - * internal options to appear again. - */ - if (nm_config_keyfile_has_global_dns_config(keyfile_conf, FALSE)) { - if (g_key_file_remove_group(keyfile_intern, - NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, - NULL)) - needs_rewrite = TRUE; - for (g = 0; groups && groups[g]; g++) { - if (NM_STR_HAS_PREFIX(groups[g], NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN) - && groups[g][NM_STRLEN(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN)]) { - g_key_file_remove_group(keyfile_intern, groups[g], NULL); - needs_rewrite = TRUE; - } - } - } - g_key_file_unref(keyfile); if (out_needs_rewrite) diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 45dab093..a88c6f1a 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -844,6 +844,18 @@ nm_firewall_nft_stdio_mlag(gboolean up, chain_name, previous_member); _append(&strbuf, "delete chain netdev %s %s", table_name, chain_name); + + chain_name = + _strbuf_set_sanitized(&strbuf_1, "tx-redirect-igmp-reports-", previous_member); + + _append(&strbuf, + "add chain netdev %s %s {" + " type filter hook egress device %s priority filter + 1; " + "}", + table_name, + chain_name, + previous_member); + _append(&strbuf, "delete chain netdev %s %s", table_name, chain_name); } /* OVS SLB rule 1 @@ -940,6 +952,57 @@ nm_firewall_nft_stdio_mlag(gboolean up, "add rule netdev %s rx-drop-looped-packets ether saddr @macset-untagged%s drop", table_name, s_counter); + + /* IGMP SNOOPING + * + * This redirects all IGMP reports to the primary member port. The TOR switches + * may prune the multicast tree. If we let the bonding vlan+srcmac hash occur, + * then the TOR may send the pruned multicast stream to a bond member port for + * which the RX filters will drop (e.g. report out non-primary, stream in + * non-primary). If it's known multicast then we must control the pruned tree by + * only sending the IGMP reports on a port for which we will accept the traffic, + * i.e. the primary member port. + */ + for (i = 0; i < n_active_members; i++) { + const char *active_member = active_members[i]; + const char *chain_name; + + if (!_nft_ifname_valid(active_member)) + continue; + + chain_name = + _strbuf_set_sanitized(&strbuf_1, "tx-redirect-igmp-reports-", active_member); + + _append(&strbuf, + "add chain netdev %s %s {" + " type filter hook egress device %s priority filter + 1; " + "}", + table_name, + chain_name, + active_member); + /* first is primary, we clean up in case it was previously a non-primary member */ + if (i == 0) { + _append(&strbuf, "delete chain netdev %s %s", table_name, chain_name); + continue; + } + + _append(&strbuf, + "add rule netdev %s %s igmp type {" + " membership-report-v1, membership-report-v2, membership-report-v3 " + "}%s fwd to %s", + table_name, + chain_name, + s_counter, + active_members[0]); + _append(&strbuf, + "add rule netdev %s %s icmpv6 type {" + " mld-listener-report, mld2-listener-report " + "}%s fwd to %s", + table_name, + chain_name, + s_counter, + active_members[0]); + } } out: diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 2c977991..a7189d3e 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -3143,6 +3143,15 @@ handle_start_defending: * warning and start a timer to retry. This way (of having a timer pending) * we also back off and are rate limited from retrying too frequently. */ _LOGT_acd(acd_data, "start announcing failed to create probe (%s)", failure_reason); + + if (!nm_platform_link_uses_arp(self->priv.platform, self->priv.ifindex)) { + _LOGT_acd( + acd_data, + "give up on ACD and never retry since interface '%s' is configured with NOARP", + nmp_object_link_get_ifname(self->priv.plobj)); + return; + } + _l3_acd_data_timeout_schedule(acd_data, ACD_WAIT_TIME_ANNOUNCE_RESTART_MSEC); return; } @@ -4999,7 +5008,7 @@ _l3_commit_one(NML3Cfg *self, } if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) - route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN; + route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES; if (any_dirty) _obj_states_track_prune_dirty(self, TRUE); @@ -5028,6 +5037,8 @@ _l3_commit_one(NML3Cfg *self, } if (c_list_is_empty(&self->priv.p->blocked_lst_head_x[IS_IPv4])) { + gs_unref_ptrarray GPtrArray *routes_old = NULL; + addresses_prune = nm_platform_ip_address_get_prune_list(self->priv.platform, addr_family, @@ -5035,10 +5046,28 @@ _l3_commit_one(NML3Cfg *self, nm_g_array_data(ipv6_temp_addrs_keep), nm_g_array_len(ipv6_temp_addrs_keep)); + if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES) { + GHashTableIter h_iter; + ObjStateData *obj_state; + + /* Get list of all the routes that were configured by us */ + routes_old = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); + g_hash_table_iter_init(&h_iter, self->priv.p->obj_state_hash); + while (g_hash_table_iter_next(&h_iter, (gpointer *) &obj_state, NULL)) { + if (NMP_OBJECT_GET_TYPE(obj_state->obj) == NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4) + && obj_state->os_nm_configured) + g_ptr_array_add(routes_old, (gpointer) nmp_object_ref(obj_state->obj)); + } + + nm_platform_route_objs_sort(routes_old, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY); + } + routes_prune = nm_platform_ip_route_get_prune_list(self->priv.platform, addr_family, self->priv.ifindex, - route_table_sync); + route_table_sync, + routes_old); + _obj_state_zombie_lst_prune_all(self, addr_family); } } else { @@ -5390,6 +5419,30 @@ nm_l3cfg_get_best_default_route(NML3Cfg *self, int addr_family, gboolean get_com return nm_l3_config_data_get_best_default_route(l3cd, addr_family); } +in_addr_t * +nm_l3cfg_get_configured_ip4_addresses(NML3Cfg *self, gsize *out_len) +{ + GArray *array = NULL; + NMDedupMultiIter iter; + const NMPObject *obj; + const NML3ConfigData *l3cd; + + l3cd = nm_l3cfg_get_combined_l3cd(self, FALSE); + + if (!l3cd) + return NULL; + + array = g_array_new(FALSE, FALSE, sizeof(in_addr_t)); + + nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP4_ADDRESS) { + in_addr_t tmp = NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address; + nm_g_array_append_simple(array, tmp); + } + + *out_len = array->len; + return NM_CAST_ALIGN(in_addr_t, g_array_free(array, FALSE)); +} + /*****************************************************************************/ gboolean diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index f977b10f..4d3537a0 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -441,6 +441,8 @@ const NML3ConfigData *nm_l3cfg_get_combined_l3cd(NML3Cfg *self, gboolean get_com const NMPObject * nm_l3cfg_get_best_default_route(NML3Cfg *self, int addr_family, gboolean get_commited); +in_addr_t *nm_l3cfg_get_configured_ip4_addresses(NML3Cfg *self, gsize *out_len); + /*****************************************************************************/ gboolean nm_l3cfg_has_commited_ip6_addresses_pending_dad(NML3Cfg *self); diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index 0d6c1e2f..b96a9053 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -462,21 +462,24 @@ static GVariant * _version_info_get(void) { const guint32 arr[] = { + /* The array contains as first element NM_VERSION, which can be + * used to numerically compare the version (see also NM_ENCODE_VERSION, + * nm_utils_version(), nm_encode_version() and nm_decode_version(). */ NM_VERSION, - }; - /* The array contains as first element NM_VERSION, which can be - * used to numerically compare the version (see also NM_ENCODE_VERSION, - * nm_utils_version(), nm_encode_version() and nm_decode_version(). - * - * The following elements of the array are a bitfield of capabilities. - * These capabilities should only depend on compile-time abilities - * (unlike NM_MANAGER_CAPABILITIES, NMCapability). The supported values - * are from NMVersionInfoCapability enum. This way to expose capabilities - * is more cumbersome but more efficient compared to NM_MANAGER_CAPABILITIES. - * As such, it is cheap to add capabilities for something, where you would - * avoid it as NM_MANAGER_CAPABILITIES due to the overhead. - */ + /* The following elements of the array are a bitfield of capabilities. + * These capabilities should only depend on compile-time abilities + * (unlike NM_MANAGER_CAPABILITIES, NMCapability). The supported values + * are from NMVersionInfoCapability enum. This way to expose capabilities + * is more cumbersome but more efficient compared to NM_MANAGER_CAPABILITIES. + * As such, it is cheap to add capabilities for something, where you would + * avoid it as NM_MANAGER_CAPABILITIES due to the overhead. + * + * Each of the array's elements has 32 bits. This means that capabilities + * with index 0-31 goes to element #1, with index 32-63 to element #2, + * with index 64-95 to element #3 and so on. */ + 1 << NM_VERSION_INFO_CAPABILITY_SYNC_ROUTE_WITH_TABLE, + }; return nm_g_variant_new_au(arr, G_N_ELEMENTS(arr)); } diff --git a/src/core/tests/test-core-with-expect.c b/src/core/tests/test-core-with-expect.c index 680843fc..da8659b2 100644 --- a/src/core/tests/test-core-with-expect.c +++ b/src/core/tests/test-core-with-expect.c @@ -229,7 +229,7 @@ do_test_nm_utils_kill_child(void) char *argv_watchdog[] = { "bash", "-c", - "sleep 4; " + "sleep 15; " "kill -KILL 0; #watchdog for #" TEST_TOKEN, NULL, }; diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c index c5a598a6..71a3d878 100644 --- a/src/core/tests/test-core.c +++ b/src/core/tests/test-core.c @@ -2671,8 +2671,10 @@ test_nm_firewall_nft_stdio_mlag(void) "nm-mlag-bond0\012flush table netdev nm-mlag-bond0\012add chain netdev nm-mlag-bond0 " "rx-drop-bc-mc-eth2 { type filter hook ingress device eth2 priority filter; }\012delete " "chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth2\012add chain netdev nm-mlag-bond0 " - "rx-drop-bc-mc-eth1 { type filter hook ingress device eth1 priority filter; }\012delete " - "chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth1\012add set netdev nm-mlag-bond0 " + "tx-redirect-igmp-reports-eth2 { type filter hook egress device eth2 priority filter + 1; " + "}\012delete chain netdev nm-mlag-bond0 tx-redirect-igmp-reports-eth2\012add chain netdev " + "nm-mlag-bond0 rx-drop-bc-mc-eth1 { type filter hook ingress device eth1 priority filter; " + "}\012delete chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth1\012add set netdev nm-mlag-bond0 " "macset-tagged { typeof ether saddr . vlan id; flags dynamic,timeout; }\012add set netdev " "nm-mlag-bond0 macset-untagged { typeof ether saddr; flags dynamic,timeout; }\012add chain " "netdev nm-mlag-bond0 tx-snoop-source-mac { type filter hook egress device bond0 priority " @@ -2683,7 +2685,9 @@ test_nm_firewall_nft_stdio_mlag(void) "priority filter; }\012add rule netdev nm-mlag-bond0 rx-drop-looped-packets ether saddr . " "vlan id @macset-tagged counter drop\012add rule netdev nm-mlag-bond0 " "rx-drop-looped-packets ether type vlan counter return\012add rule netdev nm-mlag-bond0 " - "rx-drop-looped-packets ether saddr @macset-untagged counter drop\012"); + "rx-drop-looped-packets ether saddr @macset-untagged counter drop\012add chain netdev " + "nm-mlag-bond0 tx-redirect-igmp-reports-eth1 { type filter hook egress device eth1 priority " + "filter + 1; }\012delete chain netdev nm-mlag-bond0 tx-redirect-igmp-reports-eth1\012"); _T(TRUE, "bond0", @@ -2695,8 +2699,10 @@ test_nm_firewall_nft_stdio_mlag(void) "nm-mlag-bond0\012flush table netdev nm-mlag-bond0\012add chain netdev nm-mlag-bond0 " "rx-drop-bc-mc-eth2 { type filter hook ingress device eth2 priority filter; }\012delete " "chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth2\012add chain netdev nm-mlag-bond0 " - "rx-drop-bc-mc-eth1 { type filter hook ingress device eth1 priority filter; }\012delete " - "chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth1\012add set netdev nm-mlag-bond0 " + "tx-redirect-igmp-reports-eth2 { type filter hook egress device eth2 priority filter + 1; " + "}\012delete chain netdev nm-mlag-bond0 tx-redirect-igmp-reports-eth2\012add chain netdev " + "nm-mlag-bond0 rx-drop-bc-mc-eth1 { type filter hook ingress device eth1 priority filter; " + "}\012delete chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth1\012add set netdev nm-mlag-bond0 " "macset-tagged { typeof ether saddr . vlan id; flags dynamic,timeout; }\012add set netdev " "nm-mlag-bond0 macset-untagged { typeof ether saddr; flags dynamic,timeout; }\012add chain " "netdev nm-mlag-bond0 tx-snoop-source-mac { type filter hook egress device bond0 priority " @@ -2707,7 +2713,9 @@ test_nm_firewall_nft_stdio_mlag(void) "filter; }\012add rule netdev nm-mlag-bond0 rx-drop-looped-packets ether saddr . vlan id " "@macset-tagged drop\012add rule netdev nm-mlag-bond0 rx-drop-looped-packets ether type " "vlan return\012add rule netdev nm-mlag-bond0 rx-drop-looped-packets ether saddr " - "@macset-untagged drop\012"); + "@macset-untagged drop\012add chain netdev nm-mlag-bond0 tx-redirect-igmp-reports-eth1 { " + "type filter hook egress device eth1 priority filter + 1; }\012delete chain netdev " + "nm-mlag-bond0 tx-redirect-igmp-reports-eth1\012"); _T(TRUE, "bond0", @@ -2720,23 +2728,35 @@ test_nm_firewall_nft_stdio_mlag(void) "nm-mlag-bond0\012flush table netdev nm-mlag-bond0\012add chain netdev nm-mlag-bond0 " "rx-drop-bc-mc-eth4 { type filter hook ingress device eth4 priority filter; }\012delete " "chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth4\012add chain netdev nm-mlag-bond0 " - "rx-drop-bc-mc-eth5 { type filter hook ingress device eth5 priority filter; }\012delete " - "chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth5\012add chain netdev nm-mlag-bond0 " - "rx-drop-bc-mc-eth2 { type filter hook ingress device eth2 priority filter; }\012delete " - "chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth2\012add chain netdev nm-mlag-bond0 " - "rx-drop-bc-mc-eth3 { type filter hook ingress device eth3 priority filter; }\012add rule " - "netdev nm-mlag-bond0 rx-drop-bc-mc-eth3 pkttype { broadcast, multicast } drop\012add set " - "netdev nm-mlag-bond0 macset-tagged { typeof ether saddr . vlan id; flags dynamic,timeout; " - "}\012add set netdev nm-mlag-bond0 macset-untagged { typeof ether saddr; flags " - "dynamic,timeout; }\012add chain netdev nm-mlag-bond0 tx-snoop-source-mac { type filter " - "hook egress device bond0 priority filter; }\012add rule netdev nm-mlag-bond0 " - "tx-snoop-source-mac set update ether saddr . vlan id timeout 5s @macset-tagged " - "return\012add rule netdev nm-mlag-bond0 tx-snoop-source-mac set update ether saddr timeout " - "5s @macset-untagged\012add chain netdev nm-mlag-bond0 rx-drop-looped-packets { type filter " - "hook ingress device bond0 priority filter; }\012add rule netdev nm-mlag-bond0 " - "rx-drop-looped-packets ether saddr . vlan id @macset-tagged drop\012add rule netdev " - "nm-mlag-bond0 rx-drop-looped-packets ether type vlan return\012add rule netdev " - "nm-mlag-bond0 rx-drop-looped-packets ether saddr @macset-untagged drop\012"); + "tx-redirect-igmp-reports-eth4 { type filter hook egress device eth4 priority filter + 1; " + "}\012delete chain netdev nm-mlag-bond0 tx-redirect-igmp-reports-eth4\012add chain netdev " + "nm-mlag-bond0 rx-drop-bc-mc-eth5 { type filter hook ingress device eth5 priority filter; " + "}\012delete chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth5\012add chain netdev " + "nm-mlag-bond0 tx-redirect-igmp-reports-eth5 { type filter hook egress device eth5 priority " + "filter + 1; }\012delete chain netdev nm-mlag-bond0 tx-redirect-igmp-reports-eth5\012add " + "chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth2 { type filter hook ingress device eth2 " + "priority filter; }\012delete chain netdev nm-mlag-bond0 rx-drop-bc-mc-eth2\012add chain " + "netdev nm-mlag-bond0 rx-drop-bc-mc-eth3 { type filter hook ingress device eth3 priority " + "filter; }\012add rule netdev nm-mlag-bond0 rx-drop-bc-mc-eth3 pkttype { broadcast, " + "multicast } drop\012add set netdev nm-mlag-bond0 macset-tagged { typeof ether saddr . vlan " + "id; flags dynamic,timeout; }\012add set netdev nm-mlag-bond0 macset-untagged { typeof " + "ether saddr; flags dynamic,timeout; }\012add chain netdev nm-mlag-bond0 " + "tx-snoop-source-mac { type filter hook egress device bond0 priority filter; }\012add rule " + "netdev nm-mlag-bond0 tx-snoop-source-mac set update ether saddr . vlan id timeout 5s " + "@macset-tagged return\012add rule netdev nm-mlag-bond0 tx-snoop-source-mac set update " + "ether saddr timeout 5s @macset-untagged\012add chain netdev nm-mlag-bond0 " + "rx-drop-looped-packets { type filter hook ingress device bond0 priority filter; }\012add " + "rule netdev nm-mlag-bond0 rx-drop-looped-packets ether saddr . vlan id @macset-tagged " + "drop\012add rule netdev nm-mlag-bond0 rx-drop-looped-packets ether type vlan return\012add " + "rule netdev nm-mlag-bond0 rx-drop-looped-packets ether saddr @macset-untagged drop\012add " + "chain netdev nm-mlag-bond0 tx-redirect-igmp-reports-eth2 { type filter hook egress device " + "eth2 priority filter + 1; }\012delete chain netdev nm-mlag-bond0 " + "tx-redirect-igmp-reports-eth2\012add chain netdev nm-mlag-bond0 " + "tx-redirect-igmp-reports-eth3 { type filter hook egress device eth3 priority filter + 1; " + "}\012add rule netdev nm-mlag-bond0 tx-redirect-igmp-reports-eth3 igmp type { " + "membership-report-v1, membership-report-v2, membership-report-v3 } fwd to eth2\012add rule " + "netdev nm-mlag-bond0 tx-redirect-igmp-reports-eth3 icmpv6 type { mld-listener-report, " + "mld2-listener-report } fwd to eth2\012"); _T(FALSE, "bond0", diff --git a/src/libnm-client-aux-extern/nm-libnm-aux.c b/src/libnm-client-aux-extern/nm-libnm-aux.c index 5855bc29..77f4a195 100644 --- a/src/libnm-client-aux-extern/nm-libnm-aux.c +++ b/src/libnm-client-aux-extern/nm-libnm-aux.c @@ -169,14 +169,11 @@ nmc_client_has_version_info_capability(NMClient *nmc, NMVersionInfoCapability ca len--; ver++; - idx = (gsize) capability; - if (idx >= G_MAXSIZE - 31u) - return FALSE; - - idx_hi = ((idx + 31u) / 32u); - idx_lo = (idx % 32u); + idx = (gsize) capability; + idx_hi = idx / 32u; + idx_lo = idx % 32u; - if (idx_hi > len) + if (idx_hi >= len) return FALSE; return NM_FLAGS_ANY(ver[idx_hi], (1ull << idx_lo)); diff --git a/src/libnm-client-impl/nm-client.c b/src/libnm-client-impl/nm-client.c index 4ecc8389..6e722c5c 100644 --- a/src/libnm-client-impl/nm-client.c +++ b/src/libnm-client-impl/nm-client.c @@ -4773,8 +4773,8 @@ nm_client_save_hostname(NMClient *client, * @hostname: (nullable): the new persistent hostname to set, or %NULL to * clear any existing persistent hostname * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the operation completes + * @user_data: caller-specific data passed to @callback * * Requests that the machine's persistent hostname be set to the specified value * or cleared. @@ -5771,8 +5771,8 @@ _add_connection_call(NMClient *self, * added, not the object itself * @save_to_disk: whether to immediately save the connection to disk * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the add operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the add operation completes + * @user_data: caller-specific data passed to @callback * * Requests that the remote settings service add the given settings to a new * connection. If @save_to_disk is %TRUE, the connection is immediately written @@ -5844,8 +5844,8 @@ nm_client_add_connection_finish(NMClient *client, GAsyncResult *result, GError * * not yet provide AddConnection2(). By setting this to %FALSE, the function * under the hood always calls AddConnection2(). * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the add operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the add operation completes + * @user_data: caller-specific data passed to @callback * * Call AddConnection2() D-Bus API asynchronously. * @@ -5971,8 +5971,8 @@ nm_client_load_connections(NMClient *client, * @client: the %NMClient * @filenames: (array zero-terminated=1): %NULL-terminated array of filenames to load * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the operation completes + * @user_data: caller-specific data passed to @callback * * Requests that the remote settings service asynchronously load or reload the * given files, adding or updating the connections described within. @@ -6086,8 +6086,8 @@ nm_client_reload_connections(NMClient *client, GCancellable *cancellable, GError * nm_client_reload_connections_async: * @client: the #NMClient * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the reload operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the reload operation completes + * @user_data: caller-specific data passed to @callback * * Requests that the remote settings service begin reloading all connection * files from disk, adding, updating, and removing connections until the @@ -6315,7 +6315,7 @@ nm_client_get_capabilities(NMClient *client, gsize *length) * * If available, the first element in the array is NM_VERSION which * encodes the daemon version as "(major << 16 | minor << 8 | micro)". - * The following elements are a bitfield of %NMVersionInfoCapabilities + * The following elements are a bitfield of %NMVersionInfoCapability * that indicate that the daemon supports a certain capability. * * Returns: (transfer none) (array length=length): the @@ -6443,8 +6443,8 @@ checkpoint_create_cb(GObject *object, GAsyncResult *result, gpointer user_data) * @rollback_timeout: the rollback timeout in seconds * @flags: creation flags * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the add operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the add operation completes + * @user_data: caller-specific data passed to @callback * * Creates a checkpoint of the current networking configuration * for given interfaces. An empty @devices argument means all @@ -6516,8 +6516,8 @@ nm_client_checkpoint_create_finish(NMClient *client, GAsyncResult *result, GErro * @client: the %NMClient * @checkpoint_path: the D-Bus path for the checkpoint * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the add operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the add operation completes + * @user_data: caller-specific data passed to @callback * * Destroys an existing checkpoint without performing a rollback. * @@ -6576,8 +6576,8 @@ nm_client_checkpoint_destroy_finish(NMClient *client, GAsyncResult *result, GErr * @client: the %NMClient * @checkpoint_path: the D-Bus path to the checkpoint * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the add operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the add operation completes + * @user_data: caller-specific data passed to @callback * * Performs the rollback of a checkpoint before the timeout is reached. * @@ -6658,8 +6658,8 @@ nm_client_checkpoint_rollback_finish(NMClient *client, GAsyncResult *result, GEr * @add_timeout: the timeout in seconds counting from now. * Set to zero, to disable the timeout. * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the add operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the add operation completes + * @user_data: caller-specific data passed to @callback * * Resets the timeout for the checkpoint with path @checkpoint_path * to @timeout_add. @@ -6723,8 +6723,8 @@ nm_client_checkpoint_adjust_rollback_timeout_finish(NMClient *client, * @client: the %NMClient * @flags: flags indicating what to reload. * @cancellable: a #GCancellable, or %NULL - * @callback: (scope async): callback to be called when the add operation completes - * @user_data: (closure): caller-specific data passed to @callback + * @callback: (scope async) (closure user_data): callback to be called when the add operation completes + * @user_data: caller-specific data passed to @callback * * Reload NetworkManager's configuration and perform certain updates, like * flushing caches or rewriting external state to disk. This is similar to @@ -8312,7 +8312,7 @@ nm_client_class_init(NMClientClass *client_class) * Expose version info and capabilities of NetworkManager. If non-empty, * the first element is NM_VERSION, which encodes the version of the * daemon as "(major << 16 | minor << 8 | micro)". The following elements - * is a bitfields of %NMVersionInfoCapabilities. If a bit is set, then + * is a bitfields of %NMVersionInfoCapability. If a bit is set, then * the running NetworkManager has the respective capability. * * Since: 1.42 diff --git a/src/libnm-client-impl/nm-secret-agent-old.c b/src/libnm-client-impl/nm-secret-agent-old.c index 93b6048f..8f7e0b4d 100644 --- a/src/libnm-client-impl/nm-secret-agent-old.c +++ b/src/libnm-client-impl/nm-secret-agent-old.c @@ -970,8 +970,8 @@ nm_secret_agent_old_unregister_finish(NMSecretAgentOld *self, GAsyncResult *resu * @setting_name: the name of the secret setting * @hints: (array zero-terminated=1): hints to the agent * @flags: flags that modify the behavior of the request - * @callback: (scope async): a callback, to be invoked when the operation is done - * @user_data: (closure): caller-specific data to be passed to @callback + * @callback: (scope async) (closure user_data): a callback, to be invoked when the operation is done + * @user_data: caller-specific data to be passed to @callback * * Asynchronously retrieves secrets belonging to @connection for the * setting @setting_name. @flags indicate specific behavior that the secret @@ -1010,8 +1010,8 @@ nm_secret_agent_old_get_secrets(NMSecretAgentOld *self, * nm_secret_agent_old_save_secrets: (virtual save_secrets) * @self: a #NMSecretAgentOld * @connection: a #NMConnection - * @callback: (scope async): a callback, to be invoked when the operation is done - * @user_data: (closure): caller-specific data to be passed to @callback + * @callback: (scope async) (closure user_data): a callback, to be invoked when the operation is done + * @user_data: caller-specific data to be passed to @callback * * Asynchronously ensures that all secrets inside @connection are stored to * disk. @@ -1037,8 +1037,8 @@ nm_secret_agent_old_save_secrets(NMSecretAgentOld *self, * nm_secret_agent_old_delete_secrets: (virtual delete_secrets) * @self: a #NMSecretAgentOld * @connection: a #NMConnection - * @callback: (scope async): a callback, to be invoked when the operation is done - * @user_data: (closure): caller-specific data to be passed to @callback + * @callback: (scope async) (closure user_data): a callback, to be invoked when the operation is done + * @user_data: caller-specific data to be passed to @callback * * Asynchronously asks the agent to delete all saved secrets belonging to * @connection. diff --git a/src/libnm-core-impl/nm-setting-connection.c b/src/libnm-core-impl/nm-setting-connection.c index 3298dce6..33d088bb 100644 --- a/src/libnm-core-impl/nm-setting-connection.c +++ b/src/libnm-core-impl/nm-setting-connection.c @@ -2154,7 +2154,15 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass) * property: uuid * variable: UUID(+) * description: UUID for the connection profile. When missing, NetworkManager - * creates the UUID itself (by hashing the filename). + * creates the UUID by hashing the connection filename. + * ---end--- + */ + /* ---keyfile--- + * property: uuid + * variable: uuid + * description: UUID for the connection profile. When missing, NetworkManager + * creates the UUID by hashing the connection filename. + * example: uuid=7bdbe39a-126c-3f94-ac5e-8f156ed38383 * ---end--- */ _nm_setting_property_define_direct_string(properties_override, diff --git a/src/libnm-core-impl/nm-setting-infiniband.c b/src/libnm-core-impl/nm-setting-infiniband.c index 203e4b2b..24e5807a 100644 --- a/src/libnm-core-impl/nm-setting-infiniband.c +++ b/src/libnm-core-impl/nm-setting-infiniband.c @@ -346,7 +346,7 @@ nm_setting_infiniband_class_init(NMSettingInfinibandClass *klass) * property: mac-address * format: usual hex-digits-and-colons notation * description: MAC address in traditional hex-digits-and-colons notation, or - * or semicolon separated list of 20 decimal bytes (obsolete) + * semicolon separated list of 20 decimal bytes (obsolete) * example: mac-address= 80:00:00:6d:fe:80:00:00:00:00:00:00:00:02:55:00:70:33:cf:01 * ---end--- */ diff --git a/src/libnm-core-impl/nm-setting-ip4-config.c b/src/libnm-core-impl/nm-setting-ip4-config.c index c79d0fdf..6112137f 100644 --- a/src/libnm-core-impl/nm-setting-ip4-config.c +++ b/src/libnm-core-impl/nm-setting-ip4-config.c @@ -636,6 +636,7 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) /* ---keyfile--- * property: dns + * variable: dns * format: list of DNS IP addresses * description: List of DNS servers. * example: dns=1.2.3.4;8.8.8.8;8.8.4.4; @@ -662,9 +663,15 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) /* ---keyfile--- * property: addresses * variable: address1, address2, ... - * format: address/plen - * description: List of static IP addresses. - * example: address1=192.168.100.100/24 address2=10.1.1.5/24 + * format: address/prefix-length[,gateway] + * description: Static IPv4 addresses, one address per variable. The + * variables can also contain the gateway after a comma or semicolon; + * it is recommended to use the "gateway" variable instead. + * example: address1=192.168.100.100/24 + * + * address2=10.1.1.5/16 + * + * address1=192.168.100.100/24,192.168.100.1 * ---end--- */ /* ---ifcfg-rh--- @@ -679,8 +686,12 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * property: gateway * variable: gateway * format: string - * description: Gateway IP addresses as a string. - * example: gateway=192.168.100.1 + * description: Gateway IP address as a string. The gateway can be also specified in one + * of the "address1", "address2", etc. variables after the address, separated by a comma or + * semicolon (for example "address1=192.168.100.1/24,192.168.100.254"). + * The value from the "gateway" variable takes precedence over any gateway specified in one + * of the "address*" variables. + * example: gateway=192.168.100.254 * ---end--- */ /* ---ifcfg-rh--- @@ -722,7 +733,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * variable: routing-rule1, routing-rule2, ... * format: routing rule string * description: Routing rules as defined with `ip rule add`, but with mandatory - * fixed priority. + * fixed priority. The "lookup" and "table" options don't support a table name, + * only a number. * example: routing-rule1=priority 5 from 192.167.4.0/24 table 45 * ---end--- */ @@ -1274,7 +1286,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * A comma separated list of routing rules for policy routing. The format * is based on <command>ip rule add</command> syntax and mostly compatible. * One difference is that routing rules in NetworkManager always need a - * fixed priority. + * fixed priority. Also, the "lookup" and "table" options don't support a + * table name, only a number. * </para> * <para> * Example: <literal>priority 5 from 192.167.4.0/24 table 45</literal> diff --git a/src/libnm-core-impl/nm-setting-ip6-config.c b/src/libnm-core-impl/nm-setting-ip6-config.c index eddac9f1..32fb295f 100644 --- a/src/libnm-core-impl/nm-setting-ip6-config.c +++ b/src/libnm-core-impl/nm-setting-ip6-config.c @@ -745,9 +745,15 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) /* ---keyfile--- * property: addresses * variable: address1, address2, ... - * format: address/plen - * description: List of static IP addresses. - * example: address1=abbe::cafe/96 address2=2001::1234 + * format: address/plen[,gateway] + * description: Static IPv6 addresses, one address per variable. The + * variables can also contain the gateway after a comma or semicolon; + * it is recommended to use the "gateway" variable instead. + * example: address1=abbe::cafe/96 + * + * address2=2001::1234/56 + * + * address1=fd01::2000/64;fd01::1 * ---end--- */ /* ---ifcfg-rh--- @@ -763,7 +769,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * property: gateway * variable: gateway * format: string - * description: Gateway IP addresses as a string. + * description: Gateway IP address as a string. The gateway can be also specified in one + * of the "address1", "address2", etc. variables after the address, separated by a comma or + * semicolon (for example "address1=fd01::2000/64;fd01::1"). + * The value from the "gateway" variable takes precedence over any gateway specified in one + * of the "address*" variables. * example: gateway=abbe::1 * ---end--- */ diff --git a/src/libnm-core-impl/nm-utils.c b/src/libnm-core-impl/nm-utils.c index f7f4e770..fea13a95 100644 --- a/src/libnm-core-impl/nm-utils.c +++ b/src/libnm-core-impl/nm-utils.c @@ -3626,10 +3626,10 @@ _nm_utils_check_module_file(const char *name, * Can be empty or %NULL, in which case only @try_first is checked. * @file_test_flags: the flags passed to g_file_test() when searching * for @progname. Set it to 0 to skip the g_file_test(). - * @predicate: (scope call): if given, pass the file name to this function + * @predicate: (scope call) (closure user_data): if given, pass the file name to this function * for additional checks. This check is performed after the check for * @file_test_flags. You cannot omit both @file_test_flags and @predicate. - * @user_data: (closure) (nullable): user data for @predicate function. + * @user_data: (nullable): user data for @predicate function. * @error: on failure, set a "not found" error %G_IO_ERROR %G_IO_ERROR_NOT_FOUND. * * Searches for a @progname file in a list of search @paths. diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h index 5eedd7da..9c737dbe 100644 --- a/src/libnm-core-public/nm-dbus-interface.h +++ b/src/libnm-core-public/nm-dbus-interface.h @@ -93,16 +93,19 @@ /** * NMVersionInfoCapability: - * %_NM_VERSION_INFO_CAPABILITY_UNUSED: a dummy capability. It has no meaning, - * don't use it. + * @NM_VERSION_INFO_CAPABILITY_SYNC_ROUTE_WITH_TABLE: Contains the fix to a bug that + * caused that routes in table other than main were not removed on reapply nor + * on connection down. + * https://issues.redhat.com/browse/RHEL-66262 + * https://issues.redhat.com/browse/RHEL-67324 * - * Currently no enum values are defined. These capabilities are exposed - * on D-Bus in the "VersionInfo" bit field. + * The numeric values represent the bit index of the capability. These capabilities + * can be queried in the "VersionInfo" D-Bus property. * * Since: 1.42 */ typedef enum { - _NM_VERSION_INFO_CAPABILITY_UNUSED = 0x7FFFFFFFu, + NM_VERSION_INFO_CAPABILITY_SYNC_ROUTE_WITH_TABLE = 0, } NMVersionInfoCapability; /** diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 421e4d1b..25c78fd3 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -58,6 +58,17 @@ nm_ether_addr_from_string(NMEtherAddr *addr, const char *str) return addr; } +guint +nm_ether_addr_hash(const NMEtherAddr *a) +{ + NMHashState h; + + nm_hash_init(&h, 1947951703u); + nm_hash_update(&h, a, sizeof(NMEtherAddr)); + + return nm_hash_complete(&h); +} + /*****************************************************************************/ /** diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index ca9feb60..70f1912e 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -240,6 +240,8 @@ nm_ether_addr_is_zero(const NMEtherAddr *a) return nm_memeq(a, &nm_ether_addr_zero, sizeof(NMEtherAddr)); } +guint nm_ether_addr_hash(const NMEtherAddr *a); + /*****************************************************************************/ struct ether_addr; diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index bd495fe2..90a34102 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -323,6 +323,10 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1)); #define IFLA_VF_VLAN_INFO_UNSPEC 0 #define IFLA_VF_VLAN_INFO 1 +/*****************************************************************************/ + +#define NDA_CONTROLLER NDA_MASTER + /* valid for TRUST, SPOOFCHK, LINK_STATE, RSS_QUERY_EN */ struct _ifla_vf_setting { guint32 vf; @@ -10352,6 +10356,125 @@ link_get_driver_info(NMPlatform *platform, /*****************************************************************************/ +typedef struct { + int ifindexes_len; + int *ifindexes; + GHashTable *out_fdb_addrs; +} FdbData; + +static int +parse_fdb_cb(const struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct ndmsg *ndmsg = NLMSG_DATA(nlh); + int from_ifindex = ndmsg->ndm_ifindex; + bool match = FALSE; + + static const struct nla_policy policy[] = { + [NDA_LLADDR] = {.minlen = ETH_ALEN, .maxlen = ETH_ALEN}, + [NDA_CONTROLLER] = {.type = NLA_U32}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + FdbData *data = arg; + int fdb_controller = -1; + + if (nlmsg_parse_arr(nlh, sizeof(*ndmsg), tb, policy) < 0) + return NL_SKIP; + + if (tb[NDA_CONTROLLER]) + fdb_controller = nla_get_u32(tb[NDA_CONTROLLER]); + + for (int i = 0; i < data->ifindexes_len; i++) { + int current_ifindex = data->ifindexes[i]; + + if (NM_IN_SET(current_ifindex, from_ifindex, fdb_controller)) { + match = TRUE; + break; + } + } + + if (!match) + return NL_SKIP; + + if (tb[NDA_LLADDR]) { + NMEtherAddr *hwaddr = g_new(NMEtherAddr, 1); + memcpy(hwaddr, nla_data(tb[NDA_LLADDR]), ETH_ALEN); + g_hash_table_add(data->out_fdb_addrs, hwaddr); + } + + return NL_OK; +} + +NMEtherAddr ** +nm_linux_platform_get_link_fdb_table(NMPlatform *platform, int *ifindexes, guint ifindexes_len) +{ + int nle; + struct nl_sock *sk = NULL; + nm_auto_nlmsg struct nl_msg *msg = NULL; + gs_unref_hashtable GHashTable *fdb_addrs = NULL; + FdbData data; + const struct ndmsg ndm = { + .ndm_family = AF_BRIDGE, + }; + gpointer *ret = NULL; + + nm_assert(ifindexes); + nm_assert(ifindexes_len >= 1); + + fdb_addrs = g_hash_table_new_full((GHashFunc) nm_ether_addr_hash, + (GEqualFunc) nm_ether_addr_equal, + g_free, + NULL); + + msg = nlmsg_alloc_new(0, RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP); + + if (nlmsg_append_struct(msg, &ndm) < 0) + goto err; + + nle = nl_socket_new(&sk, NETLINK_ROUTE, NL_SOCKET_FLAGS_DISABLE_MSG_PEEK, 0, 0); + if (nle < 0) { + _LOGD("get-link-fdb: error opening socket: %s (%d)", nm_strerror(nle), nle); + goto err; + } + + nle = nl_send_auto(sk, msg); + if (nle < 0) { + _LOGD("get-link-fdb: failed sending request: %s (%d)", nm_strerror(nle), nle); + goto err; + } + + data = ((FdbData) { + .ifindexes_len = ifindexes_len, + .ifindexes = ifindexes, + .out_fdb_addrs = fdb_addrs, + }); + + do { + nle = nl_recvmsgs(sk, + &((const struct nl_cb) { + .valid_cb = parse_fdb_cb, + .valid_arg = &data, + })); + } while (nle == -EAGAIN); + + if (nle < 0) { + _LOGD("get-link-fdb: recv failed: %s (%d)", nm_strerror(nle), nle); + goto err; + } + + ret = g_hash_table_get_keys_as_array(fdb_addrs, NULL); + g_hash_table_steal_all(fdb_addrs); + nl_socket_free(sk); + return NM_CAST_ALIGN(NMEtherAddr *, ret); + +err: + if (sk) + nl_socket_free(sk); + return NULL; +} + +/*****************************************************************************/ + static gboolean ip4_address_add(NMPlatform *platform, int ifindex, diff --git a/src/libnm-platform/nm-linux-platform.h b/src/libnm-platform/nm-linux-platform.h index 08135a4a..3f591b7f 100644 --- a/src/libnm-platform/nm-linux-platform.h +++ b/src/libnm-platform/nm-linux-platform.h @@ -25,6 +25,9 @@ GType nm_linux_platform_get_type(void); struct _NMDedupMultiIndex; +NMEtherAddr ** +nm_linux_platform_get_link_fdb_table(NMPlatform *platform, int *ifindexes, guint ifindexes_len); + NMPlatform *nm_linux_platform_new(struct _NMDedupMultiIndex *multi_idx, gboolean log_with_ptr, gboolean netns_support, diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index af04f29f..658efadb 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -61,6 +61,8 @@ G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_address.data) == _NM_UTILS_H G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_perm_address.data) == _NM_UTILS_HWADDR_LEN_MAX); G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_broadcast.data) == _NM_UTILS_HWADDR_LEN_MAX); +static int _route_objs_cmp_values(gconstpointer a, gconstpointer b, gpointer user_data); + static const char * _nmp_link_port_data_to_string(NMPortKind port_kind, const NMPlatformLinkPortData *port_data, @@ -4872,11 +4874,24 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, return result; } +static gboolean +_route_obj_find_bsearch(GPtrArray *sorted_routes_objs, const NMPObject *route_obj) +{ + gssize pos = + nm_ptrarray_find_bsearch((gconstpointer *) sorted_routes_objs->pdata, + sorted_routes_objs->len, + route_obj, + _route_objs_cmp_values, + GINT_TO_POINTER((int) NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)); + return pos >= 0; +} + GPtrArray * nm_platform_ip_route_get_prune_list(NMPlatform *self, int addr_family, int ifindex, - NMIPRouteTableSyncMode route_table_sync) + NMIPRouteTableSyncMode route_table_sync, + GPtrArray *sorted_old_routes_objs) { NMPLookup lookup; GPtrArray *routes_prune = NULL; @@ -4890,10 +4905,21 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); nm_assert(NM_IN_SET(route_table_sync, NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, - NM_IP_ROUTE_TABLE_SYNC_MODE_FULL, + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL, + NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES, NM_IP_ROUTE_TABLE_SYNC_MODE_ALL, NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE)); + if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES) { + nm_assert(sorted_old_routes_objs); + nm_assert(nm_utils_ptrarray_is_sorted( + (gconstpointer *) sorted_old_routes_objs->pdata, + sorted_old_routes_objs->len, + FALSE, + _route_objs_cmp_values, + GINT_TO_POINTER((int) NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY))); + } + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), ifindex); @@ -4915,7 +4941,12 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, if (!nm_platform_route_table_is_main(nm_platform_ip_route_get_effective_table(&rt->rx))) continue; break; - case NM_IP_ROUTE_TABLE_SYNC_MODE_FULL: + case NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES: + if (!nm_platform_route_table_is_main(nm_platform_ip_route_get_effective_table(&rt->rx)) + && !_route_obj_find_bsearch(sorted_old_routes_objs, obj)) + continue; + break; + case NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL: if (nm_platform_ip_route_get_effective_table(&rt->rx) == RT_TABLE_LOCAL) continue; break; @@ -5284,7 +5315,8 @@ nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex) routes_prune = nm_platform_ip_route_get_prune_list(self, AF_INET, ifindex, - NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE); + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE, + NULL); success &= nm_platform_ip_route_sync(self, AF_INET, ifindex, NULL, routes_prune, NULL); } if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) { @@ -5293,7 +5325,8 @@ nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex) routes_prune = nm_platform_ip_route_get_prune_list(self, AF_INET6, ifindex, - NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE); + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE, + NULL); success &= nm_platform_ip_route_sync(self, AF_INET6, ifindex, NULL, routes_prune, NULL); } return success; @@ -6533,9 +6566,9 @@ nm_platform_lnk_infiniband_to_string(const NMPlatformLnkInfiniband *lnk, char *b const char * nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len) { - char str_local[30]; + char str_local[30 + NM_INET_ADDRSTRLEN]; char str_local1[NM_INET_ADDRSTRLEN]; - char str_remote[30]; + char str_remote[30 + NM_INET_ADDRSTRLEN]; char str_remote1[NM_INET_ADDRSTRLEN]; char str_ttl[30]; char str_tclass[30]; @@ -8767,6 +8800,45 @@ nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformL return 0; } +static int +_route_objs_cmp_values(gconstpointer a, gconstpointer b, gpointer user_data) +{ + const NMPObject *a_obj = a; + const NMPObject *b_obj = b; + NMPlatformIPRouteCmpType cmp_type = GPOINTER_TO_INT(user_data); + + nm_assert(a_obj && b_obj); + nm_assert(NMP_OBJECT_CAST_IP_ROUTE(a_obj) && NMP_OBJECT_CAST_IP_ROUTE(b_obj)); + + if (NMP_OBJECT_GET_ADDR_FAMILY(a_obj) != NMP_OBJECT_GET_ADDR_FAMILY(b_obj)) { + return NMP_OBJECT_GET_ADDR_FAMILY(a_obj) == AF_INET ? 1 : -1; + } else if (NMP_OBJECT_GET_ADDR_FAMILY(a_obj) == AF_INET) { + return nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(a_obj), + NMP_OBJECT_CAST_IP4_ROUTE(b_obj), + cmp_type); + } else { + return nm_platform_ip6_route_cmp(NMP_OBJECT_CAST_IP6_ROUTE(a_obj), + NMP_OBJECT_CAST_IP6_ROUTE(b_obj), + cmp_type); + } +} + +static int +_route_objs_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + nm_assert(a && b); + + return _route_objs_cmp_values(*((const NMPObject **) a), *((const NMPObject **) b), user_data); +} + +void +nm_platform_route_objs_sort(GPtrArray *routes_objs, NMPlatformIPRouteCmpType cmp_type) +{ + nm_assert(routes_objs); + + g_ptr_array_sort_with_data(routes_objs, _route_objs_cmp, GINT_TO_POINTER((int) cmp_type)); +} + void nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj, gboolean for_id, diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index e33be813..22bf0fdb 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -2389,7 +2389,8 @@ int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatf GPtrArray *nm_platform_ip_route_get_prune_list(NMPlatform *self, int addr_family, int ifindex, - NMIPRouteTableSyncMode route_table_sync); + NMIPRouteTableSyncMode route_table_sync, + GPtrArray *old_routes_objs); gboolean nm_platform_ip_route_sync(NMPlatform *self, int addr_family, @@ -2495,6 +2496,8 @@ int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatf GHashTable *nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex); +void nm_platform_route_objs_sort(GPtrArray *routes_objs, NMPlatformIPRouteCmpType cmp_type); + int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type); diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h index c7d487e2..3784a78e 100644 --- a/src/libnm-platform/nmp-base.h +++ b/src/libnm-platform/nmp-base.h @@ -211,8 +211,11 @@ nmp_object_type_to_flags(NMPObjectType obj_type) * @NM_IP_ROUTE_TABLE_SYNC_MODE_NONE: indicate an invalid setting. * @NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN: only the main table is synced. For all * other tables, NM won't delete any extra routes. - * @NM_IP_ROUTE_TABLE_SYNC_MODE_FULL: NM will sync all tables, except the - * local table (255). + * @NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES: only the main table is synced, + * plus individual routes in other tables added by NM, leaving routes that + * were not added by NM untouched. + * @NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL: NM will sync all tables, except + * the local table (255). * @NM_IP_ROUTE_TABLE_SYNC_MODE_ALL: NM will sync all tables, including the * local table (255). * @NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE: NM will sync all tables (including @@ -222,7 +225,8 @@ nmp_object_type_to_flags(NMPObjectType obj_type) typedef enum { NM_IP_ROUTE_TABLE_SYNC_MODE_NONE, NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, - NM_IP_ROUTE_TABLE_SYNC_MODE_FULL, + NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES, + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL, NM_IP_ROUTE_TABLE_SYNC_MODE_ALL, NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE, } NMIPRouteTableSyncMode; diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c index b3a51ba0..5568c05d 100644 --- a/src/libnmc-setting/nm-meta-setting-desc.c +++ b/src/libnmc-setting/nm-meta-setting-desc.c @@ -5635,7 +5635,7 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = { .property_type = &_pt_gobject_enum, ), PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_DOWN_ON_POWEROFF, - .property_type = &_pt_gobject_ternary, + .property_type = &_pt_gobject_enum, ), PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_SECONDARIES, .describe_message = diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in index 7f5bc2c7..ae3a388b 100644 --- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in +++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in @@ -693,8 +693,8 @@ values="default (-1), false (0), true (1)" /> <property name="down-on-poweroff" nmcli-description="Whether the connection will be brought down before the system is powered off. The default value is "default" (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as "no" (0)." - format="ternary" - values="true/yes/on, false/no/off, default/unknown" /> + format="choice (NMSettingConnectionDownOnPoweroff)" + values="default (-1), no (0), yes (1)" /> <property name="secondaries" nmcli-description="List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported." format="list of strings" /> |