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/core | |
| parent | 56928734cbcf1d3a02fae4f152a541df1b04e04a (diff) | |
New upstream version 1.50.1 upstream/1.50.1
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/devices/nm-device-bond.c | 85 | ||||
| -rw-r--r-- | src/core/devices/nm-device-bond.h | 3 | ||||
| -rw-r--r-- | src/core/devices/nm-device.c | 69 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-device-wifi.c | 4 | ||||
| -rw-r--r-- | src/core/devices/wwan/nm-modem.c | 1 | ||||
| -rw-r--r-- | src/core/ndisc/nm-lndp-ndisc.c | 51 | ||||
| -rw-r--r-- | src/core/nm-bond-manager.c | 114 | ||||
| -rw-r--r-- | src/core/nm-bond-manager.h | 6 | ||||
| -rw-r--r-- | src/core/nm-config.c | 38 | ||||
| -rw-r--r-- | src/core/nm-firewall-utils.c | 63 | ||||
| -rw-r--r-- | src/core/nm-l3cfg.c | 57 | ||||
| -rw-r--r-- | src/core/nm-l3cfg.h | 2 | ||||
| -rw-r--r-- | src/core/nm-manager.c | 29 | ||||
| -rw-r--r-- | src/core/tests/test-core-with-expect.c | 2 | ||||
| -rw-r--r-- | src/core/tests/test-core.c | 66 |
15 files changed, 501 insertions, 89 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", |