diff options
Diffstat (limited to 'src/core')
120 files changed, 4583 insertions, 2474 deletions
diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c index 5727aac8..8b0b4845 100644 --- a/src/core/NetworkManagerUtils.c +++ b/src/core/NetworkManagerUtils.c @@ -30,7 +30,6 @@ #include "libnm-platform/nm-linux-platform.h" #include "libnm-platform/nm-platform-utils.h" #include "nm-auth-utils.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" /*****************************************************************************/ @@ -1155,7 +1154,7 @@ nm_utils_file_is_in_path(const char *abs_filename, const char *abs_path) g_return_val_if_fail(abs_filename && abs_filename[0] == '/', NULL); g_return_val_if_fail(abs_path && abs_path[0] == '/', NULL); - path = nm_sd_utils_path_startswith(abs_filename, abs_path); + path = nm_path_startswith(abs_filename, abs_path); if (!path) return NULL; @@ -1389,16 +1388,20 @@ nm_utils_ip_route_attribute_to_platform(int addr_family, r->r_rtm_flags = ((onlink) ? (unsigned) RTNH_F_ONLINK : 0u); + GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_ADVMSS, r->mss, UINT32, uint32, 0); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_WINDOW, r->window, UINT32, uint32, 0); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_CWND, r->cwnd, UINT32, uint32, 0); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_INITCWND, r->initcwnd, UINT32, uint32, 0); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_INITRWND, r->initrwnd, UINT32, uint32, 0); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_MTU, r->mtu, UINT32, uint32, 0); + GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_RTO_MIN, r->rto_min, UINT32, uint32, 0); + GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_QUICKACK, r->quickack, BOOLEAN, boolean, FALSE); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, r->lock_window, BOOLEAN, boolean, FALSE); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, r->lock_cwnd, BOOLEAN, boolean, FALSE); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, r->lock_initcwnd, BOOLEAN, boolean, FALSE); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND, r->lock_initrwnd, BOOLEAN, boolean, FALSE); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, r->lock_mtu, BOOLEAN, boolean, FALSE); + GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_LOCK_ADVMSS, r->lock_mss, BOOLEAN, boolean, FALSE); if ((variant = nm_ip_route_get_attribute(s_route, NM_IP_ROUTE_ATTRIBUTE_SRC)) && g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) { @@ -1691,7 +1694,7 @@ nm_utils_platform_capture_ip_setting(NMPlatform *platform, return NM_SETTING(g_steal_pointer(&s_ip)); } - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex); nm_platform_iter_obj_for_each (&iter, platform, &lookup, &obj) { const NMPlatformIPXAddress *address = NMP_OBJECT_CAST_IPX_ADDRESS(obj); nm_auto_unref_ip_address NMIPAddress *s_addr = NULL; @@ -1745,7 +1748,7 @@ nm_utils_platform_capture_ip_setting(NMPlatform *platform, } g_object_set(s_ip, NM_SETTING_IP_CONFIG_METHOD, method, NULL); - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), ifindex); nm_platform_iter_obj_for_each (&iter, platform, &lookup, &obj) { const NMPlatformIPXRoute *route = NMP_OBJECT_CAST_IPX_ROUTE(obj); nm_auto_unref_ip_route NMIPRoute *s_route = NULL; diff --git a/src/core/devices/bluetooth/nm-device-bt.c b/src/core/devices/bluetooth/nm-device-bt.c index 39b5c4ec..a8258123 100644 --- a/src/core/devices/bluetooth/nm-device-bt.c +++ b/src/core/devices/bluetooth/nm-device-bt.c @@ -262,6 +262,7 @@ complete_connection(NMDevice *device, const char *ctype; gboolean is_dun = FALSE; gboolean is_pan = FALSE; + gboolean is_nap = FALSE; NMSettingGsm *s_gsm; NMSettingCdma *s_cdma; NMSettingSerial *s_serial; @@ -281,6 +282,8 @@ complete_connection(NMDevice *device, is_dun = TRUE; else if (!strcmp(ctype, NM_SETTING_BLUETOOTH_TYPE_PANU)) is_pan = TRUE; + else if (!strcmp(ctype, NM_SETTING_BLUETOOTH_TYPE_NAP)) + is_nap = TRUE; } else { if (s_gsm || s_cdma) is_dun = TRUE; @@ -288,7 +291,27 @@ complete_connection(NMDevice *device, is_pan = TRUE; } - if (is_pan) { + if (is_nap) { + /* Make sure the device supports NAP */ + if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("NAP requested, but Bluetooth device does not support NAP")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_BLUETOOTH_TYPE); + return FALSE; + } + + g_object_set(G_OBJECT(s_bt), + NM_SETTING_BLUETOOTH_TYPE, + NM_SETTING_BLUETOOTH_TYPE_NAP, + NULL); + + fallback_prefix = _("NAP connection"); + } else if (is_pan) { /* Make sure the device supports PAN */ if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) { g_set_error_literal(error, diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index 16896d57..44bb316d 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -9,6 +9,8 @@ #include <stdlib.h> #include <net/if.h> +#include <linux/if_ether.h> +#include <linux/if_link.h> #include "NetworkManagerUtils.h" #include "nm-device-private.h" @@ -16,6 +18,7 @@ #include "nm-device-factory.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-core-intern/nm-core-internal.h" +#include "nm-manager.h" #include "nm-setting-bond-port.h" #define _NMLOG_DEVICE_TYPE NMDeviceBond @@ -312,7 +315,7 @@ set_bond_attr_or_default(NMDevice *device, NMSettingBond *s_bond, const char *op NMDeviceBond *self = NM_DEVICE_BOND(device); const char *value; - value = nm_setting_bond_get_option_or_default(s_bond, opt); + value = nm_setting_bond_get_option_normalized(s_bond, opt); if (!value) { if (_LOGT_ENABLED(LOGD_BOND) && nm_setting_bond_get_option_by_name(s_bond, opt)) _LOGT(LOGD_BOND, "bond option '%s' not set as it conflicts with other options", opt); @@ -346,54 +349,147 @@ set_bond_arp_ip_targets(NMDevice *device, NMSettingBond *s_bond) set_arp_targets( device, cur_arp_ip_target, - nm_setting_bond_get_option_or_default(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)); + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)); } -static gboolean -apply_bonding_config(NMDeviceBond *self) +static guint8 +_bond_arp_ip_target_to_platform(const char *value, in_addr_t out[static NM_BOND_MAX_ARP_TARGETS]) { - NMDevice *device = NM_DEVICE(self); - NMSettingBond *s_bond; - NMBondMode mode; - const char *mode_str; - gs_free char *device_bond_mode = NULL; + gs_free const char **ip = NULL; + in_addr_t in_a; + int i; + int added = 0; - s_bond = nm_device_get_applied_setting(device, NM_TYPE_SETTING_BOND); - g_return_val_if_fail(s_bond, FALSE); + ip = nm_utils_bond_option_arp_ip_targets_split(value); - mode_str = nm_setting_bond_get_option_or_default(s_bond, NM_SETTING_BOND_OPTION_MODE); - mode = _nm_setting_bond_mode_from_string(mode_str); - g_return_val_if_fail(mode != NM_BOND_MODE_UNKNOWN, FALSE); + if (!ip) + return added; - /* Set mode first, as some other options (e.g. arp_interval) are valid - * only for certain modes. - */ - device_bond_mode = nm_platform_sysctl_master_get_option(nm_device_get_platform(device), - nm_device_get_ifindex(device), - NM_SETTING_BOND_OPTION_MODE); - /* Need to release all slaves before we can change bond mode */ - if (!nm_streq0(device_bond_mode, mode_str)) - nm_device_master_release_slaves_all(device); + for (i = 0; ip[i]; i++) { + if (added > NM_BOND_MAX_ARP_TARGETS - 1) + break; + if (!nm_utils_parse_inaddr_bin(AF_INET, ip[i], NULL, &in_a)) + nm_assert_not_reached(); /* verify() already validated the IP addresses */ + + out[added++] = in_a; + } + return added; +} - set_bond_attr_or_default(device, s_bond, NM_SETTING_BOND_OPTION_MODE); +static int +_setting_bond_primary_opt_as_ifindex(NMSettingBond *s_bond) +{ + const char *primary_str; + int ifindex = 0; - set_bond_arp_ip_targets(device, s_bond); + primary_str = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_PRIMARY); - set_bond_attrs_or_default(device, s_bond, NM_MAKE_STRV(OPTIONS_APPLY_SUBSET)); - return TRUE; + if (primary_str != NULL) + ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, primary_str); + + return ifindex; +} + +static void +_platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *props) +{ + const char *opt_value; + + *props = (NMPlatformLnkBond){ + .mode = _nm_setting_bond_mode_from_string( + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MODE)), + .primary = _setting_bond_primary_opt_as_ifindex(s_bond), + .miimon = _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_MIIMON), + .updelay = _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_UPDELAY), + .downdelay = _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY), + .arp_interval = + _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL), + .resend_igmp = + _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP), + .min_links = _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS), + .lp_interval = + _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL), + .packets_per_port = + _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE), + .peer_notif_delay = + _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY), + .arp_all_targets = _nm_setting_bond_arp_all_targets_from_string( + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS)), + .arp_validate = _nm_setting_bond_arp_validate_from_string( + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_VALIDATE)), + .ad_actor_sys_prio = + _nm_setting_bond_opt_value_as_u16(s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO), + .ad_user_port_key = + _nm_setting_bond_opt_value_as_u16(s_bond, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY), + .primary_reselect = _nm_setting_bond_primary_reselect_from_string( + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT)), + .fail_over_mac = _nm_setting_bond_fail_over_mac_from_string( + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC)), + .xmit_hash_policy = _nm_setting_bond_xmit_hash_policy_from_string( + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY)), + .num_grat_arp = + _nm_setting_bond_opt_value_as_u8(s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP), + .all_ports_active = + _nm_setting_bond_opt_value_as_u8(s_bond, NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE), + .lacp_rate = _nm_setting_bond_lacp_rate_from_string( + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_LACP_RATE)), + .ad_select = _nm_setting_bond_ad_select_from_string( + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_AD_SELECT)), + }; + + nm_ether_addr_from_string( + &props->ad_actor_system, + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM)); + + opt_value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER); + if (opt_value != NULL) + props->use_carrier = _nm_utils_ascii_str_to_bool(opt_value, FALSE); + + opt_value = + nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB); + if (opt_value != NULL) + props->tlb_dynamic_lb = _nm_utils_ascii_str_to_bool(opt_value, FALSE); + + opt_value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); + if (opt_value != NULL) + props->arp_ip_targets_num = + _bond_arp_ip_target_to_platform(opt_value, props->arp_ip_target); + + props->miimon_has = !props->arp_interval && !props->arp_validate; + props->updelay_has = props->miimon_has && props->miimon; + props->downdelay_has = props->miimon_has && props->miimon; + props->peer_notif_delay_has = (props->miimon || props->arp_interval) && props->peer_notif_delay; + props->arp_all_targets_has = props->arp_interval && props->arp_all_targets; + props->resend_igmp_has = props->resend_igmp != 1; + props->lp_interval_has = props->lp_interval != 1; + props->tlb_dynamic_lb_has = NM_IN_SET(props->mode, NM_BOND_MODE_TLB, NM_BOND_MODE_ALB); } static NMActStageReturn act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) { - NMDeviceBond *self = NM_DEVICE_BOND(device); - NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + NMConnection *connection; + NMSettingBond *s_bond; + NMPlatformLnkBond props; + int r; + int ifindex = nm_device_get_ifindex(device); + + connection = nm_device_get_applied_connection(device); + g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); + + s_bond = nm_connection_get_setting_bond(connection); + g_return_val_if_fail(s_bond, NM_ACT_STAGE_RETURN_FAILURE); + + _platform_lnk_bond_init_from_setting(s_bond, &props); /* Interface must be down to set bond options */ nm_device_take_down(device, TRUE); - if (!apply_bonding_config(self)) + r = nm_platform_link_bond_change(nm_device_get_platform(device), ifindex, &props); + if (r < 0) { ret = NM_ACT_STAGE_RETURN_FAILURE; - else { + NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + } else { if (!nm_device_hw_addr_set_cloned(device, nm_device_get_applied_connection(device), FALSE)) ret = NM_ACT_STAGE_RETURN_FAILURE; } @@ -424,8 +520,14 @@ commit_port_options(NMDevice *bond_device, NMDevice *port, NMSettingBondPort *s_ queue_id_str); } -static gboolean -enslave_slave(NMDevice *device, NMDevice *port, NMConnection *connection, gboolean configure) +static NMTernary +attach_port(NMDevice *device, + NMDevice *port, + NMConnection *connection, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { NMDeviceBond *self = NM_DEVICE_BOND(device); NMSettingBondPort *s_port; @@ -442,7 +544,7 @@ enslave_slave(NMDevice *device, NMDevice *port, NMConnection *connection, gboole nm_device_bring_up(port, TRUE, NULL); if (!success) { - _LOGI(LOGD_BOND, "assigning bond port %s: failed", nm_device_get_ip_iface(port)); + _LOGI(LOGD_BOND, "attaching bond port %s: failed", nm_device_get_ip_iface(port)); return FALSE; } @@ -450,15 +552,15 @@ enslave_slave(NMDevice *device, NMDevice *port, NMConnection *connection, gboole commit_port_options(device, port, s_port); - _LOGI(LOGD_BOND, "assigned bond port %s", nm_device_get_ip_iface(port)); + _LOGI(LOGD_BOND, "attached bond port %s", nm_device_get_ip_iface(port)); } else - _LOGI(LOGD_BOND, "bond port %s was assigned", nm_device_get_ip_iface(port)); + _LOGI(LOGD_BOND, "bond port %s was attached", nm_device_get_ip_iface(port)); return TRUE; } static void -release_slave(NMDevice *device, NMDevice *slave, gboolean configure) +detach_port(NMDevice *device, NMDevice *port, gboolean configure) { NMDeviceBond *self = NM_DEVICE_BOND(device); gboolean success; @@ -472,10 +574,10 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure) configure = FALSE; } - ifindex_slave = nm_device_get_ip_ifindex(slave); + ifindex_slave = nm_device_get_ip_ifindex(port); if (ifindex_slave <= 0) - _LOGD(LOGD_BOND, "bond slave %s is already released", nm_device_get_ip_iface(slave)); + _LOGD(LOGD_BOND, "bond port %s is already detached", nm_device_get_ip_iface(port)); if (configure) { NMConnection *applied; @@ -490,9 +592,9 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure) ifindex_slave); if (success) { - _LOGI(LOGD_BOND, "released bond slave %s", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_BOND, "detached bond port %s", nm_device_get_ip_iface(port)); } else { - _LOGW(LOGD_BOND, "failed to release bond slave %s", nm_device_get_ip_iface(slave)); + _LOGW(LOGD_BOND, "failed to detach bond port %s", nm_device_get_ip_iface(port)); } } @@ -512,12 +614,12 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure) * other state is noticed by the now-released slave. */ if (ifindex_slave > 0) { - if (!nm_device_bring_up(slave, TRUE, NULL)) - _LOGW(LOGD_BOND, "released bond slave could not be brought up."); + if (!nm_device_bring_up(port, TRUE, NULL)) + _LOGW(LOGD_BOND, "detached bond port could not be brought up."); } } else { if (ifindex_slave > 0) { - _LOGI(LOGD_BOND, "bond slave %s was released", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_BOND, "bond port %s was detached", nm_device_get_ip_iface(port)); } } } @@ -529,12 +631,19 @@ create_and_realize(NMDevice *device, const NMPlatformLink **out_plink, GError **error) { - const char *iface = nm_device_get_iface(device); - int r; + const char *iface = nm_device_get_iface(device); + NMSettingBond *s_bond; + NMPlatformLnkBond props; + int r; g_assert(iface); - r = nm_platform_link_bond_add(nm_device_get_platform(device), iface, out_plink); + s_bond = nm_connection_get_setting_bond(connection); + nm_assert(s_bond); + + _platform_lnk_bond_init_from_setting(s_bond, &props); + + r = nm_platform_link_bond_add(nm_device_get_platform(device), iface, &props, out_plink); if (r < 0) { g_set_error(error, NM_DEVICE_ERROR, @@ -612,7 +721,7 @@ reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_ne s_bond = nm_connection_get_setting_bond(con_new); g_return_if_fail(s_bond); - value = nm_setting_bond_get_option_or_default(s_bond, NM_SETTING_BOND_OPTION_MODE); + value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MODE); mode = _nm_setting_bond_mode_from_string(value); g_return_if_fail(mode != NM_BOND_MODE_UNKNOWN); @@ -663,8 +772,8 @@ nm_device_bond_class_init(NMDeviceBondClass *klass) device_class->create_and_realize = create_and_realize; device_class->act_stage1_prepare = act_stage1_prepare; device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; - device_class->enslave_slave = enslave_slave; - device_class->release_slave = release_slave; + device_class->attach_port = attach_port; + device_class->detach_port = detach_port; device_class->can_reapply_change = can_reapply_change; device_class->reapply_connection = reapply_connection; } diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c index 9bf7ac01..31cf361e 100644 --- a/src/core/devices/nm-device-bridge.c +++ b/src/core/devices/nm-device-bridge.c @@ -836,30 +836,33 @@ _platform_lnk_bridge_init_from_setting(NMSettingBridge *s_bridge, NMPlatformLnkB to_sysfs_group_address_sys(nm_setting_bridge_get_group_address(s_bridge), &props->group_addr); } -static NMActStageReturn -act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) +static gboolean +link_config(NMDevice *device, NMConnection *connection) { - NMConnection *connection; + int ifindex = nm_device_get_ifindex(device); NMSettingBridge *s_bridge; NMPlatformLnkBridge props; - int r; - int ifindex = nm_device_get_ifindex(device); - - connection = nm_device_get_applied_connection(device); - g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); s_bridge = nm_connection_get_setting_bridge(connection); - g_return_val_if_fail(s_bridge, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail(s_bridge, FALSE); _platform_lnk_bridge_init_from_setting(s_bridge, &props); - r = nm_platform_link_bridge_change(nm_device_get_platform(device), ifindex, &props); - if (r < 0) { - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; - } + if (nm_platform_link_bridge_change(nm_device_get_platform(device), ifindex, &props) < 0) + return FALSE; + + return bridge_set_vlan_options(device, s_bridge); +} + +static NMActStageReturn +act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMConnection *connection; + + connection = nm_device_get_applied_connection(device); + g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); - if (!bridge_set_vlan_options(device, s_bridge)) { + if (!link_config(device, connection)) { NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return NM_ACT_STAGE_RETURN_FAILURE; } @@ -974,8 +977,14 @@ deactivate(NMDevice *device) } } -static gboolean -enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure) +static NMTernary +attach_port(NMDevice *device, + NMDevice *port, + NMConnection *connection, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { NMDeviceBridge *self = NM_DEVICE_BRIDGE(device); NMConnection *master_connection; @@ -985,7 +994,7 @@ enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gbool if (configure) { if (!nm_platform_link_enslave(nm_device_get_platform(device), nm_device_get_ip_ifindex(device), - nm_device_get_ip_ifindex(slave))) + nm_device_get_ip_ifindex(port))) return FALSE; master_connection = nm_device_get_applied_connection(device); @@ -1010,25 +1019,25 @@ enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gbool * (except for the default one) and so there's no need to flush. */ if (plat_vlans - && !nm_platform_link_set_bridge_vlans(nm_device_get_platform(slave), - nm_device_get_ifindex(slave), + && !nm_platform_link_set_bridge_vlans(nm_device_get_platform(port), + nm_device_get_ifindex(port), TRUE, plat_vlans)) return FALSE; } - commit_slave_options(slave, s_port); + commit_slave_options(port, s_port); - _LOGI(LOGD_BRIDGE, "attached bridge port %s", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_BRIDGE, "attached bridge port %s", nm_device_get_ip_iface(port)); } else { - _LOGI(LOGD_BRIDGE, "bridge port %s was attached", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_BRIDGE, "bridge port %s was attached", nm_device_get_ip_iface(port)); } return TRUE; } static void -release_slave(NMDevice *device, NMDevice *slave, gboolean configure) +detach_port(NMDevice *device, NMDevice *port, gboolean configure) { NMDeviceBridge *self = NM_DEVICE_BRIDGE(device); gboolean success; @@ -1041,10 +1050,10 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure) configure = FALSE; } - ifindex_slave = nm_device_get_ip_ifindex(slave); + ifindex_slave = nm_device_get_ip_ifindex(port); if (ifindex_slave <= 0) { - _LOGD(LOGD_TEAM, "bond slave %s is already released", nm_device_get_ip_iface(slave)); + _LOGD(LOGD_TEAM, "bridge port %s is already detached", nm_device_get_ip_iface(port)); return; } @@ -1054,12 +1063,12 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure) ifindex_slave); if (success) { - _LOGI(LOGD_BRIDGE, "detached bridge port %s", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_BRIDGE, "detached bridge port %s", nm_device_get_ip_iface(port)); } else { - _LOGW(LOGD_BRIDGE, "failed to detach bridge port %s", nm_device_get_ip_iface(slave)); + _LOGW(LOGD_BRIDGE, "failed to detach bridge port %s", nm_device_get_ip_iface(port)); } } else { - _LOGI(LOGD_BRIDGE, "bridge port %s was detached", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_BRIDGE, "bridge port %s was detached", nm_device_get_ip_iface(port)); } } @@ -1085,10 +1094,6 @@ create_and_realize(NMDevice *device, s_bridge = nm_connection_get_setting_bridge(connection); nm_assert(s_bridge); - s_wired = nm_connection_get_setting_wired(connection); - if (s_wired) - mtu = nm_setting_wired_get_mtu(s_wired); - hwaddr = nm_setting_bridge_get_mac_address(s_bridge); if (!hwaddr && nm_device_hw_addr_get_cloned(device, connection, FALSE, &hwaddr_cloned, NULL, NULL)) { @@ -1113,6 +1118,11 @@ create_and_realize(NMDevice *device, _platform_lnk_bridge_init_from_setting(s_bridge, &props); + s_wired = nm_connection_get_setting_wired(connection); + nm_assert(s_wired); + + mtu = nm_setting_wired_get_mtu(s_wired); + /* If mtu != 0, we set the MTU of the new bridge at creation time. However, kernel will still * automatically adjust the MTU of the bridge based on the minimum of the slave's MTU. * We don't want this automatism as the user asked for a fixed MTU. @@ -1142,6 +1152,72 @@ create_and_realize(NMDevice *device, /*****************************************************************************/ +static gboolean +can_reapply_change(NMDevice *device, + const char *setting_name, + NMSetting *s_old, + NMSetting *s_new, + GHashTable *diffs, + GError **error) +{ + /* Delegate changes to other settings to parent class */ + if (!nm_streq(setting_name, NM_SETTING_BRIDGE_SETTING_NAME)) { + return NM_DEVICE_CLASS(nm_device_bridge_parent_class) + ->can_reapply_change(device, setting_name, s_old, s_new, diffs, error); + } + + return nm_device_hash_check_invalid_keys(diffs, + NM_SETTING_BRIDGE_SETTING_NAME, + error, + NM_SETTING_BRIDGE_STP, + NM_SETTING_BRIDGE_PRIORITY, + NM_SETTING_BRIDGE_FORWARD_DELAY, + NM_SETTING_BRIDGE_HELLO_TIME, + NM_SETTING_BRIDGE_MAX_AGE, + NM_SETTING_BRIDGE_AGEING_TIME, + NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, + NM_SETTING_BRIDGE_MULTICAST_HASH_MAX, + NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_COUNT, + NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL, + NM_SETTING_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL, + NM_SETTING_BRIDGE_MULTICAST_SNOOPING, + NM_SETTING_BRIDGE_MULTICAST_ROUTER, + NM_SETTING_BRIDGE_MULTICAST_QUERIER, + NM_SETTING_BRIDGE_MULTICAST_QUERIER_INTERVAL, + NM_SETTING_BRIDGE_MULTICAST_QUERY_INTERVAL, + NM_SETTING_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL, + NM_SETTING_BRIDGE_MULTICAST_QUERY_USE_IFADDR, + NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT, + NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL, + NM_SETTING_BRIDGE_GROUP_ADDRESS, + NM_SETTING_BRIDGE_VLAN_PROTOCOL, + NM_SETTING_BRIDGE_VLAN_STATS_ENABLED, + NM_SETTING_BRIDGE_VLAN_FILTERING, + NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, + NM_SETTING_BRIDGE_VLANS); +} + +static void +reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_new) +{ + NMDeviceBridge *self = NM_DEVICE_BRIDGE(device); + NMSettingBridge *s_bridge; + + NM_DEVICE_CLASS(nm_device_bridge_parent_class)->reapply_connection(device, con_old, con_new); + + _LOGD(LOGD_BRIDGE, "reapplying bridge settings"); + s_bridge = nm_connection_get_setting_bridge(con_new); + g_return_if_fail(s_bridge); + + /* Make sure bridge_set_vlan_options() called by link_config() + * sets vlan_filtering and default_pvid anew. */ + self->vlan_configured = FALSE; + + link_config(device, con_new); +} + +/*****************************************************************************/ + static void nm_device_bridge_init(NMDeviceBridge *self) { @@ -1183,9 +1259,11 @@ nm_device_bridge_class_init(NMDeviceBridgeClass *klass) device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage2_config = act_stage2_config; device_class->deactivate = deactivate; - device_class->enslave_slave = enslave_slave; - device_class->release_slave = release_slave; + device_class->attach_port = attach_port; + device_class->detach_port = detach_port; device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; + device_class->can_reapply_change = can_reapply_change; + device_class->reapply_connection = reapply_connection; } /*****************************************************************************/ diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c index 32f2fbf9..40c6d208 100644 --- a/src/core/devices/nm-device-ethernet.c +++ b/src/core/devices/nm-device-ethernet.c @@ -451,7 +451,7 @@ supplicant_auth_state_changed(NMSupplicantInterface *iface, if (state == NM_SUPPLICANT_AUTH_STATE_SUCCESS) { nm_clear_g_signal_handler(priv->supplicant.iface, &priv->supplicant.iface_state_id); - nm_device_update_dynamic_ip_setup(NM_DEVICE(self)); + nm_device_update_dynamic_ip_setup(NM_DEVICE(self), "supplicant auth state changed"); } } diff --git a/src/core/devices/nm-device-veth.c b/src/core/devices/nm-device-veth.c index 8c95a293..63dfd8bb 100644 --- a/src/core/devices/nm-device-veth.c +++ b/src/core/devices/nm-device-veth.c @@ -82,6 +82,8 @@ create_and_realize(NMDevice *device, GError **error) { const char *iface = nm_device_get_iface(device); + const char *peer; + NMDevice *peer_device; NMSettingVeth *s_veth; int r; @@ -96,10 +98,15 @@ create_and_realize(NMDevice *device, return FALSE; } - r = nm_platform_link_veth_add(nm_device_get_platform(device), - iface, - nm_setting_veth_get_peer(s_veth), - out_plink); + peer = nm_setting_veth_get_peer(s_veth); + peer_device = nm_manager_get_device(NM_MANAGER_GET, peer, NM_DEVICE_TYPE_VETH); + if (peer_device) { + /* The veth device and its peer already exist. No need to create it again. */ + if (nm_streq0(nm_device_get_iface(nm_device_parent_get_device(peer_device)), iface)) + return TRUE; + } + + r = nm_platform_link_veth_add(nm_device_get_platform(device), iface, peer, out_plink); if (r < 0) { g_set_error(error, NM_DEVICE_ERROR, diff --git a/src/core/devices/nm-device-vrf.c b/src/core/devices/nm-device-vrf.c index ae80e1d4..2aef0e3d 100644 --- a/src/core/devices/nm-device-vrf.c +++ b/src/core/devices/nm-device-vrf.c @@ -206,38 +206,44 @@ update_connection(NMDevice *device, NMConnection *connection) g_object_set(G_OBJECT(s_vrf), NM_SETTING_VRF_TABLE, priv->props.table, NULL); } -static gboolean -enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure) +static NMTernary +attach_port(NMDevice *device, + NMDevice *port, + NMConnection *connection, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { - NMDeviceVrf *self = NM_DEVICE_VRF(device); - gboolean success = TRUE; - const char *slave_iface = nm_device_get_ip_iface(slave); + NMDeviceVrf *self = NM_DEVICE_VRF(device); + gboolean success = TRUE; + const char *port_iface = nm_device_get_ip_iface(port); - nm_device_master_check_slave_physical_port(device, slave, LOGD_DEVICE); + nm_device_master_check_slave_physical_port(device, port, LOGD_DEVICE); if (configure) { - nm_device_take_down(slave, TRUE); + nm_device_take_down(port, TRUE); success = nm_platform_link_enslave(nm_device_get_platform(device), nm_device_get_ip_ifindex(device), - nm_device_get_ip_ifindex(slave)); - nm_device_bring_up(slave, TRUE, NULL); + nm_device_get_ip_ifindex(port)); + nm_device_bring_up(port, TRUE, NULL); if (!success) return FALSE; - _LOGI(LOGD_DEVICE, "enslaved VRF slave %s", slave_iface); + _LOGI(LOGD_DEVICE, "attached VRF port %s", port_iface); } else - _LOGI(LOGD_BOND, "VRF slave %s was enslaved", slave_iface); + _LOGI(LOGD_BOND, "VRF port %s was attached", port_iface); return TRUE; } static void -release_slave(NMDevice *device, NMDevice *slave, gboolean configure) +detach_port(NMDevice *device, NMDevice *port, gboolean configure) { NMDeviceVrf *self = NM_DEVICE_VRF(device); gboolean success; - int ifindex_slave; + int ifindex_port; int ifindex; if (configure) { @@ -246,26 +252,26 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure) configure = FALSE; } - ifindex_slave = nm_device_get_ip_ifindex(slave); + ifindex_port = nm_device_get_ip_ifindex(port); - if (ifindex_slave <= 0) - _LOGD(LOGD_DEVICE, "VRF slave %s is already released", nm_device_get_ip_iface(slave)); + if (ifindex_port <= 0) + _LOGD(LOGD_DEVICE, "VRF port %s is already detached", nm_device_get_ip_iface(port)); if (configure) { - if (ifindex_slave > 0) { + if (ifindex_port > 0) { success = nm_platform_link_release(nm_device_get_platform(device), nm_device_get_ip_ifindex(device), - ifindex_slave); + ifindex_port); if (success) { - _LOGI(LOGD_DEVICE, "released VRF slave %s", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_DEVICE, "detached VRF port %s", nm_device_get_ip_iface(port)); } else { - _LOGW(LOGD_DEVICE, "failed to release VRF slave %s", nm_device_get_ip_iface(slave)); + _LOGW(LOGD_DEVICE, "failed to detach VRF port %s", nm_device_get_ip_iface(port)); } } } else { - if (ifindex_slave > 0) { - _LOGI(LOGD_DEVICE, "VRF slave %s was released", nm_device_get_ip_iface(slave)); + if (ifindex_port > 0) { + _LOGI(LOGD_DEVICE, "VRF port %s was detached", nm_device_get_ip_iface(port)); } } } @@ -316,8 +322,8 @@ nm_device_vrf_class_init(NMDeviceVrfClass *klass) device_class->is_master = TRUE; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_VRF); - device_class->enslave_slave = enslave_slave; - device_class->release_slave = release_slave; + device_class->attach_port = attach_port; + device_class->detach_port = detach_port; device_class->link_changed = link_changed; device_class->unrealize_notify = unrealize_notify; device_class->create_and_realize = create_and_realize; diff --git a/src/core/devices/nm-device-wireguard.c b/src/core/devices/nm-device-wireguard.c index bdb96cb2..179c2a22 100644 --- a/src/core/devices/nm-device-wireguard.c +++ b/src/core/devices/nm-device-wireguard.c @@ -18,7 +18,7 @@ #include "nm-device-private.h" #include "libnm-platform/nm-platform.h" #include "libnm-platform/nmp-object.h" -#include "libnm-platform/nmp-route-manager.h" +#include "libnm-platform/nmp-global-tracker.h" #include "nm-device-factory.h" #include "nm-active-connection.h" #include "nm-act-request.h" diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 6b854517..d63b902b 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -41,7 +41,7 @@ #include "libnm-platform/nm-platform.h" #include "libnm-platform/nm-platform-utils.h" #include "libnm-platform/nmp-object.h" -#include "libnm-platform/nmp-route-manager.h" +#include "libnm-platform/nmp-global-tracker.h" #include "ndisc/nm-ndisc.h" #include "ndisc/nm-lndp-ndisc.h" @@ -120,11 +120,12 @@ typedef enum _nm_packed { } AddrMethodState; typedef struct { - CList lst_slave; - NMDevice *slave; - gulong watch_id; - bool slave_is_enslaved; - bool configure; + CList lst_slave; + NMDevice *slave; + GCancellable *cancellable; + gulong watch_id; + bool slave_is_enslaved; + bool configure; } SlaveInfo; typedef struct { @@ -392,6 +393,8 @@ typedef struct _NMDevicePrivate { guint device_link_changed_id; guint device_ip_link_changed_id; + GSource *delay_activation_source; + NMDeviceState state; NMDeviceStateReason state_reason; struct { @@ -604,6 +607,7 @@ typedef struct _NMDevicePrivate { const NMDeviceIPState state; NMDeviceIPState state_; }; + gulong dnsmgr_update_pending_signal_id; } ip_data; union { @@ -1394,6 +1398,65 @@ _prop_get_connection_dns_over_tls(NMDevice *self) NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT); } +static NMMptcpFlags +_prop_get_connection_mptcp_flags(NMDevice *self) +{ + NMConnection *connection; + NMMptcpFlags mptcp_flags = NM_MPTCP_FLAGS_NONE; + + g_return_val_if_fail(NM_IS_DEVICE(self), NM_MPTCP_FLAGS_DISABLED); + + connection = nm_device_get_applied_connection(self); + if (connection) { + mptcp_flags = + nm_setting_connection_get_mptcp_flags(nm_connection_get_setting_connection(connection)); + } + + if (mptcp_flags == NM_MPTCP_FLAGS_NONE) { + guint64 v; + + v = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("connection.mptcp-flags"), + self, + 0, + G_MAXINT64, + NM_MPTCP_FLAGS_NONE); + if (v != NM_MPTCP_FLAGS_NONE) { + /* We silently ignore all invalid flags (and will normalize them away below). */ + mptcp_flags = (NMMptcpFlags) v; + if (mptcp_flags == NM_MPTCP_FLAGS_NONE) + mptcp_flags = NM_MPTCP_FLAGS_ENABLED; + } + } + + if (mptcp_flags == NM_MPTCP_FLAGS_NONE) + mptcp_flags = _NM_MPTCP_FLAGS_DEFAULT; + + mptcp_flags = nm_mptcp_flags_normalize(mptcp_flags); + + if (!NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_DISABLED)) { + if (!NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_ALSO_WITHOUT_SYSCTL)) { + guint32 v; + + /* If enabled, but without "also-without-sysctl", then MPTCP is still + * disabled, if the sysctl says so... + * + * We evaluate this here. The point is that the decision is then cached + * until deactivation/reapply. The user can toggle the sysctl any time, + * but we only pick it up at certain moments (now). */ + v = nm_platform_sysctl_get_int32( + nm_device_get_platform(self), + NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/mptcp/enabled"), + -1); + if (v <= 0) + mptcp_flags = NM_MPTCP_FLAGS_DISABLED; + } else + mptcp_flags = NM_FLAGS_UNSET(mptcp_flags, NM_MPTCP_FLAGS_ALSO_WITHOUT_SYSCTL); + } + + return mptcp_flags; +} + static guint32 _prop_get_ipvx_route_table(NMDevice *self, int addr_family) { @@ -1508,6 +1571,53 @@ _prop_get_connection_lldp(NMDevice *self) return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX; } +static NMSettingIP4LinkLocal +_prop_get_ipv4_link_local(NMDevice *self) +{ + NMSettingIP4Config *s_ip4; + NMSettingIP4LinkLocal link_local; + + s_ip4 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG); + if (!s_ip4) + return NM_SETTING_IP4_LL_DISABLED; + + link_local = nm_setting_ip4_config_get_link_local(s_ip4); + + if (link_local == NM_SETTING_IP4_LL_DEFAULT) { + /* For connections without a ipv4.link-local property configured the global configuration + might defines the default value for ipv4.link-local. */ + link_local = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("ipv4.link-local"), + self, + NM_SETTING_IP4_LL_AUTO, + NM_SETTING_IP4_LL_ENABLED, + NM_SETTING_IP4_LL_DEFAULT); + if (link_local == NM_SETTING_IP4_LL_DEFAULT) { + /* If there is no global configuration for ipv4.link-local assume auto */ + link_local = NM_SETTING_IP4_LL_AUTO; + } else if (link_local == NM_SETTING_IP4_LL_ENABLED + && nm_streq(nm_setting_ip_config_get_method((NMSettingIPConfig *) s_ip4), + NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { + /* ipv4.method=disabled has higher priority than the global ipv4.link-local=enabled */ + link_local = NM_SETTING_IP4_LL_DISABLED; + } else if (link_local == NM_SETTING_IP4_LL_DISABLED + && nm_streq(nm_setting_ip_config_get_method((NMSettingIPConfig *) s_ip4), + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) { + /* ipv4.method=link-local has higher priority than the global ipv4.link-local=disabled */ + link_local = NM_SETTING_IP4_LL_ENABLED; + } + } + + if (link_local == NM_SETTING_IP4_LL_AUTO) { + link_local = nm_streq(nm_setting_ip_config_get_method((NMSettingIPConfig *) s_ip4), + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) + ? NM_SETTING_IP4_LL_ENABLED + : NM_SETTING_IP4_LL_DISABLED; + } + + return link_local; +} + static guint32 _prop_get_ipv4_dad_timeout(NMDevice *self) { @@ -2118,6 +2228,50 @@ _prop_get_ipv6_ip6_privacy(NMDevice *self) return _ip6_privacy_clamp(ip6_privacy); } +static NMSettingIP6ConfigAddrGenMode +_prop_get_ipv6_addr_gen_mode(NMDevice *self) +{ + NMSettingIP6ConfigAddrGenMode addr_gen_mode; + NMSettingIP6Config *s_ip6; + gint64 c; + + g_return_val_if_fail(self, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY); + + s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6) { + addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode(s_ip6); + if (NM_IN_SET(addr_gen_mode, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY)) + return addr_gen_mode; + } else + addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT; + + nm_assert(NM_IN_SET(addr_gen_mode, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT)); + + c = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("ipv6.addr-gen-mode"), + self, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT, + -1); + if (c != -1) + addr_gen_mode = c; + + if (addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT) + addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY; + else if (addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64) + addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; + + nm_assert(NM_IN_SET(addr_gen_mode, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY)); + + return addr_gen_mode; +} + static const char * _prop_get_x_cloned_mac_address(NMDevice *self, NMConnection *connection, gboolean is_wifi) { @@ -2678,6 +2832,7 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co nm_l3_config_data_set_llmnr(l3cd, _prop_get_connection_llmnr(self)); nm_l3_config_data_set_dns_over_tls(l3cd, _prop_get_connection_dns_over_tls(self)); nm_l3_config_data_set_ip6_privacy(l3cd, _prop_get_ipv6_ip6_privacy(self)); + nm_l3_config_data_set_mptcp_flags(l3cd, _prop_get_connection_mptcp_flags(self)); return l3cd; } @@ -2930,6 +3085,13 @@ _add_capabilities(NMDevice *self, NMDeviceCapabilities capabilities) /*****************************************************************************/ static void +_dev_ip_state_dnsmgr_update_pending_changed(NMDnsManager *dnsmgr, GParamSpec *pspec, NMDevice *self) +{ + _dev_ip_state_check(self, AF_INET); + _dev_ip_state_check(self, AF_INET6); +} + +static void _dev_ip_state_req_timeout_cancel(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -3321,6 +3483,27 @@ got_ip_state: combinedip_state = priv->ip_data.state; } + if (combinedip_state == NM_DEVICE_IP_STATE_READY + && priv->ip_data.state <= NM_DEVICE_IP_STATE_PENDING + && nm_dns_manager_get_update_pending(nm_manager_get_dns_manager(priv->manager))) { + /* We would be ready, but a DNS update is pending. That prevents us from getting fully ready. */ + if (priv->ip_data.dnsmgr_update_pending_signal_id == 0) { + priv->ip_data.dnsmgr_update_pending_signal_id = + g_signal_connect(nm_manager_get_dns_manager(priv->manager), + "notify::" NM_DNS_MANAGER_UPDATE_PENDING, + G_CALLBACK(_dev_ip_state_dnsmgr_update_pending_changed), + self); + _LOGT_ip(AF_UNSPEC, + "check-state: (combined) state: wait for DNS before becoming ready"); + } + combinedip_state = NM_DEVICE_IP_STATE_PENDING; + } + if (combinedip_state != NM_DEVICE_IP_STATE_PENDING + && priv->ip_data.dnsmgr_update_pending_signal_id != 0) { + nm_clear_g_signal_handler(nm_manager_get_dns_manager(priv->manager), + &priv->ip_data.dnsmgr_update_pending_signal_id); + } + _LOGT_ip(AF_UNSPEC, "check-state: (combined) state %s => %s", nm_device_ip_state_to_string(priv->ip_data.state), @@ -5896,43 +6079,16 @@ find_slave_info(NMDevice *self, NMDevice *slave) return NULL; } -/** - * nm_device_master_enslave_slave: - * @self: the master device - * @slave: the slave device to enslave - * @connection: (allow-none): the slave device's connection - * - * If @self is capable of enslaving other devices (ie it's a bridge, bond, team, - * etc) then this function enslaves @slave. - * - * Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave - * other devices. - */ -static gboolean -nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *connection) +static void +attach_port_done(NMDevice *self, NMDevice *slave, gboolean success) { SlaveInfo *info; - gboolean success = FALSE; - gboolean configure; - - g_return_val_if_fail(self != NULL, FALSE); - g_return_val_if_fail(slave != NULL, FALSE); - g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->enslave_slave != NULL, FALSE); info = find_slave_info(self, slave); if (!info) - return FALSE; - - if (info->slave_is_enslaved) - success = TRUE; - else { - configure = (info->configure && connection != NULL); - if (configure) - g_return_val_if_fail(nm_device_get_state(slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); + return; - success = NM_DEVICE_GET_CLASS(self)->enslave_slave(self, slave, connection, configure); - info->slave_is_enslaved = success; - } + info->slave_is_enslaved = success; nm_device_slave_notify_enslave(info->slave, success); @@ -5952,8 +6108,71 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co */ if (success) nm_device_activate_schedule_stage3_ip_config(self, FALSE); +} - return success; +static void +attach_port_cb(NMDevice *self, GError *error, gpointer user_data) +{ + NMDevice *slave = user_data; + SlaveInfo *info; + + if (nm_utils_error_is_cancelled(error)) + return; + + info = find_slave_info(self, slave); + if (!info) + return; + + nm_clear_g_cancellable(&info->cancellable); + attach_port_done(self, slave, !error); +} + +/** + * nm_device_master_enslave_slave: + * @self: the master device + * @slave: the slave device to enslave + * @connection: (allow-none): the slave device's connection + * + * If @self is capable of enslaving other devices (ie it's a bridge, bond, team, + * etc) then this function enslaves @slave. + */ +static void +nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *connection) +{ + SlaveInfo *info; + NMTernary success; + gboolean configure; + + g_return_if_fail(self); + g_return_if_fail(slave); + g_return_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port); + + info = find_slave_info(self, slave); + if (!info) + return; + + if (info->slave_is_enslaved) + success = TRUE; + else { + configure = (info->configure && connection != NULL); + if (configure) + g_return_if_fail(nm_device_get_state(slave) >= NM_DEVICE_STATE_DISCONNECTED); + + nm_clear_g_cancellable(&info->cancellable); + info->cancellable = g_cancellable_new(); + success = NM_DEVICE_GET_CLASS(self)->attach_port(self, + slave, + connection, + configure, + info->cancellable, + attach_port_cb, + slave); + + if (success == NM_TERNARY_DEFAULT) + return; + } + + attach_port_done(self, slave, success); } /** @@ -5986,7 +6205,7 @@ nm_device_master_release_slave(NMDevice *self, RELEASE_SLAVE_TYPE_NO_CONFIG, RELEASE_SLAVE_TYPE_CONFIG, RELEASE_SLAVE_TYPE_CONFIG_FORCE)); - g_return_if_fail(NM_DEVICE_GET_CLASS(self)->release_slave != NULL); + g_return_if_fail(NM_DEVICE_GET_CLASS(self)->detach_port != NULL); info = find_slave_info(self, slave); @@ -6007,13 +6226,14 @@ nm_device_master_release_slave(NMDevice *self, g_return_if_fail(self == slave_priv->master); nm_assert(slave == info->slave); + nm_clear_g_cancellable(&info->cancellable); /* first, let subclasses handle the release ... */ if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave) || release_type >= RELEASE_SLAVE_TYPE_CONFIG_FORCE) - NM_DEVICE_GET_CLASS(self)->release_slave(self, - slave, - release_type >= RELEASE_SLAVE_TYPE_CONFIG); + NM_DEVICE_GET_CLASS(self)->detach_port(self, + slave, + release_type >= RELEASE_SLAVE_TYPE_CONFIG); /* raise notifications about the release, including clearing is_enslaved. */ nm_device_slave_notify_release(slave, reason); @@ -6112,7 +6332,7 @@ _dev_unmanaged_check_external_down(NMDevice *self, gboolean only_if_unmanaged, g } void -nm_device_update_dynamic_ip_setup(NMDevice *self) +nm_device_update_dynamic_ip_setup(NMDevice *self, const char *reason) { NMDevicePrivate *priv; @@ -6123,6 +6343,8 @@ nm_device_update_dynamic_ip_setup(NMDevice *self) if (priv->state < NM_DEVICE_STATE_IP_CONFIG || priv->state > NM_DEVICE_STATE_ACTIVATED) return; + _LOGD(LOGD_DEVICE, "restarting dynamic IP configuration (%s)", reason); + g_hash_table_remove_all(priv->ip6_saved_properties); if (priv->ipdhcp_data_4.state != NM_DEVICE_IP_STATE_NONE) @@ -6344,7 +6566,7 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink) NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } - if (master && NM_DEVICE_GET_CLASS(master)->enslave_slave) { + if (master && NM_DEVICE_GET_CLASS(master)->attach_port) { nm_device_master_add_slave(master, self, FALSE); goto out; } @@ -6424,6 +6646,7 @@ device_link_changed(gpointer user_data) NMDeviceClass *klass = NM_DEVICE_GET_CLASS(self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); gboolean ip_ifname_changed = FALSE; + gboolean hw_addr_changed; nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL; const NMPlatformLink *pllink; const char *str; @@ -6470,9 +6693,9 @@ device_link_changed(gpointer user_data) if (ifindex == nm_device_get_ip_ifindex(self)) _stats_update_counters_from_pllink(self, pllink); - had_hw_addr = (priv->hw_addr != NULL); - nm_device_update_hw_address(self); - got_hw_addr = (!had_hw_addr && priv->hw_addr); + had_hw_addr = (priv->hw_addr != NULL); + hw_addr_changed = nm_device_update_hw_address(self); + got_hw_addr = (!had_hw_addr && priv->hw_addr); nm_device_update_permanent_hw_address(self, FALSE); if (pllink->name[0] && !nm_streq(priv->iface, pllink->name)) { @@ -6522,7 +6745,9 @@ device_link_changed(gpointer user_data) /* Update DHCP, etc, if needed */ if (ip_ifname_changed) - nm_device_update_dynamic_ip_setup(self); + nm_device_update_dynamic_ip_setup(self, "IP interface changed"); + else if (hw_addr_changed) + nm_device_update_dynamic_ip_setup(self, "hw-address changed"); was_up = priv->up; priv->up = NM_FLAGS_HAS(pllink->n_ifi_flags, IFF_UP); @@ -6582,7 +6807,7 @@ device_link_changed(gpointer user_data) * renew DHCP leases and such. */ if (priv->state == NM_DEVICE_STATE_ACTIVATED) { - nm_device_update_dynamic_ip_setup(self); + nm_device_update_dynamic_ip_setup(self, "interface got carrier"); } } @@ -6644,7 +6869,7 @@ device_ip_link_changed(gpointer user_data) priv->ip_iface_ = g_strdup(ip_iface); update_prop_ip_iface(self); - nm_device_update_dynamic_ip_setup(self); + nm_device_update_dynamic_ip_setup(self, "interface renamed"); } return G_SOURCE_REMOVE; @@ -7540,8 +7765,9 @@ slave_state_changed(NMDevice *slave, } if (release) { - configure = priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED - && nm_device_sys_iface_state_get(slave) != NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; + configure = (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED + && nm_device_sys_iface_state_get(slave) != NM_DEVICE_SYS_IFACE_STATE_EXTERNAL) + || nm_device_sys_iface_state_get(slave) == NM_DEVICE_SYS_IFACE_STATE_MANAGED; nm_device_master_release_slave(self, slave, @@ -7577,7 +7803,7 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure) g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); g_return_val_if_fail(NM_IS_DEVICE(slave), FALSE); - g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->enslave_slave != NULL, FALSE); + g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port, FALSE); priv = NM_DEVICE_GET_PRIVATE(self); slave_priv = NM_DEVICE_GET_PRIVATE(slave); @@ -7806,6 +8032,9 @@ nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason) g_return_if_fail(priv->master); + if (!priv->is_enslaved) + return; + if (priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state <= NM_DEVICE_STATE_ACTIVATED) { switch (nm_device_state_reason_check(reason)) { case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: @@ -7835,14 +8064,12 @@ nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason) } else _LOGI(LOGD_DEVICE, "released from master device %s", nm_device_get_iface(priv->master)); - if (priv->is_enslaved) { - priv->is_enslaved = FALSE; + priv->is_enslaved = FALSE; - _notify(self, PROP_MASTER); + _notify(self, PROP_MASTER); - nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref); - nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES); - } + nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref); + nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES); } /** @@ -8161,13 +8388,13 @@ device_has_config(NMDevice *self) head_entry = nm_platform_lookup( nm_device_get_platform(self), - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, pllink->ifindex)); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, pllink->ifindex)); if (head_entry) return TRUE; head_entry = nm_platform_lookup( nm_device_get_platform(self), - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, pllink->ifindex)); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, pllink->ifindex)); if (head_entry) return TRUE; @@ -9329,13 +9556,13 @@ lldp_setup(NMDevice *self, NMTernary enabled) static void _routing_rules_sync(NMDevice *self, NMTernary set_mode) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMPRouteManager *route_manager = nm_netns_get_route_manager(nm_device_get_netns(self)); - NMDeviceClass *klass = NM_DEVICE_GET_CLASS(self); - gboolean untrack_only_dirty = FALSE; - gboolean keep_deleted_rules; - gpointer user_tag_1; - gpointer user_tag_2; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMPGlobalTracker *global_tracker = nm_netns_get_global_tracker(nm_device_get_netns(self)); + NMDeviceClass *klass = NM_DEVICE_GET_CLASS(self); + gboolean untrack_only_dirty = FALSE; + gboolean keep_deleted_rules; + gpointer user_tag_1; + gpointer user_tag_2; /* take two arbitrary user-tag pointers that belong to @self. */ user_tag_1 = &priv->v4_route_table; @@ -9367,13 +9594,13 @@ _routing_rules_sync(NMDevice *self, NMTernary set_mode) nm_ip_routing_rule_to_platform(rule, &plrule); /* We track this rule, but we also make it explicitly not weakly-tracked - * (meaning to untrack NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG at + * (meaning to untrack NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG at * the same time). */ - nmp_route_manager_track_rule(route_manager, - &plrule, - 10, - user_tag_1, - NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG); + nmp_global_tracker_track_rule(global_tracker, + &plrule, + 10, + user_tag_1, + NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG); } } @@ -9383,25 +9610,25 @@ _routing_rules_sync(NMDevice *self, NMTernary set_mode) extra_rules = klass->get_extra_rules(self); if (extra_rules) { for (i = 0; i < extra_rules->len; i++) { - nmp_route_manager_track_rule( - route_manager, + nmp_global_tracker_track_rule( + global_tracker, NMP_OBJECT_CAST_ROUTING_RULE(extra_rules->pdata[i]), 10, user_tag_2, - NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG); + NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG); } } } } - nmp_route_manager_untrack_all(route_manager, user_tag_1, !untrack_only_dirty, TRUE); + nmp_global_tracker_untrack_all(global_tracker, user_tag_1, !untrack_only_dirty, TRUE); if (klass->get_extra_rules) - nmp_route_manager_untrack_all(route_manager, user_tag_2, !untrack_only_dirty, TRUE); + nmp_global_tracker_untrack_all(global_tracker, user_tag_2, !untrack_only_dirty, TRUE); keep_deleted_rules = FALSE; if (set_mode == NM_TERNARY_DEFAULT) { /* when exiting NM, we leave the device up and the rules configured. - * We just call nmp_route_manager_sync() to forget about the synced rules, + * We just call nmp_global_tracker_sync() to forget about the synced rules, * but we don't actually delete them. * * FIXME: that is a problem after restart of NetworkManager, because these @@ -9415,7 +9642,7 @@ _routing_rules_sync(NMDevice *self, NMTernary set_mode) * file and track them after restart again. */ keep_deleted_rules = TRUE; } - nmp_route_manager_sync(route_manager, NMP_OBJECT_TYPE_ROUTING_RULE, keep_deleted_rules); + nmp_global_tracker_sync(global_tracker, NMP_OBJECT_TYPE_ROUTING_RULE, keep_deleted_rules); } static gboolean @@ -10045,6 +10272,7 @@ _dev_ipdhcpx_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_d FALSE); if (notify_data->lease_update.accepted) { + nm_manager_write_device_state(priv->manager, self, NULL); if (priv->ipdhcp_data_x[IS_IPv4].state != NM_DEVICE_IP_STATE_READY) { _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_READY); nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_X(IS_IPv4), @@ -10190,7 +10418,11 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) .vendor_class_identifier = vendor_class_identifier, .use_fqdn = hostname_is_fqdn, .reject_servers = reject_servers, - .v4.request_broadcast = request_broadcast, + .v4 = + { + .request_broadcast = request_broadcast, + .acd_timeout_msec = _prop_get_ipv4_dad_timeout(self), + }, }; priv->ipdhcp_data_4.client = @@ -10205,22 +10437,25 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) duid = _prop_get_ipv6_dhcp_duid(self, connection, hwaddr, &enforce_duid); config = (NMDhcpClientConfig){ - .addr_family = AF_INET6, - .l3cfg = nm_device_get_l3cfg(self), - .iface = nm_device_get_ip_iface(self), - .uuid = nm_connection_get_uuid(connection), - .send_hostname = nm_setting_ip_config_get_dhcp_send_hostname(s_ip), - .hostname = nm_setting_ip_config_get_dhcp_hostname(s_ip), - .hostname_flags = _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET6), - .client_id = duid, - .mud_url = _prop_get_connection_mud_url(self, s_con), - .timeout = no_lease_timeout_sec, - .anycast_address = _device_get_dhcp_anycast_address(self), - .v6.enforce_duid = enforce_duid, - .v6.iaid = iaid, - .v6.iaid_explicit = iaid_explicit, - .v6.info_only = (priv->ipdhcp_data_6.v6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF), - .v6.needed_prefixes = priv->ipdhcp_data_6.v6.needed_prefixes, + .addr_family = AF_INET6, + .l3cfg = nm_device_get_l3cfg(self), + .iface = nm_device_get_ip_iface(self), + .uuid = nm_connection_get_uuid(connection), + .send_hostname = nm_setting_ip_config_get_dhcp_send_hostname(s_ip), + .hostname = nm_setting_ip_config_get_dhcp_hostname(s_ip), + .hostname_flags = _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET6), + .client_id = duid, + .mud_url = _prop_get_connection_mud_url(self, s_con), + .timeout = no_lease_timeout_sec, + .anycast_address = _device_get_dhcp_anycast_address(self), + .v6 = + { + .enforce_duid = enforce_duid, + .iaid = iaid, + .iaid_explicit = iaid_explicit, + .info_only = (priv->ipdhcp_data_6.v6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF), + .needed_prefixes = priv->ipdhcp_data_6.v6.needed_prefixes, + }, }; priv->ipdhcp_data_6.client = @@ -10635,7 +10870,6 @@ _dev_ipll6_start(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NMConnection *connection; - NMSettingIP6Config *s_ip6 = NULL; gboolean assume; const char *ifname; NML3IPv6LLState llstate; @@ -10656,14 +10890,10 @@ _dev_ipll6_start(NMDevice *self) } connection = nm_device_get_applied_connection(self); - if (connection) - s_ip6 = NM_SETTING_IP6_CONFIG(nm_connection_get_setting_ip6_config(connection)); assume = nm_device_sys_iface_state_is_external_or_assume(self); - if (s_ip6 - && nm_setting_ip6_config_get_addr_gen_mode(s_ip6) - == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { + if (_prop_get_ipv6_addr_gen_mode(self) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { NMUtilsStableType stable_type; const char *stable_id; @@ -10866,9 +11096,10 @@ _commit_mtu(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NMDeviceMtuSource source = NM_DEVICE_MTU_SOURCE_NONE; + NMSettingIPConfig *s_ip6; const NML3ConfigData *l3cd; guint32 ip6_mtu_orig; - guint32 ip6_mtu; + guint32 ip6_mtu = 0; guint32 mtu_desired_orig; guint32 mtu_desired; guint32 mtu_plat; @@ -10953,10 +11184,9 @@ _commit_mtu(NMDevice *self) } } - if (mtu_desired && mtu_desired < 1280) { - NMSettingIPConfig *s_ip6; + s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG); - s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG); + if (mtu_desired && mtu_desired < 1280) { if (s_ip6 && !NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip6), NM_SETTING_IP6_CONFIG_METHOD_IGNORE, @@ -10971,7 +11201,12 @@ _commit_mtu(NMDevice *self) } } - ip6_mtu = priv->ip6_mtu; + if (s_ip6) + ip6_mtu = nm_setting_ip6_config_get_mtu(NM_SETTING_IP6_CONFIG(s_ip6)); + + if (!ip6_mtu) + ip6_mtu = priv->ip6_mtu; + if (!ip6_mtu && priv->mtu_source == NM_DEVICE_MTU_SOURCE_NONE) { /* initially, if the IPv6 MTU is not specified, grow it as large as the * link MTU @mtu_desired. Only exception is, if @mtu_desired is so small @@ -11353,7 +11588,7 @@ _dev_ipac6_start(NMDevice *self) .ifname = nm_device_get_ip_iface(self), .stable_type = stable_type, .network_id = stable_id, - .addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode(s_ip), + .addr_gen_mode = _prop_get_ipv6_addr_gen_mode(self), .node_type = node_type, .max_addresses = max_addresses, .router_solicitations = router_solicitations, @@ -11640,11 +11875,14 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const goto out_devip; if (IS_IPv4) { + if (_prop_get_ipv4_link_local(self) == NM_SETTING_IP4_LL_ENABLED) + _dev_ipll4_start(self); + if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) _dev_ipdhcpx_start(self, AF_INET); - else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) - _dev_ipll4_start(self); - else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) { + /* pass */ + } else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) _dev_ipshared4_start(self); else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) priv->ip_data_x[IS_IPv4].is_disabled = TRUE; @@ -12344,7 +12582,8 @@ delete_on_deactivate_check_and_schedule(NMDevice *self) static void _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean from_reapply) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); _dev_ipsharedx_cleanup(self, addr_family); @@ -12360,6 +12599,9 @@ _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gbool _dev_ipmanual_cleanup(self); + nm_clear_g_signal_handler(nm_manager_get_dns_manager(priv->manager), + &priv->ip_data.dnsmgr_update_pending_signal_id); + _dev_ip_state_cleanup(self, AF_UNSPEC, from_reapply); _dev_ip_state_cleanup(self, addr_family, from_reapply); } @@ -12479,12 +12721,15 @@ can_reapply_change(NMDevice *self, NM_SETTING_CONNECTION_STABLE_ID, NM_SETTING_CONNECTION_AUTOCONNECT, NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, NM_SETTING_CONNECTION_ZONE, NM_SETTING_CONNECTION_METERED, NM_SETTING_CONNECTION_LLDP, NM_SETTING_CONNECTION_MDNS, NM_SETTING_CONNECTION_LLMNR, - NM_SETTING_CONNECTION_DNS_OVER_TLS); + NM_SETTING_CONNECTION_DNS_OVER_TLS, + NM_SETTING_CONNECTION_MPTCP_FLAGS, + NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY); } if (NM_IN_STRSET(setting_name, @@ -12700,7 +12945,8 @@ check_and_reapply_connection(NMDevice *self, NM_SETTING_CONNECTION_LLDP, NM_SETTING_CONNECTION_MDNS, NM_SETTING_CONNECTION_LLMNR, - NM_SETTING_CONNECTION_DNS_OVER_TLS)) { + NM_SETTING_CONNECTION_DNS_OVER_TLS, + NM_SETTING_CONNECTION_MPTCP_FLAGS)) { priv->ip_data_4.do_reapply = TRUE; priv->ip_data_6.do_reapply = TRUE; } @@ -13361,29 +13607,71 @@ _dispatcher_cleanup(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + nm_clear_g_source_inst(&priv->delay_activation_source); + if (!priv->dispatcher.call_id) return FALSE; nm_dispatcher_call_cancel(g_steal_pointer(&priv->dispatcher.call_id)); priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; + return TRUE; } static void -_dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_data) +_queue_dispatcher_post_state(NMDevice *self) { - NMDevice *self = NM_DEVICE(user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - g_return_if_fail(call_id == priv->dispatcher.call_id); - - priv->dispatcher.call_id = NULL; nm_device_queue_state(self, priv->dispatcher.post_state, priv->dispatcher.post_state_reason); priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; } +static gboolean +_wait_activation_delay_timeout(gpointer user_data) +{ + NMDevice *self = user_data; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->delay_activation_source); + + _LOGD(LOGD_DEVICE, "finished waiting on activation delay"); + _queue_dispatcher_post_state(self); + + return G_SOURCE_REMOVE; +} + +static void +_dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_data) +{ + NMDevice *self = NM_DEVICE(user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMConnection *conn; + NMSettingConnection *s_conn; + gint32 delay_timeout; + + g_return_if_fail(call_id == priv->dispatcher.call_id); + nm_assert(!priv->delay_activation_source); + + priv->dispatcher.call_id = NULL; + conn = nm_device_get_applied_connection(self); + if (conn) { + s_conn = nm_connection_get_setting_connection(conn); + if (s_conn) { + delay_timeout = nm_setting_connection_get_wait_activation_delay(s_conn); + if (delay_timeout > 0) { + priv->delay_activation_source = + nm_g_timeout_add_source(delay_timeout, _wait_activation_delay_timeout, self); + return; + } + } + } + + _queue_dispatcher_post_state(self); +} + /*****************************************************************************/ static void @@ -13437,14 +13725,14 @@ spawn_ping(NMDevice *self) gs_free char *str_timeout = NULL; gs_free char *tmp_str = NULL; const char *args[] = {priv->gw_ping.binary, - "-I", - nm_device_get_ip_iface(self), - "-c", - "1", - "-w", - NULL, - priv->gw_ping.address, - NULL}; + "-I", + nm_device_get_ip_iface(self), + "-c", + "1", + "-w", + NULL, + priv->gw_ping.address, + NULL}; gs_free_error GError *error = NULL; gboolean ret; @@ -13665,10 +13953,38 @@ _get_carrier_wait_ms(NMDevice *self) CARRIER_WAIT_TIME_MS); } +/* + * Devices that support carrier detect must be IFF_UP to report carrier + * changes; so after setting the device IFF_UP we must suppress startup + * complete (via a pending action) until either the carrier turns on, or + * a timeout is reached. + */ +static void +carrier_detect_wait(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gint64 now_ms, until_ms; + + if (!nm_device_has_capability(self, NM_DEVICE_CAP_CARRIER_DETECT)) + return; + + /* we start a grace period of 5 seconds during which we will schedule + * a pending action whenever we have no carrier. + * + * If during that time carrier goes away, we declare the interface + * as not ready. */ + nm_clear_g_source(&priv->carrier_wait_id); + if (!priv->carrier) + nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); + + now_ms = nm_utils_get_monotonic_timestamp_msec(); + until_ms = NM_MAX(now_ms + _get_carrier_wait_ms(self), priv->carrier_wait_until_ms); + priv->carrier_wait_id = g_timeout_add(until_ms - now_ms, carrier_wait_timeout, self); +} + gboolean nm_device_bring_up(NMDevice *self, gboolean block, gboolean *no_firmware) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); gboolean device_is_up = FALSE; NMDeviceCapabilities capabilities; int ifindex; @@ -13724,27 +14040,7 @@ nm_device_bring_up(NMDevice *self, gboolean block, gboolean *no_firmware) capabilities |= NM_DEVICE_GET_CLASS(self)->get_generic_capabilities(self); _add_capabilities(self, capabilities); - /* Devices that support carrier detect must be IFF_UP to report carrier - * changes; so after setting the device IFF_UP we must suppress startup - * complete (via a pending action) until either the carrier turns on, or - * a timeout is reached. - */ - if (nm_device_has_capability(self, NM_DEVICE_CAP_CARRIER_DETECT)) { - gint64 now_ms, until_ms; - - /* we start a grace period of 5 seconds during which we will schedule - * a pending action whenever we have no carrier. - * - * If during that time carrier goes away, we declare the interface - * as not ready. */ - nm_clear_g_source(&priv->carrier_wait_id); - if (!priv->carrier) - nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); - - now_ms = nm_utils_get_monotonic_timestamp_msec(); - until_ms = NM_MAX(now_ms + _get_carrier_wait_ms(self), priv->carrier_wait_until_ms); - priv->carrier_wait_id = g_timeout_add(until_ms - now_ms, carrier_wait_timeout, self); - } + carrier_detect_wait(self); /* Can only get HW address of some devices when they are up */ nm_device_update_hw_address(self); @@ -14997,6 +15293,9 @@ _cancel_activation(NMDevice *self) _dispatcher_cleanup(self); ip_check_gw_ping_cleanup(self); + _dev_ip_state_cleanup(self, AF_INET, FALSE); + _dev_ip_state_cleanup(self, AF_INET6, FALSE); + /* Break the activation chain */ activation_source_clear(self); } @@ -15493,6 +15792,13 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) ip6_managed_setup(self); device_init_static_sriov_num_vfs(self); + + /* We didn't bring the device up and we have little idea + * when was it brought up. Play it safe and assume it could + * have been brought up very recently and it might one of + * those who take time to detect carrier. + */ + carrier_detect_wait(self); } if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) { @@ -16804,7 +17110,7 @@ get_address_for_hostname_dns_lookup(NMDevice *self, int addr_family) * opposed to an address that is configured? */ head_entry = nm_platform_lookup( nm_device_get_platform(self), - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex)); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex)); if (head_entry) { c_list_for_each_entry (iter, &head_entry->lst_entries_head, lst_entries) { diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index d59aecb2..de850e68 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -163,6 +163,7 @@ typedef enum { } NMDeviceCheckDevAvailableFlags; typedef void (*NMDeviceDeactivateCallback)(NMDevice *self, GError *error, gpointer user_data); +typedef void (*NMDeviceAttachPortCallback)(NMDevice *self, GError *error, gpointer user_data); typedef struct _NMDeviceClass { NMDBusObjectClass parent; @@ -373,12 +374,18 @@ typedef struct _NMDeviceClass { NMConnection *connection, GError **error); - gboolean (*enslave_slave)(NMDevice *self, - NMDevice *slave, - NMConnection *connection, - gboolean configure); - - void (*release_slave)(NMDevice *self, NMDevice *slave, gboolean configure); + /* Attachs a port asynchronously. Returns TRUE/FALSE on immediate + * success/error; in such cases, the callback is not invoked. If the + * action couldn't be completed immediately, DEFAULT is returned and + * the callback will always be invoked asynchronously. */ + NMTernary (*attach_port)(NMDevice *self, + NMDevice *port, + NMConnection *connection, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data); + void (*detach_port)(NMDevice *self, NMDevice *port, gboolean configure); void (*parent_changed_notify)(NMDevice *self, int old_ifindex, @@ -757,7 +764,7 @@ void nm_device_update_metered(NMDevice *self); gboolean nm_device_update_hw_address(NMDevice *self); void nm_device_update_initial_hw_address(NMDevice *self); void nm_device_update_permanent_hw_address(NMDevice *self, gboolean force_freeze); -void nm_device_update_dynamic_ip_setup(NMDevice *self); +void nm_device_update_dynamic_ip_setup(NMDevice *self, const char *reason); guint nm_device_get_supplicant_timeout(NMDevice *self); gboolean nm_device_auth_retries_try_next(NMDevice *self); diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.c b/src/core/devices/ovs/nm-device-ovs-bridge.c index ea77dd18..048afb02 100644 --- a/src/core/devices/ovs/nm-device-ovs-bridge.c +++ b/src/core/devices/ovs/nm-device-ovs-bridge.c @@ -78,20 +78,26 @@ act_stage3_ip_config(NMDevice *device, int addr_family) nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); } -static gboolean -enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure) +static NMTernary +attach_port(NMDevice *device, + NMDevice *port, + NMConnection *connection, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { if (!configure) return TRUE; - if (!NM_IS_DEVICE_OVS_PORT(slave)) + if (!NM_IS_DEVICE_OVS_PORT(port)) return FALSE; return TRUE; } static void -release_slave(NMDevice *device, NMDevice *slave, gboolean configure) +detach_port(NMDevice *device, NMDevice *port, gboolean configure) {} void @@ -159,8 +165,8 @@ nm_device_ovs_bridge_class_init(NMDeviceOvsBridgeClass *klass) device_class->get_generic_capabilities = get_generic_capabilities; device_class->act_stage3_ip_config = act_stage3_ip_config; device_class->ready_for_ip_config = ready_for_ip_config; - device_class->enslave_slave = enslave_slave; - device_class->release_slave = release_slave; + device_class->attach_port = attach_port; + device_class->detach_port = detach_port; device_class->can_reapply_change_ovs_external_ids = TRUE; device_class->reapply_connection = nm_device_ovs_reapply_connection; } diff --git a/src/core/devices/ovs/nm-device-ovs-port.c b/src/core/devices/ovs/nm-device-ovs-port.c index 6ba52f40..5510e39f 100644 --- a/src/core/devices/ovs/nm-device-ovs-port.c +++ b/src/core/devices/ovs/nm-device-ovs-port.c @@ -72,20 +72,42 @@ act_stage3_ip_config(NMDevice *device, int addr_family) nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); } +typedef struct { + NMDevice *device; + NMDevice *port; + GCancellable *cancellable; + NMDeviceAttachPortCallback callback; + gpointer callback_user_data; +} AttachPortData; + static void add_iface_cb(GError *error, gpointer user_data) { - NMDevice *slave = user_data; - - if (error && !g_error_matches(error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING)) { - nm_log_warn(LOGD_DEVICE, - "device %s could not be added to a ovs port: %s", - nm_device_get_iface(slave), - error->message); - nm_device_state_changed(slave, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_OVSDB_FAILED); + AttachPortData *data = user_data; + NMDeviceOvsPort *self; + gs_free_error GError *local = NULL; + + if (g_cancellable_is_cancelled(data->cancellable)) { + local = nm_utils_error_new_cancelled(FALSE, NULL); + error = local; + } else if (error && !nm_utils_error_is_cancelled_or_disposing(error)) { + self = NM_DEVICE_OVS_PORT(data->device); + _LOGW(LOGD_DEVICE, + "device %s could not be added to a ovs port: %s", + nm_device_get_iface(data->port), + error->message); + nm_device_state_changed(data->port, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_OVSDB_FAILED); } - g_object_unref(slave); + data->callback(data->device, error, data->callback_user_data); + + g_object_unref(data->device); + g_object_unref(data->port); + nm_clear_g_cancellable(&data->cancellable); + + nm_g_slice_free(data); } static gboolean @@ -115,14 +137,21 @@ set_mtu_cb(GError *error, gpointer user_data) g_object_unref(self); } -static gboolean -enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure) +static NMTernary +attach_port(NMDevice *device, + NMDevice *port, + NMConnection *connection, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { NMDeviceOvsPort *self = NM_DEVICE_OVS_PORT(device); NMActiveConnection *ac_port = NULL; NMActiveConnection *ac_bridge = NULL; NMDevice *bridge_device; NMSettingWired *s_wired; + AttachPortData *data; if (!configure) return TRUE; @@ -131,42 +160,49 @@ enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gbool ac_bridge = nm_active_connection_get_master(ac_port); if (!ac_bridge) { _LOGW(LOGD_DEVICE, - "can't enslave %s: bridge active-connection not found", - nm_device_get_iface(slave)); + "can't attach %s: bridge active-connection not found", + nm_device_get_iface(port)); return FALSE; } bridge_device = nm_active_connection_get_device(ac_bridge); if (!bridge_device) { - _LOGW(LOGD_DEVICE, "can't enslave %s: bridge device not found", nm_device_get_iface(slave)); + _LOGW(LOGD_DEVICE, "can't attach %s: bridge device not found", nm_device_get_iface(port)); return FALSE; } + data = g_slice_new(AttachPortData); + *data = (AttachPortData){ + .device = g_object_ref(device), + .port = g_object_ref(port), + .cancellable = g_object_ref(cancellable), + .callback = callback, + .callback_user_data = user_data, + }; + nm_ovsdb_add_interface(nm_ovsdb_get(), nm_active_connection_get_applied_connection(ac_bridge), nm_device_get_applied_connection(device), - nm_device_get_applied_connection(slave), + nm_device_get_applied_connection(port), bridge_device, - slave, + port, add_iface_cb, - g_object_ref(slave)); + data); /* DPDK ports does not have a link after the devbind, so the MTU must be * set on ovsdb after adding the interface. */ - if (NM_IS_DEVICE_OVS_INTERFACE(slave) && _ovs_interface_is_dpdk(slave)) { - s_wired = nm_device_get_applied_setting(slave, NM_TYPE_SETTING_WIRED); - - if (!s_wired || !nm_setting_wired_get_mtu(s_wired)) - return TRUE; - - nm_ovsdb_set_interface_mtu(nm_ovsdb_get(), - nm_device_get_ip_iface(slave), - nm_setting_wired_get_mtu(s_wired), - set_mtu_cb, - g_object_ref(slave)); + if (NM_IS_DEVICE_OVS_INTERFACE(port) && _ovs_interface_is_dpdk(port)) { + s_wired = nm_device_get_applied_setting(port, NM_TYPE_SETTING_WIRED); + if (s_wired && nm_setting_wired_get_mtu(s_wired)) { + nm_ovsdb_set_interface_mtu(nm_ovsdb_get(), + nm_device_get_ip_iface(port), + nm_setting_wired_get_mtu(s_wired), + set_mtu_cb, + g_object_ref(port)); + } } - return TRUE; + return NM_TERNARY_DEFAULT; } static void @@ -186,31 +222,31 @@ del_iface_cb(GError *error, gpointer user_data) } static void -release_slave(NMDevice *device, NMDevice *slave, gboolean configure) +detach_port(NMDevice *device, NMDevice *port, gboolean configure) { - NMDeviceOvsPort *self = NM_DEVICE_OVS_PORT(device); - bool slave_not_managed = !NM_IN_SET(nm_device_sys_iface_state_get(slave), - NM_DEVICE_SYS_IFACE_STATE_MANAGED, - NM_DEVICE_SYS_IFACE_STATE_ASSUME); + NMDeviceOvsPort *self = NM_DEVICE_OVS_PORT(device); + bool port_not_managed = !NM_IN_SET(nm_device_sys_iface_state_get(port), + NM_DEVICE_SYS_IFACE_STATE_MANAGED, + NM_DEVICE_SYS_IFACE_STATE_ASSUME); - _LOGI(LOGD_DEVICE, "releasing ovs interface %s", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_DEVICE, "detaching ovs interface %s", nm_device_get_ip_iface(port)); /* Even if the an interface's device has gone away (e.g. externally * removed and thus we're called with configure=FALSE), we still need * to make sure its OVSDB entry is gone. */ - if (configure || slave_not_managed) { + if (configure || port_not_managed) { nm_ovsdb_del_interface(nm_ovsdb_get(), - nm_device_get_iface(slave), + nm_device_get_iface(port), del_iface_cb, - g_object_ref(slave)); + g_object_ref(port)); } if (configure) { /* Open VSwitch is going to delete this one. We must ignore what happens * next with the interface. */ - if (NM_IS_DEVICE_OVS_INTERFACE(slave)) - nm_device_update_from_platform_link(slave, NULL); + if (NM_IS_DEVICE_OVS_INTERFACE(port)) + nm_device_update_from_platform_link(port, NULL); } } @@ -245,8 +281,8 @@ nm_device_ovs_port_class_init(NMDeviceOvsPortClass *klass) device_class->get_generic_capabilities = get_generic_capabilities; device_class->act_stage3_ip_config = act_stage3_ip_config; device_class->ready_for_ip_config = ready_for_ip_config; - device_class->enslave_slave = enslave_slave; - device_class->release_slave = release_slave; + device_class->attach_port = attach_port; + device_class->detach_port = detach_port; device_class->can_reapply_change_ovs_external_ids = TRUE; device_class->reapply_connection = nm_device_ovs_reapply_connection; } diff --git a/src/core/devices/ovs/nm-ovs-factory.c b/src/core/devices/ovs/nm-ovs-factory.c index 5aaa5c01..ff2c7858 100644 --- a/src/core/devices/ovs/nm-ovs-factory.c +++ b/src/core/devices/ovs/nm-ovs-factory.c @@ -178,7 +178,8 @@ ovsdb_device_removed(NMOvsdb *ovsdb, device_state = nm_device_get_state(device); if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE && nm_device_get_act_request(device) - && device_state < NM_DEVICE_STATE_DEACTIVATING) { + && (device_state > NM_DEVICE_STATE_DISCONNECTED + && device_state < NM_DEVICE_STATE_DEACTIVATING)) { nm_device_state_changed(device, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_REMOVED); diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c index 44e16cb7..e7c96852 100644 --- a/src/core/devices/ovs/nm-ovsdb.c +++ b/src/core/devices/ovs/nm-ovsdb.c @@ -376,6 +376,9 @@ ovsdb_call_method(NMOvsdb *self, NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); OvsdbMethodCall *call; + /* FIXME(shutdown): this function should accept a cancellable to + * interrupt the operation. */ + /* Ensure we're not unsynchronized before we queue the method call. */ ovsdb_try_connect(self); @@ -1550,7 +1553,7 @@ _external_ids_to_string(const GArray *arr) if (!arr) return g_strdup("empty"); - nm_str_buf_init(&strbuf, NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE); + strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE); nm_str_buf_append(&strbuf, "["); for (i = 0; i < arr->len; i++) { const NMUtilsNamedValue *n = &g_array_index(arr, NMUtilsNamedValue, i); @@ -1584,7 +1587,7 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) json_t *items; json_t *external_ids; json_error_t json_error = { - 0, + 0, }; void *iter; const char *name; @@ -1985,7 +1988,7 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg) { NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); json_error_t json_error = { - 0, + 0, }; json_t *json_id = NULL; json_int_t id = (json_int_t) -1; @@ -2127,7 +2130,7 @@ ovsdb_read_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) gssize size; json_t *msg; json_error_t json_error = { - 0, + 0, }; size = g_input_stream_read_finish(stream, res, &error); diff --git a/src/core/devices/team/nm-device-team.c b/src/core/devices/team/nm-device-team.c index b67c7100..e6d34266 100644 --- a/src/core/devices/team/nm-device-team.c +++ b/src/core/devices/team/nm-device-team.c @@ -65,6 +65,50 @@ static gboolean teamd_start(NMDeviceTeam *self); /*****************************************************************************/ +static struct teamdctl * +_tdc_connect_new(NMDeviceTeam *self, const char *iface, GError **error) +{ + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); + struct teamdctl *tdc; + const char *cli_type; + int r; + + tdc = teamdctl_alloc(); + if (!tdc) { + nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, "failure to allocate teamdctl structure"); + g_return_val_if_reached(NULL); + } + + if (priv->teamd_dbus_watch) + cli_type = "dbus"; + else if (priv->usock_monitor) + cli_type = "usock"; + else + cli_type = NULL; + +again: + r = teamdctl_connect(tdc, iface, NULL, cli_type); + if (r != 0) { + _LOGD(LOGD_TEAM, + "failure to connect to teamdctl%s%s, err=%d", + NM_PRINT_FMT_QUOTED2(cli_type, " with cli_type=", cli_type, ""), + r); + if (cli_type) { + /* How odd. Let's retry with any CLI type. */ + cli_type = NULL; + goto again; + } + teamdctl_free(tdc); + nm_utils_error_set(error, + NM_UTILS_ERROR_UNKNOWN, + "failure to connect to teamd (err=%d)", + r); + return NULL; + } + + return tdc; +} + static NMDeviceCapabilities get_generic_capabilities(NMDevice *device) { @@ -96,21 +140,16 @@ complete_connection(NMDevice *device, static gboolean ensure_teamd_connection(NMDevice *device) { - NMDeviceTeam *self = NM_DEVICE_TEAM(device); - NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); - int err; + NMDeviceTeam *self = NM_DEVICE_TEAM(device); + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); + gs_free_error GError *error = NULL; if (priv->tdc) return TRUE; - priv->tdc = teamdctl_alloc(); - g_assert(priv->tdc); - err = teamdctl_connect(priv->tdc, nm_device_get_iface(device), NULL, NULL); - if (err != 0) { - _LOGE(LOGD_TEAM, "failed to connect to teamd (err=%d)", err); - teamdctl_free(priv->tdc); - priv->tdc = NULL; - } + priv->tdc = _tdc_connect_new(self, nm_device_get_iface(device), &error); + if (!priv->tdc) + _LOGE(LOGD_TEAM, "failed to connect to teamd: %s", error->message); return !!priv->tdc; } @@ -183,42 +222,31 @@ update_connection(NMDevice *device, NMConnection *connection) /*****************************************************************************/ static gboolean -master_update_slave_connection(NMDevice *self, +master_update_slave_connection(NMDevice *device, NMDevice *slave, NMConnection *connection, GError **error) { - NMSettingTeamPort *s_port; - char *port_config = NULL; - int err = 0; - struct teamdctl *tdc; - const char *team_port_config = NULL; - const char *iface = nm_device_get_iface(self); - const char *iface_slave = nm_device_get_iface(slave); - - tdc = teamdctl_alloc(); + NMDeviceTeam *self = NM_DEVICE_TEAM(device); + NMSettingTeamPort *s_port; + char *port_config = NULL; + gs_free_error GError *connect_error = NULL; + int err = 0; + struct teamdctl *tdc; + const char *team_port_config = NULL; + const char *iface = nm_device_get_iface(device); + const char *iface_slave = nm_device_get_iface(slave); + + tdc = _tdc_connect_new(self, iface, &connect_error); if (!tdc) { g_set_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "update slave connection for slave '%s' failed to connect to teamd for master " - "%s (out of memory?)", - iface_slave, - iface); - g_return_val_if_reached(FALSE); - } - - err = teamdctl_connect(tdc, iface, NULL, NULL); - if (err) { - teamdctl_free(tdc); - g_set_error(error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_FAILED, - "update slave connection for slave '%s' failed to connect to teamd for master " - "%s (err=%d)", + "%s (%s)", iface_slave, iface, - err); + connect_error->message); return FALSE; } @@ -790,19 +818,25 @@ deactivate(NMDevice *device) teamd_cleanup(self, TRUE); } -static gboolean -enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure) +static NMTernary +attach_port(NMDevice *device, + NMDevice *port, + NMConnection *connection, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { - NMDeviceTeam *self = NM_DEVICE_TEAM(device); - NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); - gboolean success = TRUE; - const char *slave_iface = nm_device_get_ip_iface(slave); + NMDeviceTeam *self = NM_DEVICE_TEAM(device); + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); + gboolean success = TRUE; + const char *port_iface = nm_device_get_ip_iface(port); NMSettingTeamPort *s_team_port; - nm_device_master_check_slave_physical_port(device, slave, LOGD_TEAM); + nm_device_master_check_slave_physical_port(device, port, LOGD_TEAM); if (configure) { - nm_device_take_down(slave, TRUE); + nm_device_take_down(port, TRUE); s_team_port = nm_connection_get_setting_team_port(connection); if (s_team_port) { @@ -811,19 +845,19 @@ enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gbool if (config) { if (!priv->tdc) { _LOGW(LOGD_TEAM, - "enslaved team port %s config not changed, not connected to teamd", - slave_iface); + "attached team port %s config not changed, not connected to teamd", + port_iface); } else { gs_free char *sanitized_config = NULL; int err; sanitized_config = g_strdup(config); g_strdelimit(sanitized_config, "\r\n", ' '); - err = teamdctl_port_config_update_raw(priv->tdc, slave_iface, sanitized_config); + err = teamdctl_port_config_update_raw(priv->tdc, port_iface, sanitized_config); if (err != 0) { _LOGE(LOGD_TEAM, "failed to update config for port %s (err=%d)", - slave_iface, + port_iface, err); return FALSE; } @@ -832,8 +866,8 @@ enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gbool } success = nm_platform_link_enslave(nm_device_get_platform(device), nm_device_get_ip_ifindex(device), - nm_device_get_ip_ifindex(slave)); - nm_device_bring_up(slave, TRUE, NULL); + nm_device_get_ip_ifindex(port)); + nm_device_bring_up(port, TRUE, NULL); if (!success) return FALSE; @@ -841,21 +875,21 @@ enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gbool nm_clear_g_source(&priv->teamd_read_timeout); priv->teamd_read_timeout = g_timeout_add_seconds(5, teamd_read_timeout_cb, self); - _LOGI(LOGD_TEAM, "enslaved team port %s", slave_iface); + _LOGI(LOGD_TEAM, "attached team port %s", port_iface); } else - _LOGI(LOGD_TEAM, "team port %s was enslaved", slave_iface); + _LOGI(LOGD_TEAM, "team port %s was attached", port_iface); return TRUE; } static void -release_slave(NMDevice *device, NMDevice *slave, gboolean configure) +detach_port(NMDevice *device, NMDevice *port, gboolean configure) { NMDeviceTeam *self = NM_DEVICE_TEAM(device); NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); gboolean do_release, success; NMSettingTeamPort *s_port; - int ifindex_slave; + int ifindex_port; int ifindex; do_release = configure; @@ -865,39 +899,39 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure) do_release = FALSE; } - ifindex_slave = nm_device_get_ip_ifindex(slave); + ifindex_port = nm_device_get_ip_ifindex(port); - if (ifindex_slave <= 0) { - _LOGD(LOGD_TEAM, "team port %s is already released", nm_device_get_ip_iface(slave)); + if (ifindex_port <= 0) { + _LOGD(LOGD_TEAM, "team port %s is already detached", nm_device_get_ip_iface(port)); } else if (do_release) { success = nm_platform_link_release(nm_device_get_platform(device), nm_device_get_ip_ifindex(device), - ifindex_slave); + ifindex_port); if (success) - _LOGI(LOGD_TEAM, "released team port %s", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_TEAM, "detached team port %s", nm_device_get_ip_iface(port)); else - _LOGW(LOGD_TEAM, "failed to release team port %s", nm_device_get_ip_iface(slave)); + _LOGW(LOGD_TEAM, "failed to detach team port %s", nm_device_get_ip_iface(port)); /* Kernel team code "closes" the port when releasing it, (which clears * IFF_UP), so we must bring it back up here to ensure carrier changes and * other state is noticed by the now-released port. */ - if (!nm_device_bring_up(slave, TRUE, NULL)) { + if (!nm_device_bring_up(port, TRUE, NULL)) { _LOGW(LOGD_TEAM, - "released team port %s could not be brought up", - nm_device_get_ip_iface(slave)); + "detached team port %s could not be brought up", + nm_device_get_ip_iface(port)); } nm_clear_g_source(&priv->teamd_read_timeout); priv->teamd_read_timeout = g_timeout_add_seconds(5, teamd_read_timeout_cb, self); } else - _LOGI(LOGD_TEAM, "team port %s was released", nm_device_get_ip_iface(slave)); + _LOGI(LOGD_TEAM, "team port %s was detached", nm_device_get_ip_iface(port)); /* Delete any port configuration we previously set */ if (configure && priv->tdc - && (s_port = nm_device_get_applied_setting(slave, NM_TYPE_SETTING_TEAM_PORT)) + && (s_port = nm_device_get_applied_setting(port, NM_TYPE_SETTING_TEAM_PORT)) && (nm_setting_team_port_get_config(s_port))) - teamdctl_port_config_update_raw(priv->tdc, nm_device_get_ip_iface(slave), "{}"); + teamdctl_port_config_update_raw(priv->tdc, nm_device_get_ip_iface(port), "{}"); } static gboolean @@ -1064,8 +1098,8 @@ nm_device_team_class_init(NMDeviceTeamClass *klass) device_class->act_stage1_prepare = act_stage1_prepare; device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; device_class->deactivate = deactivate; - device_class->enslave_slave = enslave_slave; - device_class->release_slave = release_slave; + device_class->attach_port = attach_port; + device_class->detach_port = detach_port; obj_properties[PROP_CONFIG] = g_param_spec_string(NM_DEVICE_TEAM_CONFIG, "", diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index 04608d14..43798b85 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -2520,7 +2520,7 @@ supplicant_iface_state(NMDeviceWifi *self, _LOGD(LOGD_WIFI, "supplicant state settled after roaming, renew dynamic IP configuration"); nm_clear_g_source_inst(&priv->roam_supplicant_wait_source); - nm_device_update_dynamic_ip_setup(device); + nm_device_update_dynamic_ip_setup(device, "roamed to a different AP"); } } break; @@ -2663,7 +2663,7 @@ supplicant_iface_notify_current_bss(NMSupplicantInterface *iface, if (nm_supplicant_interface_get_state(priv->sup_iface) == NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) { - nm_device_update_dynamic_ip_setup(NM_DEVICE(self)); + nm_device_update_dynamic_ip_setup(NM_DEVICE(self), "roamed to a different AP"); } else { /* Wait that the authentication to new the AP completes before * trying to renew, otherwise the DHCP REQUEST could be lost @@ -3180,21 +3180,21 @@ ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP gsize ssid_len; const guint8 *ssid_data; const guint8 random_seed[16] = {0x9a, - 0xdc, - 0x86, - 0x9a, - 0xa8, - 0xa2, - 0x07, - 0x97, - 0xbe, - 0x6d, - 0xe6, - 0x99, - 0x9f, - 0xa8, - 0x09, - 0x2b}; + 0xdc, + 0x86, + 0x9a, + 0xa8, + 0xa2, + 0x07, + 0x97, + 0xbe, + 0x6d, + 0xe6, + 0x99, + 0x9f, + 0xa8, + 0x09, + 0x2b}; /* Calculate a stable "random" number based on the SSID. */ ssid = nm_setting_wireless_get_ssid(s_wifi); diff --git a/src/core/devices/wifi/tests/test-devices-wifi.c b/src/core/devices/wifi/tests/test-devices-wifi.c index 69d365e3..a52696ea 100644 --- a/src/core/devices/wifi/tests/test-devices-wifi.c +++ b/src/core/devices/wifi/tests/test-devices-wifi.c @@ -412,8 +412,8 @@ test_ap_wpa_psk_connection_base(const char *key_mgmt, const char *ssid = "blahblah"; const char *bssid = "01:02:03:04:05:06"; const KeyData exp_wifi[] = {{NM_SETTING_WIRELESS_SSID, ssid, 0}, - {NM_SETTING_WIRELESS_MODE, "infrastructure", 0}, - {NULL}}; + {NM_SETTING_WIRELESS_MODE, "infrastructure", 0}, + {NULL}}; const KeyData both_wsec[] = {{NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, 0}, {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, 0}, {NM_SETTING_WIRELESS_SECURITY_PSK, "asdfasdfasdfasdfasdfafs", 0}, @@ -541,8 +541,8 @@ test_ap_wpa_eap_connection_base(const char *key_mgmt, const char *bssid = "01:02:03:04:05:06"; const KeyData src_empty[] = {{NULL}}; const KeyData src_wsec[] = {{NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, 0}, - {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, 0}, - {NULL}}; + {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, 0}, + {NULL}}; gboolean success; GError *error = NULL; @@ -778,12 +778,12 @@ test_priv_ap_leap_connection_1(gconstpointer add_wifi) const char *bssid = "01:02:03:04:05:06"; const char *leap_username = "Bill Smith"; const KeyData src_wsec[] = {{NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0}, - {NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0}, - {NULL}}; + {NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0}, + {NULL}}; const KeyData exp_wsec[] = {{NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0}, - {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0}, - {NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0}, - {NULL}}; + {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0}, + {NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0}, + {NULL}}; gboolean success; GError *error = NULL; @@ -862,15 +862,15 @@ test_priv_ap_dynamic_wep_1(void) const char *ssid = "blahblah"; const char *bssid = "01:02:03:04:05:06"; const KeyData src_wsec[] = {{NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0}, - {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0}, - {NULL}}; + {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0}, + {NULL}}; const KeyData both_8021x[] = {{NM_SETTING_802_1X_EAP, "peap", 0}, {NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0}, {NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0}, {NULL}}; const KeyData exp_wsec[] = {{NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0}, - {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0}, - {NULL}}; + {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0}, + {NULL}}; gboolean success; GError *error = NULL; @@ -917,8 +917,8 @@ test_priv_ap_dynamic_wep_2(void) {NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0}, {NULL}}; const KeyData exp_wsec[] = {{NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0}, - {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0}, - {NULL}}; + {NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0}, + {NULL}}; gboolean success; GError *error = NULL; @@ -1130,8 +1130,8 @@ test_wpa_ap_leap_connection_1(gconstpointer data) const char *bssid = "01:02:03:04:05:06"; const char *leap_username = "Bill Smith"; const KeyData src_wsec[] = {{NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0}, - {NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0}, - {NULL}}; + {NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0}, + {NULL}}; gboolean success; GError *error = NULL; diff --git a/src/core/devices/wwan/nm-modem-ofono.c b/src/core/devices/wwan/nm-modem-ofono.c index 0db30046..c003880e 100644 --- a/src/core/devices/wwan/nm-modem-ofono.c +++ b/src/core/devices/wwan/nm-modem-ofono.c @@ -805,7 +805,7 @@ handle_settings(GVariant *v_dict, gpointer user_data) _LOGW("invalid 'Netmask': %s", s ?: ""); goto out; } - address.plen = nm_utils_ip4_netmask_to_prefix(address_network); + address.plen = _nm_utils_ip4_netmask_to_prefix(address_network); _LOGI("Address: %s", nm_platform_ip4_address_to_string(&address, sbuf, sizeof(sbuf))); nm_l3_config_data_add_address_4(priv->l3cd_4, &address); diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index 00a2d207..77cfeecf 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -32,6 +32,38 @@ /*****************************************************************************/ +/* This is how long we do ACD for each entry and reject new offers for + * the same address. Note that the maximum ACD timeout is limited to 30 seconds + * (NM_ACD_TIMEOUT_MAX_MSEC). + **/ +#define ACD_REGLIST_GRACE_PERIOD_MSEC 300000u + +G_STATIC_ASSERT(ACD_REGLIST_GRACE_PERIOD_MSEC > (NM_ACD_TIMEOUT_MAX_MSEC + 1000)); + +#define ACD_REGLIST_MAX_ENTRIES 30 + +/* To do ACD for an address (new lease), we will register a NML3ConfigData + * with l3cfg. After ACD completes, we still continue having NML3Cfg + * watch that address, for ACD_REGLIST_GRACE_PERIOD_MSEC. The reasons are: + * + * - the caller is supposed to actually configure the address right after + * ACD passed. We would not want to drop the ACD state before the caller + * got a chance to do that. + * - when ACD fails, we decline the address and expect the DHCP client + * to present a new lease. We may want to outright reject the address, + * if ACD is bad. Thus, we want to keep running ACD for the address a bit + * longer, so that future requests for the same address can be rejected. + * + * This data structure is used for tracking the registered ACD address. + */ +typedef struct { + const NML3ConfigData *l3cd; + gint64 expiry_msec; + in_addr_t addr; +} AcdRegListData; + +/*****************************************************************************/ + enum { SIGNAL_NOTIFY, LAST_SIGNAL, @@ -42,15 +74,48 @@ static guint signals[LAST_SIGNAL] = {0}; NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpClient, PROP_CONFIG, ); typedef struct _NMDhcpClientPrivate { - NMDhcpClientConfig config; - const NML3ConfigData *l3cd; - GSource *no_lease_timeout_source; - GSource *watch_source; - GBytes *effective_client_id; + NMDhcpClientConfig config; + + /* This is the "next" data. That is, the one what was received last via + * _nm_dhcp_client_notify(), but which is currently pending on ACD. */ + const NML3ConfigData *l3cd_next; + + /* This is the currently exposed data. It passed ACD (or no ACD was performed), + * and is set from l3cd_next. */ + const NML3ConfigData *l3cd_curr; + + GSource *no_lease_timeout_source; + GSource *watch_source; + GBytes *effective_client_id; union { struct { struct { + NML3CfgCommitTypeHandle *l3cfg_commit_handle; + GSource *done_source; + + /* When we do ACD for a l3cd lease, we will keep running ACD for + * the grace period ACD_REGLIST_GRACE_PERIOD_MSEC, even if we already + * determined the state. There are two reasons for that: + * + * - after ACD completes we notify the lease to the user, who is supposed + * to configure the address in NML3Cfg. If we were already removing the + * ACD state from NML3Cfg, ACD might need to start over. Instead, when + * the caller tries to configure the address, ACD state is already good. + * + * - if we decline on ACD offer, we may want to keep running and + * select other offers. Offers for which we just failed ACD (within + * ACD_REGLIST_GRACE_PERIOD_MSEC) are rejected. See _nm_dhcp_client_accept_offer(). + * For that, we keep monitoring the ACD state for up to ACD_REGLIST_MAX_ENTRIES + * addresses, to not restart and select the same lease twice in a row. + */ + GArray *reglist; + GSource *reglist_timeout_source; + + in_addr_t addr; + NMOptionBool state; + } acd; + struct { GDBusMethodInvocation *invocation; } bound; } v4; @@ -77,16 +142,22 @@ G_DEFINE_ABSTRACT_TYPE(NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT) /*****************************************************************************/ +#define L3CD_ACD_TAG(priv) (&(priv)->v4.acd.addr) + static gboolean _dhcp_client_accept(NMDhcpClient *self, const NML3ConfigData *l3cd, GError **error); -_nm_unused static gboolean _dhcp_client_decline(NMDhcpClient *self, - const NML3ConfigData *l3cd, - const char *error_message, - GError **error); +static gboolean _dhcp_client_decline(NMDhcpClient *self, + const NML3ConfigData *l3cd, + const char *error_message, + GError **error); static void l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcpClient *self); +static void _acd_reglist_timeout_reschedule(NMDhcpClient *self, gint64 now_msec); + +static void _acd_reglist_data_remove(NMDhcpClient *self, guint idx, gboolean do_log); + /*****************************************************************************/ /* we use pid=-1 for invalid PIDs. Ensure that pid_t can hold negative values. */ @@ -157,6 +228,16 @@ nm_dhcp_client_get_effective_client_id(NMDhcpClient *self) return priv->effective_client_id; } +NML3ConfigData * +nm_dhcp_client_create_l3cd(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return nm_l3_config_data_new(nm_l3cfg_get_multi_idx(priv->config.l3cfg), + nm_l3cfg_get_ifindex(priv->config.l3cfg), + NM_IP_CONFIG_SOURCE_DHCP); +} + /*****************************************************************************/ void @@ -200,7 +281,8 @@ l3_cfg_notify_check_connected(NMDhcpClient *self) gboolean do_connect; do_connect = priv->l3cfg_notify.wait_dhcp_commit | priv->l3cfg_notify.wait_ll_address - | priv->l3cfg_notify.wait_ipv6_dad; + | priv->l3cfg_notify.wait_ipv6_dad + | (NM_IS_IPv4(priv->config.addr_family) && priv->v4.acd.l3cfg_commit_handle); if (!do_connect) { nm_clear_g_signal_handler(priv->config.l3cfg, &priv->l3cfg_notify.id); @@ -306,6 +388,333 @@ _no_lease_timeout_schedule(NMDhcpClient *self) /*****************************************************************************/ +static void +_acd_state_reset(NMDhcpClient *self, gboolean forget_addr, gboolean forget_reglist) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + if (!NM_IS_IPv4(priv->config.addr_family)) + return; + + if (priv->v4.acd.addr != INADDR_ANY) { + nm_l3cfg_commit_type_clear(priv->config.l3cfg, &priv->v4.acd.l3cfg_commit_handle); + l3_cfg_notify_check_connected(self); + nm_clear_g_source_inst(&priv->v4.acd.done_source); + if (forget_addr) { + priv->v4.acd.addr = INADDR_ANY; + priv->v4.acd.state = NM_OPTION_BOOL_DEFAULT; + } + } else + nm_assert(priv->v4.acd.state == NM_OPTION_BOOL_DEFAULT); + + if (forget_reglist) { + guint n; + + while ((n = nm_g_array_len(priv->v4.acd.reglist)) > 0) + _acd_reglist_data_remove(self, n - 1, TRUE); + } + + nm_assert(!priv->v4.acd.l3cfg_commit_handle); + nm_assert(!priv->v4.acd.done_source); + nm_assert(!forget_reglist + || !nm_l3cfg_remove_config_all(priv->config.l3cfg, L3CD_ACD_TAG(priv))); +} + +static gboolean +_acd_complete_on_idle_cb(gpointer user_data) +{ + NMDhcpClient *self = user_data; + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + nm_assert(NM_IS_IPv4(priv->config.addr_family)); + nm_assert(priv->v4.acd.addr != INADDR_ANY); + nm_assert(!priv->v4.acd.l3cfg_commit_handle); + nm_assert(priv->l3cd_next); + + _acd_state_reset(self, FALSE, FALSE); + + _nm_dhcp_client_notify(self, NM_DHCP_CLIENT_EVENT_TYPE_BOUND, priv->l3cd_next); + + return G_SOURCE_CONTINUE; +} + +#define _acd_reglist_data_get(priv, idx) \ + nm_g_array_index_p((priv)->v4.acd.reglist, AcdRegListData, (idx)) + +static guint +_acd_reglist_data_find(NMDhcpClientPrivate *priv, in_addr_t addr_needle) +{ + const guint n = nm_g_array_len(priv->v4.acd.reglist); + guint i; + + nm_assert(addr_needle != INADDR_ANY); + + for (i = 0; i < n; i++) { + AcdRegListData *reglist_data = _acd_reglist_data_get(priv, i); + + if (reglist_data->addr == addr_needle) + return i; + } + return G_MAXUINT; +} + +static void +_acd_reglist_data_remove(NMDhcpClient *self, guint idx, gboolean do_log) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + AcdRegListData *reglist_data; + + nm_assert(idx < nm_g_array_len(priv->v4.acd.reglist)); + + reglist_data = _acd_reglist_data_get(priv, idx); + + if (do_log) { + char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + + _LOGD("acd: drop check for address %s (l3cd " NM_HASH_OBFUSCATE_PTR_FMT ")", + _nm_utils_inet4_ntop(reglist_data->addr, sbuf_addr), + NM_HASH_OBFUSCATE_PTR(reglist_data->l3cd)); + } + + if (!nm_l3cfg_remove_config(priv->config.l3cfg, L3CD_ACD_TAG(priv), reglist_data->l3cd)) + nm_assert_not_reached(); + + nm_clear_l3cd(®list_data->l3cd); + + nm_l3cfg_commit_on_idle_schedule(priv->config.l3cfg, NM_L3_CFG_COMMIT_TYPE_UPDATE); + + g_array_remove_index(priv->v4.acd.reglist, idx); + + if (priv->v4.acd.reglist->len == 0) { + nm_clear_pointer(&priv->v4.acd.reglist, g_array_unref); + nm_clear_g_source_inst(&priv->v4.acd.reglist_timeout_source); + } +} + +static gboolean +_acd_reglist_timeout_cb(gpointer user_data) +{ + NMDhcpClient *self = user_data; + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + gint64 now_msec; + + nm_clear_g_source_inst(&priv->v4.acd.reglist_timeout_source); + + now_msec = nm_utils_get_monotonic_timestamp_msec(); + + while (nm_g_array_len(priv->v4.acd.reglist) > 0) { + AcdRegListData *reglist_data = _acd_reglist_data_get(priv, 0); + + if (reglist_data->expiry_msec > now_msec) + break; + + _acd_reglist_data_remove(self, 0, TRUE); + } + + _acd_reglist_timeout_reschedule(self, now_msec); + + return G_SOURCE_CONTINUE; +} + +static void +_acd_reglist_timeout_reschedule(NMDhcpClient *self, gint64 now_msec) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + AcdRegListData *reglist_data; + + if (nm_g_array_len(priv->v4.acd.reglist) == 0) { + nm_assert(!priv->v4.acd.reglist_timeout_source); + return; + } + + if (priv->v4.acd.reglist_timeout_source) { + /* already pending. As we only add new elements with a *later* + * expiry, we don't need to ever cancel a pending timer. Worst + * case, the timer fires, and there is nothing to do and we + * reschedule. */ + return; + } + + now_msec = nm_utils_get_monotonic_timestamp_msec(); + + reglist_data = _acd_reglist_data_get(priv, 0); + + nm_assert(reglist_data->expiry_msec > now_msec); + + priv->v4.acd.reglist_timeout_source = + nm_g_timeout_add_source(reglist_data->expiry_msec - now_msec, + _acd_reglist_timeout_cb, + self); +} + +static void +_acd_check_lease(NMDhcpClient *self, NMOptionBool *out_acd_state) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + in_addr_t addr; + gboolean addr_changed = FALSE; + guint idx; + gint64 now_msec; + + if (!NM_IS_IPv4(priv->config.addr_family)) + goto handle_no_acd; + + if (!priv->l3cd_next) + goto handle_no_acd; + + /* an IPv4 lease is always expected to have exactly one address. */ + nm_assert(nm_l3_config_data_get_num_addresses(priv->l3cd_next, AF_INET) == 1); + + if (priv->config.v4.acd_timeout_msec == 0) + goto handle_no_acd; + + addr = NMP_OBJECT_CAST_IP4_ADDRESS( + nm_l3_config_data_get_first_obj(priv->l3cd_next, NMP_OBJECT_TYPE_IP4_ADDRESS, NULL)) + ->address; + nm_assert(addr != INADDR_ANY); + + nm_clear_g_source_inst(&priv->v4.acd.done_source); + + if (priv->v4.acd.state != NM_OPTION_BOOL_DEFAULT && priv->v4.acd.addr == addr) { + /* the ACD state is already determined. Return right away. */ + nm_assert(!priv->v4.acd.l3cfg_commit_handle); + *out_acd_state = !!priv->v4.acd.state; + return; + } + + if (priv->v4.acd.addr != addr) { + addr_changed = TRUE; + priv->v4.acd.addr = addr; + } + + _LOGD("acd: %s check for address %s (timeout %u msec, l3cd " NM_HASH_OBFUSCATE_PTR_FMT ")", + addr_changed ? "add" : "update", + _nm_utils_inet4_ntop(addr, sbuf_addr), + priv->config.v4.acd_timeout_msec, + NM_HASH_OBFUSCATE_PTR(priv->l3cd_next)); + + priv->v4.acd.state = NM_OPTION_BOOL_DEFAULT; + + if (nm_l3cfg_add_config(priv->config.l3cfg, + L3CD_ACD_TAG(priv), + FALSE, + priv->l3cd_next, + NM_L3CFG_CONFIG_PRIORITY_IPV4LL, + 0, + 0, + NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4, + NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, + 0, + 0, + NM_DNS_PRIORITY_DEFAULT_NORMAL, + NM_DNS_PRIORITY_DEFAULT_NORMAL, + NM_L3_ACD_DEFEND_TYPE_ONCE, + NM_MIN(priv->config.v4.acd_timeout_msec, NM_ACD_TIMEOUT_MAX_MSEC), + NM_L3CFG_CONFIG_FLAGS_ONLY_FOR_ACD, + NM_L3_CONFIG_MERGE_FLAGS_NONE)) + addr_changed = TRUE; + + if (!priv->v4.acd.reglist) + priv->v4.acd.reglist = g_array_new(FALSE, FALSE, sizeof(AcdRegListData)); + + idx = _acd_reglist_data_find(priv, addr); + + now_msec = nm_utils_get_monotonic_timestamp_msec(); + + g_array_append_val(priv->v4.acd.reglist, + ((AcdRegListData){ + .l3cd = nm_l3_config_data_ref(priv->l3cd_next), + .addr = addr, + .expiry_msec = now_msec + ACD_REGLIST_GRACE_PERIOD_MSEC, + })); + + if (idx != G_MAXUINT) { + /* we already tracked this "addr". We don't need to track it twice, + * forget about this one. This also has the effect, that we will + * always append the new entry to the list (so the list + * stays sorted by the increasing timestamp). */ + _acd_reglist_data_remove(self, idx, FALSE); + } + + if (priv->v4.acd.reglist->len > ACD_REGLIST_MAX_ENTRIES) { + /* rate limit how many addresses we track for ACD. */ + _acd_reglist_data_remove(self, 0, TRUE); + } + + _acd_reglist_timeout_reschedule(self, now_msec); + + if (!priv->v4.acd.l3cfg_commit_handle) { + priv->v4.acd.l3cfg_commit_handle = + nm_l3cfg_commit_type_register(priv->config.l3cfg, + NM_L3_CFG_COMMIT_TYPE_UPDATE, + NULL, + "dhcp4-acd"); + l3_cfg_notify_check_connected(self); + } + + if (addr_changed) + nm_l3cfg_commit_on_idle_schedule(priv->config.l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO); + + /* ACD is started/pending... */ + nm_assert(priv->v4.acd.addr != INADDR_ANY); + nm_assert(priv->v4.acd.state == NM_OPTION_BOOL_DEFAULT); + nm_assert(priv->v4.acd.l3cfg_commit_handle); + nm_assert(priv->l3cfg_notify.id); + *out_acd_state = NM_OPTION_BOOL_DEFAULT; + return; + +handle_no_acd: + /* Indicate that ACD is good (or disabled) by returning TRUE. */ + _acd_state_reset(self, TRUE, FALSE); + *out_acd_state = NM_OPTION_BOOL_TRUE; + return; +} + +/*****************************************************************************/ + +gboolean +_nm_dhcp_client_accept_offer(NMDhcpClient *self, gconstpointer p_yiaddr) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + NMIPAddr yiaddr; + const NML3AcdAddrInfo *acd_info; + + if (!NM_IS_IPv4(priv->config.addr_family)) + return nm_assert_unreachable_val(FALSE); + + if (priv->config.v4.acd_timeout_msec == 0) { + /* ACD is disabled. Note that we might track the address for other + * reasons and have information about the ACD state below. But + * with ACD disabled, we always ignore that information. */ + return TRUE; + } + + nm_ip_addr_set(priv->config.addr_family, &yiaddr, p_yiaddr); + + /* Note that once we do ACD for a certain address, even after completing + * it, we keep the l3cd registered in NML3Cfg for ACD_REGLIST_GRACE_PERIOD_MSEC + * The idea is, that we don't yet turn off ACD for a grace period, so that + * we can avoid selecting the same lease again. + * + * Note that we even check whether we have an ACD state if priv->v4.acd.reglist + * is empty. Maybe for odd reasons, we track ACD for the address already. */ + + acd_info = nm_l3cfg_get_acd_addr_info(priv->config.l3cfg, yiaddr.addr4); + + if (!acd_info) + return TRUE; + + if (!NM_IN_SET(acd_info->state, NM_L3_ACD_ADDR_STATE_USED, NM_L3_ACD_ADDR_STATE_CONFLICT)) + return TRUE; + + _LOGD("offered lease rejected: address %s failed ACD check", + _nm_utils_inet4_ntop(yiaddr.addr4, sbuf_addr)); + + return FALSE; +} + void _nm_dhcp_client_notify(NMDhcpClient *self, NMDhcpClientEventType client_event_type, @@ -313,6 +722,8 @@ _nm_dhcp_client_notify(NMDhcpClient *self, { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); GHashTable *options; + gboolean l3cd_changed; + NMOptionBool acd_state; const int IS_IPv4 = NM_IS_IPv4(priv->config.addr_family); nm_auto_unref_l3cd const NML3ConfigData *l3cd_merged = NULL; char sbuf1[NM_HASH_OBFUSCATE_PTR_STR_BUF_SIZE]; @@ -343,8 +754,7 @@ _nm_dhcp_client_notify(NMDhcpClient *self, nm_dhcp_client_event_type_to_string(client_event_type), NM_PRINT_FMT_QUOTED2(l3cd, ", l3cd=", NM_HASH_OBFUSCATE_PTR_STR(l3cd, sbuf1), "")); - if (l3cd) - nm_l3_config_data_seal(l3cd); + nm_l3_config_data_seal(l3cd); if (client_event_type >= NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT) watch_cleanup(self); @@ -353,33 +763,40 @@ _nm_dhcp_client_notify(NMDhcpClient *self, /* nm_dhcp_utils_merge_new_dhcp6_lease() relies on "life_starts" option * for merging, which is only set by dhclient. Internal client never sets that, * but it supports multiple IP addresses per lease. */ - if (nm_dhcp_utils_merge_new_dhcp6_lease(priv->l3cd, l3cd, &l3cd_merged)) { + if (nm_dhcp_utils_merge_new_dhcp6_lease(priv->l3cd_next, l3cd, &l3cd_merged)) { + _LOGD("lease merged with existing one"); l3cd = nm_l3_config_data_seal(l3cd_merged); } } - if (priv->l3cd == l3cd) - return; - if (l3cd) { nm_clear_g_source_inst(&priv->no_lease_timeout_source); - } else { - if (priv->l3cd) - _no_lease_timeout_schedule(self); - } + } else + _no_lease_timeout_schedule(self); - /* FIXME(l3cfg:dhcp): the API of NMDhcpClient is changing to expose a simpler API. - * The internals like the state should not be exposed (or possibly dropped in large - * parts). */ + l3cd_changed = nm_l3_config_data_reset(&priv->l3cd_next, l3cd); - nm_l3_config_data_reset(&priv->l3cd, l3cd); + _acd_check_lease(self, &acd_state); - options = l3cd ? nm_dhcp_lease_get_options( - nm_l3_config_data_get_dhcp_lease(l3cd, priv->config.addr_family)) - : NULL; + options = priv->l3cd_next ? nm_dhcp_lease_get_options( + nm_l3_config_data_get_dhcp_lease(priv->l3cd_next, priv->config.addr_family)) + : NULL; + + if (_LOGI_ENABLED()) { + const char *req_str = + IS_IPv4 ? nm_dhcp_option_request_string(AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS) + : nm_dhcp_option_request_string(AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS); + const char *addr = nm_g_hash_table_lookup(options, req_str); + + _LOGI("state changed %s%s%s%s", + priv->l3cd_next ? "new lease" : "no lease", + NM_PRINT_FMT_QUOTED2(addr, ", address=", addr, ""), + acd_state == NM_OPTION_BOOL_DEFAULT ? ", acd pending" + : (acd_state ? "" : ", acd conflict")); + } if (_LOGD_ENABLED()) { - if (options) { + if (l3cd_changed && options) { gs_free const char **keys = NULL; guint nkeys; guint i; @@ -390,58 +807,41 @@ _nm_dhcp_client_notify(NMDhcpClient *self, keys[i], (char *) g_hash_table_lookup(options, keys[i])); } - - if (priv->config.addr_family == AF_INET6) { - gs_free char *event_id = NULL; - - event_id = nm_dhcp_utils_get_dhcp6_event_id(options); - if (event_id) - _LOGT("event-id: \"%s\"", event_id); - } } } - if (_LOGI_ENABLED()) { - const char *req_str = - IS_IPv4 ? nm_dhcp_option_request_string(AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS) - : nm_dhcp_option_request_string(AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS); - const char *addr = nm_g_hash_table_lookup(options, req_str); + if (acd_state == NM_OPTION_BOOL_DEFAULT) { + /* ACD is in progress... */ + return; + } - _LOGI("state changed %s%s%s%s", - priv->l3cd ? "new lease" : "no lease", - NM_PRINT_FMT_QUOTED(addr, ", address=", addr, "", "")); + if (!acd_state) { + gs_free_error GError *error = NULL; + + /* We only decline. We don't actually emit to the caller that + * something is wrong (like NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD). + * If we would, NMDevice might decide to tear down the device, when + * we actually should continue trying to get a better lease. There + * is already "ipv4.dhcp-timeout" which will handle the failure if + * we don't get a good lease. */ + if (!_dhcp_client_decline(self, priv->l3cd_next, "acd failed", &error)) + _LOGD("decline failed: %s", error->message); + return; } - /* FIXME(l3cfg:dhcp:acd): NMDhcpClient must also do ACD. It needs acd_timeout_msec - * as a configuration parameter (in NMDhcpClientConfig). When ACD is enabled, - * when a new lease gets announced, it must first use NML3Cfg to run ACD on the - * interface (the previous lease -- if any -- will still be used at that point). - * If ACD fails, we call _dhcp_client_decline() and try to get a different - * lease. - * If ACD passes, we need to notify the new lease, and the user (NMDevice) may - * then configure the address. We need to watch the configured addresses (in NML3Cfg), - * and if the address appears there, we need to accept the lease. That is complicated - * but necessary, because we can only accept the lease after we configured the - * address. - * - * As a whole, ACD is transparent for the user (NMDevice). It's entirely managed - * by NMDhcpClient. Note that we do ACD through NML3Cfg, which centralizes IP handling - * for one interface, so for example if the same address happens to be configured - * as a static address (bypassing ACD), then NML3Cfg is aware of that and signals - * immediate success. */ - - if (client_event_type == NM_DHCP_CLIENT_EVENT_TYPE_BOUND && priv->l3cd - && nm_l3_config_data_get_num_addresses(priv->l3cd, priv->config.addr_family) > 0) { + nm_l3_config_data_reset(&priv->l3cd_curr, priv->l3cd_next); + + if (client_event_type == NM_DHCP_CLIENT_EVENT_TYPE_BOUND && priv->l3cd_curr + && nm_l3_config_data_get_num_addresses(priv->l3cd_curr, priv->config.addr_family) > 0) priv->l3cfg_notify.wait_dhcp_commit = TRUE; - } else { + else priv->l3cfg_notify.wait_dhcp_commit = FALSE; - } - if (!priv->l3cfg_notify.wait_dhcp_commit && priv->l3cd) { + if (!priv->l3cfg_notify.wait_dhcp_commit && priv->l3cd_curr) { gs_free_error GError *error = NULL; _LOGD("accept lease right away"); - if (!_dhcp_client_accept(self, priv->l3cd, &error)) { + if (!_dhcp_client_accept(self, priv->l3cd_curr, &error)) { _LOGD("accept failed: %s", error->message); /* Unclear why this happened, or what to do about it. Just proceed. */ } @@ -454,7 +854,7 @@ _nm_dhcp_client_notify(NMDhcpClient *self, .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, .lease_update = { - .l3cd = priv->l3cd, + .l3cd = priv->l3cd_curr, .accepted = !priv->l3cfg_notify.wait_dhcp_commit, }, }; @@ -529,7 +929,7 @@ _dhcp_client_accept(NMDhcpClient *self, const NML3ConfigData *l3cd, GError **err klass = NM_DHCP_CLIENT_GET_CLASS(self); - g_return_val_if_fail(NM_DHCP_CLIENT_GET_PRIVATE(self)->l3cd, FALSE); + g_return_val_if_fail(NM_DHCP_CLIENT_GET_PRIVATE(self)->l3cd_curr, FALSE); return klass->accept(self, l3cd, error); } @@ -569,7 +969,7 @@ _dhcp_client_decline(NMDhcpClient *self, klass = NM_DHCP_CLIENT_GET_CLASS(self); - g_return_val_if_fail(NM_DHCP_CLIENT_GET_PRIVATE(self)->l3cd, FALSE); + g_return_val_if_fail(NM_DHCP_CLIENT_GET_PRIVATE(self)->l3cd_next, FALSE); return klass->decline(self, l3cd, error_message, error); } @@ -626,7 +1026,9 @@ ipv6_lladdr_find(NMDhcpClient *self) nm_assert(!NM_IS_IPv4(priv->config.addr_family)); l3cfg = priv->config.l3cfg; - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, nm_l3cfg_get_ifindex(l3cfg)); + nmp_lookup_init_object_by_ifindex(&lookup, + NMP_OBJECT_TYPE_IP6_ADDRESS, + nm_l3cfg_get_ifindex(l3cfg)); nm_platform_iter_obj_for_each (&iter, nm_l3cfg_get_platform(l3cfg), &lookup, &obj) { const NMPlatformIP6Address *pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(obj); @@ -651,7 +1053,7 @@ ipv6_tentative_addr_find(NMDhcpClient *self) /* For each address in the lease, check whether it's tentative * in platform. */ - nm_l3_config_data_iter_ip6_address_for_each (&iter, priv->l3cd, &addr) { + nm_l3_config_data_iter_ip6_address_for_each (&iter, priv->l3cd_curr, &addr) { const NMPlatformIP6Address *pladdr; NMPObject needle; @@ -676,6 +1078,7 @@ static void l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcpClient *self) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; nm_assert(l3cfg == priv->config.l3cfg); @@ -717,7 +1120,7 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp self, &((NMDhcpClientNotifyData){.notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, .lease_update = { - .l3cd = priv->l3cd, + .l3cd = priv->l3cd_curr, .accepted = TRUE, }})); } @@ -736,7 +1139,7 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp * lease and notifying NMDevice. */ nm_l3_config_data_iter_ip_address_for_each (&ipconf_iter, - priv->l3cd, + priv->l3cd_curr, priv->config.addr_family, &lease_address) break; @@ -778,9 +1181,11 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp _LOGD("accept lease"); - if (!_dhcp_client_accept(self, priv->l3cd, &error)) { + if (!_dhcp_client_accept(self, priv->l3cd_curr, &error)) { gs_free char *reason = g_strdup_printf("error accepting lease: %s", error->message); + _LOGD("accept failed: %s", error->message); + _emit_notify(self, &((NMDhcpClientNotifyData){ .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, @@ -794,12 +1199,54 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp self, &((NMDhcpClientNotifyData){.notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, .lease_update = { - .l3cd = priv->l3cd, + .l3cd = priv->l3cd_curr, .accepted = TRUE, }})); } } -wait_dhcp_commit_done:; +wait_dhcp_commit_done: + + if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT + && priv->v4.acd.l3cfg_commit_handle) { + nm_assert(priv->v4.acd.addr != INADDR_ANY); + nm_assert(priv->v4.acd.state == NM_OPTION_BOOL_DEFAULT); + nm_assert(!priv->v4.acd.done_source); + + if (priv->v4.acd.addr == notify_data->acd_event.info.addr + && nm_l3_acd_addr_info_find_track_info(¬ify_data->acd_event.info, + L3CD_ACD_TAG(priv), + NULL, + NULL)) { + NMOptionBool acd_state; + + switch (notify_data->acd_event.info.state) { + default: + nm_assert_not_reached(); + /* fall-through */ + case NM_L3_ACD_ADDR_STATE_INIT: + case NM_L3_ACD_ADDR_STATE_PROBING: + acd_state = NM_OPTION_BOOL_DEFAULT; + break; + case NM_L3_ACD_ADDR_STATE_USED: + case NM_L3_ACD_ADDR_STATE_CONFLICT: + case NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED: + acd_state = NM_OPTION_BOOL_FALSE; + break; + case NM_L3_ACD_ADDR_STATE_READY: + case NM_L3_ACD_ADDR_STATE_DEFENDING: + acd_state = NM_OPTION_BOOL_TRUE; + break; + } + if (acd_state != NM_OPTION_BOOL_DEFAULT) { + _LOGD("acd: acd %s for %s", + acd_state ? "ready" : "conflict", + _nm_utils_inet4_ntop(priv->v4.acd.addr, sbuf_addr)); + nm_l3cfg_commit_type_clear(priv->config.l3cfg, &priv->v4.acd.l3cfg_commit_handle); + priv->v4.acd.state = acd_state; + priv->v4.acd.done_source = nm_g_idle_add_source(_acd_complete_on_idle_cb, self); + } + } + } } gboolean @@ -928,6 +1375,8 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release) "dhcp stopping"); } + _acd_state_reset(self, TRUE, TRUE); + priv->l3cfg_notify.wait_dhcp_commit = FALSE; priv->l3cfg_notify.wait_ll_address = FALSE; priv->l3cfg_notify.wait_ipv6_dad = FALSE; @@ -942,6 +1391,9 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release) _LOGI("canceled DHCP transaction"); nm_assert(priv->pid == -1); + nm_clear_l3cd(&priv->l3cd_next); + nm_clear_l3cd(&priv->l3cd_curr); + _nm_dhcp_client_notify(self, NM_DHCP_CLIENT_EVENT_TYPE_TERMINATED, NULL); } @@ -951,37 +1403,31 @@ static char * bytearray_variant_to_string(NMDhcpClient *self, GVariant *value, const char *key) { const guint8 *array; + char *str; gsize length; - GString *str; - int i; - unsigned char c; - char *converted = NULL; + gsize i; - g_return_val_if_fail(value != NULL, NULL); + nm_assert(value); array = g_variant_get_fixed_array(value, &length, 1); - /* Since the DHCP options come through environment variables, they should - * already be UTF-8 safe, but just make sure. + /* Since the DHCP options come originally came as environment variables, they + * have not guaranteed encoding. Let's only accept ASCII here. */ - str = g_string_sized_new(length); + str = g_malloc(length + 1); for (i = 0; i < length; i++) { - c = array[i]; + guint8 c = array[i]; - /* Convert NULLs to spaces and non-ASCII characters to ? */ if (c == '\0') - c = ' '; + str[i] = ' '; else if (c > 127) - c = '?'; - str = g_string_append_c(str, c); + str[i] = '?'; + else + str[i] = (char) c; } - str = g_string_append_c(str, '\0'); + str[i] = '\0'; - converted = str->str; - if (!g_utf8_validate(converted, -1, NULL)) - _LOGW("option '%s' couldn't be converted to UTF-8", key); - g_string_free(str, FALSE); - return converted; + return str; } static int @@ -1004,11 +1450,13 @@ label_is_unknown_xyz(const char *label) static void maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant *value) { - char *str_value = NULL; + char *str_value; + int priv_opt_num; - g_return_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING)); + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING)) + return; - if (g_str_has_prefix(key, OLD_TAG)) + if (NM_STR_HAS_PREFIX(key, OLD_TAG)) return; /* Filter out stuff that's not actually new DHCP options */ @@ -1021,34 +1469,33 @@ maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant return; str_value = bytearray_variant_to_string(self, value, key); - if (str_value) { - int priv_opt_num; + if (!str_value) + return; - g_hash_table_insert(hash, g_strdup(key), str_value); + g_hash_table_insert(hash, g_strdup(key), str_value); - /* dhclient has no special labels for private dhcp options: it uses "unknown_xyz" + /* dhclient has no special labels for private dhcp options: it uses "unknown_xyz" * labels for that. We need to identify those to alias them to our "private_xyz" * format unused in the internal dchp plugins. */ - if ((priv_opt_num = label_is_unknown_xyz(key)) > 0) { - gs_free guint8 *check_val = NULL; - char *hex_str = NULL; - gsize len; + if ((priv_opt_num = label_is_unknown_xyz(key)) > 0) { + gs_free guint8 *check_val = NULL; + char *hex_str = NULL; + gsize len; - /* dhclient passes values from dhcp private options in its own "string" format: + /* dhclient passes values from dhcp private options in its own "string" format: * if the raw values are printable as ascii strings, it will pass the string * representation; if the values are not printable as an ascii string, it will * pass a string displaying the hex values (hex string). Try to enforce passing * always an hex string, converting string representation if needed. */ - check_val = nm_utils_hexstr2bin_alloc(str_value, FALSE, TRUE, ":", 0, &len); - hex_str = nm_utils_bin2hexstr_full(check_val ?: (guint8 *) str_value, - check_val ? len : strlen(str_value), - ':', - FALSE, - NULL); - g_hash_table_insert(hash, g_strdup_printf("private_%d", priv_opt_num), hex_str); - } + check_val = nm_utils_hexstr2bin_alloc(str_value, FALSE, TRUE, ":", 0, &len); + hex_str = nm_utils_bin2hexstr_full(check_val ?: (guint8 *) str_value, + check_val ? len : strlen(str_value), + ':', + FALSE, + NULL); + g_hash_table_insert(hash, g_strdup_printf("private_%d", priv_opt_num), hex_str); } } @@ -1079,8 +1526,9 @@ nm_dhcp_client_handle_event(gpointer unused, nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; NMDhcpClientEventType client_event_type; NMPlatformIP6Address prefix = { - 0, + 0, }; + int IS_IPv4; g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); g_return_val_if_fail(iface != NULL, FALSE); @@ -1091,6 +1539,8 @@ nm_dhcp_client_handle_event(gpointer unused, priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + g_return_val_if_fail(!priv->is_stopped, FALSE); + if (!nm_streq0(priv->config.iface, iface)) return FALSE; if (priv->pid != pid) @@ -1175,10 +1625,12 @@ nm_dhcp_client_handle_event(gpointer unused, client_event_type = NM_DHCP_CLIENT_EVENT_TYPE_FAIL; } - if (priv->v4.bound.invocation) + IS_IPv4 = NM_IS_IPv4(priv->config.addr_family); + + if (IS_IPv4 && priv->v4.bound.invocation) g_dbus_method_invocation_return_value(g_steal_pointer(&priv->v4.bound.invocation), NULL); - if (NM_IS_IPv4(priv->config.addr_family) + if (IS_IPv4 && NM_IN_SET(client_event_type, NM_DHCP_CLIENT_EVENT_TYPE_BOUND, NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED)) @@ -1202,23 +1654,23 @@ nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr) /* IPv6 not implemented yet */ nm_assert(priv->config.addr_family == AF_INET); - if (!priv->config.reject_servers || !priv->config.reject_servers[0]) - return FALSE; - - for (i = 0; priv->config.reject_servers[i]; i++) { - in_addr_t r_addr; - in_addr_t mask; - int r_prefix; - - if (!nm_utils_parse_inaddr_prefix_bin(AF_INET, - priv->config.reject_servers[i], - NULL, - &r_addr, - &r_prefix)) - nm_assert_not_reached(); - mask = _nm_utils_ip4_prefix_to_netmask(r_prefix < 0 ? 32 : r_prefix); - if ((addr4 & mask) == (r_addr & mask)) - return TRUE; + if (priv->config.reject_servers) { + for (i = 0; priv->config.reject_servers[i]; i++) { + in_addr_t r_addr; + in_addr_t mask; + int r_prefix; + + if (!nm_utils_parse_inaddr_prefix_bin(AF_INET, + priv->config.reject_servers[i], + NULL, + &r_addr, + &r_prefix)) + nm_assert_not_reached(); + + mask = _nm_utils_ip4_prefix_to_netmask(r_prefix < 0 ? 32 : r_prefix); + if ((addr4 & mask) == (r_addr & mask)) + return TRUE; + } } return FALSE; @@ -1251,7 +1703,7 @@ config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src) config->hostname = g_strdup(config->hostname); config->mud_url = g_strdup(config->mud_url); - config->reject_servers = (const char *const *) nm_strv_dup(config->reject_servers, -1, TRUE); + config->reject_servers = nm_strv_dup_packed(config->reject_servers, -1); if (NM_IS_IPv4(config->addr_family)) config->v4.last_address = g_strdup(config->v4.last_address); @@ -1284,7 +1736,7 @@ config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src) if (!config->send_hostname) { nm_clear_g_free((gpointer *) &config->hostname); } else if ((config->use_fqdn && !nm_sd_dns_name_is_valid(config->hostname)) - || (!config->use_fqdn && !nm_sd_hostname_is_valid(config->hostname, FALSE))) { + || (!config->use_fqdn && !nm_hostname_is_valid(config->hostname, FALSE))) { nm_log_warn(LOGD_DHCP, "dhcp%c: %s '%s' is invalid, will be ignored", nm_utils_addr_family_to_char(config->addr_family), @@ -1310,8 +1762,7 @@ config_clear(NMDhcpClientConfig *config) nm_clear_g_free((gpointer *) &config->anycast_address); nm_clear_g_free((gpointer *) &config->hostname); nm_clear_g_free((gpointer *) &config->mud_url); - - nm_clear_pointer((gpointer *) &config->reject_servers, g_strfreev); + nm_clear_g_free((gpointer *) &config->reject_servers); if (config->addr_family == AF_INET) { nm_clear_g_free((gpointer *) &config->v4.last_address); @@ -1338,6 +1789,13 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps { .invocation = NULL, }, + .acd = + { + .addr = INADDR_ANY, + .state = NM_OPTION_BOOL_DEFAULT, + .l3cfg_commit_handle = NULL, + .done_source = NULL, + }, }; } else { priv->v6 = (typeof(priv->v6)){ @@ -1383,6 +1841,11 @@ dispose(GObject *object) nm_clear_pointer(&priv->effective_client_id, g_bytes_unref); + nm_assert(!priv->watch_source); + nm_assert(!priv->l3cd_next); + nm_assert(!priv->l3cd_curr); + nm_assert(priv->l3cfg_notify.id == 0); + G_OBJECT_CLASS(nm_dhcp_client_parent_class)->dispose(object); } diff --git a/src/core/dhcp/nm-dhcp-client.h b/src/core/dhcp/nm-dhcp-client.h index e4b99929..51c6bc04 100644 --- a/src/core/dhcp/nm-dhcp-client.h +++ b/src/core/dhcp/nm-dhcp-client.h @@ -150,6 +150,10 @@ typedef struct { /* The address from the previous lease */ const char *last_address; + /* Whether to do ACD for the DHCPv4 address. With timeout zero, ACD + * is disabled. */ + guint acd_timeout_msec; + /* Set BOOTP broadcast flag in request packets, so that servers * will always broadcast replies. */ bool request_broadcast : 1; @@ -261,6 +265,8 @@ void _nm_dhcp_client_notify(NMDhcpClient *self, NMDhcpClientEventType client_event_type, const NML3ConfigData *l3cd); +gboolean _nm_dhcp_client_accept_offer(NMDhcpClient *self, gconstpointer p_yiaddr); + gboolean nm_dhcp_client_handle_event(gpointer unused, const char *iface, int pid, @@ -282,6 +288,8 @@ int nm_dhcp_client_get_ifindex(NMDhcpClient *self); void nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id); GBytes *nm_dhcp_client_get_effective_client_id(NMDhcpClient *self); +NML3ConfigData *nm_dhcp_client_create_l3cd(NMDhcpClient *self); + /***************************************************************************** * Client data *****************************************************************************/ diff --git a/src/core/dhcp/nm-dhcp-helper.c b/src/core/dhcp/nm-dhcp-helper.c index aab658a2..5a17f4e8 100644 --- a/src/core/dhcp/nm-dhcp-helper.c +++ b/src/core/dhcp/nm-dhcp-helper.c @@ -103,14 +103,17 @@ next:; int main(int argc, char *argv[]) { - gs_unref_object GDBusConnection *connection = NULL; - gs_free_error GError *error = NULL; - gs_unref_variant GVariant *parameters = NULL; - gs_unref_variant GVariant *result = NULL; - gboolean success = FALSE; + gs_unref_object GDBusConnection *connection = NULL; + gs_free_error GError *error = NULL; + gs_free_error GError *error_flush = NULL; + gs_unref_variant GVariant *parameters = NULL; + gs_unref_variant GVariant *result = NULL; + gs_free char *s_err = NULL; + gboolean success; guint try_count; gint64 time_start; gint64 time_end; + gint64 remaining_time; /* Connecting to the unix socket can fail with EAGAIN if there are too * many pending connections and the server can't accept them in time @@ -121,6 +124,8 @@ main(int argc, char *argv[]) time_end = time_start + (5000 * 1000L); try_count = 0; + _LOGi("nm-dhcp-helper: event called"); + do_connect: try_count++; connection = @@ -131,16 +136,16 @@ do_connect: &error); if (!connection) { if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { - gint64 time_remaining = time_end - g_get_monotonic_time(); - gint64 interval; + remaining_time = time_end - g_get_monotonic_time(); + if (remaining_time > 0) { + gint64 interval; - if (time_remaining > 0) { _LOGi("failure to connect: %s (retry %u, waited %lld ms)", error->message, try_count, - (long long) (time_end - time_remaining - time_start) / 1000); + (long long) (time_end - remaining_time - time_start) / 1000); interval = NM_CLAMP((gint64) (100L * (1L << NM_MIN(try_count, 31))), 5000, 100000); - g_usleep(NM_MIN(interval, time_remaining)); + g_usleep(NM_MIN(interval, remaining_time)); g_clear_error(&error); goto do_connect; } @@ -148,6 +153,7 @@ do_connect: g_dbus_error_strip_remote_error(error); _LOGE("could not connect to NetworkManager D-Bus socket: %s", error->message); + success = FALSE; goto out; } @@ -169,57 +175,74 @@ do_notify: NULL, &error); - if (!result) { - gs_free char *s_err = NULL; + if (result) { + success = TRUE; + goto out; + } - s_err = g_dbus_error_get_remote_error(error); - if (NM_IN_STRSET(s_err, "org.freedesktop.DBus.Error.UnknownMethod")) { - gint64 remaining_time = time_end - g_get_monotonic_time(); - gint64 interval; + s_err = g_dbus_error_get_remote_error(error); - /* I am not sure that a race can actually happen, as we register the object - * on the server side during GDBusServer:new-connection signal. - * - * However, there was also a race for subscribing to an event, so let's just - * do some retry. */ - if (remaining_time > 0) { - _LOGi("failure to call notify: %s (retry %u)", error->message, try_count); - interval = NM_CLAMP((gint64) (100L * (1L << NM_MIN(try_count, 31))), 5000, 25000); - g_usleep(NM_MIN(interval, remaining_time)); - g_clear_error(&error); - goto do_notify; - } - } + if (NM_IN_STRSET(s_err, "org.freedesktop.NetworkManager.Device.Failed")) { + _LOGi("notify failed with reason: %s", error->message); + success = FALSE; + goto out; + } + + if (!NM_IN_STRSET(s_err, "org.freedesktop.DBus.Error.UnknownMethod")) { + /* Some unexpected error. We treat that as a failure. In particular, + * the daemon will fail the request if ACD fails. This causes nm-dhcp-helper + * to fail, which in turn causes dhclient to send a DECLINE. */ _LOGW("failure to call notify: %s (try signal via Event)", error->message); + success = FALSE; + goto out; + } + + /* I am not sure that a race can actually happen, as we register the object + * on the server side during GDBusServer:new-connection signal. + * + * However, there was also a race for subscribing to an event, so let's just + * do some retry. */ + remaining_time = time_end - g_get_monotonic_time(); + if (remaining_time > 0) { + gint64 interval; + + _LOGi("failure to call notify: %s (retry %u)", error->message, try_count); + interval = NM_CLAMP((gint64) (100L * (1L << NM_MIN(try_count, 31))), 5000, 25000); + g_usleep(NM_MIN(interval, remaining_time)); g_clear_error(&error); + goto do_notify; + } - /* for backward compatibility, try to emit the signal. There is no stable - * API between the dhcp-helper and NetworkManager. However, while upgrading - * the NetworkManager package, a newer helper might want to notify an - * older server, which still uses the "Event". */ - if (!g_dbus_connection_emit_signal(connection, - NULL, - "/", - NM_DHCP_CLIENT_DBUS_IFACE, - "Event", - parameters, - &error)) { - g_dbus_error_strip_remote_error(error); - _LOGE("could not send DHCP Event signal: %s", error->message); - goto out; - } + /* for backward compatibility, try to emit the signal. There is no stable + * API between the dhcp-helper and NetworkManager. However, while upgrading + * the NetworkManager package, a newer helper might want to notify an + * older server, which still uses the "Event". */ + + _LOGW("failure to call notify: %s (try signal via Event)", error->message); + g_clear_error(&error); + + if (g_dbus_connection_emit_signal(connection, + NULL, + "/", + NM_DHCP_CLIENT_DBUS_IFACE, + "Event", + parameters, + &error)) { /* We were able to send the asynchronous Event. Consider that a success. */ success = TRUE; - } else - success = TRUE; - - if (!g_dbus_connection_flush_sync(connection, NULL, &error)) { - g_dbus_error_strip_remote_error(error); - _LOGE("could not flush D-Bus connection: %s", error->message); - success = FALSE; goto out; } + g_dbus_error_strip_remote_error(error); + _LOGE("could not send DHCP Event signal: %s", error->message); + success = FALSE; + out: + if (!g_dbus_connection_flush_sync(connection, NULL, &error_flush)) { + _LOGE("could not flush D-Bus connection: %s", error_flush->message); + /* if we considered this a success so far, don't fail because of this. */ + } + + _LOGi("success: %s", success ? "YES" : "NO"); return success ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/core/dhcp/nm-dhcp-manager.c b/src/core/dhcp/nm-dhcp-manager.c index 9fea1666..cfff23f8 100644 --- a/src/core/dhcp/nm-dhcp-manager.c +++ b/src/core/dhcp/nm-dhcp-manager.c @@ -42,6 +42,30 @@ G_DEFINE_TYPE(NMDhcpManager, nm_dhcp_manager, G_TYPE_OBJECT) /*****************************************************************************/ +#undef _NMLOG_ENABLED +#define _NMLOG_ENABLED(level, addr_family) nm_logging_enabled((level), _LOGD_DHCP(addr_family)) + +#define _NMLOG(level, addr_family, ...) \ + G_STMT_START \ + { \ + const int _addr_family = (addr_family); \ + const NMLogLevel _log_level = (level); \ + const NMLogDomain _log_domain = LOGD_DHCP_af(_addr_family); \ + \ + if (nm_logging_enabled(_log_level, _log_domain)) { \ + _nm_log(_log_level, \ + _log_domain, \ + 0, \ + NULL, \ + NULL, \ + "dhcp%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + nm_utils_addr_family_to_str(_addr_family) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ + G_STMT_END + +/*****************************************************************************/ + /* default to installed helper, but can be modified for testing */ const char *nm_dhcp_helper_path = LIBEXECDIR "/nm-dhcp-helper"; @@ -167,11 +191,10 @@ nm_dhcp_manager_start_client(NMDhcpManager *self, NMDhcpClientConfig *config, GE gtype = _client_factory_get_gtype(priv->client_factory, config->addr_family); - nm_log_trace(LOGD_DHCP, - "dhcp%c: creating IPv%c DHCP client of type %s", - nm_utils_addr_family_to_char(config->addr_family), - nm_utils_addr_family_to_char(config->addr_family), - g_type_name(gtype)); + _LOGT(config->addr_family, + "creating IPv%c DHCP client of type %s", + nm_utils_addr_family_to_char(config->addr_family), + g_type_name(gtype)); client = g_object_new(gtype, NM_DHCP_CLIENT_CONFIG, config, NULL); @@ -244,11 +267,11 @@ nm_dhcp_manager_init(NMDhcpManager *self) if (!f) continue; - nm_log_dbg(LOGD_DHCP, - "dhcp-init: enabled DHCP client '%s'%s%s", - f->name, - _client_factory_available(f) ? "" : " (not available)", - f->undocumented ? " (undocumented internal plugin)" : ""); + _LOGD(AF_UNSPEC, + "init: enabled DHCP client '%s'%s%s", + f->name, + _client_factory_available(f) ? "" : " (not available)", + f->undocumented ? " (undocumented internal plugin)" : ""); } /* Client-specific setup */ @@ -261,20 +284,20 @@ nm_dhcp_manager_init(NMDhcpManager *self) if (client) { client_factory = _client_factory_available(_client_factory_find_by_name(client)); if (!client_factory) - nm_log_warn(LOGD_DHCP, "dhcp-init: DHCP client '%s' not available", client); + _LOGW(AF_UNSPEC, "init: DHCP client '%s' not available", client); } if (!client_factory) { client_factory = _client_factory_find_by_name("" NM_CONFIG_DEFAULT_MAIN_DHCP); if (!client_factory) - nm_log_err(LOGD_DHCP, - "dhcp-init: default DHCP client '%s' is not installed", - NM_CONFIG_DEFAULT_MAIN_DHCP); + _LOGE(AF_UNSPEC, + "init: default DHCP client '%s' is not installed", + NM_CONFIG_DEFAULT_MAIN_DHCP); else { client_factory = _client_factory_available(client_factory); if (!client_factory) - nm_log_info(LOGD_DHCP, - "dhcp-init: default DHCP client '%s' is not available", - NM_CONFIG_DEFAULT_MAIN_DHCP); + _LOGI(AF_UNSPEC, + "init: default DHCP client '%s' is not available", + NM_CONFIG_DEFAULT_MAIN_DHCP); } } if (!client_factory) { @@ -287,7 +310,7 @@ nm_dhcp_manager_init(NMDhcpManager *self) g_return_if_fail(client_factory); - nm_log_info(LOGD_DHCP, "dhcp-init: Using DHCP client '%s'", client_factory->name); + _LOGI(AF_UNSPEC, "init: Using DHCP client '%s'", client_factory->name); /* NOTE: currently the DHCP plugin is chosen once at start. It's not * possible to reload that configuration. If that ever becomes possible, diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index 2e41cf15..05b7b52e 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -13,22 +13,24 @@ #include <ctype.h> #include <net/if_arp.h> +#include "n-dhcp4/src/n-dhcp4.h" + #include "libnm-glib-aux/nm-dedup-multi.h" -#include "libnm-std-aux/unaligned.h" +#include "libnm-glib-aux/nm-io-utils.h" #include "libnm-glib-aux/nm-str-buf.h" +#include "libnm-std-aux/unaligned.h" -#include "nm-l3-config-data.h" -#include "nm-utils.h" -#include "nm-config.h" -#include "nm-dhcp-utils.h" -#include "nm-dhcp-options.h" -#include "nm-core-utils.h" #include "NetworkManagerUtils.h" #include "libnm-platform/nm-platform.h" +#include "nm-config.h" +#include "nm-core-utils.h" #include "nm-dhcp-client-logging.h" -#include "n-dhcp4/src/n-dhcp4.h" +#include "nm-dhcp-options.h" +#include "nm-dhcp-utils.h" +#include "nm-l3-config-data.h" +#include "nm-utils.h" + #include "libnm-systemd-shared/nm-sd-utils-shared.h" -#include "libnm-systemd-core/nm-sd-utils-dhcp.h" /*****************************************************************************/ @@ -56,6 +58,8 @@ typedef struct { const NML3ConfigData *lease_l3cd; } granted; + GSource *pop_all_events_on_idle_source; + GSource *event_source; char *lease_file; } NMDhcpNettoolsPrivate; @@ -76,6 +80,10 @@ G_DEFINE_TYPE(NMDhcpNettools, nm_dhcp_nettools, NM_TYPE_DHCP_CLIENT) /*****************************************************************************/ +static void dhcp4_event_pop_all_events_on_idle(NMDhcpNettools *self); + +/*****************************************************************************/ + static void set_error_nettools(GError **error, int r, const char *message) { @@ -156,8 +164,10 @@ lease_option_consume_route(const uint8_t **datap, /*****************************************************************************/ static gboolean -lease_parse_address(NDhcp4ClientLease *lease, +lease_parse_address(NMDhcpNettools *self /* for logging context only */, + NDhcp4ClientLease *lease, NML3ConfigData *l3cd, + const char *iface, GHashTable *options, in_addr_t *out_address, GError **error) @@ -228,15 +238,33 @@ lease_parse_address(NDhcp4ClientLease *lease, } r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_SUBNET_MASK, &l_data, &l_data_len); - if (r != 0 || !nm_dhcp_lease_data_parse_in_addr(l_data, l_data_len, &a_netmask)) { - nm_utils_error_set_literal(error, - NM_UTILS_ERROR_UNKNOWN, - "could not get netmask from lease"); - return FALSE; + if (r == N_DHCP4_E_UNSET) { + char str1[NM_UTILS_INET_ADDRSTRLEN]; + char str2[NM_UTILS_INET_ADDRSTRLEN]; + + /* Some DHCP servers may not set the subnet-mask (issue#1037). + * Do the same as the dhclient plugin and use a default. */ + a_plen = _nm_utils_ip4_get_default_prefix(a_address.s_addr); + a_netmask = _nm_utils_ip4_prefix_to_netmask(a_plen); + _LOGT("missing subnet mask (option 1). Guess %s based on IP address %s", + _nm_utils_inet4_ntop(a_netmask, str1), + _nm_utils_inet4_ntop(a_address.s_addr, str2)); + } else { + if (r != 0 + || !nm_dhcp_lease_data_parse_in_addr(l_data, + l_data_len, + &a_netmask, + iface, + NM_DHCP_OPTION_DHCP4_SUBNET_MASK)) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_UNKNOWN, + "could not get netmask from lease"); + return FALSE; + } + a_plen = _nm_utils_ip4_netmask_to_prefix(a_netmask); + a_netmask = _nm_utils_ip4_prefix_to_netmask(a_plen); } - a_plen = nm_utils_ip4_netmask_to_prefix(a_netmask); - nm_dhcp_option_add_option_in_addr(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, @@ -282,6 +310,7 @@ lease_parse_address(NDhcp4ClientLease *lease, static void lease_parse_address_list(NDhcp4ClientLease *lease, NML3ConfigData *l3cd, + const char *iface, NMDhcpOptionDhcp4Options option, GHashTable *options, NMStrBuf *sbuf) @@ -294,8 +323,14 @@ lease_parse_address_list(NDhcp4ClientLease *lease, if (r != 0) return; - if (l_data_len == 0 || l_data_len % 4 != 0) + if (l_data_len == 0 || l_data_len % 4 != 0) { + nm_dhcp_lease_log_invalid_option(iface, + AF_INET, + option, + "wrong option length %lu", + (unsigned long) l_data_len); return; + } nm_str_buf_reset(sbuf); @@ -308,9 +343,14 @@ lease_parse_address_list(NDhcp4ClientLease *lease, switch (option) { case NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER: - if (addr == 0 || nm_ip4_addr_is_localhost(addr)) { + if (addr == 0 || nm_utils_ip4_address_is_loopback(addr)) { /* Skip localhost addresses, like also networkd does. * See https://github.com/systemd/systemd/issues/4524. */ + nm_dhcp_lease_log_invalid_option(iface, + AF_INET, + option, + "address %s is ignored", + _nm_utils_inet4_ntop(addr, addr_str)); continue; } nm_l3_config_data_add_nameserver(l3cd, AF_INET, &addr); @@ -502,7 +542,10 @@ lease_parse_routes(NDhcp4ClientLease *lease, } static void -lease_parse_search_domains(NDhcp4ClientLease *lease, NML3ConfigData *l3cd, GHashTable *options) +lease_parse_search_domains(NDhcp4ClientLease *lease, + NML3ConfigData *l3cd, + const char *iface, + GHashTable *options) { gs_strfreev char **domains = NULL; const guint8 *l_data; @@ -514,7 +557,11 @@ lease_parse_search_domains(NDhcp4ClientLease *lease, NML3ConfigData *l3cd, GHash if (r != 0) return; - domains = nm_dhcp_lease_data_parse_search_list(l_data, l_data_len); + domains = nm_dhcp_lease_data_parse_search_list(l_data, + l_data_len, + iface, + AF_INET, + NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST); if (!domains || !domains[0]) return; @@ -556,12 +603,9 @@ lease_parse_private_options(NDhcp4ClientLease *lease, GHashTable *options) } static NML3ConfigData * -lease_to_ip4_config(NMDedupMultiIndex *multi_idx, - const char *iface, - int ifindex, - NDhcp4ClientLease *lease, - GError **error) +lease_to_ip4_config(NMDhcpNettools *self, NDhcp4ClientLease *lease, GError **error) { + const char *iface; nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(0, FALSE); nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; gs_unref_hashtable GHashTable *options = NULL; @@ -574,13 +618,15 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, struct in_addr v_inaddr_s; int r; - g_return_val_if_fail(lease != NULL, NULL); + nm_assert(lease); + + iface = nm_dhcp_client_get_iface(NM_DHCP_CLIENT(self)); - l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_DHCP); + l3cd = nm_dhcp_client_create_l3cd(NM_DHCP_CLIENT(self)); options = nm_dhcp_option_create_options_dict(); - if (!lease_parse_address(lease, l3cd, options, &lease_address, error)) + if (!lease_parse_address(self, lease, l3cd, iface, options, &lease_address, error)) return NULL; r = n_dhcp4_client_lease_get_server_identifier(lease, &v_inaddr_s); @@ -592,7 +638,12 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, } r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_BROADCAST, &l_data, &l_data_len); - if (r == 0 && nm_dhcp_lease_data_parse_in_addr(l_data, l_data_len, &v_inaddr)) { + if (r == 0 + && nm_dhcp_lease_data_parse_in_addr(l_data, + l_data_len, + &v_inaddr, + iface, + NM_DHCP_OPTION_DHCP4_BROADCAST)) { nm_dhcp_option_add_option_in_addr(options, AF_INET, NM_DHCP_OPTION_DHCP4_BROADCAST, @@ -601,10 +652,21 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, lease_parse_routes(lease, l3cd, lease_address, options, &sbuf); - lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, options, &sbuf); + lease_parse_address_list(lease, + l3cd, + iface, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, + options, + &sbuf); r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, &l_data, &l_data_len); - if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { + if (r == 0 + && nm_dhcp_lease_data_parse_cstr(l_data, + l_data_len, + &l_data_len, + iface, + AF_INET, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME)) { gs_free const char **domains = NULL; nm_str_buf_reset(&sbuf); @@ -620,7 +682,10 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, for (i = 0; domains[i]; i++) { gs_free char *s = NULL; - s = nm_dhcp_lease_data_parse_domain_validate(domains[i]); + s = nm_dhcp_lease_data_parse_domain_validate(domains[i], + iface, + AF_INET, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME); if (!s) continue; @@ -638,10 +703,16 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, } } - lease_parse_search_domains(lease, l3cd, options); + lease_parse_search_domains(lease, l3cd, iface, options); r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, &l_data, &l_data_len); - if (r == 0 && nm_dhcp_lease_data_parse_mtu(l_data, l_data_len, &v_u16)) { + if (r == 0 + && nm_dhcp_lease_data_parse_mtu(l_data, + l_data_len, + &v_u16, + iface, + AF_INET, + NM_DHCP_OPTION_DHCP4_INTERFACE_MTU)) { nm_dhcp_option_add_option_u64(options, AF_INET, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, v_u16); nm_l3_config_data_set_mtu(l3cd, v_u16); } @@ -654,15 +725,26 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, if (r == 0) { gs_free char *s = NULL; - if (nm_dhcp_lease_data_parse_domain(l_data, l_data_len, &s)) { + if (nm_dhcp_lease_data_parse_domain(l_data, + l_data_len, + &s, + iface, + AF_INET, + NM_DHCP_OPTION_DHCP4_HOST_NAME)) { nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_HOST_NAME, s); } } - lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_NTP_SERVER, options, &sbuf); + lease_parse_address_list(lease, l3cd, iface, NM_DHCP_OPTION_DHCP4_NTP_SERVER, options, &sbuf); r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_ROOT_PATH, &l_data, &l_data_len); - if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { + if (r == 0 + && nm_dhcp_lease_data_parse_cstr(l_data, + l_data_len, + &l_data_len, + iface, + AF_INET, + NM_DHCP_OPTION_DHCP4_ROOT_PATH)) { /* https://tools.ietf.org/html/rfc2132#section-3.19 * * The path is formatted as a character string consisting of @@ -684,7 +766,13 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, &l_data, &l_data_len); - if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { + if (r == 0 + && nm_dhcp_lease_data_parse_cstr(l_data, + l_data_len, + &l_data_len, + iface, + AF_INET, + NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY)) { /* https://tools.ietf.org/html/draft-ietf-wrec-wpad-01#section-4.4.1 * * We reject NUL characters inside the string (except trailing NULs). @@ -704,7 +792,13 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, } r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, &l_data, &l_data_len); - if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { + if (r == 0 + && nm_dhcp_lease_data_parse_cstr(l_data, + l_data_len, + &l_data_len, + iface, + AF_INET, + NM_DHCP_OPTION_DHCP4_NIS_DOMAIN)) { gs_free char *to_free = NULL; /* https://tools.ietf.org/html/rfc2132#section-8.1 */ @@ -730,7 +824,13 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, } r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME, &l_data, &l_data_len); - if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { + if (r == 0 + && nm_dhcp_lease_data_parse_cstr(l_data, + l_data_len, + &l_data_len, + iface, + AF_INET, + NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME)) { gs_free char *to_free = NULL; v_str = nm_utils_buf_utf8safe_escape((char *) l_data, @@ -743,9 +843,14 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, v_str ?: ""); } - lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_NIS_SERVERS, options, &sbuf); + lease_parse_address_list(lease, l3cd, iface, NM_DHCP_OPTION_DHCP4_NIS_SERVERS, options, &sbuf); - lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_NETBIOS_NAMESERVER, options, &sbuf); + lease_parse_address_list(lease, + l3cd, + iface, + NM_DHCP_OPTION_DHCP4_NETBIOS_NAMESERVER, + options, + &sbuf); lease_parse_private_options(lease, options); @@ -785,9 +890,7 @@ lease_save(NMDhcpNettools *self, NDhcp4ClientLease *lease, const char *lease_fil static void bound4_handle(NMDhcpNettools *self, guint event, NDhcp4ClientLease *lease) { - NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); - NMDhcpClient *client = NM_DHCP_CLIENT(self); - const NMDhcpClientConfig *client_config; + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; gs_free_error GError *error = NULL; @@ -796,17 +899,14 @@ bound4_handle(NMDhcpNettools *self, guint event, NDhcp4ClientLease *lease) _LOGT("lease available (%s)", (event == N_DHCP4_CLIENT_EVENT_GRANTED) ? "granted" : "extended"); - client_config = nm_dhcp_client_get_config(client); - l3cd = lease_to_ip4_config(nm_dhcp_client_get_multi_idx(client), - client_config->iface, - nm_dhcp_client_get_ifindex(client), - lease, - &error); + l3cd = lease_to_ip4_config(self, lease, &error); if (!l3cd) { _LOGW("failure to parse lease: %s", error->message); - if (event == N_DHCP4_CLIENT_EVENT_GRANTED) + if (event == N_DHCP4_CLIENT_EVENT_GRANTED) { n_dhcp4_client_lease_decline(lease, "invalid lease"); + dhcp4_event_pop_all_events_on_idle(self); + } _nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL); return; @@ -854,7 +954,7 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event) case N_DHCP4_CLIENT_EVENT_OFFER: r = n_dhcp4_client_lease_get_server_identifier(event->offer.lease, &server_id); if (r) { - _LOGW("selecting lease failed: %d", r); + _LOGW("selecting lease failed: could not get DHCP server identifier (%d)", r); return; } @@ -870,11 +970,19 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event) return; } + if (!_nm_dhcp_client_accept_offer(NM_DHCP_CLIENT(self), &yiaddr.s_addr)) { + /* We don't log about this, the parent class is expected to notify about the reasons. */ + return; + } + _LOGT("selecting offered lease from %s for %s", _nm_utils_inet4_ntop(server_id.s_addr, addr_str), _nm_utils_inet4_ntop(yiaddr.s_addr, addr_str2)); r = n_dhcp4_client_lease_select(event->offer.lease); + + dhcp4_event_pop_all_events_on_idle(self); + if (r) { _LOGW("selecting lease failed: %d", r); return; @@ -906,22 +1014,79 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event) } } +static void +dhcp4_event_pop_all_events(NMDhcpNettools *self) +{ + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); + NDhcp4ClientEvent *event; + + while (!n_dhcp4_client_pop_event(priv->client, &event) && event) + dhcp4_event_handle(self, event); + + nm_clear_g_source_inst(&priv->pop_all_events_on_idle_source); +} + +static gboolean +dhcp4_event_pop_all_events_on_idle_cb(gpointer user_data) +{ + NMDhcpNettools *self = user_data; + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->pop_all_events_on_idle_source); + dhcp4_event_pop_all_events(self); + return G_SOURCE_CONTINUE; +} + +static void +dhcp4_event_pop_all_events_on_idle(NMDhcpNettools *self) +{ + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); + + /* For the most part, NDhcp4Client gets driven from internal, that is + * by having events ready on the socket or the timerfd. For those + * events, we will poll on the (epoll) FD, then let it be processed + * by n_dhcp4_client_dispatch(), and pop the queued events. + * + * But certain commands (n_dhcp4_client_lease_select(), n_dhcp4_client_lease_accept(), + * n_dhcp4_client_lease_decline()) are initiated by the user. And they tend + * to log events. Logging is done by queuing a message, but that won't be processed, + * unless we pop the event. + * + * To ensure that those logging events get popped, schedule an idle handler to do that. + * + * Yes, this means, that the messages only get logged later, when the idle handler + * runs. The alternative seems even more problematic, because we don't know + * the current call-state, and it seems dangerous to pop unexpected events. + * E.g. we call n_dhcp4_client_lease_select() from inside the event-handler, + * it seems wrong to call dhcp4_event_pop_all_events() in that context again. + * + * See-also: https://github.com/nettools/n-dhcp4/issues/34 + */ + + if (!priv->pop_all_events_on_idle_source) { + priv->pop_all_events_on_idle_source = + nm_g_idle_add_source(dhcp4_event_pop_all_events_on_idle_cb, self); + } +} + static gboolean dhcp4_event_cb(int fd, GIOCondition condition, gpointer user_data) { NMDhcpNettools *self = user_data; NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); - NDhcp4ClientEvent *event; int r; r = n_dhcp4_client_dispatch(priv->client); if (r < 0) { - /* FIXME: if any operation (e.g. send()) fails during the + /* If any operation (e.g. send()) fails during the * dispatch, n-dhcp4 returns an error without arming timers * or progressing state, so the only reasonable thing to do * is to move to failed state so that the client will be - * restarted. Ideally n-dhcp4 should retry failed operations - * a predefined number of times (possibly infinite). + * restarted. + * + * That means, n_dhcp4_client_dispatch() must not fail if it can + * somehow workaround the problem. A failure is really fatal + * and the client needs to be restarted. */ _LOGE("error %d dispatching events", r); nm_clear_g_source_inst(&priv->event_source); @@ -929,8 +1094,7 @@ dhcp4_event_cb(int fd, GIOCondition condition, gpointer user_data) return G_SOURCE_REMOVE; } - while (!n_dhcp4_client_pop_event(priv->client, &event) && event) - dhcp4_event_handle(self, event); + dhcp4_event_pop_all_events(self); return G_SOURCE_CONTINUE; } @@ -1056,6 +1220,8 @@ _accept(NMDhcpClient *client, const NML3ConfigData *l3cd, GError **error) if (!r) lease_save(self, priv->granted.lease, priv->lease_file); + dhcp4_event_pop_all_events_on_idle(self); + nm_clear_pointer(&priv->granted.lease, n_dhcp4_client_lease_unref); nm_clear_l3cd(&priv->granted.lease_l3cd); @@ -1091,6 +1257,8 @@ decline(NMDhcpClient *client, const NML3ConfigData *l3cd, const char *error_mess r = n_dhcp4_client_lease_decline(lease, error_message); + dhcp4_event_pop_all_events_on_idle(self); + if (r) { set_error_nettools(error, r, "failed to decline lease"); return FALSE; @@ -1155,18 +1323,20 @@ ip4_start(NMDhcpClient *client, GError **error) if (client_config->v4.last_address) inet_pton(AF_INET, client_config->v4.last_address, &last_addr); else { - /* - * TODO: we stick to the systemd-networkd lease file format. Quite easy for now to - * just use the functions in systemd code. Anyway, as in the end we just use the - * ip address from all the options found in the lease, write a function that parses - * the lease file just for the assigned address and returns it in &last_address. - * Then drop reference to systemd-networkd structures and functions. - */ - nm_auto(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; - - dhcp_lease_load(&lease, lease_file); - if (lease) - sd_dhcp_lease_get_address(lease, &last_addr); + gs_free char *contents = NULL; + gs_free char *s_addr = NULL; + + nm_utils_file_get_contents(-1, + lease_file, + 64 * 1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, + NULL, + NULL, + NULL); + nm_parse_env_file(contents, "ADDRESS", &s_addr); + if (s_addr) + nm_utils_parse_inaddr_bin(AF_INET, s_addr, NULL, &last_addr); } if (last_addr.s_addr) { @@ -1304,6 +1474,7 @@ dispose(GObject *object) nm_clear_g_free(&priv->lease_file); nm_clear_g_source_inst(&priv->event_source); + nm_clear_g_source_inst(&priv->pop_all_events_on_idle_source); nm_clear_pointer(&priv->granted.lease, n_dhcp4_client_lease_unref); nm_clear_l3cd(&priv->granted.lease_l3cd); nm_clear_pointer(&priv->probe, n_dhcp4_client_probe_free); diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c index f2dd1823..49e21d97 100644 --- a/src/core/dhcp/nm-dhcp-systemd.c +++ b/src/core/dhcp/nm-dhcp-systemd.c @@ -25,7 +25,6 @@ #include "libnm-platform/nm-platform.h" #include "nm-dhcp-client-logging.h" #include "libnm-systemd-core/nm-sd.h" -#include "libnm-systemd-core/nm-sd-utils-dhcp.h" /*****************************************************************************/ @@ -47,7 +46,6 @@ static GType nm_dhcp_systemd_get_type(void); /*****************************************************************************/ typedef struct { - sd_dhcp_client *client4; sd_dhcp6_client *client6; char *lease_file; @@ -70,683 +68,7 @@ G_DEFINE_TYPE(NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT) /*****************************************************************************/ static NML3ConfigData * -lease_to_ip4_config(NMDedupMultiIndex *multi_idx, - const char *iface, - int ifindex, - sd_dhcp_lease *lease, - GError **error) -{ - nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; - gs_unref_hashtable GHashTable *options = NULL; - const struct in_addr *addr_list; - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; - const char *s; - nm_auto_free_gstring GString *str = NULL; - nm_auto_free sd_dhcp_route **routes_static = NULL; - nm_auto_free sd_dhcp_route **routes_classless = NULL; - const char *const *search_domains = NULL; - guint32 default_route_metric_offset; - guint16 mtu; - int i; - int num; - int is_classless; - int n_routes_static; - int n_routes_classless; - const void *data; - gsize data_len; - gboolean has_router_from_classless = FALSE; - const gint32 ts = nm_utils_get_monotonic_timestamp_sec(); - gint64 ts_time = time(NULL); - struct in_addr a_address; - struct in_addr a_netmask; - struct in_addr a_next_server; - struct in_addr server_id; - struct in_addr broadcast; - const struct in_addr *a_router; - guint32 a_plen; - guint32 a_lifetime; - guint32 renewal; - guint32 rebinding; - gs_free nm_sd_dhcp_option *private_options = NULL; - - nm_assert(lease != NULL); - - if (sd_dhcp_lease_get_address(lease, &a_address) < 0) { - nm_utils_error_set_literal(error, - NM_UTILS_ERROR_UNKNOWN, - "could not get address from lease"); - return NULL; - } - - if (sd_dhcp_lease_get_netmask(lease, &a_netmask) < 0) { - nm_utils_error_set_literal(error, - NM_UTILS_ERROR_UNKNOWN, - "could not get netmask from lease"); - return NULL; - } - - if (sd_dhcp_lease_get_lifetime(lease, &a_lifetime) < 0) { - nm_utils_error_set_literal(error, - NM_UTILS_ERROR_UNKNOWN, - "could not get lifetime from lease"); - return NULL; - } - - l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_DHCP); - - options = nm_dhcp_option_create_options_dict(); - - _nm_utils_inet4_ntop(a_address.s_addr, addr_str); - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, addr_str); - - a_plen = nm_utils_ip4_netmask_to_prefix(a_netmask.s_addr); - nm_dhcp_option_add_option(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_SUBNET_MASK, - _nm_utils_inet4_ntop(a_netmask.s_addr, addr_str)); - - nm_dhcp_option_add_option_u64(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME, - a_lifetime); - nm_dhcp_option_add_option_u64(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_NM_EXPIRY, - (guint64) (ts_time + a_lifetime)); - - if (sd_dhcp_lease_get_next_server(lease, &a_next_server) == 0) { - _nm_utils_inet4_ntop(a_next_server.s_addr, addr_str); - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER, addr_str); - } - - nm_l3_config_data_add_address_4(l3cd, - &((const NMPlatformIP4Address){ - .address = a_address.s_addr, - .peer_address = a_address.s_addr, - .plen = a_plen, - .addr_source = NM_IP_CONFIG_SOURCE_DHCP, - .timestamp = ts, - .lifetime = a_lifetime, - .preferred = a_lifetime, - })); - - if (sd_dhcp_lease_get_server_identifier(lease, &server_id) >= 0) { - _nm_utils_inet4_ntop(server_id.s_addr, addr_str); - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_SERVER_ID, addr_str); - } - - if (sd_dhcp_lease_get_broadcast(lease, &broadcast) >= 0) { - _nm_utils_inet4_ntop(broadcast.s_addr, addr_str); - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_BROADCAST, addr_str); - } - - num = sd_dhcp_lease_get_dns(lease, &addr_list); - if (num > 0) { - nm_gstring_prepare(&str); - for (i = 0; i < num; i++) { - _nm_utils_inet4_ntop(addr_list[i].s_addr, addr_str); - g_string_append(nm_gstring_add_space_delimiter(str), addr_str); - - if (addr_list[i].s_addr == 0 || nm_ip4_addr_is_localhost(addr_list[i].s_addr)) { - /* Skip localhost addresses, like also networkd does. - * See https://github.com/systemd/systemd/issues/4524. */ - continue; - } - nm_l3_config_data_add_nameserver(l3cd, AF_INET, &addr_list[i].s_addr); - } - nm_dhcp_option_add_option(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, - str->str); - } - - num = sd_dhcp_lease_get_search_domains(lease, (char ***) &search_domains); - if (num > 0) { - nm_gstring_prepare(&str); - for (i = 0; i < num; i++) { - g_string_append(nm_gstring_add_space_delimiter(str), search_domains[i]); - nm_l3_config_data_add_search(l3cd, AF_INET, search_domains[i]); - } - nm_dhcp_option_add_option(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST, - str->str); - } - - if (sd_dhcp_lease_get_domainname(lease, &s) >= 0) { - gs_strfreev char **domains = NULL; - char **d; - - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, s); - - /* Multiple domains sometimes stuffed into option 15 "Domain Name". - * As systemd escapes such characters, split them at \\032. */ - domains = g_strsplit(s, "\\032", 0); - for (d = domains; *d; d++) - nm_l3_config_data_add_domain(l3cd, AF_INET, *d); - } - - if (sd_dhcp_lease_get_hostname(lease, &s) >= 0) { - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_HOST_NAME, s); - } - - default_route_metric_offset = 0; - n_routes_static = sd_dhcp_lease_get_static_routes(lease, &routes_static); - n_routes_classless = sd_dhcp_lease_get_classless_routes(lease, &routes_classless); - for (is_classless = 1; is_classless >= 0; is_classless--) { - int n_routes = (is_classless ? n_routes_classless : n_routes_static); - sd_dhcp_route *const *routes = (is_classless ? routes_classless : routes_static); - - if (n_routes <= 0) - continue; - - nm_gstring_prepare(&str); - - for (i = 0; i < n_routes; i++) { - char network_net_str[NM_UTILS_INET_ADDRSTRLEN]; - char gateway_str[NM_UTILS_INET_ADDRSTRLEN]; - guint8 r_plen; - struct in_addr r_network; - struct in_addr r_gateway; - in_addr_t network_net; - guint32 m; - - if (sd_dhcp_route_get_destination(routes[i], &r_network) < 0) - continue; - if (sd_dhcp_route_get_destination_prefix_length(routes[i], &r_plen) < 0 || r_plen > 32) - continue; - if (sd_dhcp_route_get_gateway(routes[i], &r_gateway) < 0) - continue; - - network_net = nm_utils_ip4_address_clear_host_address(r_network.s_addr, r_plen); - _nm_utils_inet4_ntop(network_net, network_net_str); - _nm_utils_inet4_ntop(r_gateway.s_addr, gateway_str); - - g_string_append_printf(nm_gstring_add_space_delimiter(str), - "%s/%d %s", - network_net_str, - (int) r_plen, - gateway_str); - - if (!is_classless && n_routes_classless > 0) { - /* RFC 3443: if the DHCP server returns both a Classless Static Routes - * option and a Static Routes option, the DHCP client MUST ignore the - * Static Routes option. */ - continue; - } - - if (r_plen == 0) { - if (!is_classless) { - /* for option 33 (static route), RFC 2132 says: - * - * The default route (0.0.0.0) is an illegal destination for a static - * route. */ - continue; - } - - /* if there are multiple default routes, we add them with differing - * metrics. */ - m = default_route_metric_offset++; - has_router_from_classless = TRUE; - } else - m = 0; - - nm_l3_config_data_add_route_4(l3cd, - &((const NMPlatformIP4Route){ - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .network = network_net, - .plen = r_plen, - .gateway = r_gateway.s_addr, - .pref_src = a_address.s_addr, - .metric_any = TRUE, - .metric = m, - .table_any = TRUE, - .table_coerced = 0, - })); - } - - if (str->len > 0) { - nm_dhcp_option_add_option(options, - AF_INET, - is_classless ? NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE - : NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, - str->str); - } - } - - num = sd_dhcp_lease_get_router(lease, &a_router); - if (num > 0) { - default_route_metric_offset = 0; - - nm_gstring_prepare(&str); - for (i = 0; i < num; i++) { - guint32 m; - - s = _nm_utils_inet4_ntop(a_router[i].s_addr, addr_str); - g_string_append(nm_gstring_add_space_delimiter(str), s); - - if (a_router[i].s_addr == 0) { - /* silently skip 0.0.0.0 */ - continue; - } - - if (has_router_from_classless) { - /* If the DHCP server returns both a Classless Static Routes option and a - * Router option, the DHCP client MUST ignore the Router option [RFC 3442]. - * - * Be more lenient and ignore the Router option only if Classless Static - * Routes contain a default gateway (as other DHCP backends do). - */ - continue; - } - - /* if there are multiple default routes, we add them with differing - * metrics. */ - m = default_route_metric_offset++; - - nm_l3_config_data_add_route_4(l3cd, - &((const NMPlatformIP4Route){ - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .gateway = a_router[i].s_addr, - .pref_src = a_address.s_addr, - .table_any = TRUE, - .table_coerced = 0, - .metric_any = TRUE, - .metric = m, - })); - } - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_ROUTER, str->str); - } - - if (sd_dhcp_lease_get_mtu(lease, &mtu) >= 0 && mtu) { - nm_dhcp_option_add_option_u64(options, AF_INET, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, mtu); - nm_l3_config_data_set_mtu(l3cd, mtu); - } - - num = sd_dhcp_lease_get_ntp(lease, &addr_list); - if (num > 0) { - nm_gstring_prepare(&str); - for (i = 0; i < num; i++) { - _nm_utils_inet4_ntop(addr_list[i].s_addr, addr_str); - g_string_append(nm_gstring_add_space_delimiter(str), addr_str); - } - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NTP_SERVER, str->str); - } - - if (sd_dhcp_lease_get_root_path(lease, &s) >= 0) { - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_ROOT_PATH, s); - } - - if (sd_dhcp_lease_get_t1(lease, &renewal) >= 0) { - nm_dhcp_option_add_option_u64(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME, - renewal); - } - - if (sd_dhcp_lease_get_t2(lease, &rebinding) >= 0) { - nm_dhcp_option_add_option_u64(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME, - rebinding); - } - - if (sd_dhcp_lease_get_timezone(lease, &s) >= 0) { - nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE, s); - } - - if (sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len) >= 0) { - if (!!memmem(data, data_len, "ANDROID_METERED", NM_STRLEN("ANDROID_METERED"))) - nm_l3_config_data_set_metered(l3cd, TRUE); - } - - num = nm_sd_dhcp_lease_get_private_options(lease, &private_options); - if (num > 0) { - for (i = 0; i < num; i++) { - guint8 code = private_options[i].code; - const guint8 *l_data = private_options[i].data; - gsize l_data_len = private_options[i].data_len; - char *option_string; - - if (code == NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY) { - if (nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { - gs_free char *to_free = NULL; - const char *escaped; - - escaped = - nm_utils_buf_utf8safe_escape((char *) l_data, l_data_len, 0, &to_free); - nm_dhcp_option_add_option(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, - escaped ?: ""); - - nm_l3_config_data_set_proxy_method(l3cd, NM_PROXY_CONFIG_METHOD_AUTO); - nm_l3_config_data_set_proxy_pac_url(l3cd, escaped ?: ""); - } - continue; - } - if (code == NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE) { - /* nettools and dhclient parse option 249 (Microsoft Classless Static Route) - * as fallback for routes and ignores them from private options. - * - * The systemd plugin does not, and for consistency with nettools we - * also don't expose it as private option either. */ - continue; - } - - option_string = nm_utils_bin2hexstr_full(l_data, l_data_len, ':', FALSE, NULL); - nm_dhcp_option_take_option(options, AF_INET, code, option_string); - } - } - - nm_dhcp_option_add_requests_to_options(options, AF_INET); - - nm_l3_config_data_set_dhcp_lease_from_options(l3cd, AF_INET, g_steal_pointer(&options)); - - return g_steal_pointer(&l3cd); -} - -/*****************************************************************************/ - -static void -bound4_handle(NMDhcpSystemd *self, gboolean extended) -{ - NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); - const char *iface = nm_dhcp_client_get_iface(NM_DHCP_CLIENT(self)); - nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; - sd_dhcp_lease *lease = NULL; - GError *error = NULL; - - if (sd_dhcp_client_get_lease(priv->client4, &lease) < 0 || !lease) { - _LOGW("no lease!"); - _nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL); - return; - } - - _LOGD("lease available"); - - l3cd = lease_to_ip4_config(nm_dhcp_client_get_multi_idx(NM_DHCP_CLIENT(self)), - iface, - nm_dhcp_client_get_ifindex(NM_DHCP_CLIENT(self)), - lease, - &error); - if (!l3cd) { - _LOGW("%s", error->message); - g_clear_error(&error); - _nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL); - return; - } - - dhcp_lease_save(lease, priv->lease_file); - - _nm_dhcp_client_notify(NM_DHCP_CLIENT(self), - extended ? NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED - : NM_DHCP_CLIENT_EVENT_TYPE_BOUND, - l3cd); -} - -static int -dhcp_event_cb(sd_dhcp_client *client, int event, gpointer user_data) -{ - NMDhcpSystemd *self = NM_DHCP_SYSTEMD(user_data); - NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); - char addr_str[INET_ADDRSTRLEN]; - sd_dhcp_lease *lease = NULL; - struct in_addr addr; - int r; - - nm_assert(priv->client4 == client); - - _LOGD("client event %d", event); - - switch (event) { - case SD_DHCP_CLIENT_EVENT_EXPIRED: - _nm_dhcp_client_notify(NM_DHCP_CLIENT(user_data), NM_DHCP_CLIENT_EVENT_TYPE_EXPIRE, NULL); - break; - case SD_DHCP_CLIENT_EVENT_STOP: - _nm_dhcp_client_notify(NM_DHCP_CLIENT(user_data), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL); - break; - case SD_DHCP_CLIENT_EVENT_RENEW: - case SD_DHCP_CLIENT_EVENT_IP_CHANGE: - bound4_handle(self, TRUE); - break; - case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE: - bound4_handle(self, FALSE); - break; - case SD_DHCP_CLIENT_EVENT_SELECTING: - r = sd_dhcp_client_get_lease(priv->client4, &lease); - if (r < 0) - return r; - r = sd_dhcp_lease_get_server_identifier(lease, &addr); - if (r < 0) - return r; - if (nm_dhcp_client_server_id_is_rejected(NM_DHCP_CLIENT(user_data), &addr)) { - _LOGD("server-id %s is in the reject-list, ignoring", - nm_utils_inet_ntop(AF_INET, &addr, addr_str)); - return -ENOMSG; - } - break; - case SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE: - break; - default: - _LOGW("unhandled DHCP event %d", event); - break; - } - - return 0; -} - -static gboolean -ip4_start(NMDhcpClient *client, GError **error) -{ - nm_auto(sd_dhcp_client_unrefp) sd_dhcp_client *sd_client = NULL; - NMDhcpSystemd *self = NM_DHCP_SYSTEMD(client); - NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); - const NMDhcpClientConfig *client_config; - gs_free char *lease_file = NULL; - GBytes *hwaddr; - const uint8_t *hwaddr_arr; - gsize hwaddr_len; - int arp_type; - GBytes *client_id; - gs_unref_bytes GBytes *client_id_new = NULL; - GBytes *vendor_class_identifier; - const uint8_t *client_id_arr; - size_t client_id_len; - struct in_addr last_addr = {0}; - const char *hostname; - const char *mud_url; - int r, i; - GBytes *bcast_hwaddr; - const uint8_t *bcast_hwaddr_arr; - gsize bcast_hwaddr_len; - - g_return_val_if_fail(!priv->client4, FALSE); - g_return_val_if_fail(!priv->client6, FALSE); - - client_config = nm_dhcp_client_get_config(client); - - /* TODO: honor nm_dhcp_client_get_anycast_address() */ - - r = sd_dhcp_client_new(&sd_client, FALSE); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to create dhcp-client: %s"); - return FALSE; - } - - _LOGT("dhcp-client4: set " NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(sd_client)); - - r = sd_dhcp_client_attach_event(sd_client, NULL, 0); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to attach event: %s"); - return FALSE; - } - - hwaddr = client_config->hwaddr; - if (!hwaddr || !(hwaddr_arr = g_bytes_get_data(hwaddr, &hwaddr_len)) - || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen(hwaddr_len)) < 0) { - nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); - return FALSE; - } - - bcast_hwaddr_arr = NULL; - bcast_hwaddr = client_config->bcast_hwaddr; - if (bcast_hwaddr) { - bcast_hwaddr_arr = g_bytes_get_data(bcast_hwaddr, &bcast_hwaddr_len); - if (bcast_hwaddr_len != hwaddr_len) - bcast_hwaddr_arr = NULL; - } - - r = sd_dhcp_client_set_mac(sd_client, - hwaddr_arr, - bcast_hwaddr_arr, - hwaddr_len, - (guint16) arp_type); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to set MAC address: %s"); - return FALSE; - } - - r = sd_dhcp_client_set_ifindex(sd_client, nm_dhcp_client_get_ifindex(client)); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to set ifindex: %s"); - return FALSE; - } - - nm_dhcp_utils_get_leasefile_path(AF_INET, - "internal", - client_config->iface, - client_config->uuid, - &lease_file); - - if (client_config->v4.last_address) - inet_pton(AF_INET, client_config->v4.last_address, &last_addr); - else { - nm_auto(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; - - dhcp_lease_load(&lease, lease_file); - if (lease) - sd_dhcp_lease_get_address(lease, &last_addr); - } - - r = sd_dhcp_client_set_request_broadcast(sd_client, client_config->v4.request_broadcast); - nm_assert(r >= 0); - - if (last_addr.s_addr) { - r = sd_dhcp_client_set_request_address(sd_client, &last_addr); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to set last IPv4 address: %s"); - return FALSE; - } - } - - client_id = client_config->client_id; - if (!client_id) { - client_id_new = nm_utils_dhcp_client_id_mac(arp_type, hwaddr_arr, hwaddr_len); - client_id = client_id_new; - } - - if (!(client_id_arr = g_bytes_get_data(client_id, &client_id_len)) || client_id_len < 2) { - /* invalid client-ids are not expected. */ - nm_assert_not_reached(); - - nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "no valid IPv4 client-id"); - return FALSE; - } - - /* Note that we always set a client-id. In particular for infiniband that is necessary, - * see https://tools.ietf.org/html/rfc4390#section-2.1 . */ - r = sd_dhcp_client_set_client_id(sd_client, - client_id_arr[0], - client_id_arr + 1, - NM_MIN(client_id_len - 1, _NM_MAX_CLIENT_ID_LEN)); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to set IPv4 client-id: %s"); - return FALSE; - } - - /* Add requested options */ - for (i = 0; i < (int) G_N_ELEMENTS(_nm_dhcp_option_dhcp4_options); i++) { - if (_nm_dhcp_option_dhcp4_options[i].include) { - nm_assert(_nm_dhcp_option_dhcp4_options[i].option_num <= 255); - r = sd_dhcp_client_set_request_option(sd_client, - _nm_dhcp_option_dhcp4_options[i].option_num); - nm_assert(r >= 0 || r == -EEXIST); - } - } - - hostname = client_config->hostname; - if (hostname) { - /* FIXME: sd-dhcp decides which hostname/FQDN option to send (12 or 81) - * only based on whether the hostname has a domain part or not. At the - * moment there is no way to force one or another. - */ - r = sd_dhcp_client_set_hostname(sd_client, hostname); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to set DHCP hostname: %s"); - return FALSE; - } - } - - mud_url = client_config->mud_url; - if (mud_url) { - r = sd_dhcp_client_set_mud_url(sd_client, mud_url); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to set DHCP MUDURL: %s"); - return FALSE; - } - } - - vendor_class_identifier = client_config->vendor_class_identifier; - if (vendor_class_identifier) { - const char *option_data; - gsize len; - - option_data = g_bytes_get_data(vendor_class_identifier, &len); - nm_assert(option_data); - nm_assert(len <= 255); - - option_data = nm_strndup_a(300, option_data, len, NULL); - - r = sd_dhcp_client_set_vendor_class_identifier(sd_client, option_data); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to set DHCP vendor class identifier: %s"); - return FALSE; - } - } - - r = sd_dhcp_client_set_callback(sd_client, dhcp_event_cb, client); - if (r < 0) { - nm_utils_error_set_errno(error, r, "failed to set callback: %s"); - return FALSE; - } - - priv->client4 = g_steal_pointer(&sd_client); - - g_free(priv->lease_file); - priv->lease_file = g_steal_pointer(&lease_file); - - nm_dhcp_client_set_effective_client_id(client, client_id); - - r = sd_dhcp_client_start(priv->client4); - if (r < 0) { - sd_dhcp_client_set_callback(priv->client4, NULL, NULL); - nm_clear_pointer(&priv->client4, sd_dhcp_client_unref); - nm_utils_error_set_errno(error, r, "failed to start DHCP client: %s"); - return FALSE; - } - - return TRUE; -} - -static NML3ConfigData * -lease_to_ip6_config(NMDedupMultiIndex *multi_idx, - const char *iface, - int ifindex, - sd_dhcp6_lease *lease, - gboolean info_only, - gint32 ts, - GError **error) +lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GError **error) { nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; gs_unref_hashtable GHashTable *options = NULL; @@ -762,11 +84,11 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx, nm_assert(lease); - l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_DHCP); + l3cd = nm_dhcp_client_create_l3cd(NM_DHCP_CLIENT(self)); options = nm_dhcp_option_create_options_dict(); - if (!info_only) { + if (!nm_dhcp_client_get_config(NM_DHCP_CLIENT(self))->v6.info_only) { gboolean has_any_addresses = FALSE; uint32_t lft_pref; uint32_t lft_valid; @@ -864,17 +186,13 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx, static void bound6_handle(NMDhcpSystemd *self) { - NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); - const gint32 ts = nm_utils_get_monotonic_timestamp_sec(); - const char *iface = nm_dhcp_client_get_iface(NM_DHCP_CLIENT(self)); - const NMDhcpClientConfig *client_config; + NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); + const gint32 ts = nm_utils_get_monotonic_timestamp_sec(); nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; gs_free_error GError *error = NULL; NMPlatformIP6Address prefix = {0}; sd_dhcp6_lease *lease = NULL; - client_config = nm_dhcp_client_get_config(NM_DHCP_CLIENT(self)); - if (sd_dhcp6_client_get_lease(priv->client6, &lease) < 0 || !lease) { _LOGW(" no lease!"); _nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL); @@ -883,13 +201,7 @@ bound6_handle(NMDhcpSystemd *self) _LOGD("lease available"); - l3cd = lease_to_ip6_config(nm_dhcp_client_get_multi_idx(NM_DHCP_CLIENT(self)), - iface, - nm_dhcp_client_get_ifindex(NM_DHCP_CLIENT(self)), - lease, - client_config->v6.info_only, - ts, - &error); + l3cd = lease_to_ip6_config(self, lease, ts, &error); if (!l3cd) { _LOGW("%s", error->message); @@ -953,7 +265,6 @@ ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error) GBytes *duid; gboolean prefix_delegation; - g_return_val_if_fail(!priv->client4, FALSE); g_return_val_if_fail(!priv->client6, FALSE); client_config = nm_dhcp_client_get_config(client); @@ -1080,18 +391,13 @@ stop(NMDhcpClient *client, gboolean release) NM_DHCP_CLIENT_CLASS(nm_dhcp_systemd_parent_class)->stop(client, release); - _LOGT("dhcp-client%d: stop %p", - priv->client4 ? '4' : '6', - priv->client4 ? (gpointer) priv->client4 : (gpointer) priv->client6); + _LOGT("dhcp-client6: stop"); - if (priv->client4) { - sd_dhcp_client_set_callback(priv->client4, NULL, NULL); - r = sd_dhcp_client_stop(priv->client4); - } else if (priv->client6) { - sd_dhcp6_client_set_callback(priv->client6, NULL, NULL); - r = sd_dhcp6_client_stop(priv->client6); - } + if (!priv->client6) + return; + sd_dhcp6_client_set_callback(priv->client6, NULL, NULL); + r = sd_dhcp6_client_stop(priv->client6); if (r) _LOGW("failed to stop client (%d)", r); } @@ -1109,12 +415,6 @@ dispose(GObject *object) nm_clear_g_free(&priv->lease_file); - if (priv->client4) { - sd_dhcp_client_stop(priv->client4); - sd_dhcp_client_unref(priv->client4); - priv->client4 = NULL; - } - if (priv->client6) { sd_dhcp6_client_stop(priv->client6); sd_dhcp6_client_unref(priv->client6); @@ -1132,14 +432,13 @@ nm_dhcp_systemd_class_init(NMDhcpSystemdClass *sdhcp_class) object_class->dispose = dispose; - client_class->ip4_start = ip4_start; client_class->ip6_start = ip6_start; client_class->stop = stop; } const NMDhcpClientFactory _nm_dhcp_client_factory_systemd = { .name = "systemd", - .get_type_4 = nm_dhcp_systemd_get_type, + .get_type_4 = nm_dhcp_nettools_get_type, .get_type_6 = nm_dhcp_systemd_get_type, .undocumented = TRUE, }; diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c index 88fe83f3..1bffb3c4 100644 --- a/src/core/dhcp/nm-dhcp-utils.c +++ b/src/core/dhcp/nm-dhcp-utils.c @@ -14,6 +14,7 @@ #include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "nm-dhcp-utils.h" +#include "nm-dhcp-options.h" #include "nm-l3-config-data.h" #include "nm-utils.h" #include "nm-config.h" @@ -295,7 +296,7 @@ process_classful_routes(const char *iface, return; if ((NM_PTRARRAY_LEN(searches) % 2) != 0) { - _LOG2I(LOGD_DHCP, iface, " static routes provided, but invalid"); + _LOG2I(LOGD_DHCP4, iface, " static routes provided, but invalid"); return; } @@ -305,11 +306,11 @@ process_classful_routes(const char *iface, guint32 rt_addr, rt_route; if (inet_pton(AF_INET, *s, &rt_addr) <= 0) { - _LOG2W(LOGD_DHCP, iface, "DHCP provided invalid static route address: '%s'", *s); + _LOG2W(LOGD_DHCP4, iface, "DHCP provided invalid static route address: '%s'", *s); continue; } if (inet_pton(AF_INET, *(s + 1), &rt_route) <= 0) { - _LOG2W(LOGD_DHCP, iface, "DHCP provided invalid static route gateway: '%s'", *(s + 1)); + _LOG2W(LOGD_DHCP4, iface, "DHCP provided invalid static route gateway: '%s'", *(s + 1)); continue; } @@ -340,7 +341,7 @@ process_classful_routes(const char *iface, nm_l3_config_data_add_route_4(l3cd, &route); - _LOG2I(LOGD_DHCP, + _LOG2I(LOGD_DHCP4, iface, " static route %s", nm_platform_ip4_route_to_string(&route, sbuf, sizeof(sbuf))); @@ -352,6 +353,7 @@ process_domain_search(int addr_family, const char *iface, const char *str, NML3C { gs_free const char **searches = NULL; gs_free char *unescaped = NULL; + NMLogDomain logd = NM_IS_IPv4(addr_family) ? LOGD_DHCP4 : LOGD_DHCP6; const char **s; char *p; int i; @@ -373,13 +375,13 @@ process_domain_search(int addr_family, const char *iface, const char *str, NML3C } while (*p++); if (strchr(unescaped, '\\')) { - _LOG2W(LOGD_DHCP, iface, " invalid domain search: '%s'", unescaped); + _LOG2W(logd, iface, " invalid domain search: '%s'", unescaped); return; } searches = nm_strsplit_set(unescaped, " "); for (s = searches; searches && *s; s++) { - _LOG2I(LOGD_DHCP, iface, " domain search '%s'", *s); + _LOG2I(logd, iface, " domain search '%s'", *s); nm_l3_config_data_add_search(l3cd, addr_family, *s); } } @@ -414,18 +416,21 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, str = g_hash_table_lookup(options, "ip_address"); if (!str || !nm_utils_parse_inaddr_bin(AF_INET, str, NULL, &addr)) return NULL; + if (addr == INADDR_ANY) + return NULL; _LOG2I(LOGD_DHCP4, iface, " address %s", str); str = g_hash_table_lookup(options, "subnet_mask"); if (str && (inet_pton(AF_INET, str, &tmp_addr) > 0)) { - plen = nm_utils_ip4_netmask_to_prefix(tmp_addr); + plen = _nm_utils_ip4_netmask_to_prefix(tmp_addr); _LOG2I(LOGD_DHCP4, iface, " plen %d (%s)", plen, str); } else { /* Get default netmask for the IP according to appropriate class. */ plen = _nm_utils_ip4_get_default_prefix(addr); _LOG2I(LOGD_DHCP4, iface, " plen %d (default)", plen); } + nm_platform_ip4_address_set_addr(&address, addr, plen); /* Routes: if the server returns classless static routes, we MUST ignore @@ -588,7 +593,7 @@ nm_dhcp_utils_ip6_prefix_from_options(GHashTable *options) { gs_strfreev char **split_addr = NULL; NMPlatformIP6Address address = { - 0, + 0, }; struct in6_addr tmp_addr; char *str = NULL; @@ -822,26 +827,6 @@ nm_dhcp_utils_get_leasefile_path(int addr_family, return FALSE; } -char * -nm_dhcp_utils_get_dhcp6_event_id(GHashTable *lease) -{ - const char *start; - const char *iaid; - - if (!lease) - return NULL; - - iaid = g_hash_table_lookup(lease, "iaid"); - if (!iaid) - return NULL; - - start = g_hash_table_lookup(lease, "life_starts"); - if (!start) - return NULL; - - return g_strdup_printf("%s|%s", iaid, start); -} - gboolean nm_dhcp_utils_merge_new_dhcp6_lease(const NML3ConfigData *l3cd_old, const NML3ConfigData *l3cd_new, @@ -902,28 +887,75 @@ nm_dhcp_utils_merge_new_dhcp6_lease(const NML3ConfigData *l3cd_old, /*****************************************************************************/ +void +nm_dhcp_lease_log_invalid_option(const char *iface, + int addr_family, + guint option, + const char *fmt, + ...) +{ + const char *option_name; + gs_free char *msg = NULL; + va_list ap; + + option_name = nm_dhcp_option_request_string(addr_family, option); + + va_start(ap, fmt); + msg = g_strdup_vprintf(fmt, ap); + va_end(ap); + + _LOG2I(NM_IS_IPv4(addr_family) ? LOGD_DHCP4 : LOGD_DHCP6, + iface, + "error parsing DHCP option %d (%s)%s%s", + option, + option_name, + msg ? ": " : "", + msg ?: ""); +} + gboolean -nm_dhcp_lease_data_parse_u16(const guint8 *data, gsize n_data, uint16_t *out_val) +nm_dhcp_lease_data_parse_u16(const guint8 *data, + gsize n_data, + uint16_t *out_val, + const char *iface, + int addr_family, + guint option) { - if (n_data != 2) + if (n_data != 2) { + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "invalid option length %lu", + (unsigned long) n_data); return FALSE; + } *out_val = unaligned_read_be16(data); return TRUE; } gboolean -nm_dhcp_lease_data_parse_mtu(const guint8 *data, gsize n_data, uint16_t *out_val) +nm_dhcp_lease_data_parse_mtu(const guint8 *data, + gsize n_data, + uint16_t *out_val, + const char *iface, + int addr_family, + guint option) { uint16_t mtu; - if (!nm_dhcp_lease_data_parse_u16(data, n_data, &mtu)) + if (!nm_dhcp_lease_data_parse_u16(data, n_data, &mtu, iface, addr_family, option)) return FALSE; if (mtu < 68) { /* https://tools.ietf.org/html/rfc2132#section-5.1: * * The minimum legal value for the MTU is 68. */ + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "value %u is smaller than 68", + mtu); return FALSE; } @@ -932,7 +964,12 @@ nm_dhcp_lease_data_parse_mtu(const guint8 *data, gsize n_data, uint16_t *out_val } gboolean -nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_len) +nm_dhcp_lease_data_parse_cstr(const guint8 *data, + gsize n_data, + gsize *out_new_len, + const char *iface, + int addr_family, + guint option) { /* WARNING: this function only validates that the string does not contain * NUL characters (and ignores trailing NULs). It does not check character @@ -947,6 +984,10 @@ nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_l * * https://tools.ietf.org/html/rfc2132#section-2 * https://github.com/systemd/systemd/issues/1337 */ + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "string contains embedded NUL"); return FALSE; } } @@ -956,32 +997,47 @@ nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_l } char * -nm_dhcp_lease_data_parse_domain_validate(const char *str) +nm_dhcp_lease_data_parse_domain_validate(const char *str, + const char *iface, + int addr_family, + guint option) { gs_free char *s = NULL; s = nm_sd_dns_name_normalize(str); if (!s) - return NULL; + goto err; if (nm_str_is_empty(s) || (s[0] == '.' && s[1] == '\0')) { /* root domains are not allowed. */ - return NULL; + goto err; } if (nm_utils_is_localhost(s)) - return NULL; + goto err; if (!g_utf8_validate(s, -1, NULL)) { /* the result must be valid UTF-8. */ - return NULL; + goto err; } return g_steal_pointer(&s); +err: + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "'%s' is not a valid DNS domain", + str); + return NULL; } gboolean -nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val) +nm_dhcp_lease_data_parse_domain(const guint8 *data, + gsize n_data, + char **out_val, + const char *iface, + int addr_family, + guint option) { gs_free char *str1_free = NULL; const char *str1; @@ -989,7 +1045,7 @@ nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val /* this is mostly the same as systemd's lease_parse_domain(). */ - if (!nm_dhcp_lease_data_parse_cstr(data, n_data, &n_data)) + if (!nm_dhcp_lease_data_parse_cstr(data, n_data, &n_data, iface, addr_family, option)) return FALSE; if (n_data == 0) { @@ -1001,12 +1057,13 @@ nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val * * Note that this is *after* we potentially stripped trailing NULs. */ + nm_dhcp_lease_log_invalid_option(iface, addr_family, option, "empty value"); return FALSE; } str1 = nm_strndup_a(300, (char *) data, n_data, &str1_free); - s = nm_dhcp_lease_data_parse_domain_validate(str1); + s = nm_dhcp_lease_data_parse_domain_validate(str1, iface, addr_family, option); if (!s) return FALSE; @@ -1015,7 +1072,11 @@ nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val } gboolean -nm_dhcp_lease_data_parse_in_addr(const guint8 *data, gsize n_data, in_addr_t *out_val) +nm_dhcp_lease_data_parse_in_addr(const guint8 *data, + gsize n_data, + in_addr_t *out_val, + const char *iface, + guint option) { /* - option 1, https://tools.ietf.org/html/rfc2132#section-3.3 * - option 28, https://tools.ietf.org/html/rfc2132#section-5.3 @@ -1025,8 +1086,14 @@ nm_dhcp_lease_data_parse_in_addr(const guint8 *data, gsize n_data, in_addr_t *ou * according to RFC 3396 section 7. Therefore, it's possible that a * option carrying a IPv4 address has a length > 4. */ - if (n_data < 4) + if (n_data < 4) { + nm_dhcp_lease_log_invalid_option(iface, + AF_INET, + option, + "invalid address length %lu", + (unsigned long) n_data); return FALSE; + } *out_val = unaligned_read_ne32(data); return TRUE; @@ -1069,7 +1136,8 @@ static char * lease_option_print_domain_name(const uint8_t *cache, size_t *n_cachep, const uint8_t **datap, - size_t *n_datap) + size_t *n_datap, + gboolean *invalid) { nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_40, FALSE); const uint8_t *domain; @@ -1080,6 +1148,8 @@ lease_option_print_domain_name(const uint8_t *cache, gboolean first = TRUE; uint8_t c; + NM_SET_OUT(invalid, FALSE); + /* * We are given two adjacent memory regions. The @cache contains alreday parsed * domain names, and the @datap contains the remaining data to parse. @@ -1096,8 +1166,10 @@ lease_option_print_domain_name(const uint8_t *cache, * Note, that each time a jump to an offset is performed, the size of the * cache shrinks, so this is guaranteed to terminate. */ - if (cache + n_cache != *datap) + if (cache + n_cache != *datap) { + NM_SET_OUT(invalid, TRUE); return NULL; + } for (;;) { if (!nm_dhcp_lease_data_consume(domainp, n_domainp, &c, sizeof(c))) @@ -1122,8 +1194,10 @@ lease_option_print_domain_name(const uint8_t *cache, else first = FALSE; - if (!lease_option_print_label(&sbuf, n_label, domainp, n_domainp)) + if (!lease_option_print_label(&sbuf, n_label, domainp, n_domainp)) { + NM_SET_OUT(invalid, TRUE); return NULL; + } break; } @@ -1136,13 +1210,17 @@ lease_option_print_domain_name(const uint8_t *cache, * two high bits are masked out. */ - if (!nm_dhcp_lease_data_consume(domainp, n_domainp, &c, sizeof(c))) + if (!nm_dhcp_lease_data_consume(domainp, n_domainp, &c, sizeof(c))) { + NM_SET_OUT(invalid, TRUE); return NULL; + } offset += c; - if (offset >= n_cache) + if (offset >= n_cache) { + NM_SET_OUT(invalid, TRUE); return NULL; + } domain = cache + offset; n_domain = n_cache - offset; @@ -1154,29 +1232,44 @@ lease_option_print_domain_name(const uint8_t *cache, break; } default: + NM_SET_OUT(invalid, TRUE); return NULL; } } } char ** -nm_dhcp_lease_data_parse_search_list(const guint8 *data, gsize n_data) +nm_dhcp_lease_data_parse_search_list(const guint8 *data, + gsize n_data, + const char *iface, + int addr_family, + guint option) { GPtrArray *array = NULL; const guint8 *cache = data; gsize n_cache = 0; + guint i = 0; for (;;) { gs_free char *s = NULL; - - s = lease_option_print_domain_name(cache, &n_cache, &data, &n_data); - if (!s) + gboolean invalid; + + s = lease_option_print_domain_name(cache, &n_cache, &data, &n_data, &invalid); + if (!s) { + if (iface && invalid) + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "search domain #%u is invalid", + i); break; + } if (!array) array = g_ptr_array_new(); g_ptr_array_add(array, g_steal_pointer(&s)); + i++; } if (!array) diff --git a/src/core/dhcp/nm-dhcp-utils.h b/src/core/dhcp/nm-dhcp-utils.h index bef50c52..00199b02 100644 --- a/src/core/dhcp/nm-dhcp-utils.h +++ b/src/core/dhcp/nm-dhcp-utils.h @@ -59,13 +59,48 @@ nm_dhcp_lease_data_consume_in_addr(const uint8_t **datap, size_t *n_datap, in_ad return nm_dhcp_lease_data_consume(datap, n_datap, addrp, sizeof(struct in_addr)); } -char *nm_dhcp_lease_data_parse_domain_validate(const char *str); - -gboolean nm_dhcp_lease_data_parse_u16(const guint8 *data, gsize n_data, guint16 *out_val); -gboolean nm_dhcp_lease_data_parse_mtu(const guint8 *data, gsize n_data, guint16 *out_val); -gboolean nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_len); -gboolean nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val); -gboolean nm_dhcp_lease_data_parse_in_addr(const guint8 *data, gsize n_data, in_addr_t *out_val); -char **nm_dhcp_lease_data_parse_search_list(const guint8 *data, gsize n_data); +void nm_dhcp_lease_log_invalid_option(const char *iface, + int addr_family, + guint option, + const char *fmt, + ...) G_GNUC_PRINTF(4, 5); +char *nm_dhcp_lease_data_parse_domain_validate(const char *str, + const char *iface, + int addr_family, + guint option); +gboolean nm_dhcp_lease_data_parse_u16(const guint8 *data, + gsize n_data, + guint16 *out_val, + const char *iface, + int addr_family, + guint option); +gboolean nm_dhcp_lease_data_parse_mtu(const guint8 *data, + gsize n_data, + guint16 *out_val, + const char *iface, + int addr_family, + guint option); +gboolean nm_dhcp_lease_data_parse_cstr(const guint8 *data, + gsize n_data, + gsize *out_new_len, + const char *iface, + int addr_family, + guint option); +gboolean nm_dhcp_lease_data_parse_domain(const guint8 *data, + gsize n_data, + char **out_val, + const char *iface, + int addr_family, + guint option); +gboolean nm_dhcp_lease_data_parse_in_addr(const guint8 *data, + gsize n_data, + in_addr_t *out_val, + const char *iface, + guint option); +char **nm_dhcp_lease_data_parse_search_list(const guint8 *data, + gsize n_data, + const char *iface, + int addr_family, + guint option); #endif /* __NETWORKMANAGER_DHCP_UTILS_H__ */ diff --git a/src/core/dhcp/tests/test-dhcp-utils.c b/src/core/dhcp/tests/test-dhcp-utils.c index f021b642..7597f516 100644 --- a/src/core/dhcp/tests/test-dhcp-utils.c +++ b/src/core/dhcp/tests/test-dhcp-utils.c @@ -196,16 +196,16 @@ test_parse_search_list(void) char **domains; data = (guint8[]){0x05, 'l', 'o', 'c', 'a', 'l', 0x00}; - domains = nm_dhcp_lease_data_parse_search_list(data, 7); + domains = nm_dhcp_lease_data_parse_search_list(data, 7, NULL, 0, 0); g_assert(domains); g_assert_cmpint(g_strv_length(domains), ==, 1); g_assert_cmpstr(domains[0], ==, "local"); g_strfreev(domains); data = (guint8[]){0x04, 't', 'e', 's', 't', 0x07, 'e', 'x', 'a', 'm', 'p', 'l', - 'e', 0x03, 'c', 'o', 'm', 0x00, 0xc0, 0x05, 0x03, 'a', 'b', 'c', - 0xc0, 0x0d, 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x00}; - domains = nm_dhcp_lease_data_parse_search_list(data, 34); + 'e', 0x03, 'c', 'o', 'm', 0x00, 0xc0, 0x05, 0x03, 'a', 'b', 'c', + 0xc0, 0x0d, 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x00}; + domains = nm_dhcp_lease_data_parse_search_list(data, 34, NULL, 0, 0); g_assert(domains); g_assert_cmpint(g_strv_length(domains), ==, 4); g_assert_cmpstr(domains[0], ==, "test.example.com"); @@ -220,7 +220,7 @@ test_parse_search_list(void) 'a', 'd', }; - domains = nm_dhcp_lease_data_parse_search_list(data, 4); + domains = nm_dhcp_lease_data_parse_search_list(data, 4, NULL, 0, 0); g_assert(!domains); data = (guint8[]){ @@ -235,7 +235,7 @@ test_parse_search_list(void) 'a', 'd', }; - domains = nm_dhcp_lease_data_parse_search_list(data, 10); + domains = nm_dhcp_lease_data_parse_search_list(data, 10, NULL, 0, 0); g_assert(domains); g_assert_cmpint(g_strv_length(domains), ==, 1); g_assert_cmpstr(domains[0], ==, "okay"); @@ -285,7 +285,7 @@ test_classless_static_routes_1(void) const char *expected_route2_dest = "10.0.0.0"; const char *expected_route2_gw = "10.17.66.41"; static const Option data[] = { - /* dhclient custom format */ + /* dhclient custom format */ {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41"}, {NULL, NULL}}; @@ -310,7 +310,7 @@ test_classless_static_routes_2(void) const char *expected_route2_dest = "10.0.0.0"; const char *expected_route2_gw = "10.17.66.41"; static const Option data[] = { - /* dhcpcd format */ + /* dhcpcd format */ {"classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41"}, {NULL, NULL}}; @@ -336,9 +336,9 @@ test_fedora_dhclient_classless_static_routes(void) const char *expected_route2_gw = "10.34.255.6"; const char *expected_gateway = "192.168.0.113"; static const Option data[] = { - /* Fedora dhclient format */ + /* Fedora dhclient format */ {"classless_static_routes", - "0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6"}, + "0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6"}, {NULL, NULL}}; options = fill_table(generic_options, NULL); @@ -362,7 +362,7 @@ test_dhclient_invalid_classless_routes_1(void) const char *expected_route1_dest = "192.168.10.0"; const char *expected_route1_gw = "192.168.1.1"; static const Option data[] = { - /* dhclient format */ + /* dhclient format */ {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41"}, {NULL, NULL}}; @@ -389,7 +389,7 @@ test_dhcpcd_invalid_classless_routes_1(void) const char *expected_route2_dest = "100.99.88.56"; const char *expected_route2_gw = "10.1.1.1"; static const Option data[] = { - /* dhcpcd format */ + /* dhcpcd format */ {"classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41"}, {NULL, NULL}}; @@ -419,8 +419,8 @@ test_dhclient_invalid_classless_routes_2(void) const char *expected_route2_dest = "100.99.88.56"; const char *expected_route2_gw = "10.1.1.1"; static const Option data[] = { - {"rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1"}, - {NULL, NULL}}; + {"rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -448,8 +448,8 @@ test_dhcpcd_invalid_classless_routes_2(void) const char *expected_route2_dest = "100.99.88.56"; const char *expected_route2_gw = "10.1.1.1"; static const Option data[] = { - {"classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1"}, - {NULL, NULL}}; + {"classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -477,8 +477,8 @@ test_dhclient_invalid_classless_routes_3(void) const char *expected_route1_dest = "192.168.10.0"; const char *expected_route1_gw = "192.168.1.1"; static const Option data[] = { - {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41"}, - {NULL, NULL}}; + {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -501,8 +501,8 @@ test_dhcpcd_invalid_classless_routes_3(void) const char *expected_route1_dest = "192.168.10.0"; const char *expected_route1_gw = "192.168.1.1"; static Option data[] = { - {"classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41"}, - {NULL, NULL}}; + {"classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -526,8 +526,8 @@ test_dhclient_gw_in_classless_routes(void) const char *expected_route1_gw = "192.168.1.1"; const char *expected_gateway = "192.2.3.4"; static Option data[] = { - {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4"}, - {NULL, NULL}}; + {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -550,8 +550,8 @@ test_dhcpcd_gw_in_classless_routes(void) const char *expected_route1_gw = "192.168.1.1"; const char *expected_gateway = "192.2.3.4"; static Option data[] = { - {"classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4"}, - {NULL, NULL}}; + {"classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c index 43426882..7d0f0490 100644 --- a/src/core/dns/nm-dns-dnsmasq.c +++ b/src/core/dns/nm-dns-dnsmasq.c @@ -678,19 +678,23 @@ typedef struct { char *name_owner; + GSource *main_timeout_source; + GSource *burst_retry_timeout_source; + gint64 burst_start_at; GPid process_pid; guint name_owner_changed_id; - guint main_timeout_id; - - guint burst_retry_timeout_id; guint8 burst_count; bool is_stopped : 1; + bool set_server_ex_args_dirty : 1; + + bool update_pending : 1; + } NMDnsDnsmasqPrivate; struct _NMDnsDnsmasq { @@ -704,7 +708,8 @@ struct _NMDnsDnsmasqClass { G_DEFINE_TYPE(NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) -#define NM_DNS_DNSMASQ_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsDnsmasq, NM_IS_DNS_DNSMASQ) +#define NM_DNS_DNSMASQ_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDnsDnsmasq, NM_IS_DNS_DNSMASQ, NMDnsPlugin) /*****************************************************************************/ @@ -717,6 +722,55 @@ static gboolean start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError * /*****************************************************************************/ +static gboolean +_update_pending_detect(NMDnsDnsmasq *self) +{ + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); + + if (priv->is_stopped) + return FALSE; + if (priv->main_timeout_source) { + /* we are waiting for dnsmasq to start. */ + return TRUE; + } + if (priv->update_cancellable) { + /* An update is in progress. Busy. */ + return TRUE; + } + if (priv->set_server_ex_args_dirty) { + /* the args just changed and were not yet sent. Busy. */ + return TRUE; + } + + return FALSE; +} + +static void +_update_pending_maybe_changed(NMDnsDnsmasq *self) +{ + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); + gboolean update_pending; + + update_pending = _update_pending_detect(self); + if (priv->update_pending == update_pending) + return; + + priv->update_pending = update_pending; + _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self)); +} + +static gboolean +get_update_pending(NMDnsPlugin *plugin) +{ + NMDnsDnsmasq *self = NM_DNS_DNSMASQ(plugin); + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); + + nm_assert(priv->update_pending == _update_pending_detect(self)); + return priv->update_pending; +} + +/*****************************************************************************/ + static void add_dnsmasq_nameserver(NMDnsDnsmasq *self, GVariantBuilder *servers, @@ -871,6 +925,7 @@ static void dnsmasq_update_done(GObject *source_object, GAsyncResult *res, gpointer user_data) { NMDnsDnsmasq *self; + NMDnsDnsmasqPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *response = NULL; @@ -880,10 +935,16 @@ dnsmasq_update_done(GObject *source_object, GAsyncResult *res, gpointer user_dat return; self = user_data; + priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); + + nm_clear_g_cancellable(&priv->update_cancellable); + if (!response) _LOGW("dnsmasq update failed: %s", error->message); else _LOGD("dnsmasq update successful"); + + _update_pending_maybe_changed(self); } static void @@ -899,6 +960,8 @@ send_dnsmasq_update(NMDnsDnsmasq *self) nm_clear_g_cancellable(&priv->update_cancellable); priv->update_cancellable = g_cancellable_new(); + priv->set_server_ex_args_dirty = FALSE; + g_dbus_connection_call(priv->dbus_connection, priv->name_owner, DNSMASQ_DBUS_PATH, @@ -911,6 +974,8 @@ send_dnsmasq_update(NMDnsDnsmasq *self) priv->update_cancellable, dnsmasq_update_done, self); + + _update_pending_maybe_changed(self); } /*****************************************************************************/ @@ -928,17 +993,19 @@ _main_cleanup(NMDnsDnsmasq *self, gboolean emit_failed) nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); - nm_clear_g_source(&priv->main_timeout_id); + nm_clear_g_source_inst(&priv->main_timeout_source); nm_clear_g_cancellable(&priv->update_cancellable); /* cancelling the main_cancellable will also cause _gl_pid_spawn*() to terminate the * process in the background. */ nm_clear_g_cancellable(&priv->main_cancellable); - if (!priv->is_stopped && priv->burst_retry_timeout_id == 0) { + if (!priv->is_stopped && !priv->burst_retry_timeout_source) { start_dnsmasq(self, FALSE, NULL); send_dnsmasq_update(self); } + + _update_pending_maybe_changed(self); } static void @@ -961,8 +1028,10 @@ name_owner_changed(NMDnsDnsmasq *self, const char *name_owner) } _LOGT("D-Bus name for dnsmasq got owner %s", name_owner); - nm_clear_g_source(&priv->main_timeout_id); + nm_clear_g_source_inst(&priv->main_timeout_source); send_dnsmasq_update(self); + + _update_pending_maybe_changed(self); } static void @@ -1047,11 +1116,11 @@ _burst_retry_timeout_cb(gpointer user_data) NMDnsDnsmasq *self = user_data; NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - priv->burst_retry_timeout_id = 0; + nm_clear_g_source_inst(&priv->burst_retry_timeout_source); start_dnsmasq(self, TRUE, NULL); send_dnsmasq_update(self); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } static gboolean @@ -1090,33 +1159,35 @@ start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError **error) || priv->burst_start_at + RATELIMIT_INTERVAL_MSEC <= now) { priv->burst_start_at = now; priv->burst_count = 1; - nm_clear_g_source(&priv->burst_retry_timeout_id); + nm_clear_g_source_inst(&priv->burst_retry_timeout_source); _LOGT("rate-limit: start burst interval of %d seconds %s", RATELIMIT_INTERVAL_MSEC / 1000, force_start ? " (force)" : ""); } else if (priv->burst_count < RATELIMIT_BURST) { - nm_assert(priv->burst_retry_timeout_id == 0); + nm_assert(!priv->burst_retry_timeout_source); priv->burst_count++; _LOGT("rate-limit: %u try within burst interval of %d seconds", (guint) priv->burst_count, RATELIMIT_INTERVAL_MSEC / 1000); } else { - if (priv->burst_retry_timeout_id == 0) { + if (!priv->burst_retry_timeout_source) { _LOGW("dnsmasq dies and gets respawned too quickly. Back off. Something is very wrong"); - priv->burst_retry_timeout_id = - g_timeout_add_seconds((2 * RATELIMIT_INTERVAL_MSEC) / 1000, - _burst_retry_timeout_cb, - self); + priv->burst_retry_timeout_source = + nm_g_timeout_add_seconds_source((2 * RATELIMIT_INTERVAL_MSEC) / 1000, + _burst_retry_timeout_cb, + self); } else _LOGT("rate-limit: currently rate-limited from restart"); return TRUE; } - priv->main_timeout_id = g_timeout_add(10000, spawn_timeout_cb, self); + priv->main_timeout_source = nm_g_timeout_add_source(10000, spawn_timeout_cb, self); priv->main_cancellable = g_cancellable_new(); _gl_pid_spawn(dm_binary, priv->main_cancellable, spawn_notify, self); + + _update_pending_maybe_changed(self); return TRUE; } @@ -1136,8 +1207,11 @@ update(NMDnsPlugin *plugin, nm_clear_pointer(&priv->set_server_ex_args, g_variant_unref); priv->set_server_ex_args = g_variant_ref_sink(create_update_args(self, global_config, ip_data_lst_head, hostdomain)); + priv->set_server_ex_args_dirty = TRUE; send_dnsmasq_update(self); + + _update_pending_maybe_changed(self); return TRUE; } @@ -1151,11 +1225,13 @@ stop(NMDnsPlugin *plugin) priv->is_stopped = TRUE; priv->burst_start_at = 0; - nm_clear_g_source(&priv->burst_retry_timeout_id); + nm_clear_g_source_inst(&priv->burst_retry_timeout_source); /* Cancelling the cancellable will also terminate the * process (in the background). */ _main_cleanup(self, FALSE); + + _update_pending_maybe_changed(self); } /*****************************************************************************/ @@ -1178,7 +1254,7 @@ dispose(GObject *object) priv->is_stopped = TRUE; - nm_clear_g_source(&priv->burst_retry_timeout_id); + nm_clear_g_source_inst(&priv->burst_retry_timeout_source); _main_cleanup(self, FALSE); @@ -1197,8 +1273,9 @@ nm_dns_dnsmasq_class_init(NMDnsDnsmasqClass *dns_class) object_class->dispose = dispose; - plugin_class->plugin_name = "dnsmasq"; - plugin_class->is_caching = TRUE; - plugin_class->stop = stop; - plugin_class->update = update; + plugin_class->plugin_name = "dnsmasq"; + plugin_class->is_caching = TRUE; + plugin_class->stop = stop; + plugin_class->update = update; + plugin_class->get_update_pending = get_update_pending; } diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c index 566f3d66..1e54452a 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -26,7 +26,6 @@ #include "libnm-core-intern/nm-core-internal.h" #include "libnm-glib-aux/nm-str-buf.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "NetworkManagerUtils.h" #include "devices/nm-device.h" @@ -35,7 +34,6 @@ #include "nm-dns-dnsmasq.h" #include "nm-dns-plugin.h" #include "nm-dns-systemd-resolved.h" -#include "nm-dns-unbound.h" #include "nm-ip-config.h" #include "nm-l3-config-data.h" #include "nm-manager.h" @@ -57,6 +55,8 @@ #define HAS_NETCONFIG 1 #endif +#define UPDATE_PENDING_UNBLOCK_TIMEOUT_MSEC 5000 + /*****************************************************************************/ typedef enum { SR_SUCCESS, SR_NOTFOUND, SR_ERROR } SpawnResult; @@ -78,7 +78,11 @@ enum { LAST_SIGNAL }; -NM_GOBJECT_PROPERTIES_DEFINE(NMDnsManager, PROP_MODE, PROP_RC_MANAGER, PROP_CONFIGURATION, ); +NM_GOBJECT_PROPERTIES_DEFINE(NMDnsManager, + PROP_MODE, + PROP_RC_MANAGER, + PROP_CONFIGURATION, + PROP_UPDATE_PENDING, ); static guint signals[LAST_SIGNAL] = {0}; @@ -89,6 +93,11 @@ typedef struct { CList ip_data_lst_head; GVariant *config_variant; + /* A DNS plugin should not be marked as pending indefinitely. + * We are only blocked if "update_pending" is TRUE and we have + * "update_pending_unblock" timer ticking. */ + GSource *update_pending_unblock; + bool ip_data_lst_need_sort : 1; bool configs_lst_need_sort : 1; @@ -98,6 +107,8 @@ typedef struct { bool config_changed : 1; + bool update_pending : 1; + char *hostdomain; guint updates_queue; @@ -109,6 +120,9 @@ typedef struct { NMDnsPlugin *sd_resolve_plugin; NMDnsPlugin *plugin; + gulong update_changed_signal_id_sd; + gulong update_changed_signal_id; + NMConfig *config; struct { @@ -137,28 +151,23 @@ NM_DEFINE_SINGLETON_GETTER(NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER #define _NMLOG_PREFIX_NAME "dns-mgr" #define _NMLOG_DOMAIN LOGD_DNS -#define _NMLOG(level, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - char __prefix[20]; \ - const NMDnsManager *const __self = (self); \ - \ - _nm_log(__level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - ((!__self || __self == singleton_instance) \ - ? "" \ - : nm_sprintf_buf(__prefix, "[%p]", __self)) \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ +#define _NMLOG(level, ...) \ + G_STMT_START \ + { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ + _nm_unused const NMDnsManager *const __self = (self); \ + \ + _nm_log(__level, \ + _NMLOG_DOMAIN, \ + 0, \ + NULL, \ + NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ G_STMT_END /*****************************************************************************/ @@ -207,6 +216,85 @@ static NM_UTILS_LOOKUP_STR_DEFINE( /*****************************************************************************/ +static gboolean +_update_pending_detect(NMDnsManager *self) +{ + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); + + if (priv->plugin && nm_dns_plugin_get_update_pending(priv->plugin)) + return TRUE; + if (priv->sd_resolve_plugin && nm_dns_plugin_get_update_pending(priv->sd_resolve_plugin)) + return TRUE; + return FALSE; +} + +static gboolean +_update_pending_unblock_cb(gpointer user_data) +{ + NMDnsManager *self = user_data; + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); + + nm_assert(priv->update_pending); + nm_assert(priv->update_pending_unblock); + nm_assert(_update_pending_detect(self)); + + nm_clear_g_source_inst(&priv->update_pending_unblock); + + _LOGW( + "update-pending changed: DNS plugin did not become ready again. Assume something is wrong"); + + _notify(self, PROP_UPDATE_PENDING); + return G_SOURCE_CONTINUE; +} + +static void +_update_pending_maybe_changed(NMDnsManager *self) +{ + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); + gboolean update_pending; + + update_pending = _update_pending_detect(self); + if (priv->update_pending == update_pending) + return; + + if (update_pending) { + nm_assert(!priv->update_pending_unblock); + priv->update_pending_unblock = nm_g_timeout_add_source(UPDATE_PENDING_UNBLOCK_TIMEOUT_MSEC, + _update_pending_unblock_cb, + self); + } else + nm_clear_g_source_inst(&priv->update_pending_unblock); + + priv->update_pending = update_pending; + _LOGD("update-pending changed: %spending", update_pending ? "" : "not "); + _notify(self, PROP_UPDATE_PENDING); +} + +static void +_update_pending_changed_cb(NMDnsPlugin *plugin, gboolean update_pending, NMDnsManager *self) +{ + _update_pending_maybe_changed(self); +} + +gboolean +nm_dns_manager_get_update_pending(NMDnsManager *self) +{ + NMDnsManagerPrivate *priv; + + g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE); + + priv = NM_DNS_MANAGER_GET_PRIVATE(self); + nm_assert(priv->update_pending == _update_pending_detect(self)); + nm_assert(priv->update_pending || !priv->update_pending_unblock); + + /* update-pending can only be TRUE for a certain time (before we assume + * something is really wrong with the plugin). That is, as long as + * update_pending_unblock is ticking. */ + return !!priv->update_pending_unblock; +} + +/*****************************************************************************/ + static int _dns_config_ip_data_get_dns_priority1(const NML3ConfigData *l3cd, int addr_family) { @@ -2015,7 +2103,7 @@ nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean s domain = hostname; } - if (!nm_sd_hostname_is_valid(domain, FALSE)) + if (!nm_hostname_is_valid(domain, FALSE)) domain = NULL; } } @@ -2120,6 +2208,7 @@ _clear_plugin(NMDnsManager *self) nm_clear_g_source(&priv->plugin_ratelimit.timer); if (priv->plugin) { + nm_clear_g_signal_handler(priv->plugin, &priv->update_changed_signal_id); nm_dns_plugin_stop(priv->plugin); g_clear_object(&priv->plugin); return TRUE; @@ -2127,6 +2216,20 @@ _clear_plugin(NMDnsManager *self) return FALSE; } +static gboolean +_clear_sd_resolved_plugin(NMDnsManager *self) +{ + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); + + if (priv->sd_resolve_plugin) { + nm_clear_g_signal_handler(priv->sd_resolve_plugin, &priv->update_changed_signal_id_sd); + nm_dns_plugin_stop(priv->sd_resolve_plugin); + g_clear_object(&priv->sd_resolve_plugin); + return TRUE; + } + return FALSE; +} + static NMDnsManagerResolvConfManager _check_resconf_immutable(NMDnsManagerResolvConfManager rc_manager) { @@ -2313,16 +2416,14 @@ again: priv->plugin = nm_dns_dnsmasq_new(); plugin_changed = TRUE; } - } else if (nm_streq0(mode, "unbound")) { - if (force_reload_plugin || !NM_IS_DNS_UNBOUND(priv->plugin)) { - _clear_plugin(self); - priv->plugin = nm_dns_unbound_new(); - plugin_changed = TRUE; - } } else { if (!NM_IN_STRSET(mode, "none", "default")) { - if (mode) - _LOGW("init: unknown dns mode '%s'", mode); + if (mode) { + if (nm_streq(mode, "unbound")) + _LOGW("init: ns mode 'unbound' was removed. Update your configuration"); + else + _LOGW("init: unknown dns mode '%s'", mode); + } mode = "default"; } if (_clear_plugin(self)) @@ -2359,7 +2460,7 @@ again: priv->sd_resolve_plugin = nm_dns_systemd_resolved_new(); systemd_resolved_changed = TRUE; } - } else if (nm_clear_g_object(&priv->sd_resolve_plugin)) + } else if (_clear_sd_resolved_plugin(self)) systemd_resolved_changed = TRUE; g_object_freeze_notify(G_OBJECT(self)); @@ -2390,6 +2491,23 @@ again: "")); } + if (plugin_changed && priv->plugin && priv->update_changed_signal_id == 0) { + priv->update_changed_signal_id = g_signal_connect(priv->plugin, + NM_DNS_PLUGIN_UPDATE_PENDING_CHANGED, + G_CALLBACK(_update_pending_changed_cb), + self); + } + + if (systemd_resolved_changed && priv->sd_resolve_plugin + && priv->update_changed_signal_id_sd == 0) { + priv->update_changed_signal_id_sd = g_signal_connect(priv->sd_resolve_plugin, + NM_DNS_PLUGIN_UPDATE_PENDING_CHANGED, + G_CALLBACK(_update_pending_changed_cb), + self); + } + + _update_pending_maybe_changed(self); + g_object_thaw_notify(G_OBJECT(self)); } @@ -2594,6 +2712,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_CONFIGURATION: g_value_set_variant(value, _get_config_variant(self)); break; + case PROP_UPDATE_PENDING: + g_value_set_boolean(value, nm_dns_manager_get_update_pending(self)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -2641,9 +2762,11 @@ dispose(GObject *object) if (priv->config) g_signal_handlers_disconnect_by_func(priv->config, config_changed_cb, self); - g_clear_object(&priv->sd_resolve_plugin); + _clear_sd_resolved_plugin(self); _clear_plugin(self); + nm_clear_g_source_inst(&priv->update_pending_unblock); + c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_data_lst_head, ip_data_lst) _dns_config_ip_data_free(ip_data); @@ -2719,6 +2842,13 @@ nm_dns_manager_class_init(NMDnsManagerClass *klass) NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_UPDATE_PENDING] = + g_param_spec_boolean(NM_DNS_MANAGER_UPDATE_PENDING, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[CONFIG_CHANGED] = g_signal_new(NM_DNS_MANAGER_CONFIG_CHANGED, diff --git a/src/core/dns/nm-dns-manager.h b/src/core/dns/nm-dns-manager.h index c30d4b3a..210f9f6c 100644 --- a/src/core/dns/nm-dns-manager.h +++ b/src/core/dns/nm-dns-manager.h @@ -80,9 +80,10 @@ typedef struct _NMDnsConfigData { (G_TYPE_INSTANCE_GET_CLASS((o), NM_TYPE_DNS_MANAGER, NMDnsManagerClass)) /* properties */ -#define NM_DNS_MANAGER_MODE "mode" -#define NM_DNS_MANAGER_RC_MANAGER "rc-manager" -#define NM_DNS_MANAGER_CONFIGURATION "configuration" +#define NM_DNS_MANAGER_MODE "mode" +#define NM_DNS_MANAGER_RC_MANAGER "rc-manager" +#define NM_DNS_MANAGER_CONFIGURATION "configuration" +#define NM_DNS_MANAGER_UPDATE_PENDING "update-pending" /* internal signals */ #define NM_DNS_MANAGER_CONFIG_CHANGED "config-changed" @@ -149,6 +150,8 @@ void nm_dns_manager_stop(NMDnsManager *self); NMDnsPlugin *nm_dns_manager_get_systemd_resolved(NMDnsManager *self); +gboolean nm_dns_manager_get_update_pending(NMDnsManager *self); + /*****************************************************************************/ char *nmtst_dns_create_resolv_conf(const char *const *searches, diff --git a/src/core/dns/nm-dns-plugin.c b/src/core/dns/nm-dns-plugin.c index 847d7839..41a0dbc1 100644 --- a/src/core/dns/nm-dns-plugin.c +++ b/src/core/dns/nm-dns-plugin.c @@ -17,11 +17,16 @@ /*****************************************************************************/ +enum { + UPDATE_PENDING_CHANGED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = {0}; + typedef struct _NMDnsPluginPrivate { - GPid pid; - guint watch_id; - char *progname; - char *pidfile; + bool update_pending_inited : 1; + bool update_pending : 1; } NMDnsPluginPrivate; G_DEFINE_ABSTRACT_TYPE(NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT) @@ -32,26 +37,29 @@ G_DEFINE_ABSTRACT_TYPE(NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT) #define _NMLOG_PREFIX_NAME "dns-plugin" #define _NMLOG_DOMAIN LOGD_DNS -#define _NMLOG(level, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - char __prefix[20]; \ - const NMDnsPlugin *const __self = (self); \ - \ - _nm_log(__level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - (!__self ? "" : nm_sprintf_buf(__prefix, "[%p]", __self)) \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ +#define _NMLOG(level, ...) \ + G_STMT_START \ + { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ + char __prefix[20]; \ + const NMDnsPlugin *const __self = (self); \ + \ + _nm_log(__level, \ + _NMLOG_DOMAIN, \ + 0, \ + NULL, \ + NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + (!__self ? "" \ + : nm_sprintf_buf(__prefix, \ + "[" NM_HASH_OBFUSCATE_PTR_FMT "]", \ + NM_HASH_OBFUSCATE_PTR( \ + __self))) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ G_STMT_END /*****************************************************************************/ @@ -104,10 +112,109 @@ nm_dns_plugin_stop(NMDnsPlugin *self) /*****************************************************************************/ +static gboolean +_get_update_pending(NMDnsPlugin *self) +{ + NMDnsPluginClass *klass; + + nm_assert(NM_IS_DNS_PLUGIN(self)); + + klass = NM_DNS_PLUGIN_GET_CLASS(self); + if (klass->get_update_pending) { + if (klass->get_update_pending(self)) + return TRUE; + } + return FALSE; +} + +gboolean +nm_dns_plugin_get_update_pending(NMDnsPlugin *self) +{ + NMDnsPluginPrivate *priv; + + g_return_val_if_fail(NM_IS_DNS_PLUGIN(self), FALSE); + + priv = NM_DNS_PLUGIN_GET_PRIVATE(self); + + /* We cache the boolean and rely on the subclass to call + * _nm_dns_plugin_update_pending_maybe_changed(). The subclass + * anyway must get it right to notify us when the value (maybe) + * changes. By caching the value, the subclass is free to notify + * even if the value did not actually change. + * + * Also, this allows the base implementation to combine multiple + * sources/reasons (if we need that in the future). */ + + if (!priv->update_pending_inited) { + priv->update_pending_inited = TRUE; + priv->update_pending = _get_update_pending(self); + _LOGD("[%s] update-pending changed (%spending)", + nm_dns_plugin_get_name(self), + priv->update_pending ? "" : "not "); + } else + nm_assert(priv->update_pending == _get_update_pending(self)); + + return priv->update_pending; +} + +void +_nm_dns_plugin_update_pending_maybe_changed(NMDnsPlugin *self) +{ + NMDnsPluginPrivate *priv; + gboolean v; + + g_return_if_fail(NM_IS_DNS_PLUGIN(self)); + + priv = NM_DNS_PLUGIN_GET_PRIVATE(self); + + v = _get_update_pending(self); + + if (!priv->update_pending_inited) + priv->update_pending_inited = TRUE; + else if (priv->update_pending == v) + return; + + priv->update_pending = v; + + _LOGD("[%s] update-pending changed (%spending)", + nm_dns_plugin_get_name(self), + priv->update_pending ? "" : "not "); + + g_signal_emit(self, signals[UPDATE_PENDING_CHANGED], 0, (gboolean) priv->update_pending); +} + +/*****************************************************************************/ + static void nm_dns_plugin_init(NMDnsPlugin *self) -{} +{ + NMDnsPluginPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_DNS_PLUGIN, NMDnsPluginPrivate); + + self->_priv = priv; + + nm_assert(priv->update_pending_inited == FALSE); + nm_assert(priv->update_pending == FALSE); + + nm_shutdown_wait_obj_register_object(self, "dns-plugin"); +} static void -nm_dns_plugin_class_init(NMDnsPluginClass *plugin_class) -{} +nm_dns_plugin_class_init(NMDnsPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(object_class, sizeof(NMDnsPluginPrivate)); + + signals[UPDATE_PENDING_CHANGED] = g_signal_new(NM_DNS_PLUGIN_UPDATE_PENDING_CHANGED, + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); +} diff --git a/src/core/dns/nm-dns-plugin.h b/src/core/dns/nm-dns-plugin.h index f9c424ab..24d6083b 100644 --- a/src/core/dns/nm-dns-plugin.h +++ b/src/core/dns/nm-dns-plugin.h @@ -19,8 +19,13 @@ #define NM_DNS_PLUGIN_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass)) +#define NM_DNS_PLUGIN_UPDATE_PENDING_CHANGED "update-pending-changed" + +struct _NMDnsPluginPrivate; + typedef struct { - GObject parent; + GObject parent; + struct _NMDnsPluginPrivate *_priv; } NMDnsPlugin; typedef struct { @@ -39,6 +44,8 @@ typedef struct { void (*stop)(NMDnsPlugin *self); + gboolean (*get_update_pending)(NMDnsPlugin *self); + const char *plugin_name; /* Types should set to TRUE if they start a local caching nameserver @@ -63,4 +70,8 @@ gboolean nm_dns_plugin_update(NMDnsPlugin *self, void nm_dns_plugin_stop(NMDnsPlugin *self); +gboolean nm_dns_plugin_get_update_pending(NMDnsPlugin *self); + +void _nm_dns_plugin_update_pending_maybe_changed(NMDnsPlugin *self); + #endif /* __NM_DNS_PLUGIN_H__ */ diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index c4993884..e0b94647 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -40,8 +40,8 @@ static const char *const DBUS_OP_SET_LINK_DNS_OVER_TLS = "SetLinkDNSOverTLS"; /*****************************************************************************/ typedef struct { - int ifindex; - CList configs_lst_head; + int ifindex; + GPtrArray *ip_data_list; } InterfaceConfig; typedef struct { @@ -50,6 +50,7 @@ typedef struct { GVariant *argument; NMDnsSystemdResolved *self; int ifindex; + int ref_count; } RequestItem; struct _NMDnsSystemdResolvedResolveHandle { @@ -77,15 +78,18 @@ typedef struct { GDBusConnection *dbus_connection; GHashTable *dirty_interfaces; GCancellable *cancellable; - GSource *try_start_timeout_source; + GCancellable *service_start_cancellable; CList request_queue_lst_head; char *dbus_owner; CList handle_lst_head; guint name_owner_changed_id; + guint n_pending; bool send_updates_warn_ratelimited : 1; bool try_start_blocked : 1; + bool stopped : 1; bool dbus_initied : 1; bool send_updates_waiting : 1; + bool update_pending : 1; /* These two variables ensure that the log is not spammed with * API (not) supported messages. * They can be removed when no distro uses systemd-resolved < v240 anymore @@ -106,7 +110,7 @@ struct _NMDnsSystemdResolvedClass { G_DEFINE_TYPE(NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN) #define NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self) \ - _NM_GET_PRIVATE(self, NMDnsSystemdResolved, NM_IS_DNS_SYSTEMD_RESOLVED) + _NM_GET_PRIVATE(self, NMDnsSystemdResolved, NM_IS_DNS_SYSTEMD_RESOLVED, NMDnsPlugin) /*****************************************************************************/ @@ -146,10 +150,88 @@ static void _resolve_start(NMDnsSystemdResolved *self, NMDnsSystemdResolvedResol /*****************************************************************************/ +static gboolean +_update_pending_detect(NMDnsSystemdResolved *self) +{ + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + + if (priv->n_pending > 0) { + /* we have pending calls. We definitely want to wait for them to complete. */ + return TRUE; + } + if (!priv->dbus_initied) { + if (!priv->dbus_connection) + return FALSE; + /* D-Bus not yet initialized (and we don't know the name owner yet). Pending. */ + return TRUE; + } + if (priv->service_start_cancellable) { + /* We are waiting to D-Bus activate resolved. Pending. */ + return TRUE; + } + if (priv->try_start_blocked) { + /* We earlier tried to start resolved, but are rate limited. We are not pending an update + * (that we expect to complete any time soon). */ + return FALSE; + } + if (priv->send_updates_waiting) { + /* we wait to send updates. We are pending. */ + return TRUE; + } + return FALSE; +} + +static void +_update_pending_maybe_changed(NMDnsSystemdResolved *self) +{ + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + gboolean update_pending; + + /* Important: we need to make sure that we call _update_pending_maybe_changed(), when + * the state changes. */ + + update_pending = _update_pending_detect(self); + if (priv->update_pending != update_pending) { + priv->update_pending = update_pending; + _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self)); + } +} + +static gboolean +get_update_pending(NMDnsPlugin *plugin) +{ + NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin); + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + + nm_assert(priv->update_pending == _update_pending_detect(self)); + return priv->update_pending; +} + +/*****************************************************************************/ + +static RequestItem * +_request_item_ref(RequestItem *request_item) +{ + nm_assert(request_item); + nm_assert(request_item->ref_count > 0); + nm_assert(request_item->ref_count < G_MAXINT); + nm_assert(!c_list_is_empty(&request_item->request_queue_lst)); + + request_item->ref_count++; + return request_item; +} + static void -_request_item_free(RequestItem *request_item) +_request_item_unref(RequestItem *request_item) { - c_list_unlink_stale(&request_item->request_queue_lst); + nm_assert(request_item); + nm_assert(request_item->ref_count > 0); + + if (--request_item->ref_count > 0) + return; + + nm_assert(c_list_is_empty(&request_item->request_queue_lst)); + g_variant_unref(request_item->argument); nm_g_slice_free(request_item); } @@ -165,6 +247,7 @@ _request_item_append(NMDnsSystemdResolved *self, request_item = g_slice_new(RequestItem); *request_item = (RequestItem){ + .ref_count = 1, .operation = operation, .argument = g_variant_ref_sink(argument), .self = self, @@ -178,8 +261,8 @@ _request_item_append(NMDnsSystemdResolved *self, static void _interface_config_free(InterfaceConfig *config) { - nm_c_list_elem_free_all(&config->configs_lst_head, NULL); - g_slice_free(InterfaceConfig, config); + nm_g_ptr_array_unref(config->ip_data_list); + nm_g_slice_free(config); } static void @@ -191,42 +274,48 @@ call_done(GObject *source, GAsyncResult *r, gpointer user_data) NMDnsSystemdResolvedPrivate *priv; RequestItem *request_item; NMLogLevel log_level; - - v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), r, &error); - if (nm_utils_error_is_cancelled(error)) - return; + const char *operation; + int ifindex; request_item = user_data; self = request_item->self; - priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + operation = request_item->operation; + ifindex = request_item->ifindex; + _request_item_unref(request_item); + + priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + + v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), r, &error); + if (nm_utils_error_is_cancelled(error)) + goto out_dec_pending; if (v) { - if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE + if (operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE && priv->has_link_default_route == NM_TERNARY_DEFAULT) { priv->has_link_default_route = NM_TERNARY_TRUE; _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API supported"); } - if (request_item->operation == DBUS_OP_SET_LINK_DNS_OVER_TLS + if (operation == DBUS_OP_SET_LINK_DNS_OVER_TLS && priv->has_link_dns_over_tls == NM_TERNARY_DEFAULT) { priv->has_link_dns_over_tls = NM_TERNARY_TRUE; _LOGD("systemd-resolved support for SetLinkDNSOverTLS(): API supported"); } priv->send_updates_warn_ratelimited = FALSE; - return; + goto out_dec_pending; } if (nm_g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { if (priv->has_link_default_route == NM_TERNARY_DEFAULT - && request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE) { + && operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE) { priv->has_link_default_route = NM_TERNARY_FALSE; _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API not supported"); } if (priv->has_link_dns_over_tls == NM_TERNARY_DEFAULT - && request_item->operation == DBUS_OP_SET_LINK_DNS_OVER_TLS) { + && operation == DBUS_OP_SET_LINK_DNS_OVER_TLS) { priv->has_link_dns_over_tls = NM_TERNARY_FALSE; _LOGD("systemd-resolved support for SetLinkDNSOverTLS(): API not supported"); } - return; + goto out_dec_pending; } log_level = LOGL_DEBUG; @@ -234,18 +323,25 @@ call_done(GObject *source, GAsyncResult *r, gpointer user_data) priv->send_updates_warn_ratelimited = TRUE; log_level = LOGL_WARN; } - _NMLOG(log_level, - "send-updates %s@%d failed: %s", - request_item->operation, - request_item->ifindex, - error->message); + _NMLOG(log_level, "send-updates %s@%d failed: %s", operation, ifindex, error->message); + +out_dec_pending: + nm_assert(priv->n_pending > 0); + if (--priv->n_pending <= 0) { + _update_pending_maybe_changed(self); + /* We keep @self alive while pending operations are in progress. It's simpler + * to implement. But this requires that we implement "stop()" signal to cancel + * all pending requests. Cancelling is necessary, because during shutdown, + * we must wrap up fast, and not hang an undefined amount time. */ + g_object_unref(self); + } } static gboolean -update_add_ip_config(NMDnsSystemdResolved *self, - GVariantBuilder *dns, - GVariantBuilder *domains, - NMDnsConfigIPData *ip_data) +update_add_ip_config(NMDnsSystemdResolved *self, + GVariantBuilder *dns, + GVariantBuilder *domains, + const NMDnsConfigIPData *ip_data) { gsize addr_size; guint n; @@ -258,8 +354,12 @@ update_add_ip_config(NMDnsSystemdResolved *self, addr_size = nm_utils_addr_family_to_size(ip_data->addr_family); if ((!ip_data->domains.search || !ip_data->domains.search[0]) - && !ip_data->domains.has_default_route_exclusive && !ip_data->domains.has_default_route) + && !ip_data->domains.has_default_route_exclusive && !ip_data->domains.has_default_route) { + /* we have no search domain (which systemd-resolved uses to routing the request), but + * also the "DefaultRoute" is not set on the interface. This setting has no effect and + * gets ignored. */ return FALSE; + } nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &n); for (i = 0; i < n; i++) { @@ -295,23 +395,28 @@ free_pending_updates(NMDnsSystemdResolved *self) NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); RequestItem *request_item; - while ((request_item = - c_list_first_entry(&priv->request_queue_lst_head, RequestItem, request_queue_lst))) - _request_item_free(request_item); + while ( + (request_item = + c_list_first_entry(&priv->request_queue_lst_head, RequestItem, request_queue_lst))) { + c_list_unlink(&request_item->request_queue_lst); + _request_item_unref(request_item); + } } static gboolean -prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) +prepare_one_interface(NMDnsSystemdResolved *self, const InterfaceConfig *ic) { GVariantBuilder dns; GVariantBuilder domains; - NMCListElem *elem; - NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; - NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT; - NMSettingConnectionDnsOverTls dns_over_tls = NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT; - const char *mdns_arg = NULL, *llmnr_arg = NULL, *dns_over_tls_arg = NULL; + NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; + NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT; + NMSettingConnectionDnsOverTls dns_over_tls = NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT; + const char *mdns_arg = NULL; + const char *llmnr_arg = NULL; + const char *dns_over_tls_arg = NULL; gboolean has_config = FALSE; gboolean has_default_route = FALSE; + guint i; g_variant_builder_init(&dns, G_VARIANT_TYPE("(ia(iay))")); g_variant_builder_add(&dns, "i", ic->ifindex); @@ -321,18 +426,22 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) g_variant_builder_add(&domains, "i", ic->ifindex); g_variant_builder_open(&domains, G_VARIANT_TYPE("a(sb)")); - c_list_for_each_entry (elem, &ic->configs_lst_head, lst) { - NMDnsConfigIPData *ip_data = elem->data; + if (ic->ip_data_list) { + for (i = 0; i < ic->ip_data_list->len; i++) { + const NMDnsConfigIPData *ip_data = ic->ip_data_list->pdata[i]; - has_config |= update_add_ip_config(self, &dns, &domains, ip_data); + if (update_add_ip_config(self, &dns, &domains, ip_data)) + has_config = TRUE; - if (ip_data->domains.has_default_route) - has_default_route = TRUE; + if (ip_data->domains.has_default_route) + has_default_route = TRUE; - if (NM_IS_IPv4(ip_data->addr_family)) { - mdns = NM_MAX(mdns, nm_l3_config_data_get_mdns(ip_data->l3cd)); - llmnr = NM_MAX(llmnr, nm_l3_config_data_get_llmnr(ip_data->l3cd)); - dns_over_tls = NM_MAX(dns_over_tls, nm_l3_config_data_get_dns_over_tls(ip_data->l3cd)); + if (NM_IS_IPv4(ip_data->addr_family)) { + mdns = NM_MAX(mdns, nm_l3_config_data_get_mdns(ip_data->l3cd)); + llmnr = NM_MAX(llmnr, nm_l3_config_data_get_llmnr(ip_data->l3cd)); + dns_over_tls = + NM_MAX(dns_over_tls, nm_l3_config_data_get_dns_over_tls(ip_data->l3cd)); + } } } @@ -413,33 +522,45 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) return has_config; } -static gboolean -_ensure_resolved_running_timeout(gpointer user_data) +static void +start_resolved_cb(GObject *source, GAsyncResult *result, gpointer user_data) { - NMDnsSystemdResolved *self = user_data; - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + NMDnsSystemdResolved *self; + NMDnsSystemdResolvedPrivate *priv; NMDnsSystemdResolvedResolveHandle *handle; - nm_clear_g_source_inst(&priv->try_start_timeout_source); + res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (nm_utils_error_is_cancelled(error)) + return; + + self = user_data; + priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + nm_clear_g_cancellable(&priv->service_start_cancellable); - _LOGT("timeout waiting to D-Bus activate systemd-resolved. Systemd-resolved won't be " - "used until it appears on the bus"); + if (!res) { + g_dbus_error_strip_remote_error(error); + _LOGD("error activating systemd-resolved: %s", error->message); again: - c_list_for_each_entry (handle, &priv->handle_lst_head, handle_lst) { - gs_free_error GError *error = NULL; + c_list_for_each_entry (handle, &priv->handle_lst_head, handle_lst) { + gs_free_error GError *local = NULL; - if (handle->is_failing_on_idle) - continue; + if (handle->is_failing_on_idle) + continue; - nm_utils_error_set_literal(&error, - NM_UTILS_ERROR_NOT_READY, - "timeout waiting for systemd-resolved to start"); - _resolve_complete_error(handle, error); - goto again; - } + nm_utils_error_set(&local, + NM_UTILS_ERROR_NOT_READY, + "error activating systemd-resolved: %s", + error->message); + _resolve_complete_error(handle, local); + goto again; + } + } else + _LOGD("systemd-resolved successfully started"); - return G_SOURCE_CONTINUE; + _update_pending_maybe_changed(self); } static NMTernary @@ -447,6 +568,9 @@ ensure_resolved_running(NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + if (priv->stopped) + return NM_TERNARY_FALSE; + if (!priv->dbus_initied) return NM_TERNARY_DEFAULT; @@ -459,16 +583,15 @@ ensure_resolved_running(NMDnsSystemdResolved *self) _LOGT("try D-Bus activating systemd-resolved..."); priv->try_start_blocked = TRUE; - - priv->try_start_timeout_source = - nm_g_timeout_add_source(4000, _ensure_resolved_running_timeout, self); - + nm_clear_g_cancellable(&priv->service_start_cancellable); + priv->service_start_cancellable = g_cancellable_new(); nm_dbus_connection_call_start_service_by_name(priv->dbus_connection, SYSTEMD_RESOLVED_DBUS_SERVICE, - -1, - NULL, - NULL, - NULL); + 4000, + priv->service_start_cancellable, + start_resolved_cb, + self); + _update_pending_maybe_changed(self); return NM_TERNARY_DEFAULT; } @@ -523,6 +646,12 @@ send_updates(NMDnsSystemdResolved *self) request_item->operation, (ss = g_variant_print(request_item->argument, FALSE))); + if (priv->n_pending++ == 0) { + /* We are inside send_updates(). All callers are already calling + * _update_pending_maybe_changed() afterwards. */ + g_object_ref(self); + } + g_dbus_connection_call(priv->dbus_connection, priv->dbus_owner, SYSTEMD_RESOLVED_DBUS_PATH, @@ -534,7 +663,7 @@ send_updates(NMDnsSystemdResolved *self) -1, priv->cancellable, call_done, - request_item); + _request_item_ref(request_item)); } start_resolve: @@ -554,43 +683,54 @@ update(NMDnsPlugin *plugin, const char *hostdomain, GError **error) { - NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin); - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - gs_unref_hashtable GHashTable *interfaces = NULL; - gs_free gpointer *interfaces_keys = NULL; + NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin); + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + gs_unref_hashtable GHashTable *interfaces = NULL; + const NMUtilsNamedValue *interfaces_arr; + NMUtilsNamedValue interfaces_arr_stack[50]; + gs_free NMUtilsNamedValue *interfaces_arr_heap = NULL; guint interfaces_len; - int ifindex; gpointer pointer; NMDnsConfigIPData *ip_data; GHashTableIter iter; + gs_unref_array GArray *dirty_array = NULL; guint i; + nm_assert(!priv->stopped); + + /* Group configs by ifindex/interfaces. */ interfaces = g_hash_table_new_full(nm_direct_hash, NULL, NULL, (GDestroyNotify) _interface_config_free); c_list_for_each_entry (ip_data, ip_data_lst_head, ip_data_lst) { - InterfaceConfig *ic = NULL; + InterfaceConfig *ic = NULL; + int ifindex = ip_data->data->ifindex; - ifindex = ip_data->data->ifindex; nm_assert(ifindex == nm_l3_config_data_get_ifindex(ip_data->l3cd)); ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(ifindex)); if (!ic) { - ic = g_slice_new(InterfaceConfig); - ic->ifindex = ifindex; - c_list_init(&ic->configs_lst_head); + ic = g_slice_new(InterfaceConfig); + *ic = (InterfaceConfig){ + .ifindex = ifindex, + .ip_data_list = g_ptr_array_sized_new(4), + }; g_hash_table_insert(interfaces, GINT_TO_POINTER(ifindex), ic); } - c_list_link_tail(&ic->configs_lst_head, &nm_c_list_elem_new_stale(ip_data)->lst); + g_ptr_array_add(ic->ip_data_list, ip_data); } free_pending_updates(self); - interfaces_keys = - nm_utils_hash_keys_to_array(interfaces, nm_cmp_int2ptr_p_with_data, NULL, &interfaces_len); + interfaces_arr = nm_utils_hash_to_array_with_buffer(interfaces, + &interfaces_len, + nm_cmp_int2ptr_p_with_data, + NULL, + interfaces_arr_stack, + &interfaces_arr_heap); for (i = 0; i < interfaces_len; i++) { - InterfaceConfig *ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(interfaces_keys[i])); + const InterfaceConfig *ic = interfaces_arr[i].value_ptr; if (prepare_one_interface(self, ic)) g_hash_table_add(priv->dirty_interfaces, GINT_TO_POINTER(ic->ifindex)); @@ -602,23 +742,38 @@ update(NMDnsPlugin *plugin, * resolved, and the current update doesn't contain that interface, * reset the resolved configuration for that ifindex. */ g_hash_table_iter_init(&iter, priv->dirty_interfaces); - while (g_hash_table_iter_next(&iter, (gpointer *) &pointer, NULL)) { - ifindex = GPOINTER_TO_INT(pointer); - if (!g_hash_table_contains(interfaces, GINT_TO_POINTER(ifindex))) { + while (g_hash_table_iter_next(&iter, &pointer, NULL)) { + int ifindex = GPOINTER_TO_INT(pointer); + + if (g_hash_table_contains(interfaces, GINT_TO_POINTER(ifindex))) { + /* the interface is still tracked and still dirty. Keep. */ + continue; + } + + if (!dirty_array) + dirty_array = g_array_new(FALSE, FALSE, sizeof(int)); + g_array_append_val(dirty_array, ifindex); + + g_hash_table_iter_remove(&iter); + } + if (dirty_array) { + g_array_sort_with_data(dirty_array, nm_cmp_int2ptr_p_with_data, NULL); + for (i = 0; i < dirty_array->len; i++) { + int ifindex = g_array_index(dirty_array, int, i); InterfaceConfig ic; _LOGT("clear previously configured ifindex %d", ifindex); ic = (InterfaceConfig){ - .ifindex = ifindex, - .configs_lst_head = C_LIST_INIT(ic.configs_lst_head), + .ifindex = ifindex, + .ip_data_list = NULL, }; prepare_one_interface(self, &ic); - g_hash_table_iter_remove(&iter); } } priv->send_updates_waiting = TRUE; send_updates(self); + _update_pending_maybe_changed(self); return TRUE; } @@ -636,8 +791,7 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner) else _LOGT("D-Bus name for systemd-resolved has owner %s", owner); - nm_clear_g_source_inst(&priv->try_start_timeout_source); - + nm_clear_g_cancellable(&priv->service_start_cancellable); nm_strdup_reset(&priv->dbus_owner, owner); if (owner) { @@ -649,6 +803,7 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner) } send_updates(self); + _update_pending_maybe_changed(self); } static void @@ -957,6 +1112,48 @@ nm_dns_systemd_resolved_resolve_cancel(NMDnsSystemdResolvedResolveHandle *handle /*****************************************************************************/ static void +stop(NMDnsPlugin *plugin) +{ + NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin); + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + NMDnsSystemdResolvedResolveHandle *handle; + + /* This function must be re-entrant!! + * + * Currently there is no concept of unregistering/shutting down. It's not + * clear whether we should de-configure anything in systemd-resolved, we + * don't. + * + * Implementing stop() is important because pending operations take a + * reference on @self. We can only cancel (fast shutdown) the instance + * by cancelling those requests. */ + + priv->stopped = TRUE; + priv->try_start_blocked = TRUE; + + nm_clear_g_cancellable(&priv->cancellable); + + nm_clear_g_free(&priv->dbus_owner); + + while ((handle = c_list_first_entry(&priv->handle_lst_head, + NMDnsSystemdResolvedResolveHandle, + handle_lst))) { + gs_free_error GError *error = NULL; + + nm_utils_error_set_cancelled(&error, TRUE, "NMDnsSystemdResolved"); + _resolve_complete_error(handle, error); + } + + free_pending_updates(self); + + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); + + nm_clear_g_cancellable(&priv->service_start_cancellable); +} + +/*****************************************************************************/ + +static void nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); @@ -974,6 +1171,8 @@ nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self) return; } + priv->update_pending = TRUE; + priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, SYSTEMD_RESOLVED_DBUS_SERVICE, @@ -998,33 +1197,15 @@ nm_dns_systemd_resolved_new(void) static void dispose(GObject *object) { - NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(object); - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - NMDnsSystemdResolvedResolveHandle *handle; - - while ((handle = c_list_first_entry(&priv->handle_lst_head, - NMDnsSystemdResolvedResolveHandle, - handle_lst))) { - gs_free_error GError *error = NULL; - - nm_utils_error_set_cancelled(&error, TRUE, "NMDnsSystemdResolved"); - _resolve_complete_error(handle, error); - } - - free_pending_updates(self); - - nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); - - nm_clear_g_cancellable(&priv->cancellable); + NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(object); + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - nm_clear_g_source_inst(&priv->try_start_timeout_source); + stop(NM_DNS_PLUGIN(self)); g_clear_object(&priv->dbus_connection); - nm_clear_pointer(&priv->dirty_interfaces, g_hash_table_unref); + nm_clear_pointer(&priv->dirty_interfaces, g_hash_table_destroy); G_OBJECT_CLASS(nm_dns_systemd_resolved_parent_class)->dispose(object); - - nm_clear_g_free(&priv->dbus_owner); } static void @@ -1035,7 +1216,9 @@ nm_dns_systemd_resolved_class_init(NMDnsSystemdResolvedClass *dns_class) object_class->dispose = dispose; - plugin_class->plugin_name = "systemd-resolved"; - plugin_class->is_caching = TRUE; - plugin_class->update = update; + plugin_class->plugin_name = "systemd-resolved"; + plugin_class->is_caching = TRUE; + plugin_class->stop = stop; + plugin_class->update = update; + plugin_class->get_update_pending = get_update_pending; } diff --git a/src/core/dns/nm-dns-unbound.c b/src/core/dns/nm-dns-unbound.c deleted file mode 100644 index 8a75cf08..00000000 --- a/src/core/dns/nm-dns-unbound.c +++ /dev/null @@ -1,84 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2014 Red Hat, Inc. - * Author: Pavel Å imerda <psimerda@redhat.com> - */ - -#include "src/core/nm-default-daemon.h" - -#include "nm-dns-unbound.h" - -#include "NetworkManagerUtils.h" - -/*****************************************************************************/ - -struct _NMDnsUnbound { - NMDnsPlugin parent; -}; - -struct _NMDnsUnboundClass { - NMDnsPluginClass parent; -}; - -G_DEFINE_TYPE(NMDnsUnbound, nm_dns_unbound, NM_TYPE_DNS_PLUGIN) - -/*****************************************************************************/ - -static gboolean -update(NMDnsPlugin *plugin, - const NMGlobalDnsConfig *global_config, - const CList *ip_config_lst_head, - const char *hostdomain, - GError **error) -{ - char *argv[] = {DNSSEC_TRIGGER_PATH, "--async", "--update", NULL}; - gs_free_error GError *local = NULL; - int status; - - /* TODO: We currently call a script installed with the dnssec-trigger - * package that queries all information itself. Later, the dependency - * on that package will be optional and the only hard dependency will - * be unbound. - * - * Unbound configuration should be later handled by this plugin directly, - * without calling custom scripts. The dnssec-trigger functionality - * may be eventually merged into NetworkManager. - */ - if (!g_spawn_sync("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &local)) { - nm_utils_error_set(error, - NM_UTILS_ERROR_UNKNOWN, - "error spawning dns-trigger: %s", - local->message); - return FALSE; - } - if (status != 0) { - nm_utils_error_set(error, - NM_UTILS_ERROR_UNKNOWN, - "dns-trigger exited with error code %d", - status); - return FALSE; - } - return TRUE; -} - -/*****************************************************************************/ - -static void -nm_dns_unbound_init(NMDnsUnbound *unbound) -{} - -NMDnsPlugin * -nm_dns_unbound_new(void) -{ - return g_object_new(NM_TYPE_DNS_UNBOUND, NULL); -} - -static void -nm_dns_unbound_class_init(NMDnsUnboundClass *klass) -{ - NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS(klass); - - plugin_class->plugin_name = "unbound"; - plugin_class->is_caching = TRUE; - plugin_class->update = update; -} diff --git a/src/core/dns/nm-dns-unbound.h b/src/core/dns/nm-dns-unbound.h deleted file mode 100644 index feb33099..00000000 --- a/src/core/dns/nm-dns-unbound.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2014 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_DNS_UNBOUND_H__ -#define __NETWORKMANAGER_DNS_UNBOUND_H__ - -#include "nm-dns-plugin.h" - -#define NM_TYPE_DNS_UNBOUND (nm_dns_unbound_get_type()) -#define NM_DNS_UNBOUND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_UNBOUND, NMDnsUnbound)) -#define NM_DNS_UNBOUND_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNS_UNBOUND, NMDnsUnboundClass)) -#define NM_IS_DNS_UNBOUND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DNS_UNBOUND)) -#define NM_IS_DNS_UNBOUND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DNS_UNBOUND)) -#define NM_DNS_UNBOUND_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DNS_UNBOUND, NMDnsUnboundClass)) - -typedef struct _NMDnsUnbound NMDnsUnbound; -typedef struct _NMDnsUnboundClass NMDnsUnboundClass; - -GType nm_dns_unbound_get_type(void); - -NMDnsPlugin *nm_dns_unbound_new(void); - -#endif /* __NETWORKMANAGER_DNS_UNBOUND_H__ */ diff --git a/src/core/main.c b/src/core/main.c index 1a9c3a65..2eb230d9 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -293,6 +293,7 @@ main(int argc, char *argv[]) GError *error_invalid_logging_config = NULL; const char *const *warnings; int errsv; + gboolean has_logging = FALSE; _nm_utils_is_manager_process = TRUE; @@ -354,10 +355,14 @@ main(int argc, char *argv[]) g_free(path); } - if (!nm_logging_setup(global_opt.opt_log_level, - global_opt.opt_log_domains, - &bad_domains, - &error)) { + if (nm_config_kernel_command_line_nm_debug()) { + /* we honor kernel command line. If "nm.debug" is set, we always enable trace logging. */ + nm_logging_setup("TRACE", "ALL", NULL, NULL); + has_logging = TRUE; + } else if (!nm_logging_setup(global_opt.opt_log_level, + global_opt.opt_log_domains, + &bad_domains, + &error)) { fprintf(stderr, _("%s. Please use --help to see a list of valid options.\n"), error->message); @@ -379,7 +384,7 @@ main(int argc, char *argv[]) /* Initialize logging from config file *only* if not explicitly * specified by commandline. */ - if (global_opt.opt_log_level == NULL && global_opt.opt_log_domains == NULL) { + if (!has_logging && !global_opt.opt_log_level && !global_opt.opt_log_domains) { if (!nm_logging_setup(nm_config_get_log_level(config), nm_config_get_log_domains(config), &bad_domains, @@ -417,9 +422,10 @@ main(int argc, char *argv[]) } nm_log_info(LOGD_CORE, - "NetworkManager (version " NM_DIST_VERSION ") is starting... (%s%s)", - nm_config_get_first_start(config) ? "for the first time" : "after a restart", - NM_MORE_ASSERTS != 0 ? ", asserts:" G_STRINGIFY(NM_MORE_ASSERTS) : ""); + "NetworkManager (version " NM_DIST_VERSION ") is starting... (%s%sboot:%s)", + nm_config_get_first_start(config) ? "" : "after a restart, ", + NM_MORE_ASSERTS != 0 ? "asserts:" G_STRINGIFY(NM_MORE_ASSERTS) ", " : "", + nm_utils_boot_id_str()); nm_log_info(LOGD_CORE, "Read config: %s", diff --git a/src/core/meson.build b/src/core/meson.build index 2148d23b..f3359ad0 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -123,7 +123,6 @@ libNetworkManager = static_library( 'dns/nm-dns-manager.c', 'dns/nm-dns-plugin.c', 'dns/nm-dns-systemd-resolved.c', - 'dns/nm-dns-unbound.c', 'dnsmasq/nm-dnsmasq-manager.c', 'dnsmasq/nm-dnsmasq-utils.c', 'ppp/nm-ppp-manager-call.c', diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c index ddb7787f..0f512e38 100644 --- a/src/core/nm-config-data.c +++ b/src/core/nm-config-data.c @@ -839,6 +839,13 @@ nm_config_data_log(const NMConfigData *self, _LOG(stream, prefix, "# no-auto-default specs \"%s\"", msg); } + if (nm_config_kernel_command_line_nm_debug()) { + _LOG(stream, + prefix, + "# /proc/cmdline contains \"" NM_CONFIG_KERNEL_CMDLINE_NM_DEBUG + "\". Debug log enabled"); + } + #undef _LOG } @@ -1681,7 +1688,7 @@ nm_config_data_get_connection_default_int64(const NMConfigData *self, const char *value; value = nm_config_data_get_connection_default(self, property, device); - return _nm_utils_ascii_str_to_int64(value, 10, min, max, fallback); + return _nm_utils_ascii_str_to_int64(value, 0, min, max, fallback); } static const char * diff --git a/src/core/nm-config.c b/src/core/nm-config.c index ea1c2ab3..3d23d4f6 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -11,7 +11,9 @@ #include <stdio.h> #include "nm-utils.h" +#include "nm-dhcp-config.h" #include "devices/nm-device.h" +#include "dhcp/nm-dhcp-options.h" #include "NetworkManagerUtils.h" #include "libnm-core-intern/nm-core-internal.h" #include "libnm-core-intern/nm-keyfile-internal.h" @@ -573,93 +575,93 @@ nm_config_cmd_line_options_add_to_entries(NMConfigCmdLineOptions *cli, GOptionCo { GOptionGroup *group; GOptionEntry config_options[] = { - {"config", - 0, - 0, - G_OPTION_ARG_FILENAME, - &cli->config_main_file, - N_("Config file location"), - DEFAULT_CONFIG_MAIN_FILE}, - {"config-dir", - 0, - 0, - G_OPTION_ARG_FILENAME, - &cli->config_dir, - N_("Config directory location"), - DEFAULT_CONFIG_DIR}, - {"system-config-dir", - 0, - 0, - G_OPTION_ARG_FILENAME, - &cli->system_config_dir, - N_("System config directory location"), - DEFAULT_SYSTEM_CONFIG_DIR}, - {"intern-config", - 0, - 0, - G_OPTION_ARG_FILENAME, - &cli->intern_config_file, - N_("Internal config file location"), - DEFAULT_INTERN_CONFIG_FILE}, - {"state-file", - 0, - 0, - G_OPTION_ARG_FILENAME, - &cli->state_file, - N_("State file location"), - DEFAULT_STATE_FILE}, - {"no-auto-default", - 0, - G_OPTION_FLAG_HIDDEN, - G_OPTION_ARG_FILENAME, - &cli->no_auto_default_file, - N_("State file for no-auto-default devices"), - DEFAULT_NO_AUTO_DEFAULT_FILE}, - {"plugins", - 0, - 0, - G_OPTION_ARG_STRING, - &cli->plugins, - N_("List of plugins separated by ','"), - NM_CONFIG_DEFAULT_MAIN_PLUGINS}, - {"configure-and-quit", - 0, - G_OPTION_FLAG_OPTIONAL_ARG, - G_OPTION_ARG_CALLBACK, - parse_configure_and_quit, - N_("Quit after initial configuration"), - NULL}, - {"debug", - 'd', - 0, - G_OPTION_ARG_NONE, - &cli->is_debug, - N_("Don't become a daemon, and log to stderr"), - NULL}, - - /* These three are hidden for now, and should eventually just go away. */ - {"connectivity-uri", - 0, - G_OPTION_FLAG_HIDDEN, - G_OPTION_ARG_STRING, - &cli->connectivity_uri, - N_("An http(s) address for checking internet connectivity"), - "http://example.com"}, - {"connectivity-interval", - 0, - G_OPTION_FLAG_HIDDEN, - G_OPTION_ARG_INT, - &cli->connectivity_interval, - N_("The interval between connectivity checks (in seconds)"), - G_STRINGIFY(NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL)}, - {"connectivity-response", - 0, - G_OPTION_FLAG_HIDDEN, - G_OPTION_ARG_STRING, - &cli->connectivity_response, - N_("The expected start of the response"), - NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE}, - {0}, + {"config", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->config_main_file, + N_("Config file location"), + DEFAULT_CONFIG_MAIN_FILE}, + {"config-dir", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->config_dir, + N_("Config directory location"), + DEFAULT_CONFIG_DIR}, + {"system-config-dir", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->system_config_dir, + N_("System config directory location"), + DEFAULT_SYSTEM_CONFIG_DIR}, + {"intern-config", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->intern_config_file, + N_("Internal config file location"), + DEFAULT_INTERN_CONFIG_FILE}, + {"state-file", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->state_file, + N_("State file location"), + DEFAULT_STATE_FILE}, + {"no-auto-default", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_FILENAME, + &cli->no_auto_default_file, + N_("State file for no-auto-default devices"), + DEFAULT_NO_AUTO_DEFAULT_FILE}, + {"plugins", + 0, + 0, + G_OPTION_ARG_STRING, + &cli->plugins, + N_("List of plugins separated by ','"), + NM_CONFIG_DEFAULT_MAIN_PLUGINS}, + {"configure-and-quit", + 0, + G_OPTION_FLAG_OPTIONAL_ARG, + G_OPTION_ARG_CALLBACK, + parse_configure_and_quit, + N_("Quit after initial configuration"), + NULL}, + {"debug", + 'd', + 0, + G_OPTION_ARG_NONE, + &cli->is_debug, + N_("Don't become a daemon, and log to stderr"), + NULL}, + + /* These three are hidden for now, and should eventually just go away. */ + {"connectivity-uri", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING, + &cli->connectivity_uri, + N_("An http(s) address for checking internet connectivity"), + "http://example.com"}, + {"connectivity-interval", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_INT, + &cli->connectivity_interval, + N_("The interval between connectivity checks (in seconds)"), + G_STRINGIFY(NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL)}, + {"connectivity-response", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING, + &cli->connectivity_response, + N_("The expected start of the response"), + NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE}, + {0}, }; g_return_if_fail(opt_ctx); @@ -2573,13 +2575,16 @@ nm_config_device_state_write(int ifindex, NMTernary nm_owned, guint32 route_metric_default_aspired, guint32 route_metric_default_effective, - const char *next_server, - const char *root_path, - const char *dhcp_bootfile) + NMDhcpConfig *dhcp4_config, + NMDhcpConfig *dhcp6_config) { char path[NM_STRLEN(NM_CONFIG_DEVICE_STATE_DIR "/") + DEVICE_STATE_FILENAME_LEN_MAX + 1]; - GError *local = NULL; - nm_auto_unref_keyfile GKeyFile *kf = NULL; + GError *local = NULL; + nm_auto_unref_keyfile GKeyFile *kf = NULL; + const char *root_path = NULL; + const char *next_server = NULL; + const char *dhcp_bootfile = NULL; + int IS_IPv4; g_return_val_if_fail(ifindex > 0, FALSE); g_return_val_if_fail(!connection_uuid || *connection_uuid, FALSE); @@ -2630,6 +2635,15 @@ nm_config_device_state_write(int ifindex, route_metric_default_aspired); } } + + if (dhcp4_config) { + next_server = nm_dhcp_config_get_option(dhcp4_config, "next_server"); + root_path = nm_dhcp_config_get_option(dhcp4_config, "root_path"); + dhcp_bootfile = nm_dhcp_config_get_option(dhcp4_config, "filename"); + if (!dhcp_bootfile) + dhcp_bootfile = nm_dhcp_config_get_option(dhcp4_config, "bootfile_name"); + } + if (next_server) { g_key_file_set_string(kf, DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, @@ -2649,6 +2663,28 @@ nm_config_device_state_write(int ifindex, dhcp_bootfile); } + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + NMDhcpConfig *dhcp_config = IS_IPv4 ? dhcp4_config : dhcp6_config; + gs_free NMUtilsNamedValue *values = NULL; + guint i; + guint num; + + if (!dhcp_config) + continue; + + values = nm_dhcp_config_get_option_values(dhcp_config, &num); + for (i = 0; i < num; i++) { + gs_free char *name_full = NULL; + const char *prefix = IS_IPv4 ? "dhcp4" : "dhcp6"; + + if (NM_STR_HAS_PREFIX(values[i].name, NM_DHCP_OPTION_REQPREFIX)) + continue; + + name_full = g_strdup_printf("%s.%s", prefix, values[i].name); + g_key_file_set_string(kf, prefix, name_full, values[i].value_str); + } + } + if (!g_key_file_save_to_file(kf, path, &local)) { _LOGW("device-state: write #%d (%s) failed: %s", ifindex, path, local->message); g_error_free(local); @@ -2978,6 +3014,15 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps /*****************************************************************************/ +gboolean +nm_config_kernel_command_line_nm_debug(void) +{ + return (nm_strv_find_first(nm_utils_proc_cmdline_split(), -1, NM_CONFIG_KERNEL_CMDLINE_NM_DEBUG) + >= 0); +} + +/*****************************************************************************/ + static gboolean init_sync(GInitable *initable, GCancellable *cancellable, GError **error) { diff --git a/src/core/nm-config.h b/src/core/nm-config.h index 2c23ff20..0dd159f4 100644 --- a/src/core/nm-config.h +++ b/src/core/nm-config.h @@ -18,6 +18,8 @@ #define NM_IS_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_CONFIG)) #define NM_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_CONFIG, NMConfigClass)) +#define NM_CONFIG_KERNEL_CMDLINE_NM_DEBUG "nm.debug" + /* Properties */ #define NM_CONFIG_CMD_LINE_OPTIONS "cmd-line-options" #define NM_CONFIG_ATOMIC_SECTION_PREFIXES "atomic-section-prefixes" @@ -183,9 +185,8 @@ gboolean nm_config_device_state_write(int NMTernary nm_owned, guint32 route_metric_default_aspired, guint32 route_metric_default_effective, - const char *next_server, - const char *root_path, - const char *dhcp_bootfile); + NMDhcpConfig *dhcp4_config, + NMDhcpConfig *dhcp6_config); void nm_config_device_state_prune_stale(GHashTable *preserve_ifindexes, NMPlatform *preserve_in_platform); @@ -198,4 +199,8 @@ void nm_config_clear_warnings(NMConfig *config); /*****************************************************************************/ +gboolean nm_config_kernel_command_line_nm_debug(void); + +/*****************************************************************************/ + #endif /* __NETWORKMANAGER_CONFIG_H__ */ diff --git a/src/core/nm-connectivity.c b/src/core/nm-connectivity.c index eea8adb2..25ac3c7f 100644 --- a/src/core/nm-connectivity.c +++ b/src/core/nm-connectivity.c @@ -816,8 +816,7 @@ system_resolver_resolve_cb(GObject *source_object, GAsyncResult *res, gpointer u addr_family = AF_INET6; break; default: - addr_family = AF_UNSPEC; - break; + goto next; } if (cb_data->addr_family != AF_UNSPEC && cb_data->addr_family != addr_family) continue; @@ -832,6 +831,7 @@ system_resolver_resolve_cb(GObject *source_object, GAsyncResult *res, gpointer u nm_str_buf_append(&strbuf_hosts, nm_utils_inet_ntop(addr_family, g_inet_address_to_bytes(a), str_addr)); +next:; } g_list_free_full(list, g_object_unref); diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c index 60c286ee..480e9b28 100644 --- a/src/core/nm-core-utils.c +++ b/src/core/nm-core-utils.c @@ -30,7 +30,6 @@ #include "libnm-glib-aux/nm-secret-utils.h" #include "libnm-glib-aux/nm-time-utils.h" #include "libnm-glib-aux/nm-str-buf.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "nm-utils.h" #include "libnm-core-intern/nm-core-internal.h" #include "nm-setting-connection.h" @@ -3094,7 +3093,10 @@ again: if (G_UNLIKELY(!proc_cmdline)) { gs_free char *str = NULL; - g_file_get_contents("/proc/cmdline", &str, NULL, NULL); + /* /run/NetworkManager/proc-cmdline can be used to overrule /proc/cmdline. */ + if (!g_file_get_contents(NMRUNDIR "/proc-cmdline", &str, NULL, NULL)) + g_file_get_contents("/proc/cmdline", &str, NULL, NULL); + str = nm_str_realloc(str); proc_cmdline = str ?: ""; @@ -5117,7 +5119,7 @@ nm_utils_spawn_helper(const char *const *args, fcntl(info->child_stdout, F_SETFL, fd_flags | O_NONBLOCK); /* Watch process stdin */ - nm_str_buf_init(&info->out_buffer, 32, TRUE); + info->out_buffer = NM_STR_BUF_INIT(32, TRUE); for (arg = args; *arg; arg++) { nm_str_buf_append(&info->out_buffer, *arg); nm_str_buf_append_c(&info->out_buffer, '\0'); @@ -5131,7 +5133,7 @@ nm_utils_spawn_helper(const char *const *args, g_source_attach(info->output_source, g_main_context_get_thread_default()); /* Watch process stdout */ - nm_str_buf_init(&info->in_buffer, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); + info->in_buffer = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); info->input_source = nm_g_unix_fd_source_new(info->child_stdout, G_IO_IN | G_IO_ERR | G_IO_HUP, G_PRIORITY_DEFAULT, @@ -5226,7 +5228,7 @@ again: * @shortened: (out) (transfer full): on return, the shortened hostname * * Checks whether the input hostname is valid. If not, tries to shorten it - * to HOST_NAME_MAX or to the first dot, whatever comes earlier. + * to HOST_NAME_MAX (64) or to the first dot, whatever comes earlier. * The new hostname is returned in @shortened. * * Returns: %TRUE if the input hostname was already valid or if was shortened @@ -5242,7 +5244,7 @@ nm_utils_shorten_hostname(const char *hostname, char **shortened) nm_assert(hostname); nm_assert(shortened); - if (nm_sd_hostname_is_valid(hostname, FALSE)) { + if (nm_hostname_is_valid(hostname, FALSE)) { *shortened = NULL; return TRUE; } @@ -5252,11 +5254,11 @@ nm_utils_shorten_hostname(const char *hostname, char **shortened) l = (dot - hostname); else l = strlen(hostname); - l = MIN(l, (gsize) HOST_NAME_MAX); + l = MIN(l, (gsize) NM_HOST_NAME_MAX); s = g_strndup(hostname, l); - if (!nm_sd_hostname_is_valid(s, FALSE)) { + if (!nm_hostname_is_valid(s, FALSE)) { *shortened = NULL; return FALSE; } diff --git a/src/core/nm-dhcp-config.c b/src/core/nm-dhcp-config.c index b32c9b70..394a3a49 100644 --- a/src/core/nm-dhcp-config.c +++ b/src/core/nm-dhcp-config.c @@ -125,6 +125,29 @@ nm_dhcp_config_set_lease(NMDhcpConfig *self, const NML3ConfigData *l3cd) _notify(self, PROP_OPTIONS); } +NMUtilsNamedValue * +nm_dhcp_config_get_option_values(NMDhcpConfig *self, guint *num) +{ + NMDhcpConfigPrivate *priv = NM_DHCP_CONFIG_GET_PRIVATE(self); + NMDhcpLease *lease; + NMUtilsNamedValue *buffer = NULL; + + if (!priv->l3cd) { + NM_SET_OUT(num, 0); + return NULL; + } + + lease = nm_l3_config_data_get_dhcp_lease(priv->l3cd, nm_dhcp_config_get_addr_family(self)); + nm_utils_named_values_from_strdict_full(nm_dhcp_lease_get_options(lease), + num, + nm_strcmp_p_with_data, + NULL, + NULL, + 0, + &buffer); + return buffer; +} + const char * nm_dhcp_config_get_option(NMDhcpConfig *self, const char *key) { diff --git a/src/core/nm-dhcp-config.h b/src/core/nm-dhcp-config.h index cae0e11a..cdca4ef1 100644 --- a/src/core/nm-dhcp-config.h +++ b/src/core/nm-dhcp-config.h @@ -30,7 +30,8 @@ int nm_dhcp_config_get_addr_family(NMDhcpConfig *self); void nm_dhcp_config_set_lease(NMDhcpConfig *self, const NML3ConfigData *l3cd); -const char *nm_dhcp_config_get_option(NMDhcpConfig *self, const char *option); +NMUtilsNamedValue *nm_dhcp_config_get_option_values(NMDhcpConfig *self, guint *num); +const char *nm_dhcp_config_get_option(NMDhcpConfig *self, const char *option); GVariant *nm_dhcp_config_get_options(NMDhcpConfig *self); diff --git a/src/core/nm-dispatcher.c b/src/core/nm-dispatcher.c index b6ff25ca..d3529ee4 100644 --- a/src/core/nm-dispatcher.c +++ b/src/core/nm-dispatcher.c @@ -56,6 +56,7 @@ struct NMDispatcherCallId { gpointer user_data; const char *log_ifname; const char *log_con_uuid; + gint64 start_at_msec; NMDispatcherAction action; guint idle_id; guint32 request_id; @@ -82,6 +83,7 @@ static struct { static NMDispatcherCallId * dispatcher_call_id_new(guint32 request_id, + gint64 start_at_msec, NMDispatcherAction action, NMDispatcherFunc callback, gpointer user_data, @@ -98,11 +100,12 @@ dispatcher_call_id_new(guint32 request_id, call_id = g_malloc(sizeof(NMDispatcherCallId) + l_log_ifname + l_log_con_uuid); - call_id->action = action; - call_id->request_id = request_id; - call_id->callback = callback; - call_id->user_data = user_data; - call_id->idle_id = 0; + call_id->action = action; + call_id->start_at_msec = start_at_msec; + call_id->request_id = request_id; + call_id->callback = callback; + call_id->user_data = user_data; + call_id->idle_id = 0; extra_strings = &call_id->extra_strings[0]; @@ -366,6 +369,8 @@ dispatch_result_to_string(DispatchResult result) static void dispatcher_results_process(guint32 request_id, + gint64 start_at_msec, + gint64 now_msec, const char *log_ifname, const char *log_con_uuid, GVariant *v_results) @@ -373,13 +378,22 @@ dispatcher_results_process(guint32 request_id, nm_auto_free_variant_iter GVariantIter *results = NULL; const char *script, *err; guint32 result; + gsize n_children; g_variant_get(v_results, "(a(sus))", &results); - if (g_variant_iter_n_children(results) == 0) { - _LOG2D(request_id, log_ifname, log_con_uuid, "succeeded but no scripts invoked"); + n_children = g_variant_iter_n_children(results); + + _LOG2D(request_id, + log_ifname, + log_con_uuid, + "succeeded (after %ld.%03d sec, %zu scripts invoked)", + (long int) ((now_msec - start_at_msec) / 1000), + (int) ((now_msec - start_at_msec) % 1000), + n_children); + + if (n_children == 0) return; - } while (g_variant_iter_next(results, "(&su&s)", &script, &result, &err)) { if (result == DISPATCH_RESULT_SUCCESS) { @@ -402,19 +416,30 @@ dispatcher_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) gs_unref_variant GVariant *ret = NULL; gs_free_error GError *error = NULL; NMDispatcherCallId *call_id = user_data; + gint64 now_msec; nm_assert((gpointer) source == gl.dbus_connection); + now_msec = nm_utils_get_monotonic_timestamp_msec(); + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); if (!ret) { + NMLogLevel log_level = LOGL_DEBUG; + if (_nm_dbus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed")) { g_dbus_error_strip_remote_error(error); - _LOG3W(call_id, "failed to call dispatcher scripts: %s", error->message); - } else { - _LOG3D(call_id, "failed to call dispatcher scripts: %s", error->message); + log_level = LOGL_WARN; } + _NMLOG3(log_level, + call_id, + "failed to call dispatcher scripts (after %ld.%03d sec): %s", + (long int) ((now_msec - call_id->start_at_msec) / 1000), + (int) ((now_msec - call_id->start_at_msec) % 1000), + error->message); } else { dispatcher_results_process(call_id->request_id, + call_id->start_at_msec, + now_msec, call_id->log_ifname, call_id->log_con_uuid, ret); @@ -481,6 +506,8 @@ _dispatcher_call(NMDispatcherAction action, const char *connectivity_state_string = "UNKNOWN"; const char *log_ifname; const char *log_con_uuid; + gint64 start_at_msec; + gint64 now_msec; g_return_val_if_fail(!blocking || (!callback && !user_data), FALSE); @@ -596,6 +623,8 @@ _dispatcher_call(NMDispatcherAction action, &vpn_ip6_props, nm_logging_enabled(LOGL_DEBUG, LOGD_DISPATCH)); + start_at_msec = nm_utils_get_monotonic_timestamp_msec(); + /* Send the action to the dispatcher */ if (blocking) { gs_unref_variant GVariant *ret = NULL; @@ -612,17 +641,36 @@ _dispatcher_call(NMDispatcherAction action, CALL_TIMEOUT, NULL, &error); + + now_msec = nm_utils_get_monotonic_timestamp_msec(); + if (!ret) { g_dbus_error_strip_remote_error(error); - _LOG2W(request_id, log_ifname, log_con_uuid, "failed: %s", error->message); + _LOG2W(request_id, + log_ifname, + log_con_uuid, + "failed (after %ld.%03d sec): %s", + (long int) ((now_msec - start_at_msec) / 1000), + (int) ((now_msec - start_at_msec) % 1000), + error->message); return FALSE; } - dispatcher_results_process(request_id, log_ifname, log_con_uuid, ret); + dispatcher_results_process(request_id, + start_at_msec, + now_msec, + log_ifname, + log_con_uuid, + ret); return TRUE; } - call_id = - dispatcher_call_id_new(request_id, action, callback, user_data, log_ifname, log_con_uuid); + call_id = dispatcher_call_id_new(request_id, + start_at_msec, + action, + callback, + user_data, + log_ifname, + log_con_uuid); g_dbus_connection_call(gl.dbus_connection, NM_DISPATCHER_DBUS_SERVICE, diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 8fa8059e..1311f503 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -679,8 +679,7 @@ _fw_nft_set(gboolean add, const char *ip_iface, in_addr_t addr, guint8 plen) NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, &ss1)); - stdin_buf = g_bytes_new_static(nm_str_buf_get_str(&strbuf), strbuf.len); - + stdin_buf = nm_str_buf_finalize_to_gbytes(&strbuf); _fw_nft_call_sync(stdin_buf, NULL); } diff --git a/src/core/nm-hostname-manager.c b/src/core/nm-hostname-manager.c index 64c2531e..ba0c6c9d 100644 --- a/src/core/nm-hostname-manager.c +++ b/src/core/nm-hostname-manager.c @@ -239,7 +239,7 @@ _set_hostname_read_file(NMHostnameManager *self) #if defined(HOSTNAME_PERSIST_GENTOO) hostname = read_hostname_gentoo(HOSTNAME_FILE); #elif defined(HOSTNAME_PERSIST_SLACKWARE) - hostname = read_hostname_slackware(HOSTNAME_FILE); + hostname = read_hostname_slackware(HOSTNAME_FILE); #else if (g_file_get_contents(HOSTNAME_FILE, &hostname, NULL, NULL)) g_strchomp(hostname); @@ -321,39 +321,50 @@ nm_hostname_manager_get_transient_hostname(NMHostnameManager *self, char **hostn return TRUE; } -gboolean -nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname) +/*****************************************************************************/ + +static void +_write_hostname_dbus_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + gs_unref_object GTask *task = G_TASK(user_data); + gs_unref_variant GVariant *res = NULL; + GError *error = NULL; + + res = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), result, &error); + if (!res) { + g_task_return_error(task, error); + return; + } + g_task_return_boolean(task, TRUE); +} + +static void +_write_hostname_on_idle_cb(gpointer user_data, GCancellable *cancellable) { - NMHostnameManagerPrivate *priv; - char *hostname_eol; - gboolean ret; - gs_free_error GError *error = NULL; - const char *file = HOSTNAME_FILE; - gs_free char *link_path = NULL; - gs_unref_variant GVariant *var = NULL; - struct stat file_stat; + gs_unref_object GTask *task = G_TASK(user_data); + NMHostnameManager *self; + NMHostnameManagerPrivate *priv; + const char *hostname; + gs_free char *hostname_eol = NULL; + gboolean ret; + gs_free_error GError *error = NULL; + const char *file = HOSTNAME_FILE; + gs_free char *link_path = NULL; + struct stat file_stat; #if HAVE_SELINUX gboolean fcon_was_set = FALSE; char *fcon_prev = NULL; #endif - g_return_val_if_fail(NM_IS_HOSTNAME_MANAGER(self), FALSE); + if (g_task_return_error_if_cancelled(task)) + return; + self = g_task_get_source_object(task); priv = NM_HOSTNAME_MANAGER_GET_PRIVATE(self); - if (priv->hostnamed_proxy) { - var = g_dbus_proxy_call_sync(priv->hostnamed_proxy, - "SetStaticHostname", - g_variant_new("(sb)", hostname, FALSE), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - &error); - if (error) - _LOGW("could not set hostname: %s", error->message); - - return !error; - } + nm_assert(!priv->hostnamed_proxy); + + hostname = g_task_get_task_data(task); /* If the hostname file is a symbolic link, follow it to find where the * real file is located, otherwise g_file_set_contents will attempt to @@ -363,13 +374,15 @@ nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname && (link_path = nm_utils_read_link_absolute(file, NULL))) file = link_path; + if (hostname) { #if defined(HOSTNAME_PERSIST_GENTOO) - hostname_eol = g_strdup_printf("#Generated by NetworkManager\n" - "hostname=\"%s\"\n", - hostname); + hostname_eol = g_strdup_printf("#Generated by NetworkManager\n" + "hostname=\"%s\"\n", + hostname); #else - hostname_eol = g_strdup_printf("%s\n", hostname); + hostname_eol = g_strdup_printf("%s\n", hostname); #endif + } #if HAVE_SELINUX /* Get default context for hostname file and set it for fscreate */ @@ -396,7 +409,7 @@ nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname } #endif - ret = g_file_set_contents(file, hostname_eol, -1, &error); + ret = g_file_set_contents(file, hostname_eol ?: "", -1, &error); #if HAVE_SELINUX /* Restore previous context and cleanup */ @@ -406,14 +419,63 @@ nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname freecon(fcon_prev); #endif - g_free(hostname_eol); - if (!ret) { - _LOGW("could not save hostname to %s: %s", file, error->message); - return FALSE; + g_task_return_new_error(task, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "could not save hostname to %s: %s", + file, + error->message); + return; } - return TRUE; + g_task_return_boolean(task, TRUE); +} + +void +nm_hostname_manager_write_hostname(NMHostnameManager *self, + const char *hostname, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMHostnameManagerPrivate *priv; + GTask *task; + + g_return_if_fail(NM_IS_HOSTNAME_MANAGER(self)); + + priv = NM_HOSTNAME_MANAGER_GET_PRIVATE(self); + + task = + nm_g_task_new(self, cancellable, nm_hostname_manager_write_hostname, callback, user_data); + + g_task_set_task_data(task, g_strdup(hostname), g_free); + + if (priv->hostnamed_proxy) { + g_dbus_proxy_call(priv->hostnamed_proxy, + "SetStaticHostname", + g_variant_new("(sb)", hostname ?: "", FALSE), + G_DBUS_CALL_FLAGS_NONE, + 15000, + cancellable, + _write_hostname_dbus_cb, + task); + return; + } + + nm_utils_invoke_on_idle(cancellable, _write_hostname_on_idle_cb, task); +} + +gboolean +nm_hostname_manager_write_hostname_finish(NMHostnameManager *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail(NM_IS_HOSTNAME_MANAGER(self), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_hostname_manager_write_hostname), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); } /*****************************************************************************/ diff --git a/src/core/nm-hostname-manager.h b/src/core/nm-hostname-manager.h index b871d177..31596819 100644 --- a/src/core/nm-hostname-manager.h +++ b/src/core/nm-hostname-manager.h @@ -36,7 +36,15 @@ NMHostnameManager *nm_hostname_manager_get(void); const char *nm_hostname_manager_get_static_hostname(NMHostnameManager *self); -gboolean nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname); +void nm_hostname_manager_write_hostname(NMHostnameManager *self, + const char *hostname, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean nm_hostname_manager_write_hostname_finish(NMHostnameManager *self, + GAsyncResult *result, + GError **error); void nm_hostname_manager_set_transient_hostname(NMHostnameManager *self, const char *hostname, diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index 2a722756..bcd0b62a 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -149,6 +149,8 @@ struct _NML3ConfigData { NMSettingIP6ConfigPrivacy ip6_privacy : 4; + NMMptcpFlags mptcp_flags : 18; + bool is_sealed : 1; bool has_routes_with_type_local_4_set : 1; @@ -582,6 +584,16 @@ nm_l3_config_data_log(const NML3ConfigData *self, NULL))); } + if (self->mptcp_flags != NM_MPTCP_FLAGS_NONE) { + gs_free char *s = NULL; + + _L("mptcp-flags: %s", + (s = _nm_utils_enum_to_str_full(nm_mptcp_flags_get_type(), + self->mptcp_flags, + " ", + NULL))); + } + if (self->ip6_token.id != 0) { _L("ipv6-token: %s", nm_utils_inet6_interface_identifier_to_token(&self->ip6_token, sbuf_addr)); @@ -694,6 +706,7 @@ nm_l3_config_data_new(NMDedupMultiIndex *multi_idx, int ifindex, NMIPConfigSourc .never_default_4 = NM_OPTION_BOOL_DEFAULT, .source = source, .ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, + .mptcp_flags = NM_MPTCP_FLAGS_NONE, .ndisc_hop_limit_set = FALSE, .ndisc_reachable_time_msec_set = FALSE, .ndisc_retrans_timer_msec_set = FALSE, @@ -1875,6 +1888,30 @@ nm_l3_config_data_set_ip6_token(NML3ConfigData *self, NMUtilsIPv6IfaceId ipv6_to return TRUE; } +NMMptcpFlags +nm_l3_config_data_get_mptcp_flags(const NML3ConfigData *self) +{ + nm_assert(!self || _NM_IS_L3_CONFIG_DATA(self, TRUE)); + + if (!self) + return NM_MPTCP_FLAGS_NONE; + + return self->mptcp_flags; +} + +gboolean +nm_l3_config_data_set_mptcp_flags(NML3ConfigData *self, NMMptcpFlags mptcp_flags) +{ + nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); + nm_assert(!NM_FLAGS_ANY(mptcp_flags, ~_NM_MPTCP_FLAGS_ALL)); + + if (self->mptcp_flags == mptcp_flags) + return FALSE; + self->mptcp_flags = mptcp_flags; + nm_assert(self->mptcp_flags == mptcp_flags); + return TRUE; +} + NMProxyConfigMethod nm_l3_config_data_get_proxy_method(const NML3ConfigData *self) { @@ -2324,6 +2361,7 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a, NM_CMP_DIRECT_REF_STRING(a->proxy_pac_url, b->proxy_pac_url); NM_CMP_DIRECT_REF_STRING(a->proxy_pac_script, b->proxy_pac_script); NM_CMP_DIRECT_UNSAFE(a->ip6_privacy, b->ip6_privacy); + NM_CMP_DIRECT_UNSAFE(a->mptcp_flags, b->mptcp_flags); NM_CMP_DIRECT_UNSAFE(a->ndisc_hop_limit_set, b->ndisc_hop_limit_set); if (a->ndisc_hop_limit_set) @@ -2613,9 +2651,6 @@ nm_l3_config_data_add_dependent_device_routes(NML3ConfigData *self, const gboolean has_peer = !IN6_IS_ADDR_UNSPECIFIED(&addr_src->a6.peer_address); int routes_i; - if (addr_src->ax.plen == 0) - continue; - if (NM_FLAGS_HAS(addr_src->a6.n_ifa_flags, IFA_F_NOPREFIXROUTE)) continue; @@ -2999,10 +3034,10 @@ nm_l3_config_data_merge(NML3ConfigData *self, gpointer hook_user_data) { static const guint32 x_default_route_metric_x[2] = {NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, - NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4}; + NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4}; static const guint32 x_default_route_penalty_x[2] = {0, 0}; static const int x_default_dns_priority_x[2] = {NM_DNS_PRIORITY_DEFAULT_NORMAL, - NM_DNS_PRIORITY_DEFAULT_NORMAL}; + NM_DNS_PRIORITY_DEFAULT_NORMAL}; guint32 default_route_table_coerced_x[2]; NMDedupMultiIter iter; const NMPObject *obj; @@ -3245,6 +3280,9 @@ nm_l3_config_data_merge(NML3ConfigData *self, if (self->ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) self->ip6_privacy = src->ip6_privacy; + if (self->mptcp_flags == NM_MPTCP_FLAGS_NONE) + self->mptcp_flags = src->mptcp_flags; + if (!self->ndisc_hop_limit_set && src->ndisc_hop_limit_set) { self->ndisc_hop_limit_set = TRUE; self->ndisc_hop_limit_val = src->ndisc_hop_limit_val; diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index 20a32c62..ca42d4a1 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -487,6 +487,10 @@ NMUtilsIPv6IfaceId nm_l3_config_data_get_ip6_token(const NML3ConfigData *self); gboolean nm_l3_config_data_set_ip6_token(NML3ConfigData *self, NMUtilsIPv6IfaceId ipv6_token); +NMMptcpFlags nm_l3_config_data_get_mptcp_flags(const NML3ConfigData *self); + +gboolean nm_l3_config_data_set_mptcp_flags(NML3ConfigData *self, NMMptcpFlags mptcp_flags); + const in_addr_t *nm_l3_config_data_get_wins(const NML3ConfigData *self, guint *out_len); gboolean nm_l3_config_data_add_wins(NML3ConfigData *self, in_addr_t wins); diff --git a/src/core/nm-l3-ipv4ll.c b/src/core/nm-l3-ipv4ll.c index 2aedab56..3fc1e165 100644 --- a/src/core/nm-l3-ipv4ll.c +++ b/src/core/nm-l3-ipv4ll.c @@ -672,7 +672,9 @@ _ipv4ll_platform_find_addr(NML3IPv4LL *self, const NML3AcdAddrInfo **out_acd_inf const NML3AcdAddrInfo *acd_info; const NMPlatformIP4Address *addr; - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, nm_l3_ipv4ll_get_ifindex(self)); + nmp_lookup_init_object_by_ifindex(&lookup, + NMP_OBJECT_TYPE_IP4_ADDRESS, + nm_l3_ipv4ll_get_ifindex(self)); nm_platform_iter_obj_for_each (&iter, nm_l3_ipv4ll_get_platform(self), &lookup, &obj) { addr = NMP_OBJECT_CAST_IP4_ADDRESS(obj); if (!_ip4_address_is_link_local(addr)) diff --git a/src/core/nm-l3-ipv6ll.c b/src/core/nm-l3-ipv6ll.c index 2b9a1a0e..2640c075 100644 --- a/src/core/nm-l3-ipv6ll.c +++ b/src/core/nm-l3-ipv6ll.c @@ -343,7 +343,9 @@ _pladdr_find_ll(NML3IPv6LL *self, gboolean *out_cur_addr_failed) if (!NM_IN_SET(self->state, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, NM_L3_IPV6LL_STATE_READY)) cur_addr_check = FALSE; - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, nm_l3_ipv6ll_get_ifindex(self)); + nmp_lookup_init_object_by_ifindex(&lookup, + NMP_OBJECT_TYPE_IP6_ADDRESS, + nm_l3_ipv6ll_get_ifindex(self)); nm_platform_iter_obj_for_each (&iter, nm_l3_ipv6ll_get_platform(self), &lookup, &obj) { const NMPlatformIP6Address *pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(obj); diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index af6099e9..69ea0d26 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -4,6 +4,8 @@ #include "nm-l3cfg.h" +#include "libnm-std-aux/nm-linux-compat.h" + #include <net/if.h> #include <linux/if_addr.h> #include <linux/if_ether.h> @@ -12,7 +14,7 @@ #include "libnm-glib-aux/nm-time-utils.h" #include "libnm-platform/nm-platform.h" #include "libnm-platform/nmp-object.h" -#include "libnm-platform/nmp-route-manager.h" +#include "libnm-platform/nmp-global-tracker.h" #include "nm-netns.h" #include "n-acd/src/n-acd.h" #include "nm-l3-ipv4ll.h" @@ -37,7 +39,6 @@ G_STATIC_ASSERT(NM_ACD_TIMEOUT_RFC5227_MSEC == N_ACD_TIMEOUT_RFC5227); #define ACD_ENSURE_RATELIMIT_MSEC ((guint32) 4000u) #define ACD_WAIT_PROBING_EXTRA_TIME_MSEC ((guint32) (1000u + ACD_ENSURE_RATELIMIT_MSEC)) #define ACD_WAIT_PROBING_EXTRA_TIME2_MSEC ((guint32) 1000u) -#define ACD_MAX_TIMEOUT_MSEC ((guint32) 30000u) #define ACD_WAIT_TIME_PROBING_FULL_RESTART_MSEC ((guint32) 30000u) #define ACD_WAIT_TIME_CONFLICT_RESTART_MSEC ((guint32) 120000u) #define ACD_WAIT_TIME_ANNOUNCE_RESTART_MSEC ((guint32) 30000u) @@ -271,6 +272,15 @@ typedef struct _NML3CfgPrivate { NMIPConfig *ipconfig_x[2]; }; + /* Whether we earlier configured MPTCP endpoints for the interface. */ + union { + struct { + bool mptcp_set_6; + bool mptcp_set_4; + }; + bool mptcp_set_x[2]; + }; + /* This is for rate-limiting the creation of nacd instance. */ GSource *nacd_instance_ensure_retry; @@ -312,6 +322,9 @@ typedef struct _NML3CfgPrivate { bool changed_configs_configs : 1; bool changed_configs_acd_state : 1; + + bool rp_filter_handled : 1; + bool rp_filter_set : 1; } NML3CfgPrivate; struct _NML3CfgClass { @@ -322,6 +335,13 @@ G_DEFINE_TYPE(NML3Cfg, nm_l3cfg, G_TYPE_OBJECT) /*****************************************************************************/ +#define _NODEV_ROUTES_TAG(self, IS_IPv4) \ + ((gconstpointer) (&(((const char *) (self))[0 + (!(IS_IPv4))]))) + +#define _MPTCP_TAG(self, IS_IPv4) ((gconstpointer) (&(((const char *) (self))[2 + (!(IS_IPv4))]))) + +/*****************************************************************************/ + #define _NMLOG_DOMAIN LOGD_CORE #define _NMLOG_PREFIX_NAME "l3cfg" #define _NMLOG(level, ...) \ @@ -1880,10 +1900,10 @@ _l3_acd_data_add(NML3Cfg *self, acd_data = _l3_acd_data_find(self, addr); - if (acd_timeout_msec > ACD_MAX_TIMEOUT_MSEC) { + if (acd_timeout_msec > NM_ACD_TIMEOUT_MAX_MSEC) { /* we limit the maximum timeout. Otherwise we have to handle integer overflow * when adding timeouts. */ - acd_timeout_msec = ACD_MAX_TIMEOUT_MSEC; + acd_timeout_msec = NM_ACD_TIMEOUT_MAX_MSEC; } if (!acd_data) { @@ -3243,8 +3263,8 @@ nm_l3cfg_add_config(NML3Cfg *self, nm_assert(l3cd); nm_assert(nm_l3_config_data_get_ifindex(l3cd) == self->priv.ifindex); - if (acd_timeout_msec > ACD_MAX_TIMEOUT_MSEC) - acd_timeout_msec = ACD_MAX_TIMEOUT_MSEC; + if (acd_timeout_msec > NM_ACD_TIMEOUT_MAX_MSEC) + acd_timeout_msec = NM_ACD_TIMEOUT_MAX_MSEC; nm_assert(NM_IN_SET(acd_defend_type, NM_L3_ACD_DEFEND_TYPE_NEVER, @@ -3451,15 +3471,13 @@ nm_l3cfg_remove_config_all_dirty(NML3Cfg *self, gconstpointer tag) /*****************************************************************************/ -#define _NODEV_ROUTES_TAG(self, IS_IPv4) ((gconstpointer) (&(&(self)->priv.route_manager)[IS_IPv4])) - static gboolean _nodev_routes_untrack(NML3Cfg *self, int addr_family) { - return nmp_route_manager_untrack_all(self->priv.route_manager, - _NODEV_ROUTES_TAG(self, NM_IS_IPv4(addr_family)), - FALSE, - TRUE); + return nmp_global_tracker_untrack_all(self->priv.global_tracker, + _NODEV_ROUTES_TAG(self, NM_IS_IPv4(addr_family)), + FALSE, + TRUE); } static void @@ -3479,12 +3497,12 @@ _nodev_routes_sync(NML3Cfg *self, for (i = 0; i < routes_nodev->len; i++) { const NMPObject *obj = routes_nodev->pdata[i]; - if (nmp_route_manager_track(self->priv.route_manager, - obj_type, - NMP_OBJECT_CAST_IP_ROUTE(obj), - 1, - _NODEV_ROUTES_TAG(self, IS_IPv4), - NULL)) + if (nmp_global_tracker_track(self->priv.global_tracker, + obj_type, + NMP_OBJECT_CAST_IP_ROUTE(obj), + 1, + _NODEV_ROUTES_TAG(self, IS_IPv4), + NULL)) changed = TRUE; } @@ -3492,8 +3510,11 @@ out_clear: if (_nodev_routes_untrack(self, addr_family)) changed = TRUE; - if (changed || commit_type >= NM_L3_CFG_COMMIT_TYPE_REAPPLY) - nmp_route_manager_sync(self->priv.route_manager, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), FALSE); + if (changed || commit_type >= NM_L3_CFG_COMMIT_TYPE_REAPPLY) { + nmp_global_tracker_sync(self->priv.global_tracker, + NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), + FALSE); + } } /*****************************************************************************/ @@ -4182,6 +4203,259 @@ _l3_commit_ip6_token(NML3Cfg *self, NML3CfgCommitType commit_type) } } +/*****************************************************************************/ + +static void +_rp_filter_update(NML3Cfg *self, gboolean reapply) +{ + gboolean rp_filter_relax = FALSE; + const char *ifname; + int rf_val; + + /* The rp_filter sysctl is only an IPv4 thing. We only enable the rp-filter + * handling, if we did anything to MPTCP about IPv4 addresses. + * + * While we only have one "connection.mptcp-flags=enabled" property, whether + * we handle MPTCP is still tracked per AF. In particular, with "enabled-on-global-iface" + * flag, which honors the AF-specific default route. */ + if (self->priv.p->mptcp_set_4) + rp_filter_relax = TRUE; + + if (!rp_filter_relax) { + if (self->priv.p->rp_filter_handled) { + self->priv.p->rp_filter_handled = FALSE; + if (self->priv.p->rp_filter_set) { + self->priv.p->rp_filter_set = FALSE; + + ifname = nm_l3cfg_get_ifname(self, TRUE); + rf_val = nm_platform_sysctl_ip_conf_get_rp_filter_ipv4(self->priv.platform, + ifname, + FALSE, + NULL); + if (rf_val == 2) { + /* We only relaxed from 1 to 2. Only if that is still the case, reset. */ + nm_platform_sysctl_ip_conf_set(self->priv.platform, + AF_INET, + ifname, + "rp_filter", + "1"); + } + } + } + return; + } + + if (self->priv.p->rp_filter_handled && !reapply) { + /* We only set rp_filter once (except reapply). */ + return; + } + + /* No matter whether we actually reset the value below, we set the "handled" flag + * so that we only do this once (except during reapply). */ + self->priv.p->rp_filter_handled = TRUE; + + ifname = nm_l3cfg_get_ifname(self, TRUE); + + rf_val = + nm_platform_sysctl_ip_conf_get_rp_filter_ipv4(self->priv.platform, ifname, FALSE, NULL); + + if (rf_val != 1) { + /* We only relax from strict (1) to loose (2). No other transition. Nothing to do. */ + return; + } + + /* We actually loosen the flag. We need to remember to reset it. */ + self->priv.p->rp_filter_set = TRUE; + nm_platform_sysctl_ip_conf_set(self->priv.platform, AF_INET, ifname, "rp_filter", "2"); +} + +/*****************************************************************************/ + +static gboolean +_global_tracker_mptcp_untrack(NML3Cfg *self, int addr_family) +{ + return nmp_global_tracker_untrack_all(self->priv.global_tracker, + _MPTCP_TAG(self, NM_IS_IPv4(addr_family)), + FALSE, + TRUE); +} + +static gboolean +_l3_commit_mptcp_af(NML3Cfg *self, + NML3CfgCommitType commit_type, + int addr_family, + gboolean *out_reapply) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMMptcpFlags mptcp_flags; + gboolean reapply = FALSE; + gboolean changed = FALSE; + + nm_assert(NM_IS_L3CFG(self)); + nm_assert(NM_IN_SET(commit_type, + NM_L3_CFG_COMMIT_TYPE_NONE, + NM_L3_CFG_COMMIT_TYPE_REAPPLY, + NM_L3_CFG_COMMIT_TYPE_UPDATE)); + + if (commit_type >= NM_L3_CFG_COMMIT_TYPE_REAPPLY) + reapply = TRUE; + + if (commit_type != NM_L3_CFG_COMMIT_TYPE_NONE && self->priv.p->combined_l3cd_commited) + mptcp_flags = nm_l3_config_data_get_mptcp_flags(self->priv.p->combined_l3cd_commited); + else + mptcp_flags = NM_MPTCP_FLAGS_DISABLED; + + if (mptcp_flags == NM_MPTCP_FLAGS_NONE || NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_DISABLED)) + mptcp_flags = NM_MPTCP_FLAGS_DISABLED; + else if (!NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_ALSO_WITHOUT_DEFAULT_ROUTE)) { + /* Whether MPTCP is enabled/disabled (per address family), depends on whether we have a unicast + * default route (in the main routing table). */ + if (self->priv.p->combined_l3cd_commited + && nm_l3_config_data_get_best_default_route(self->priv.p->combined_l3cd_commited, + addr_family)) + mptcp_flags = NM_FLAGS_UNSET(mptcp_flags, NM_MPTCP_FLAGS_ALSO_WITHOUT_DEFAULT_ROUTE) + | NM_MPTCP_FLAGS_ENABLED; + else + mptcp_flags = NM_MPTCP_FLAGS_DISABLED; + } else + mptcp_flags |= NM_MPTCP_FLAGS_ENABLED; + + if (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_DISABLED)) { + if (!self->priv.p->mptcp_set_x[IS_IPv4] && !reapply) { + /* Nothing to configure, and we did not earlier configure MPTCP. Nothing to do. */ + NM_SET_OUT(out_reapply, FALSE); + return FALSE; + } + + self->priv.p->mptcp_set_x[IS_IPv4] = FALSE; + reapply = TRUE; + } else { + const NMPlatformIPXAddress *addr; + NMDedupMultiIter iter; + const guint32 FLAGS = + (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_SIGNAL) ? MPTCP_PM_ADDR_FLAG_SIGNAL : 0) + | (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_SUBFLOW) ? MPTCP_PM_ADDR_FLAG_SUBFLOW : 0) + | (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_BACKUP) ? MPTCP_PM_ADDR_FLAG_BACKUP : 0) + | (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_FULLMESH) ? MPTCP_PM_ADDR_FLAG_FULLMESH + : 0); + NMPlatformMptcpAddr a = { + .ifindex = self->priv.ifindex, + .id = 0, + .flags = FLAGS, + .addr_family = addr_family, + .port = 0, + }; + gboolean any_tracked = FALSE; + + self->priv.p->mptcp_set_x[IS_IPv4] = TRUE; + + if (self->priv.p->combined_l3cd_commited) { + gint32 addr_prio; + + addr_prio = 100; + + nm_l3_config_data_iter_ip_address_for_each (&iter, + self->priv.p->combined_l3cd_commited, + addr_family, + (const NMPlatformIPAddress **) &addr) { + /* We want to evaluate the with-{loopback,link_local}-{4,6} flags based on the actual + * ifa_scope that the address will have once we configure it. + * "addr" is an address we want to configure, we expect that it will + * later have the scope nm_platform_ip_address_get_scope() based on + * the address. */ + switch (nm_platform_ip_address_get_scope(addr_family, addr->ax.address_ptr)) { + case RT_SCOPE_HOST: + goto skip_addr; + case RT_SCOPE_LINK: + goto skip_addr; + default: + if (IS_IPv4) { + /* We take all addresses, including rfc1918 private addresses + * (nm_utils_ip_is_site_local()). */ + } else { + if (nm_utils_ip6_is_ula(&addr->a6.address)) { + /* Exclude unique local IPv6 addresses fc00::/7. */ + goto skip_addr; + } else { + /* We take all other addresses, including deprecated IN6_IS_ADDR_SITELOCAL() + * (fec0::/10). */ + } + } + break; + } + + a.addr = nm_ip_addr_init(addr_family, addr->ax.address_ptr); + + /* We track the address with different priorities, that depends + * on the order in which they are listed here. NMPGlobalTracker + * will sort all tracked addresses by priority. That means, if we + * have multiple interfaces then we will prefer the first address + * of those interfaces, then the second, etc. + * + * That is relevant, because the overall number of addresses we + * can configure in kernel is strongly limited (MPTCP_PM_ADDR_MAX). */ + if (addr_prio > 10) + addr_prio--; + + if (nmp_global_tracker_track(self->priv.global_tracker, + NMP_OBJECT_TYPE_MPTCP_ADDR, + &a, + addr_prio, + _MPTCP_TAG(self, IS_IPv4), + NULL)) + changed = TRUE; + + any_tracked = TRUE; + +skip_addr: + (void) 0; + } + } + + if (!any_tracked) { + /* We need to make it known that this ifindex is used. Track a dummy object. */ + if (nmp_global_tracker_track( + self->priv.global_tracker, + NMP_OBJECT_TYPE_MPTCP_ADDR, + nmp_global_tracker_mptcp_addr_init_for_ifindex(&a, self->priv.ifindex), + 1, + _MPTCP_TAG(self, IS_IPv4), + NULL)) + changed = TRUE; + } + } + + if (_global_tracker_mptcp_untrack(self, addr_family)) + changed = TRUE; + + NM_SET_OUT(out_reapply, reapply); + return changed || reapply; +} + +static void +_l3_commit_mptcp(NML3Cfg *self, NML3CfgCommitType commit_type) +{ + gboolean changed = FALSE; + gboolean reapply = FALSE; + gboolean i_reapply = FALSE; + + if (_l3_commit_mptcp_af(self, commit_type, AF_INET, &i_reapply)) + changed = TRUE; + reapply |= i_reapply; + if (_l3_commit_mptcp_af(self, commit_type, AF_INET6, &i_reapply)) + changed = TRUE; + reapply |= i_reapply; + + nm_assert(commit_type < NM_L3_CFG_COMMIT_TYPE_REAPPLY || reapply); + + if (changed) + nmp_global_tracker_sync_mptcp_addrs(self->priv.global_tracker, reapply); + else + nm_assert(!reapply); + + _rp_filter_update(self, reapply); +} + static gboolean _l3_commit_one(NML3Cfg *self, int addr_family, @@ -4380,6 +4654,8 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle) _l3_commit_one(self, AF_INET, commit_type, changed_combined_l3cd, l3cd_old); _l3_commit_one(self, AF_INET6, commit_type, changed_combined_l3cd, l3cd_old); + _l3_commit_mptcp(self, commit_type); + _l3_acd_data_process_changes(self); if (self->priv.p->l3_config_datas) { @@ -4626,7 +4902,9 @@ nm_l3cfg_has_commited_ip6_addresses_pending_dad(NML3Cfg *self) * Of course, all lookups are O(1) anyway, so in any case the operation is * O(n) (once "n" being the addresses in platform, and once in l3cd). */ - nmp_lookup_init_object(&plat_lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, self->priv.ifindex); + nmp_lookup_init_object_by_ifindex(&plat_lookup, + NMP_OBJECT_TYPE_IP6_ADDRESS, + self->priv.ifindex); nm_platform_iter_obj_for_each (&iter, self->priv.platform, &plat_lookup, &plat_obj) { const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj); @@ -4763,7 +5041,8 @@ constructed(GObject *object) self->priv.platform = g_object_ref(nm_netns_get_platform(self->priv.netns)); nm_assert(NM_IS_PLATFORM(self->priv.platform)); - self->priv.route_manager = nmp_route_manager_ref(nm_netns_get_route_manager(self->priv.netns)); + self->priv.global_tracker = + nmp_global_tracker_ref(nm_netns_get_global_tracker(self->priv.netns)); _LOGT("created (netns=" NM_HASH_OBFUSCATE_PTR_FMT ")", NM_HASH_OBFUSCATE_PTR(self->priv.netns)); @@ -4785,6 +5064,7 @@ static void finalize(GObject *object) { NML3Cfg *self = NM_L3CFG(object); + gboolean changed; nm_assert(!self->priv.p->ipconfig_4); nm_assert(!self->priv.p->ipconfig_6); @@ -4818,13 +5098,21 @@ finalize(GObject *object) nm_assert(c_list_is_empty(&self->priv.p->obj_state_zombie_lst_head)); if (_nodev_routes_untrack(self, AF_INET)) - nmp_route_manager_sync(self->priv.route_manager, NMP_OBJECT_TYPE_IP4_ROUTE, FALSE); + nmp_global_tracker_sync(self->priv.global_tracker, NMP_OBJECT_TYPE_IP4_ROUTE, FALSE); if (_nodev_routes_untrack(self, AF_INET6)) - nmp_route_manager_sync(self->priv.route_manager, NMP_OBJECT_TYPE_IP6_ROUTE, FALSE); + nmp_global_tracker_sync(self->priv.global_tracker, NMP_OBJECT_TYPE_IP6_ROUTE, FALSE); + + changed = FALSE; + if (_global_tracker_mptcp_untrack(self, AF_INET)) + changed = TRUE; + if (_global_tracker_mptcp_untrack(self, AF_INET6)) + changed = TRUE; + if (changed) + nmp_global_tracker_sync_mptcp_addrs(self->priv.global_tracker, FALSE); g_clear_object(&self->priv.netns); g_clear_object(&self->priv.platform); - nm_clear_pointer(&self->priv.route_manager, nmp_route_manager_unref); + nm_clear_pointer(&self->priv.global_tracker, nmp_global_tracker_unref); nm_clear_l3cd(&self->priv.p->combined_l3cd_merged); nm_clear_l3cd(&self->priv.p->combined_l3cd_commited); diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index 215d21e9..e0257623 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -10,6 +10,7 @@ #define NM_L3CFG_CONFIG_PRIORITY_IPV6LL 1 #define NM_L3CFG_CONFIG_PRIORITY_VPN 9 #define NM_ACD_TIMEOUT_RFC5227_MSEC 9000u +#define NM_ACD_TIMEOUT_MAX_MSEC 30000u #define NM_TYPE_L3CFG (nm_l3cfg_get_type()) #define NM_L3CFG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_L3CFG, NML3Cfg)) @@ -195,18 +196,18 @@ typedef struct { } NML3ConfigNotifyData; struct _NML3CfgPrivate; -struct _NMPRouteManager; +struct _NMPGlobalTracker; struct _NML3Cfg { GObject parent; struct { - struct _NML3CfgPrivate *p; - NMNetns *netns; - NMPlatform *platform; - struct _NMPRouteManager *route_manager; - const NMPObject *plobj; - const NMPObject *plobj_next; - int ifindex; + struct _NML3CfgPrivate *p; + NMNetns *netns; + NMPlatform *platform; + struct _NMPGlobalTracker *global_tracker; + const NMPObject *plobj; + const NMPObject *plobj_next; + int ifindex; } priv; }; diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index 52153697..822df7ad 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -20,6 +20,7 @@ #include "devices/nm-device-factory.h" #include "devices/nm-device-generic.h" #include "devices/nm-device.h" +#include "dns/nm-dns-manager.h" #include "dhcp/nm-dhcp-manager.h" #include "libnm-core-aux-intern/nm-common-macros.h" #include "libnm-core-intern/nm-core-internal.h" @@ -144,6 +145,9 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMManager, typedef struct { NMPlatform *platform; + NMDnsManager *dns_mgr; + gulong dns_mgr_update_pending_signal_id; + GArray *capabilities; CList active_connections_lst_head; /* Oldest ACs at the beginning */ @@ -209,6 +213,10 @@ typedef struct { unsigned connectivity_check_enabled_last : 2; + /* List of GDBusMethodInvocation of in progress Sleep() and Enable() + * calls. They return only if all in-flight deactivations finished. */ + GSList *sleep_invocations; + guint delete_volatile_connection_idle_id; CList delete_volatile_connection_lst_head; } NMManagerPrivate; @@ -346,6 +354,9 @@ static NMActiveConnection *_new_active_connection(NMManager *self, static void policy_activating_ac_changed(GObject *object, GParamSpec *pspec, gpointer user_data); +static void device_has_pending_action_changed(NMDevice *device, GParamSpec *pspec, NMManager *self); +static void check_if_startup_complete(NMManager *self); + static gboolean find_master(NMManager *self, NMConnection *connection, NMDevice *device, @@ -1601,7 +1612,11 @@ manager_device_state_changed(NMDevice *device, nm_settings_device_added(priv->settings, device); } -static void device_has_pending_action_changed(NMDevice *device, GParamSpec *pspec, NMManager *self); +static void +_dns_mgr_update_pending_cb(NMDevice *device, GParamSpec *pspec, NMManager *self) +{ + check_if_startup_complete(self); +} static void check_if_startup_complete(NMManager *self) @@ -1616,6 +1631,20 @@ check_if_startup_complete(NMManager *self) if (!priv->devices_inited) return; + if (nm_dns_manager_get_update_pending(nm_manager_get_dns_manager(self))) { + if (priv->dns_mgr_update_pending_signal_id == 0) { + priv->dns_mgr_update_pending_signal_id = + g_signal_connect(nm_manager_get_dns_manager(self), + "notify::" NM_DNS_MANAGER_UPDATE_PENDING, + G_CALLBACK(_dns_mgr_update_pending_cb), + self); + } + return; + } + + nm_clear_g_signal_handler(nm_manager_get_dns_manager(self), + &priv->dns_mgr_update_pending_signal_id); + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { reason = nm_device_has_pending_action_reason(device); if (reason) { @@ -2221,6 +2250,18 @@ connection_updated_cb(NMSettings *settings, connection_changed(self, sett_conn); } +static void +connections_changed(NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + NMSettingsConnection *const *connections; + guint i; + + connections = nm_settings_get_connections_sorted_by_autoconnect_priority(priv->settings, NULL); + for (i = 0; connections[i]; i++) + connection_changed(self, connections[i]); +} + /*****************************************************************************/ static void @@ -6336,6 +6377,22 @@ done: g_clear_object(&subject); } +static void +sleep_devices_check_empty(NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + GDBusMethodInvocation *invocation; + + if (g_hash_table_size(priv->sleep_devices) > 0) + return; + + while (priv->sleep_invocations) { + invocation = priv->sleep_invocations->data; + g_dbus_method_invocation_return_value(invocation, NULL); + priv->sleep_invocations = g_slist_remove(priv->sleep_invocations, invocation); + } +} + static gboolean sleep_devices_add(NMManager *self, NMDevice *device, gboolean suspending) { @@ -6378,6 +6435,9 @@ sleep_devices_remove(NMManager *self, NMDevice *device) g_signal_handlers_disconnect_by_func(device, device_sleep_cb, self); g_hash_table_remove(priv->sleep_devices, device); g_object_unref(device); + + sleep_devices_check_empty(self); + return TRUE; } @@ -6389,9 +6449,6 @@ sleep_devices_clear(NMManager *self) NMSleepMonitorInhibitorHandle *handle; GHashTableIter iter; - if (!priv->sleep_devices) - return; - g_hash_table_iter_init(&iter, priv->sleep_devices); while (g_hash_table_iter_next(&iter, (gpointer *) &device, (gpointer *) &handle)) { g_signal_handlers_disconnect_by_func(device, device_sleep_cb, self); @@ -6400,6 +6457,8 @@ sleep_devices_clear(NMManager *self) g_object_unref(device); g_hash_table_iter_remove(&iter); } + + sleep_devices_check_empty(self); } static void @@ -6521,7 +6580,7 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed) && !nm_device_get_unmanaged_flags(device, NM_UNMANAGED_SLEEPING)) { /* DHCP leases of software devices could have gone stale * so we need to renew them. */ - nm_device_update_dynamic_ip_setup(device); + nm_device_update_dynamic_ip_setup(device, "wake up"); continue; } @@ -6551,6 +6610,10 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed) FALSE, NM_DEVICE_STATE_REASON_NOW_MANAGED); } + + /* Give the connections a chance to recreate the virtual devices. + * We've torn them down on sleep. */ + connections_changed(self); } nm_manager_update_state(self); @@ -6628,7 +6691,10 @@ impl_manager_sleep(NMDBusObject *obj, TRUE, subject, NULL); - g_dbus_method_invocation_return_value(invocation, NULL); + + priv->sleep_invocations = g_slist_prepend(priv->sleep_invocations, invocation); + sleep_devices_check_empty(self); + return; } @@ -6668,10 +6734,11 @@ _internal_enable(NMManager *self, gboolean enable) static void enable_net_done_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) { - NMManager *self = NM_MANAGER(user_data); - NMAuthCallResult result; - gboolean enable; - NMAuthSubject *subject; + NMManager *self = NM_MANAGER(user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + NMAuthCallResult result; + gboolean enable; + NMAuthSubject *subject; nm_assert(G_IS_DBUS_METHOD_INVOCATION(context)); @@ -6696,8 +6763,10 @@ enable_net_done_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer } _internal_enable(self, enable); - g_dbus_method_invocation_return_value(context, NULL); nm_audit_log_control_op(NM_AUDIT_OP_NET_CONTROL, enable ? "on" : "off", TRUE, subject, NULL); + + priv->sleep_invocations = g_slist_prepend(priv->sleep_invocations, context); + sleep_devices_check_empty(self); } static void @@ -7014,10 +7083,6 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde guint32 route_metric_default_aspired; guint32 route_metric_default_effective; NMTernary nm_owned; - NMDhcpConfig *dhcp_config; - const char *next_server = NULL; - const char *root_path = NULL; - const char *dhcp_bootfile = NULL; NM_SET_OUT(out_ifindex, 0); @@ -7059,15 +7124,6 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde TRUE, &route_metric_default_aspired); - dhcp_config = nm_device_get_dhcp_config(device, AF_INET); - if (dhcp_config) { - root_path = nm_dhcp_config_get_option(dhcp_config, "root_path"); - next_server = nm_dhcp_config_get_option(dhcp_config, "next_server"); - dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "filename"); - if (!dhcp_bootfile) - dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "bootfile_name"); - } - if (!nm_config_device_state_write(ifindex, managed_type, perm_hw_addr_fake, @@ -7075,9 +7131,8 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde nm_owned, route_metric_default_aspired, route_metric_default_effective, - next_server, - root_path, - dhcp_bootfile)) + nm_device_get_dhcp_config(device, AF_INET), + nm_device_get_dhcp_config(device, AF_INET6))) return FALSE; NM_SET_OUT(out_ifindex, ifindex); @@ -7119,9 +7174,8 @@ devices_inited_cb(gpointer user_data) gboolean nm_manager_start(NMManager *self, GError **error) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMSettingsConnection *const *connections; - guint i; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + guint i; nm_device_factory_manager_load_factories(_register_device_factory, self); @@ -7175,9 +7229,10 @@ nm_manager_start(NMManager *self, GError **error) NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK(connection_updated_cb), self); - connections = nm_settings_get_connections_sorted_by_autoconnect_priority(priv->settings, NULL); - for (i = 0; connections[i]; i++) - connection_changed(self, connections[i]); + + /* Make sure virtual devices for all connections are created so + * that they could be autoconnected. */ + connections_changed(self); nm_clear_g_source(&priv->devices_inited_id); priv->devices_inited_id = g_idle_add_full(G_PRIORITY_LOW + 10, devices_inited_cb, self, NULL); @@ -7790,6 +7845,28 @@ impl_manager_checkpoint_adjust_rollback_timeout(NMDBusObject /*****************************************************************************/ +NMDnsManager * +nm_manager_get_dns_manager(NMManager *self) +{ + NMManagerPrivate *priv; + + g_return_val_if_fail(NM_IS_MANAGER(self), NULL); + + priv = NM_MANAGER_GET_PRIVATE(self); + + if (G_UNLIKELY(!priv->dns_mgr)) { + /* Initialize lazily on first use. + * + * But keep a reference. This is to ensure proper lifetimes between + * singleton instances (i.e. nm_dns_manager_get() outlives NMManager). */ + priv->dns_mgr = g_object_ref(nm_dns_manager_get()); + } + + return priv->dns_mgr; +} + +/*****************************************************************************/ + static void auth_mgr_changed(NMAuthManager *auth_manager, gpointer user_data) { @@ -8250,6 +8327,9 @@ dispose(GObject *object) g_clear_object(&priv->concheck_mgr); } + nm_clear_g_signal_handler(priv->dns_mgr, &priv->dns_mgr_update_pending_signal_id); + g_clear_object(&priv->dns_mgr); + if (priv->auth_mgr) { g_signal_handlers_disconnect_by_func(priv->auth_mgr, G_CALLBACK(auth_mgr_changed), self); g_clear_object(&priv->auth_mgr); @@ -8300,8 +8380,10 @@ dispose(GObject *object) g_clear_object(&priv->vpn_manager); - sleep_devices_clear(self); - nm_clear_pointer(&priv->sleep_devices, g_hash_table_unref); + if (priv->sleep_devices) { + sleep_devices_clear(self); + nm_clear_pointer(&priv->sleep_devices, g_hash_table_unref); + } if (priv->sleep_monitor) { g_signal_handlers_disconnect_by_func(priv->sleep_monitor, sleeping_cb, self); diff --git a/src/core/nm-manager.h b/src/core/nm-manager.h index f8563c3a..fcb00227 100644 --- a/src/core/nm-manager.h +++ b/src/core/nm-manager.h @@ -200,6 +200,10 @@ NMMetered nm_manager_get_metered(NMManager *self); void nm_manager_notify_device_availability_maybe_changed(NMManager *self); +struct _NMDnsManager; + +struct _NMDnsManager *nm_manager_get_dns_manager(NMManager *self); + /*****************************************************************************/ void nm_manager_device_auth_request(NMManager *self, diff --git a/src/core/nm-netns.c b/src/core/nm-netns.c index f120e43d..5ee63152 100644 --- a/src/core/nm-netns.c +++ b/src/core/nm-netns.c @@ -15,21 +15,21 @@ #include "nm-l3cfg.h" #include "libnm-platform/nm-platform.h" #include "libnm-platform/nmp-netns.h" -#include "libnm-platform/nmp-route-manager.h" +#include "libnm-platform/nmp-global-tracker.h" /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PLATFORM, ); typedef struct { - NMNetns *_self_signal_user_data; - NMPlatform *platform; - NMPNetns *platform_netns; - NMPRouteManager *route_manager; - GHashTable *l3cfgs; - GHashTable *shared_ips; - CList l3cfg_signal_pending_lst_head; - GSource *signal_pending_idle_source; + NMNetns *_self_signal_user_data; + NMPlatform *platform; + NMPNetns *platform_netns; + NMPGlobalTracker *global_tracker; + GHashTable *l3cfgs; + GHashTable *shared_ips; + CList l3cfg_signal_pending_lst_head; + GSource *signal_pending_idle_source; } NMNetnsPrivate; struct _NMNetns { @@ -79,10 +79,10 @@ nm_netns_get_platform(NMNetns *self) return NM_NETNS_GET_PRIVATE(self)->platform; } -NMPRouteManager * -nm_netns_get_route_manager(NMNetns *self) +NMPGlobalTracker * +nm_netns_get_global_tracker(NMNetns *self) { - return NM_NETNS_GET_PRIVATE(self)->route_manager; + return NM_NETNS_GET_PRIVATE(self)->global_tracker; } NMDedupMultiIndex * @@ -397,14 +397,14 @@ constructed(GObject *object) priv->platform_netns = nm_platform_netns_get(priv->platform); - priv->route_manager = nmp_route_manager_new(priv->platform); + priv->global_tracker = nmp_global_tracker_new(priv->platform); /* Weakly track the default rules with a dummy user-tag. These * rules are always weekly tracked... */ - nmp_route_manager_track_rule_default(priv->route_manager, - AF_UNSPEC, - 0, - nm_netns_parent_class /* static dummy user-tag */); + nmp_global_tracker_track_rule_default(priv->global_tracker, + AF_UNSPEC, + 0, + nm_netns_parent_class /* static dummy user-tag */); /* Also weakly track all existing rules. These were added before NetworkManager * starts, so they are probably none of NetworkManager's business. @@ -414,12 +414,12 @@ constructed(GObject *object) * of NetworkManager, we just don't know. * * For that reason, whenever we will touch such rules later one, we make them - * fully owned and no longer weekly tracked. See %NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG. */ - nmp_route_manager_track_rule_from_platform(priv->route_manager, - NULL, - AF_UNSPEC, - 0, - NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG); + * fully owned and no longer weekly tracked. See %NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG. */ + nmp_global_tracker_track_rule_from_platform(priv->global_tracker, + NULL, + AF_UNSPEC, + 0, + NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG); G_OBJECT_CLASS(nm_netns_parent_class)->constructed(object); @@ -469,7 +469,7 @@ dispose(GObject *object) g_clear_object(&priv->platform); nm_clear_pointer(&priv->l3cfgs, g_hash_table_unref); - nm_clear_pointer(&priv->route_manager, nmp_route_manager_unref); + nm_clear_pointer(&priv->global_tracker, nmp_global_tracker_unref); G_OBJECT_CLASS(nm_netns_parent_class)->dispose(object); } diff --git a/src/core/nm-netns.h b/src/core/nm-netns.h index deb1d1f0..17c1c637 100644 --- a/src/core/nm-netns.h +++ b/src/core/nm-netns.h @@ -29,7 +29,7 @@ NMNetns *nm_netns_new(struct _NMPlatform *platform); struct _NMPlatform *nm_netns_get_platform(NMNetns *self); NMPNetns *nm_netns_get_platform_netns(NMNetns *self); -struct _NMPRouteManager *nm_netns_get_route_manager(NMNetns *self); +struct _NMPGlobalTracker *nm_netns_get_global_tracker(NMNetns *self); struct _NMDedupMultiIndex *nm_netns_get_multi_idx(NMNetns *self); diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index 1a824fa4..16b0211f 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -826,7 +826,7 @@ update_system_hostname(NMPolicy *self, const char *msg) && (nm_utils_is_specific_hostname(temp_hostname) || nm_utils_is_specific_hostname(priv->last_hostname))) { external_hostname = TRUE; - _LOGI(LOGD_DNS, + _LOGD(LOGD_DNS, "set-hostname: current hostname was changed outside NetworkManager: '%s'", temp_hostname); priv->dhcp_hostname = FALSE; diff --git a/src/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c index 7d8986d7..a1ca5434 100644 --- a/src/core/platform/nm-fake-platform.c +++ b/src/core/platform/nm-fake-platform.c @@ -323,6 +323,15 @@ link_add(NMPlatform *platform, dev_lnk = nmp_object_new(NMP_OBJECT_TYPE_LNK_BRIDGE, props); break; } + case NM_LINK_TYPE_BOND: + { + const NMPlatformLnkBond *props = extra_data; + + nm_assert(props); + + dev_lnk = nmp_object_new(NMP_OBJECT_TYPE_LNK_BOND, props); + break; + } case NM_LINK_TYPE_VETH: veth_peer = extra_data; g_assert(veth_peer); diff --git a/src/core/platform/tests/monitor.c b/src/core/platform/tests/monitor.c index ff6fae65..8d0e0107 100644 --- a/src/core/platform/tests/monitor.c +++ b/src/core/platform/tests/monitor.c @@ -9,6 +9,7 @@ #include <syslog.h> #include "libnm-platform/nm-linux-platform.h" +#include "libnm-platform/nmp-object.h" #include "nm-test-utils-core.h" @@ -25,14 +26,14 @@ read_argv(int *argc, char ***argv) { GOptionContext *context; GOptionEntry options[] = { - {"no-persist", - 'P', - G_OPTION_FLAG_REVERSE, - G_OPTION_ARG_NONE, - &global_opt.persist, - "Exit after processing netlink messages", - NULL}, - {0}, + {"no-persist", + 'P', + G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, + &global_opt.persist, + "Exit after processing netlink messages", + NULL}, + {0}, }; gs_free_error GError *error = NULL; @@ -50,6 +51,18 @@ read_argv(int *argc, char ***argv) return TRUE; } +/*****************************************************************************/ + +static void +mptcp_addr_dump(NMPlatform *platform) +{ + gs_unref_ptrarray GPtrArray *addrs = NULL; + + addrs = nm_platform_mptcp_addrs_dump(platform); +} + +/*****************************************************************************/ + int main(int argc, char **argv) { @@ -69,6 +82,8 @@ main(int argc, char **argv) nm_linux_platform_setup(); + mptcp_addr_dump(NM_PLATFORM_GET); + if (global_opt.persist) g_main_loop_run(loop); diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c index d7fed220..34ec5cc2 100644 --- a/src/core/platform/tests/test-common.c +++ b/src/core/platform/tests/test-common.c @@ -70,7 +70,7 @@ _ipx_address_get_all(NMPlatform *self, int ifindex, NMPObjectType obj_type) g_assert(NM_IS_PLATFORM(self)); g_assert(ifindex > 0); g_assert(NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); - nmp_lookup_init_object(&lookup, obj_type, ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, obj_type, ifindex); return nmp_cache_lookup_to_array(nm_platform_lookup(self, &lookup), obj_type, FALSE /*addresses are always visible. */); @@ -103,7 +103,7 @@ nmtstp_platform_ip_address_find(NMPlatform *self, int ifindex, int addr_family, nm_assert(addr); if (ifindex > 0) - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex); else nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)); @@ -187,7 +187,9 @@ _nmtstp_platform_ip_addresses_assert(const char *filename, plat_addrs = nm_platform_lookup_clone( self, - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex), + nmp_lookup_init_object_by_ifindex(&lookup, + NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), + ifindex), NULL, NULL); @@ -1936,10 +1938,10 @@ nmtstp_link_macvlan_add(NMPlatform *platform, if (external_command) { const char *dev; char *modes[] = { - [MACVLAN_MODE_BRIDGE] = "bridge", - [MACVLAN_MODE_VEPA] = "vepa", - [MACVLAN_MODE_PRIVATE] = "private", - [MACVLAN_MODE_PASSTHRU] = "passthru", + [MACVLAN_MODE_BRIDGE] = "bridge", + [MACVLAN_MODE_VEPA] = "vepa", + [MACVLAN_MODE_PRIVATE] = "private", + [MACVLAN_MODE_PASSTHRU] = "passthru", }; dev = nm_platform_link_get_name(platform, parent); diff --git a/src/core/platform/tests/test-common.h b/src/core/platform/tests/test-common.h index 77f2c50d..da30b0ed 100644 --- a/src/core/platform/tests/test-common.h +++ b/src/core/platform/tests/test-common.h @@ -518,6 +518,11 @@ void nmtstp_link_delete(NMPlatform *platform, /*****************************************************************************/ +#define nmtst_object_new_mptcp_addr(...) \ + nmp_object_new(NMP_OBJECT_TYPE_MPTCP_ADDR, &((const NMPlatformMptcpAddr){__VA_ARGS__})) + +/*****************************************************************************/ + extern int NMTSTP_ENV1_IFINDEX; extern int NMTSTP_ENV1_EX; diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c index 48b321d2..b72bcb65 100644 --- a/src/core/platform/tests/test-link.c +++ b/src/core/platform/tests/test-link.c @@ -111,13 +111,16 @@ software_add(NMLinkType link_type, const char *name) { gboolean bond0_exists = !!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "bond0"); int r; + const NMPlatformLnkBond nm_platform_lnk_bond_default = { + .mode = 3, + }; - r = nm_platform_link_bond_add(NM_PLATFORM_GET, name, NULL); + r = nm_platform_link_bond_add(NM_PLATFORM_GET, name, &nm_platform_lnk_bond_default, NULL); /* Check that bond0 is *not* automatically created. */ if (!bond0_exists) g_assert(!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "bond0")); - return r >= 0; + return NMTST_NM_ERR_SUCCESS(r); } case NM_LINK_TYPE_TEAM: return NMTST_NM_ERR_SUCCESS(nm_platform_link_team_add(NM_PLATFORM_GET, name, NULL)); @@ -853,7 +856,7 @@ static void _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode) { const KeyPair self_key = {"yOWEsaXFxX9/DOkQPzqB9RufZOpfSP4LZZCErP0N0Xo=", - "s6pVT2xPwktor9O5bVOSzcPqBu9uzQOUzPQHXLU2jmk="}; + "s6pVT2xPwktor9O5bVOSzcPqBu9uzQOUzPQHXLU2jmk="}; const KeyPair keys[100] = { {"+BDHMh11bkheGfvlQpqt8P/H7N1sPXtVi05XraZS0E8=", "QItu7PJadBVXFXGv55CMtVnbRHdrI6E2CGlu2N5oGx4=", diff --git a/src/core/platform/tests/test-nmp-object.c b/src/core/platform/tests/test-nmp-object.c index ddd5aa93..193e750f 100644 --- a/src/core/platform/tests/test-nmp-object.c +++ b/src/core/platform/tests/test-nmp-object.c @@ -551,7 +551,9 @@ test_cache_qdisc(void) g_assert(nmp_cache_lookup_obj(cache, obj1b) == obj1b); g_assert(nmp_cache_lookup_obj(cache, obj2) == obj2); - head_entry = nmp_cache_lookup(cache, nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_QDISC, 1)); + head_entry = + nmp_cache_lookup(cache, + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_QDISC, 1)); g_assert(head_entry->len == 2); nmp_cache_free(cache); diff --git a/src/core/platform/tests/test-platform-general.c b/src/core/platform/tests/test-platform-general.c index 908ad4dd..c18886c9 100644 --- a/src/core/platform/tests/test-platform-general.c +++ b/src/core/platform/tests/test-platform-general.c @@ -788,6 +788,64 @@ test_route_type_is_nodev(void) /*****************************************************************************/ +static void +test_nmp_genl_family_type_from_name(void) +{ + int n_run; + int i; + + for (i = 0; i < (int) _NMP_GENL_FAMILY_TYPE_NUM; i++) { + const char *name = nmp_genl_family_infos[i].name; + + g_assert(name); + if (i > 0) + g_assert_cmpint(strcmp(nmp_genl_family_infos[i - 1].name, name), <, 0); + } + + g_assert_cmpint(nmp_genl_family_type_from_name("ethtool"), ==, NMP_GENL_FAMILY_TYPE_ETHTOOL); + g_assert_cmpint(nmp_genl_family_type_from_name("mptcp_pm"), ==, NMP_GENL_FAMILY_TYPE_MPTCP_PM); + g_assert_cmpint(nmp_genl_family_type_from_name("nl80211"), ==, NMP_GENL_FAMILY_TYPE_NL80211); + g_assert_cmpint(nmp_genl_family_type_from_name("nl802154"), ==, NMP_GENL_FAMILY_TYPE_NL802154); + g_assert_cmpint(nmp_genl_family_type_from_name("wireguard"), + ==, + NMP_GENL_FAMILY_TYPE_WIREGUARD); + + g_assert_cmpint(nmp_genl_family_type_from_name(NULL), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name("a"), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name("wireguara"), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name("wireguarb"), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name(""), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name("z"), ==, _NMP_GENL_FAMILY_TYPE_NONE); + + for (n_run = 0; n_run < 20; n_run++) { + for (i = 0; i < (int) _NMP_GENL_FAMILY_TYPE_NUM; i++) { + const char *cname = nmp_genl_family_infos[i].name; + const int ch_idx = nmtst_get_rand_uint() % strlen(cname); + char name[200]; + char ch; + gsize l; + + l = g_strlcpy(name, cname, sizeof(name)); + g_assert_cmpint(l, <, sizeof(name)); + + if (n_run == 0) { + g_assert_cmpint(nmp_genl_family_type_from_name(cname), ==, i); + g_assert_cmpint(nmp_genl_family_type_from_name(name), ==, i); + } + + /* randomly change one character in the name. Such a name becomes invalid. + * There are no two valid names which only differ by one characters. */ + do { + ch = nmtst_get_rand_uint() % 256; + } while (cname[ch_idx] == ch); + name[ch_idx] = ch; + g_assert_cmpint(nmp_genl_family_type_from_name(name), ==, _NMP_GENL_FAMILY_TYPE_NONE); + } + } +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -808,6 +866,8 @@ main(int argc, char **argv) GINT_TO_POINTER(2), test_platform_ip_address_pretty_sort_cmp); g_test_add_func("/general/test_route_type_is_nodev", test_route_type_is_nodev); + g_test_add_func("/nm-platform/test_nmp_genl_family_type_from_name", + test_nmp_genl_family_type_from_name); return g_test_run(); } diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c index 85838267..148ed792 100644 --- a/src/core/platform/tests/test-route.c +++ b/src/core/platform/tests/test-route.c @@ -10,7 +10,7 @@ #include "nm-core-utils.h" #include "libnm-platform/nm-platform-utils.h" -#include "libnm-platform/nmp-route-manager.h" +#include "libnm-platform/nmp-global-tracker.h" #include "test-common.h" @@ -35,8 +35,9 @@ _wait_for_ipv4_addr_device_route(NMPlatform *platform, nmp_cache_iter_for_each ( &iter, - nm_platform_lookup(platform, - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex)), + nm_platform_lookup( + platform, + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex)), &o) { const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE(o); @@ -646,6 +647,9 @@ test_ip4_route_options(gconstpointer test_data) .initrwnd = 50, .mtu = 1350, .lock_cwnd = TRUE, + .mss = 1300, + .quickack = TRUE, + .rto_min = 1000, }); break; case 2: @@ -1167,7 +1171,7 @@ _rule_check_kernel_support(NMPlatform *platform, int attribute) .addr_family = AF_INET, .priority = PROBE_PRORITY, .uid_range = - { + { .start = 0, .end = 0, }, @@ -1643,8 +1647,8 @@ again: if (TEST_SYNC) { gs_unref_hashtable GHashTable *unique_priorities = g_hash_table_new(NULL, NULL); - nm_auto_unref_route_manager NMPRouteManager *route_manager = - nmp_route_manager_new(platform); + nm_auto_unref_global_tracker NMPGlobalTracker *global_tracker = + nmp_global_tracker_new(platform); gs_unref_ptrarray GPtrArray *objs_sync = NULL; gconstpointer USER_TAG_1 = &platform; gconstpointer USER_TAG_2 = &unique_priorities; @@ -1666,29 +1670,29 @@ again: } for (i = 0; i < objs_sync->len; i++) { - nmp_route_manager_track_rule(route_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - 1, - USER_TAG_1, - NULL); + nmp_global_tracker_track_rule(global_tracker, + NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), + 1, + USER_TAG_1, + NULL); if (nmtst_get_rand_bool()) { /* this has no effect, because a negative priority (of same absolute value) * has lower priority than the positive priority above. */ - nmp_route_manager_track_rule(route_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - -1, - USER_TAG_2, - NULL); + nmp_global_tracker_track_rule(global_tracker, + NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), + -1, + USER_TAG_2, + NULL); } if (nmtst_get_rand_uint32() % objs_sync->len == 0) { - nmp_route_manager_sync(route_manager, NMP_OBJECT_TYPE_ROUTING_RULE, FALSE); + nmp_global_tracker_sync(global_tracker, NMP_OBJECT_TYPE_ROUTING_RULE, FALSE); g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), ==, i + 1); } } - nmp_route_manager_sync(route_manager, NMP_OBJECT_TYPE_ROUTING_RULE, FALSE); + nmp_global_tracker_sync(global_tracker, NMP_OBJECT_TYPE_ROUTING_RULE, FALSE); g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), ==, objs_sync->len); @@ -1696,37 +1700,37 @@ again: for (i = 0; i < objs_sync->len; i++) { switch (nmtst_get_rand_uint32() % 3) { case 0: - nmp_route_manager_untrack_rule(route_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - USER_TAG_1); - nmp_route_manager_untrack_rule(route_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - USER_TAG_1); + nmp_global_tracker_untrack_rule(global_tracker, + NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), + USER_TAG_1); + nmp_global_tracker_untrack_rule(global_tracker, + NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), + USER_TAG_1); break; case 1: - nmp_route_manager_track_rule(route_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - -1, - USER_TAG_1, - NULL); + nmp_global_tracker_track_rule(global_tracker, + NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), + -1, + USER_TAG_1, + NULL); break; case 2: - nmp_route_manager_track_rule(route_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - -2, - USER_TAG_2, - NULL); + nmp_global_tracker_track_rule(global_tracker, + NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), + -2, + USER_TAG_2, + NULL); break; } if (nmtst_get_rand_uint32() % objs_sync->len == 0) { - nmp_route_manager_sync(route_manager, NMP_OBJECT_TYPE_ROUTING_RULE, FALSE); + nmp_global_tracker_sync(global_tracker, NMP_OBJECT_TYPE_ROUTING_RULE, FALSE); g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), ==, objs_sync->len - i - 1); } } - nmp_route_manager_sync(route_manager, NMP_OBJECT_TYPE_ROUTING_RULE, FALSE); + nmp_global_tracker_sync(global_tracker, NMP_OBJECT_TYPE_ROUTING_RULE, FALSE); } else { for (i = 0; i < objs->len;) { @@ -1957,6 +1961,200 @@ test_blackhole(gconstpointer test_data) /*****************************************************************************/ +static gboolean +_mptcp_has_permissions(void) +{ + static int has_permissions = -1; + int p; + + /* We create a new netns for testing, where we also have CAP_NET_ADMIN. + * However, that is not enough for configuring MPTCP endpoints. Probably + * you can only create them, by running the test as root. Detect the + * inability, to skip the test. + * + * See https://lore.kernel.org/mptcp/20220805115020.525181-1-thaller@redhat.com/T/#u */ + +again: + p = g_atomic_int_get(&has_permissions); + + if (p == -1) { + static gsize lock; + const NMPlatformMptcpAddr mptcp_addr = (NMPlatformMptcpAddr){ + .id = 1, + .addr_family = AF_INET, + .addr.addr4 = nmtst_inet4_from_string("1.2.3.4"), + }; + int r; + + if (!g_once_init_enter(&lock)) + goto again; + + if (nmtst_get_rand_one_case_in(3)) { + gs_unref_ptrarray GPtrArray *arr = NULL; + + arr = nm_platform_mptcp_addrs_dump(NM_PLATFORM_GET); + g_assert_cmpint(nm_g_ptr_array_len(arr), ==, 0); + } + + r = nm_platform_mptcp_addr_update(NM_PLATFORM_GET, TRUE, &mptcp_addr); + if (r == 0) + p = TRUE; + else if (r == -EPERM) + p = FALSE; + else + g_assert_cmpint(r, ==, 0); + + if (p) { + if (nmtst_get_rand_one_case_in(3)) { + gs_unref_ptrarray GPtrArray *arr = NULL; + + arr = nm_platform_mptcp_addrs_dump(NM_PLATFORM_GET); + g_assert_cmpint(nm_g_ptr_array_len(arr), ==, 1); + } + + r = nm_platform_mptcp_addr_update(NM_PLATFORM_GET, FALSE, &mptcp_addr); + g_assert_cmpint(r, ==, 0); + } + + if (nmtst_get_rand_one_case_in(3)) { + gs_unref_ptrarray GPtrArray *arr = NULL; + + arr = nm_platform_mptcp_addrs_dump(NM_PLATFORM_GET); + g_assert_cmpint(nm_g_ptr_array_len(arr), ==, 0); + } + + g_atomic_int_set(&has_permissions, p); + g_once_init_leave(&lock, 1); + } + + return p; +} + +static gboolean +_mptcp_skip_test(void) +{ + if (nm_platform_genl_get_family_id(NM_PLATFORM_GET, NMP_GENL_FAMILY_TYPE_MPTCP_PM) == 0) { + g_test_skip("mptcp not available"); + return TRUE; + } + + if (!_mptcp_has_permissions()) { + g_test_skip("No permissions to create MPTCP endpoints"); + return TRUE; + } + + return FALSE; +} + +static void +test_mptcp(gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT(test_data); + gs_unref_object NMPlatform *platform = g_object_ref(NM_PLATFORM_GET); + nm_auto_unref_global_tracker NMPGlobalTracker *global_tracker = + nmp_global_tracker_new(platform); + gconstpointer const USER_TAG = &TEST_IDX; + const int IFINDEX = nm_platform_link_get_ifindex(platform, DEVICE_NAME); + guint i; + guint j; + int r; + gs_unref_ptrarray GPtrArray *arr_external = + g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); + gs_unref_ptrarray GPtrArray *arr_tracked = + g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); + const NMPObject *obj; + gboolean delete_extra; + + g_assert_cmpint(IFINDEX, >, 0); + + if (_mptcp_skip_test()) + return; + + j = nmtst_get_rand_uint32() % 5; + for (i = 0; i < j; i++) { + obj = nmtst_object_new_mptcp_addr(.id = i + 1, + .ifindex = IFINDEX, + .addr_family = AF_INET, + .addr.addr4 = htonl(0xC0A80001u + i)); + g_ptr_array_add(arr_external, (gpointer) obj); + r = nm_platform_mptcp_addr_update(platform, TRUE, NMP_OBJECT_CAST_MPTCP_ADDR(obj)); + g_assert_cmpint(r, ==, 0); + } + + j = nmtst_get_rand_uint32() % 10; + for (i = 0; i < j; i++) { + obj = nmtst_object_new_mptcp_addr(.ifindex = IFINDEX, + .addr_family = AF_INET, + .addr.addr4 = htonl(0xC0A80001u + i)); + g_ptr_array_add(arr_tracked, (gpointer) obj); + nmp_global_tracker_track(global_tracker, + NMP_OBJECT_TYPE_MPTCP_ADDR, + NMP_OBJECT_CAST_MPTCP_ADDR(obj), + 20 - i, + USER_TAG, + NULL); + } + for (i = 0; i < arr_tracked->len;) { + if (nmtst_get_rand_bool()) { + nmp_global_tracker_untrack(global_tracker, + NMP_OBJECT_TYPE_MPTCP_ADDR, + NMP_OBJECT_CAST_MPTCP_ADDR(arr_tracked->pdata[i]), + USER_TAG); + g_ptr_array_remove_index(arr_tracked, i); + } else + i++; + } + + if (arr_tracked->len == 0 || nmtst_get_rand_bool()) { + NMPlatformMptcpAddr a; + + /* Track a dummy object that marks the ifindex as managed. */ + nmp_global_tracker_track(global_tracker, + NMP_OBJECT_TYPE_MPTCP_ADDR, + nmp_global_tracker_mptcp_addr_init_for_ifindex(&a, IFINDEX), + 10, + USER_TAG, + NULL); + } + + nmp_global_tracker_sync_mptcp_addrs(global_tracker, FALSE); + + if (nmtst_get_rand_bool()) { + gboolean reapply; + + nmp_global_tracker_untrack_all(global_tracker, USER_TAG, TRUE, FALSE); + reapply = nmtst_get_rand_bool(); + nmp_global_tracker_sync_mptcp_addrs(global_tracker, reapply); + + delete_extra = !reapply; + } else + delete_extra = TRUE; + + if (delete_extra) { + gs_unref_ptrarray GPtrArray *arr = NULL; + + /* We need to delete all MPTCP address again, because the next test uses the + * same netns (this test setup doesn't create a netns per test). */ + arr = nm_platform_mptcp_addrs_dump(platform); + for (i = 0; i < nm_g_ptr_array_len(arr); i++) { + r = nm_platform_mptcp_addr_update(platform, + FALSE, + NMP_OBJECT_CAST_MPTCP_ADDR(arr->pdata[i])); + g_assert(NMTST_NM_ERR_SUCCESS(r)); + } + } + + { + gs_unref_ptrarray GPtrArray *arr = NULL; + + arr = nm_platform_mptcp_addrs_dump(platform); + g_assert(arr); + g_assert_cmpint(arr->len, ==, 0); + } +} + +/*****************************************************************************/ + NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; void @@ -1971,6 +2169,7 @@ _nmtstp_setup_tests(void) #define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, TRUE) #define add_test_func_data(testpath, test_func, arg) \ nmtstp_env1_add_test_func_data(testpath, test_func, arg, TRUE) + add_test_func("/route/ip4", test_ip4_route); add_test_func("/route/ip6", test_ip6_route); add_test_func("/route/ip4_metric0", test_ip4_route_metric0); @@ -1998,4 +2197,8 @@ _nmtstp_setup_tests(void) add_test_func_data("/route/blackhole/1", test_blackhole, GINT_TO_POINTER(1)); add_test_func_data("/route/blackhole/2", test_blackhole, GINT_TO_POINTER(2)); } + if (nmtstp_is_root_test()) { + add_test_func_data("/route/mptcp/1", test_mptcp, GINT_TO_POINTER(1)); + add_test_func_data("/route/mptcp/2", test_mptcp, GINT_TO_POINTER(2)); + } } diff --git a/src/core/platform/tests/test-tc.c b/src/core/platform/tests/test-tc.c index db23a932..6a2019c8 100644 --- a/src/core/platform/tests/test-tc.c +++ b/src/core/platform/tests/test-tc.c @@ -30,10 +30,11 @@ qdiscs_lookup(int ifindex) { NMPLookup lookup; - return nm_platform_lookup_clone(NM_PLATFORM_GET, - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_QDISC, ifindex), - NULL, - NULL); + return nm_platform_lookup_clone( + NM_PLATFORM_GET, + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_QDISC, ifindex), + NULL, + NULL); } static void diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c index 1ff66e25..b7d846c6 100644 --- a/src/core/settings/nm-settings.c +++ b/src/core/settings/nm-settings.c @@ -376,6 +376,8 @@ typedef struct { GHashTable *sce_idx; + GCancellable *shutdown_cancellable; + CList sce_dirty_lst_head; CList connections_lst_head; @@ -389,15 +391,15 @@ typedef struct { gint64 startup_complete_start_timestamp_msec; GHashTable *startup_complete_idx; CList startup_complete_scd_lst_head; - guint startup_complete_timeout_id; + GSource *startup_complete_timeout_source; + + GSource *kf_db_flush_idle_source_timestamps; + GSource *kf_db_flush_idle_source_seen_bssids; guint connections_len; guint connections_generation; - guint kf_db_flush_idle_id_timestamps; - guint kf_db_flush_idle_id_seen_bssids; - bool kf_db_pruned_timestamps; bool kf_db_pruned_seen_bssid; @@ -541,9 +543,9 @@ _startup_complete_timeout_cb(gpointer user_data) NMSettings *self = user_data; NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self); - priv->startup_complete_timeout_id = 0; + nm_clear_g_source_inst(&priv->startup_complete_timeout_source); _startup_complete_check(self, 0); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } static void @@ -567,7 +569,7 @@ _startup_complete_check(NMSettings *self, gint64 now_msec) return; } - nm_clear_g_source(&priv->startup_complete_timeout_id); + nm_clear_g_source_inst(&priv->startup_complete_timeout_source); if (c_list_is_empty(&priv->startup_complete_scd_lst_head)) goto ready; @@ -609,8 +611,10 @@ next_with_ready: timeout_msec = priv->startup_complete_start_timestamp_msec + scd_not_ready->timeout_msec - nm_utils_get_monotonic_timestamp_msec(); - priv->startup_complete_timeout_id = - g_timeout_add(NM_CLAMP(0, timeout_msec, 60000), _startup_complete_timeout_cb, self); + priv->startup_complete_timeout_source = + nm_g_timeout_add_source(NM_CLAMP(0, timeout_msec, 60000), + _startup_complete_timeout_cb, + self); _LOGT("startup-complete: wait for suitable device for connection \"%s\" (%s) which has " "\"connection.wait-device-timeout\" set", nm_settings_connection_get_id(scd_not_ready->sett_conn), @@ -637,7 +641,7 @@ ready: _LOGT("startup-complete: ready, no more profiles to wait for"); priv->startup_complete_start_timestamp_msec = 0; nm_assert(!priv->startup_complete_idx); - nm_assert(priv->startup_complete_timeout_id == 0); + nm_assert(!priv->startup_complete_timeout_source); _notify(self, PROP_STARTUP_COMPLETE); } @@ -3464,44 +3468,93 @@ load_plugins(NMSettings *self, const char *const *plugins, GError **error) /*****************************************************************************/ static void -pk_hostname_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) +_save_hostname_write_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMSettings *self; + GDBusMethodInvocation *context; + gs_free char *hostname = NULL; + gs_unref_object NMAuthSubject *auth_subject = NULL; + gs_unref_object GCancellable *cancellable = NULL; + gs_free_error GError *error = NULL; + + nm_utils_user_data_unpack(user_data, &self, &context, &auth_subject, &hostname, &cancellable); + + nm_hostname_manager_write_hostname_finish(NM_HOSTNAME_MANAGER(source), result, &error); + + nm_audit_log_control_op(NM_AUDIT_OP_HOSTNAME_SAVE, + hostname ?: "", + !error, + auth_subject, + error ? error->message : NULL); + + if (nm_utils_error_is_cancelled(error)) { + g_dbus_method_invocation_return_error_literal(context, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "NetworkManager is shutting down"); + return; + } + + if (error) { + g_dbus_method_invocation_take_error(context, + g_error_new(NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Saving the hostname failed: %s", + error->message)); + return; + } + + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +_save_hostname_pk_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) { NMSettings *self = NM_SETTINGS(user_data); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self); NMAuthCallResult result; - GError *error = NULL; - const char *hostname; + gs_free char *hostname = NULL; nm_assert(G_IS_DBUS_METHOD_INVOCATION(context)); c_list_unlink(nm_auth_chain_parent_lst_list(chain)); result = nm_auth_chain_get_result(chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME); - hostname = nm_auth_chain_get_data(chain, "hostname"); + hostname = nm_auth_chain_steal_data(chain, "hostname"); - /* If our NMSettingsConnection is already gone, do nothing */ if (result != NM_AUTH_CALL_RESULT_YES) { - error = g_error_new_literal(NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_INSUFF_PRIV); - } else { - if (!nm_hostname_manager_write_hostname(priv->hostname_manager, hostname)) { - error = g_error_new_literal(NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "Saving the hostname failed."); - } + nm_audit_log_control_op(NM_AUDIT_OP_HOSTNAME_SAVE, + hostname ?: "", + FALSE, + nm_auth_chain_get_subject(chain), + NM_UTILS_ERROR_MSG_INSUFF_PRIV); + g_dbus_method_invocation_return_error_literal(context, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + NM_UTILS_ERROR_MSG_INSUFF_PRIV); + return; } - nm_audit_log_control_op(NM_AUDIT_OP_HOSTNAME_SAVE, - hostname, - !error, - nm_auth_chain_get_subject(chain), - error ? error->message : NULL); + if (!priv->shutdown_cancellable) { + /* we only keep a weak pointer on the cancellable, so we can + * wrap it up after use. We almost never require this, because + * SaveHostname is almost never called. */ + priv->shutdown_cancellable = g_cancellable_new(); + g_object_add_weak_pointer(G_OBJECT(priv->shutdown_cancellable), + (gpointer *) &priv->shutdown_cancellable); + } - if (error) - g_dbus_method_invocation_take_error(context, error); - else - g_dbus_method_invocation_return_value(context, NULL); + nm_hostname_manager_write_hostname( + priv->hostname_manager, + hostname, + priv->shutdown_cancellable, + _save_hostname_write_cb, + nm_utils_user_data_pack(self, + context, + g_object_ref(nm_auth_chain_get_subject(chain)), + hostname, + g_object_ref(priv->shutdown_cancellable))); + g_steal_pointer(&hostname); } static void @@ -3523,13 +3576,13 @@ impl_settings_save_hostname(NMDBusObject *obj, g_variant_get(parameters, "(&s)", &hostname); /* Minimal validation of the hostname */ - if (!nm_utils_validate_hostname(hostname)) { + if (nm_str_not_empty(hostname) && !nm_utils_validate_hostname(hostname)) { error_code = NM_SETTINGS_ERROR_INVALID_HOSTNAME; error_reason = "The hostname was too long or contained invalid characters"; goto err; } - chain = nm_auth_chain_new_context(invocation, pk_hostname_cb, self); + chain = nm_auth_chain_new_context(invocation, _save_hostname_pk_cb, self); if (!chain) { error_code = NM_SETTINGS_ERROR_PERMISSION_DENIED; error_reason = NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED; @@ -3538,7 +3591,7 @@ impl_settings_save_hostname(NMDBusObject *obj, c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE); - nm_auth_chain_set_data(chain, "hostname", g_strdup(hostname), g_free); + nm_auth_chain_set_data(chain, "hostname", nm_strdup_not_empty(hostname), g_free); return; err: nm_audit_log_control_op(NM_AUDIT_OP_HOSTNAME_SAVE, hostname, FALSE, invocation, error_reason); @@ -3842,13 +3895,13 @@ _kf_db_got_dirty_flush(NMSettings *self, gboolean is_timestamps) NMKeyFileDB *kf_db; if (is_timestamps) { - prefix = "timestamps"; - kf_db = priv->kf_db_timestamps; - priv->kf_db_flush_idle_id_timestamps = 0; + prefix = "timestamps"; + kf_db = priv->kf_db_timestamps; + nm_clear_g_source_inst(&priv->kf_db_flush_idle_source_timestamps); } else { - prefix = "seen-bssids"; - kf_db = priv->kf_db_seen_bssids; - priv->kf_db_flush_idle_id_seen_bssids = 0; + prefix = "seen-bssids"; + kf_db = priv->kf_db_seen_bssids; + nm_clear_g_source_inst(&priv->kf_db_flush_idle_source_seen_bssids); } if (nm_key_file_db_is_dirty(kf_db)) @@ -3859,7 +3912,7 @@ _kf_db_got_dirty_flush(NMSettings *self, gboolean is_timestamps) nm_key_file_db_get_filename(kf_db)); } - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } static gboolean @@ -3880,26 +3933,27 @@ _kf_db_got_dirty_fcn(NMKeyFileDB *kf_db, gpointer user_data) NMSettings *self = user_data; NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self); GSourceFunc idle_func; - guint *p_id; + GSource **p_source; const char *prefix; if (priv->kf_db_timestamps == kf_db) { prefix = "timestamps"; - p_id = &priv->kf_db_flush_idle_id_timestamps; + p_source = &priv->kf_db_flush_idle_source_timestamps; idle_func = _kf_db_got_dirty_flush_timestamps_cb; } else if (priv->kf_db_seen_bssids == kf_db) { prefix = "seen-bssids"; - p_id = &priv->kf_db_flush_idle_id_seen_bssids; + p_source = &priv->kf_db_flush_idle_source_seen_bssids; idle_func = _kf_db_got_dirty_flush_seen_bssids_cb; } else { nm_assert_not_reached(); return; } - if (*p_id != 0) + if (*p_source) return; _LOGT("[%s-keyfile]: schedule flushing changes to disk", prefix); - *p_id = g_idle_add_full(G_PRIORITY_LOW, idle_func, self, NULL); + *p_source = + nm_g_source_attach(nm_g_idle_source_new(G_PRIORITY_LOW, idle_func, self, NULL), NULL); } void @@ -4100,7 +4154,7 @@ dispose(GObject *object) nm_assert(c_list_is_empty(&priv->sce_dirty_lst_head)); nm_assert(g_hash_table_size(priv->sce_idx) == 0); - nm_clear_g_source(&priv->startup_complete_timeout_id); + nm_clear_g_source_inst(&priv->startup_complete_timeout_source); nm_clear_pointer(&priv->startup_complete_idx, g_hash_table_destroy); nm_assert(c_list_is_empty(&priv->startup_complete_scd_lst_head)); @@ -4121,6 +4175,12 @@ dispose(GObject *object) g_clear_object(&priv->session_monitor); } + if (priv->shutdown_cancellable) { + g_object_remove_weak_pointer(G_OBJECT(priv->shutdown_cancellable), + (gpointer *) &priv->shutdown_cancellable); + g_cancellable_cancel(g_steal_pointer(&priv->shutdown_cancellable)); + } + G_OBJECT_CLASS(nm_settings_parent_class)->dispose(object); } @@ -4154,8 +4214,8 @@ finalize(GObject *object) g_clear_object(&priv->agent_mgr); - nm_clear_g_source(&priv->kf_db_flush_idle_id_timestamps); - nm_clear_g_source(&priv->kf_db_flush_idle_id_seen_bssids); + nm_clear_g_source_inst(&priv->kf_db_flush_idle_source_timestamps); + nm_clear_g_source_inst(&priv->kf_db_flush_idle_source_seen_bssids); _kf_db_to_file(self, TRUE, FALSE); _kf_db_to_file(self, FALSE, FALSE); nm_key_file_db_destroy(priv->kf_db_timestamps); diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index e62c0227..6452d72a 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -393,7 +393,9 @@ make_connection_setting(const char *file, const char *v; gs_free char *stable_id = NULL; const char *const *iter; - int vint64, i_val; + gint32 vint32; + gint64 vint64; + int i_val; ifcfg_name = utils_get_ifcfg_name(file, TRUE); if (!ifcfg_name) @@ -586,8 +588,8 @@ make_connection_setting(const char *file, break; } - vint64 = svGetValueInt64(ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1); - g_object_set(s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (int) vint64, NULL); + vint32 = svGetValueInt64(ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1); + g_object_set(s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (int) vint32, NULL); nm_clear_g_free(&value); v = svGetValue(ifcfg, "DEVTIMEOUT", &value); @@ -643,6 +645,14 @@ make_connection_setting(const char *file, PARSE_WARNING("invalid DNS_OVER_TLS setting"); g_object_set(s_con, NM_SETTING_CONNECTION_DNS_OVER_TLS, i_val, NULL); + i_val = NM_MPTCP_FLAGS_NONE; + if (!svGetValueEnum(ifcfg, "MPTCP_FLAGS", nm_mptcp_flags_get_type(), &i_val, NULL)) + PARSE_WARNING("invalid MPTCP_FLAGS setting"); + g_object_set(s_con, NM_SETTING_CONNECTION_MPTCP_FLAGS, (guint) i_val, NULL); + + vint32 = svGetValueInt64(ifcfg, "WAIT_ACTIVATION_DELAY", 10, -1, G_MAXINT32, -1); + g_object_set(s_con, NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY, (int) vint32, NULL); + return NM_SETTING(s_con); } @@ -772,7 +782,7 @@ read_full_ip4_address(shvarFile *ifcfg, if (!read_ip4_address(ifcfg, numbered_tag(tag, "NETMASK", which), &has_key, &a, error)) return FALSE; if (has_key) - prefix = nm_utils_ip4_netmask_to_prefix(a); + prefix = _nm_utils_ip4_netmask_to_prefix(a); else { if (base_addr) prefix = nm_ip_address_get_prefix(base_addr); @@ -845,6 +855,7 @@ typedef struct { union { guint8 uint8; guint32 uint32; + gboolean boolean; const char *str; struct { guint32 uint32; @@ -873,6 +884,9 @@ enum { PARSE_LINE_ATTR_ROUTE_INITCWND, PARSE_LINE_ATTR_ROUTE_INITRWND, PARSE_LINE_ATTR_ROUTE_MTU, + PARSE_LINE_ATTR_ROUTE_ADVMSS, + PARSE_LINE_ATTR_ROUTE_RTO_MIN, + PARSE_LINE_ATTR_ROUTE_QUICKACK, /* iproute2 arguments that only matter when parsing the file. */ PARSE_LINE_ATTR_ROUTE_TO, @@ -886,6 +900,7 @@ enum { #define PARSE_LINE_TYPE_UINT8 '8' #define PARSE_LINE_TYPE_UINT32 'u' #define PARSE_LINE_TYPE_UINT32_WITH_LOCK 'l' +#define PARSE_LINE_TYPE_BOOL 'b' #define PARSE_LINE_TYPE_ADDR 'a' #define PARSE_LINE_TYPE_ADDR_WITH_PREFIX 'p' #define PARSE_LINE_TYPE_IFNAME 'i' @@ -972,6 +987,11 @@ parse_route_line(const char *line, .key = NM_IP_ROUTE_ATTRIBUTE_CWND, .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + [PARSE_LINE_ATTR_ROUTE_ADVMSS] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_ADVMSS, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, + }, [PARSE_LINE_ATTR_ROUTE_INITCWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITCWND, @@ -987,7 +1007,16 @@ parse_route_line(const char *line, .key = NM_IP_ROUTE_ATTRIBUTE_MTU, .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, - + [PARSE_LINE_ATTR_ROUTE_QUICKACK] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_QUICKACK, + .type = PARSE_LINE_TYPE_BOOL, + }, + [PARSE_LINE_ATTR_ROUTE_RTO_MIN] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_RTO_MIN, + .type = PARSE_LINE_TYPE_UINT32, + }, [PARSE_LINE_ATTR_ROUTE_TO] = { .key = "to", @@ -1090,6 +1119,9 @@ parse_route_line(const char *line, case PARSE_LINE_TYPE_UINT32_WITH_LOCK: i_words++; goto parse_line_type_uint32_with_lock; + case PARSE_LINE_TYPE_BOOL: + i_words++; + goto parse_line_type_bool; case PARSE_LINE_TYPE_ADDR: i_words++; goto parse_line_type_addr; @@ -1218,6 +1250,22 @@ parse_line_type_uint32_with_lock: i_words++; goto next; +parse_line_type_bool: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + p_data->v.boolean = !!_nm_utils_ascii_str_to_int64(s, 10, 0, 1, 0); + if (errno) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not a valid boolean number", + w); + return -EINVAL; + } + i_words++; + goto next; + parse_line_type_ifname: s = words[i_words]; if (!s) @@ -1370,6 +1418,9 @@ next:; p_info->key, g_variant_new_uint32(p_data->v.uint32_with_lock.uint32)); break; + case PARSE_LINE_TYPE_BOOL: + nm_ip_route_set_attribute(route, p_info->key, g_variant_new_boolean(p_data->v.boolean)); + break; case PARSE_LINE_TYPE_ADDR: case PARSE_LINE_TYPE_ADDR_WITH_PREFIX: nm_ip_route_set_attribute( @@ -1445,7 +1496,7 @@ read_one_ip4_route(shvarFile *ifcfg, guint32 which, NMIPRoute **out_route, GErro error)) return FALSE; if (has_key) { - prefix = nm_utils_ip4_netmask_to_prefix(netmask); + prefix = _nm_utils_ip4_netmask_to_prefix(netmask); if (netmask != _nm_utils_ip4_prefix_to_netmask(prefix)) { g_set_error(error, NM_SETTINGS_ERROR, @@ -1786,6 +1837,7 @@ make_ip4_setting(shvarFile *ifcfg, int priority; const char *const *item; guint32 route_table; + int ipv4_link_local; nm_assert(out_has_defroute && !*out_has_defroute); @@ -1858,6 +1910,14 @@ make_ip4_setting(shvarFile *ifcfg, route_table = 0; } + ipv4_link_local = NM_SETTING_IP4_LL_DEFAULT; + if (!svGetValueEnum(ifcfg, + "IPV4_LINK_LOCAL", + nm_setting_ip4_link_local_get_type(), + &ipv4_link_local, + NULL)) + PARSE_WARNING("invalid IPV4_LINK_LOCAL setting"); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, @@ -1873,6 +1933,8 @@ make_ip4_setting(shvarFile *ifcfg, svGetValueInt64(ifcfg, "IPV4_ROUTE_METRIC", 10, -1, G_MAXUINT32, -1), NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) route_table, + NM_SETTING_IP4_CONFIG_LINK_LOCAL, + ipv4_link_local, NULL); if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) @@ -2498,7 +2560,13 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea } } - i_val = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; + /* IPv6 tokenized interface identifier */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IPV6_TOKEN", &value); + if (v) + g_object_set(s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, v, NULL); + + i_val = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64; if (!svGetValueEnum(ifcfg, "IPV6_ADDR_GEN_MODE", nm_setting_ip6_config_addr_gen_mode_get_type(), @@ -2506,15 +2574,14 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea &local)) { PARSE_WARNING("%s", local->message); g_clear_error(&local); + } else if (errno == ENOENT) { + /* The key is not specified. If "v" (IPV6_TOKEN) is set, + * we default to EUI64. Otherwise, the connection would not verify. */ + if (v) + i_val = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; } g_object_set(s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, i_val, NULL); - /* IPv6 tokenized interface identifier */ - nm_clear_g_free(&value); - v = svGetValueStr(ifcfg, "IPV6_TOKEN", &value); - if (v) - g_object_set(s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, v, NULL); - /* DNS servers * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting()) */ diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index 15e01ba9..e1ef8174 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -960,6 +960,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { _KEY_TYPE("IPV4_DHCP_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_DNS_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_FAILURE_FATAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV4_LINK_LOCAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_REQUIRED_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_ROUTE_METRIC", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_ROUTE_TABLE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), @@ -1005,6 +1006,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { _KEY_TYPE("MDNS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("METRIC", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), _KEY_TYPE("MODE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MPTCP_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("MTU", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("MUD_URL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("MULTI_CONNECT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), @@ -1061,6 +1063,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { _KEY_TYPE("VLAN_INGRESS_PRIORITY_MAP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("VRF", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("VRF_UUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("WAIT_ACTIVATION_DELAY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("WEP_KEY_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("WPA_ALLOW_WPA", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("WPA_ALLOW_WPA2", NMS_IFCFG_KEY_TYPE_IS_PLAIN), diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index d7a1cfc6..d1f8dbad 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -33,7 +33,7 @@ typedef struct { NMSIfcfgKeyTypeFlags key_flags; } NMSIfcfgKeyTypeInfo; -extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[253]; +extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[256]; const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx); diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 551f2b91..96efc9e5 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -2095,6 +2095,7 @@ write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg) NMSettingConnectionMdns mdns; NMSettingConnectionLlmnr llmnr; NMSettingConnectionDnsOverTls dns_over_tls; + NMMptcpFlags mptcp_flags; guint32 vuint32; const char *tmp, *mud_url; @@ -2258,6 +2259,9 @@ write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg) vint = nm_setting_connection_get_auth_retries(s_con); svSetValueInt64_cond(ifcfg, "AUTH_RETRIES", vint >= 0, vint); + vint32 = nm_setting_connection_get_wait_activation_delay(s_con); + svSetValueInt64_cond(ifcfg, "WAIT_ACTIVATION_DELAY", vint32 >= 0, vint32); + vint32 = nm_setting_connection_get_wait_device_timeout(s_con); if (vint32 == -1) { /* pass */ @@ -2286,13 +2290,18 @@ write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg) nm_setting_connection_dns_over_tls_get_type(), dns_over_tls); } + + mptcp_flags = nm_setting_connection_get_mptcp_flags(s_con); + if (mptcp_flags != NM_MPTCP_FLAGS_NONE) + svSetValueEnum(ifcfg, "MPTCP_FLAGS", nm_mptcp_flags_get_type(), mptcp_flags); } static char * get_route_attributes_string(NMIPRoute *route, int family) { gs_free const char **names = NULL; - GVariant *attr, *lock; + GVariant *attr; + GVariant *lock; GString *str; guint i, len; @@ -2317,6 +2326,7 @@ get_route_attributes_string(NMIPRoute *route, int family) continue; if (NM_IN_STRSET(names[i], + NM_IP_ROUTE_ATTRIBUTE_ADVMSS, NM_IP_ROUTE_ATTRIBUTE_WINDOW, NM_IP_ROUTE_ATTRIBUTE_CWND, NM_IP_ROUTE_ATTRIBUTE_INITCWND, @@ -2332,12 +2342,12 @@ get_route_attributes_string(NMIPRoute *route, int family) names[i], (lock && g_variant_get_boolean(lock)) ? "lock " : "", g_variant_get_uint32(attr)); - } else if (strstr(names[i], "lock-")) { + } else if (NM_STR_HAS_PREFIX(names[i], "lock-")) { const char *n = &(names[i])[NM_STRLEN("lock-")]; - attr = nm_ip_route_get_attribute(route, n); - if (!attr) { - g_string_append_printf(str, "%s lock 0", n); + if (!nm_ip_route_get_attribute(route, n)) { + if (g_variant_get_boolean(attr)) + g_string_append_printf(str, "%s lock 0", n); } else { /* we also have a corresponding attribute with the numeric value. The * lock setting is handled above. */ @@ -2346,8 +2356,12 @@ get_route_attributes_string(NMIPRoute *route, int family) g_string_append_printf(str, "%s %u", names[i], (unsigned) g_variant_get_byte(attr)); } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_TOS)) { g_string_append_printf(str, "%s 0x%02x", names[i], (unsigned) g_variant_get_byte(attr)); - } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_TABLE)) { + } else if (NM_IN_STRSET(names[i], + NM_IP_ROUTE_ATTRIBUTE_TABLE, + NM_IP_ROUTE_ATTRIBUTE_RTO_MIN)) { g_string_append_printf(str, "%s %u", names[i], (unsigned) g_variant_get_uint32(attr)); + } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_QUICKACK)) { + g_string_append_printf(str, "%s %u", names[i], (unsigned) g_variant_get_boolean(attr)); } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_ONLINK)) { if (g_variant_get_boolean(attr)) g_string_append(str, "onlink"); @@ -2733,6 +2747,7 @@ write_ip4_setting(NMConnection *connection, gboolean has_netmask; NMDhcpHostnameFlags flags; const char *const *strv; + NMSettingIP4LinkLocal ipv4_link_local; NM_SET_OUT(out_route_content_svformat, NULL); NM_SET_OUT(out_route_content, NULL); @@ -2839,6 +2854,14 @@ write_ip4_setting(NMConnection *connection, value = nm_setting_ip_config_get_dhcp_hostname(s_ip4); svSetValueStr(ifcfg, "DHCP_HOSTNAME", value); + ipv4_link_local = nm_setting_ip4_config_get_link_local(NM_SETTING_IP4_CONFIG(s_ip4)); + if (ipv4_link_local != NM_SETTING_IP4_LL_DEFAULT) { + svSetValueEnum(ifcfg, + "IPV4_LINK_LOCAL", + nm_setting_ip4_link_local_get_type(), + ipv4_link_local); + } + value = nm_setting_ip4_config_get_dhcp_fqdn(NM_SETTING_IP4_CONFIG(s_ip4)); svSetValueStr(ifcfg, "DHCP_FQDN", value); @@ -3149,7 +3172,7 @@ write_ip6_setting(NMConnection *connection, shvarFile *ifcfg, GString **out_rout /* IPv6 Address generation mode */ addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode(NM_SETTING_IP6_CONFIG(s_ip6)); - if (addr_gen_mode != NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) { + if (addr_gen_mode != NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64) { svSetValueEnum(ifcfg, "IPV6_ADDR_GEN_MODE", nm_setting_ip6_config_addr_gen_mode_get_type(), diff --git a/src/core/settings/plugins/ifcfg-rh/shvar.c b/src/core/settings/plugins/ifcfg-rh/shvar.c index 2ca49558..fe8187c3 100644 --- a/src/core/settings/plugins/ifcfg-rh/shvar.c +++ b/src/core/settings/plugins/ifcfg-rh/shvar.c @@ -1328,6 +1328,7 @@ svGetValueEnum(shvarFile *s, const char *key, GType gtype, int *out_value, GErro if (!svalue) { /* don't touch out_value. The caller is supposed * to initialize it with the default value. */ + errno = ENOENT; return TRUE; } @@ -1339,10 +1340,12 @@ svGetValueEnum(shvarFile *s, const char *key, GType gtype, int *out_value, GErro err_token, svalue, key); + errno = EINVAL; return FALSE; } NM_SET_OUT(out_value, value); + errno = 0; return TRUE; } diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected index 71d2cda8..1330382d 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected @@ -13,4 +13,4 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected index a48be78a..15ced3ab 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected @@ -28,7 +28,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test User 1" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected index ccc0f35a..1aeb2cdc 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected @@ -13,7 +13,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Bond Main" UUID=${UUID} DEVICE=bond0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected index d4a978b6..a7df77e4 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected @@ -7,7 +7,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Permissions" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected index 716eedc2..7fcd2c15 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected @@ -9,7 +9,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Proxy Basic" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected index 0c2fa035..d03e9a0d 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected @@ -3,12 +3,13 @@ PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=dhcp DEFROUTE=yes +IPV4_LINK_LOCAL=enabled IPV4_FAILURE_FATAL=no IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default ROUTING_RULE_1="priority 10 from 0.0.0.0/0 table 1" ROUTING_RULE_2="priority 10 to 192.167.8.0/24 table 2" ROUTING_RULE6_3="priority 10 from ::/0 table 10" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected index 9c2a1ff0..6a233664 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected @@ -15,7 +15,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write VLAN reorder_hdr" UUID=${UUID} ONBOOT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_AP_Mode.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_AP_Mode.cexpected index ccec2b81..caeaaff8 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_AP_Mode.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_AP_Mode.cexpected @@ -13,7 +13,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wi-Fi AP Mode" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected index ddbd986f..7e3d4f02 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected @@ -12,7 +12,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wi-Fi Band A" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected index 495a24d3..01a091ec 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected @@ -11,7 +11,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wi-Fi Hidden" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected index aec6918a..7fa0fc98 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected @@ -12,7 +12,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wi-Fi MAC always" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected index 9d47163f..1b4ca948 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected @@ -11,7 +11,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wi-Fi MAC default" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected index 43c07ddb..089d1442 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected @@ -12,7 +12,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wi-Fi MAC missing" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected index 21f2e2de..880db3f7 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected @@ -12,7 +12,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wi-Fi MAC never" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected index f1b01a21..b9129c59 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected @@ -13,7 +13,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wifi LEAP" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected index 60540fbe..2dd8958b 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected @@ -12,7 +12,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wifi WEP 104 ASCII" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected index 214f5da5..8dd2cbff 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected @@ -9,7 +9,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wired Auto-Negotiate" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected index fe864c43..bc2806a9 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected @@ -19,6 +19,7 @@ ARPING_WAIT=1 IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=eui64 NAME="Test Write Wired Static Routes" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected index de66dcdd..34f89b5e 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected @@ -9,7 +9,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wired Wake-on-LAN" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected index 908cb917..318fae5d 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected @@ -10,6 +10,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=eui64 NAME="Test Write Wired with Match setting" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected index fef36f2f..0a9ef3d4 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected @@ -33,7 +33,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME=dcb-test UUID=${UUID} DEVICE=eth0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected index 1f5e04ff..0a5c2ccf 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected @@ -10,6 +10,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=eui64 NAME="random wifi connection" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected index 3dcdbda0..8a874d24 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected @@ -10,6 +10,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=eui64 NAME="random wifi connection 2" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected index b3199c01..9cba7e20 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected @@ -8,6 +8,7 @@ IPV6_DISABLED=yes IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=eui64 NAME="Test Write Wired Disabled IP6" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-link_local b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-link_local new file mode 100644 index 00000000..cfc963fb --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-link_local @@ -0,0 +1,3 @@ +TYPE=Ethernet +BOOTPROTO=dhcp +IPV4_LINK_LOCAL=enabled diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected index a77c615d..121ead79 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected @@ -14,7 +14,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write SR-IOV config" UUID=${UUID} DEVICE=eth0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write-empty.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write-empty.cexpected index 1125ef55..8f513922 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write-empty.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write-empty.cexpected @@ -11,7 +11,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write TC config" UUID=${UUID} DEVICE=eth0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected index d87efd72..698bda3a 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected @@ -12,7 +12,7 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write TC config" UUID=${UUID} DEVICE=eth0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected index 5186f90c..11fd060e 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected @@ -8,7 +8,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME=test_roundtrip_ethtool UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected index 5632ebec..abd74bbd 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected @@ -9,7 +9,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME=test_roundtrip_ethtool UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected index 96eec43b..962cbdd5 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected @@ -9,7 +9,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME=test_roundtrip_ethtool UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected index 90250a30..213045df 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected @@ -9,7 +9,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME=test_roundtrip_ethtool UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected index 721eacb2..d3b9c056 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected @@ -9,7 +9,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME=test_roundtrip_ethtool UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected index 42608576..542f5b5b 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected @@ -9,7 +9,7 @@ IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no -IPV6_ADDR_GEN_MODE=stable-privacy +IPV6_ADDR_GEN_MODE=default NAME="Test Write Wired Auto-Negotiate" UUID=${UUID} ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes index 9c05417e..717ad1ac 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes @@ -6,7 +6,7 @@ ADDRESS1=44.55.66.77 NETMASK1=255.255.255.255 GATEWAY1=192.168.1.7 METRIC1=3 -OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 scope 10 initcwnd lock 13 initrwnd 14" +OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 scope 10 initcwnd lock 13 initrwnd 14 rto_min 300 advmss 1300 quickack 1" ADDRESS2=44.55.66.78 NETMASK2=255.255.255.255 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 83e48b81..9fdae4bd 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -668,8 +668,8 @@ test_read_miscellaneous_variables(void) NMSettingWired *s_wired; NMSettingIPConfig *s_ip4; char *expected_mac_blacklist[3] = {"00:16:41:11:22:88", - "00:16:41:11:22:99", - "6a:5d:5a:fa:dd:f0"}; + "00:16:41:11:22:99", + "6a:5d:5a:fa:dd:f0"}; int mac_blacklist_num, i; guint64 expected_timestamp = 0; @@ -1352,6 +1352,7 @@ test_read_wired_static_routes(void) g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 32); g_assert_cmpstr(nm_ip_route_get_next_hop(ip4_route), ==, "192.168.1.7"); g_assert_cmpint(nm_ip_route_get_metric(ip4_route), ==, 3); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_ADVMSS, 1300); nmtst_assert_route_attribute_byte(ip4_route, NM_IP_ROUTE_ATTRIBUTE_TOS, 0x28); nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 30000); nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 12); @@ -1360,6 +1361,8 @@ test_read_wired_static_routes(void) nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000); nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_RTO_MIN, 300); + nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_QUICKACK, TRUE); nmtst_assert_route_attribute_string(ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.1.1.1"); nmtst_assert_route_attribute_byte(ip4_route, NM_IP_ROUTE_ATTRIBUTE_SCOPE, 10); @@ -2027,17 +2030,17 @@ test_read_wired_aliases_good(gconstpointer test_data) NMSettingIPConfig *s_ip4; int expected_num_addresses; const char *expected_address_0[] = {"192.168.1.5", - "192.168.1.6", - "192.168.1.9", - "192.168.1.99", - NULL}; + "192.168.1.6", + "192.168.1.9", + "192.168.1.99", + NULL}; const char *expected_address_3[] = {"192.168.1.5", "192.168.1.6", NULL}; const char *expected_label_0[] = { - NULL, - "aliasem0:1", - "aliasem0:2", - "aliasem0:99", - NULL, + NULL, + "aliasem0:1", + "aliasem0:2", + "aliasem0:99", + NULL, }; const char *expected_label_3[] = { NULL, @@ -4538,7 +4541,12 @@ test_write_routing_rules(void) _nm_connection_new_setting(connection, NM_TYPE_SETTING_WIRED); s_ip4 = _nm_connection_new_setting(connection, NM_TYPE_SETTING_IP4_CONFIG); - g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_LINK_LOCAL, + NM_SETTING_IP4_LL_ENABLED, + NULL); s_ip6 = _nm_connection_new_setting(connection, NM_TYPE_SETTING_IP6_CONFIG); g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); @@ -4634,6 +4642,34 @@ test_write_wired_dhcp_plus_ip(void) } static void +test_read_write_link_local(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingIPConfig *s_ip4; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-link_local", NULL, TYPE_ETHERNET, NULL); + + s_ip4 = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_IP4_CONFIG); + g_assert(nm_setting_ip4_config_get_link_local(NM_SETTING_IP4_CONFIG(s_ip4)) + == NM_SETTING_IP4_LL_ENABLED); + + g_object_set(s_ip4, NM_SETTING_IP4_CONFIG_LINK_LOCAL, NM_SETTING_IP4_LL_DISABLED, NULL); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + s_ip4 = nmtst_connection_assert_setting(reread, NM_TYPE_SETTING_IP4_CONFIG); + g_assert(nm_setting_ip4_config_get_link_local(NM_SETTING_IP4_CONFIG(s_ip4)) + == NM_SETTING_IP4_LL_DISABLED); +} + +static void test_read_write_wired_dhcp_send_hostname(void) { nmtst_auto_unlinkfile char *testfile = NULL; @@ -8153,8 +8189,8 @@ test_read_infiniband(void) char *unmanaged = NULL; const char *mac; char expected_mac_address[INFINIBAND_ALEN] = {0x80, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, - 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, - 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22}; + 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, + 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22}; const char *transport_mode; connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-infiniband", @@ -10245,6 +10281,7 @@ main(int argc, char **argv) g_test_add_func(TPATH "read-dhcp", test_read_wired_dhcp); g_test_add_func(TPATH "read-dhcp-plus-ip", test_read_wired_dhcp_plus_ip); g_test_add_func(TPATH "read-shared-plus-ip", test_read_wired_shared_plus_ip); + g_test_add_func(TPATH "read-write-link-local", test_read_write_link_local); g_test_add_func(TPATH "read-dhcp-send-hostname", test_read_write_wired_dhcp_send_hostname); g_test_add_func(TPATH "read-dhcpv6-hostname-fallback", test_read_wired_dhcpv6_hostname_fallback); diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c index c079266e..a1d6781b 100644 --- a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c +++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c @@ -451,7 +451,7 @@ update_ip4_setting_from_if_block(NMConnection *connection, if_block *block, GErr netmask_v); return FALSE; } else { - netmask_int = nm_utils_ip4_netmask_to_prefix(tmp_mask); + netmask_int = _nm_utils_ip4_netmask_to_prefix(tmp_mask); } } diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c index efb9bfce..1d7de8d2 100644 --- a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -26,8 +26,6 @@ #include "libnm-core-intern/nm-core-internal.h" #include "libnm-core-intern/nm-keyfile-internal.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" - #include "settings/nm-settings-plugin.h" #include "settings/nm-settings-storage.h" #include "settings/nm-settings-utils.h" @@ -1247,9 +1245,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin) /* dirname_libs are a set of read-only directories with lower priority than /etc or /run. * There is nothing complicated about having multiple of such directories, so dirname_libs * is a list (which currently only has at most one directory). */ - priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB)); + priv->dirname_libs[0] = nm_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB)); priv->dirname_libs[1] = NULL; - priv->dirname_run = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN)); + priv->dirname_run = nm_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN)); priv->dirname_etc = nm_config_data_get_value(NM_CONFIG_GET_DATA_ORIG, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, @@ -1262,9 +1260,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin) } else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') { /* either invalid path or unspecified. Use the default. */ g_free(priv->dirname_etc); - priv->dirname_etc = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT)); + priv->dirname_etc = nm_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT)); } else - nm_sd_utils_path_simplify(priv->dirname_etc); + nm_path_simplify(priv->dirname_etc); /* no duplicates */ if (NM_IN_STRSET(priv->dirname_libs[0], priv->dirname_etc, priv->dirname_run)) diff --git a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c index 11a8e416..1307d4b2 100644 --- a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c +++ b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c @@ -1805,8 +1805,8 @@ test_read_infiniband_connection(void) NMSettingInfiniband *s_ib; const char *mac; guint8 expected_mac[INFINIBAND_ALEN] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, - 0x45, 0x56, 0x67, 0x78, 0x89, 0x90}; + 0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, + 0x45, 0x56, 0x67, 0x78, 0x89, 0x90}; const char *expected_id = "Test InfiniBand Connection"; const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57"; @@ -1924,6 +1924,7 @@ test_write_bridge_main(void) gs_unref_object NMConnection *connection = NULL; NMSettingConnection *s_con; NMSettingBridge *s_bridge; + NMSettingWired *s_wired; NMSettingIPConfig *s_ip4; NMSettingIPConfig *s_ip6; @@ -1953,6 +1954,11 @@ test_write_bridge_main(void) g_assert(s_bridge); nm_connection_add_setting(connection, NM_SETTING(s_bridge)); + /* Ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + g_assert(s_wired); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + /* IP4 setting */ s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); g_assert(s_ip4); diff --git a/src/core/supplicant/nm-supplicant-interface.c b/src/core/supplicant/nm-supplicant-interface.c index e7ac0e58..fa8b4e37 100644 --- a/src/core/supplicant/nm-supplicant-interface.c +++ b/src/core/supplicant/nm-supplicant-interface.c @@ -3174,7 +3174,7 @@ _signal_handle(NMSupplicantInterface *self, _set_p2p_assigned_addr(iface, addr_data, - nm_utils_ip4_netmask_to_prefix(netmask)); + _nm_utils_ip4_netmask_to_prefix(netmask)); } else { _LOGW("P2P: GroupStarted signaled invalid IP Address information"); } diff --git a/src/core/tests/config/test-config.c b/src/core/tests/config/test-config.c index fa7fae07..17a66b06 100644 --- a/src/core/tests/config/test-config.c +++ b/src/core/tests/config/test-config.c @@ -1103,9 +1103,9 @@ test_config_set_values(void) const char *CONFIG_USER = BUILD_DIR "/test-set-values-user.conf"; const char *CONFIG_INTERN = BUILD_DIR "/test-set-values-intern.conf"; const char *atomic_section_prefixes[] = { - "atomic-prefix-1.", - "atomic-prefix-2.", - NULL, + "atomic-prefix-1.", + "atomic-prefix-2.", + NULL, }; g_assert(g_file_set_contents(CONFIG_USER, "", 0, NULL)); diff --git a/src/core/tests/test-core-with-expect.c b/src/core/tests/test-core-with-expect.c index eca098c4..de510c76 100644 --- a/src/core/tests/test-core-with-expect.c +++ b/src/core/tests/test-core-with-expect.c @@ -227,11 +227,11 @@ do_test_nm_utils_kill_child(void) { GLogLevelFlags fatal_mask; char *argv_watchdog[] = { - "bash", - "-c", - "sleep 4; " - "kill -KILL 0; #watchdog for #" TEST_TOKEN, - NULL, + "bash", + "-c", + "sleep 4; " + "kill -KILL 0; #watchdog for #" TEST_TOKEN, + NULL, }; char *argv1[] = { "bash", diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c index 11a7f323..c260788e 100644 --- a/src/core/tests/test-core.c +++ b/src/core/tests/test-core.c @@ -2353,21 +2353,21 @@ test_nm_utils_dhcp_client_id_systemd_node_specific(gconstpointer test_data) { const int TEST_IDX = GPOINTER_TO_INT(test_data); const guint8 HASH_KEY[16] = {0x80, - 0x11, - 0x8c, - 0xc2, - 0xfe, - 0x4a, - 0x03, - 0xee, - 0x3e, - 0xd6, - 0x0c, - 0x6f, - 0x36, - 0x39, - 0x14, - 0x09}; + 0x11, + 0x8c, + 0xc2, + 0xfe, + 0x4a, + 0x03, + 0xee, + 0x3e, + 0xd6, + 0x0c, + 0x6f, + 0x36, + 0x39, + 0x14, + 0x09}; const guint16 duid_type_en = htons(2); const guint32 systemd_pen = htonl(43793); const struct { diff --git a/src/core/tests/test-l3cfg.c b/src/core/tests/test-l3cfg.c index 924d98f1..a9ac4004 100644 --- a/src/core/tests/test-l3cfg.c +++ b/src/core/tests/test-l3cfg.c @@ -821,7 +821,7 @@ _test_l3_ipv6ll_find_lladdr(TestL3IPv6LLData *tdata, int ifindex) g_assert(tdata); - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex); nm_platform_iter_obj_for_each (&iter, tdata->f->platform, &lookup, &obj) { const NMPlatformIP6Address *a = NMP_OBJECT_CAST_IP6_ADDRESS(obj); diff --git a/src/core/tests/test-systemd.c b/src/core/tests/test-systemd.c index 71a6a60d..cf31ec33 100644 --- a/src/core/tests/test-systemd.c +++ b/src/core/tests/test-systemd.c @@ -13,27 +13,6 @@ /*****************************************************************************/ static void -test_dhcp_create(void) -{ - sd_dhcp_client *client4 = NULL; - int r; - - r = sd_dhcp_client_new(&client4, FALSE); - g_assert(r == 0); - g_assert(client4); - - if (/* never true */ client4 == (gpointer) &r) { - /* we don't want to call this, but ensure that the linker - * includes all these symbols. */ - sd_dhcp_client_start(client4); - } - - sd_dhcp_client_unref(client4); -} - -/*****************************************************************************/ - -static void test_lldp_create(void) { sd_lldp_rx *lldp = NULL; @@ -119,141 +98,6 @@ test_sd_event(void) /*****************************************************************************/ -static void -test_path_equal(void) -{ -#define _path_equal_check(path, expected) \ - G_STMT_START \ - { \ - const char *_path0 = (path); \ - const char *_expected = (expected); \ - gs_free char *_path = g_strdup(_path0); \ - const char *_path_result; \ - \ - _path_result = nm_sd_utils_path_simplify(_path); \ - g_assert(_path_result == _path); \ - g_assert_cmpstr(_path, ==, _expected); \ - } \ - G_STMT_END - - _path_equal_check("", ""); - _path_equal_check(".", "."); - _path_equal_check("..", ".."); - _path_equal_check("/..", "/.."); - _path_equal_check("//..", "/.."); - _path_equal_check("/.", "/"); - _path_equal_check("./", "."); - _path_equal_check("./.", "."); - _path_equal_check(".///.", "."); - _path_equal_check(".///./", "."); - _path_equal_check(".////", "."); - _path_equal_check("//..//foo/", "/../foo"); - _path_equal_check("///foo//./bar/.", "/foo/bar"); - _path_equal_check(".//./foo//./bar/.", "foo/bar"); -} - -/*****************************************************************************/ - -static void -_test_unbase64char(char ch, gboolean maybe_invalid) -{ - int r; - - r = nm_sd_utils_unbase64char(ch, FALSE); - - if (ch == '=') { - g_assert(!maybe_invalid); - g_assert_cmpint(r, <, 0); - g_assert_cmpint(nm_sd_utils_unbase64char(ch, TRUE), ==, G_MAXINT); - } else { - g_assert_cmpint(r, ==, nm_sd_utils_unbase64char(ch, TRUE)); - if (r >= 0) - g_assert_cmpint(r, <=, 255); - if (!maybe_invalid) - g_assert_cmpint(r, >=, 0); - } -} - -static void -_test_unbase64mem_mem(const char *base64, const guint8 *expected_arr, gsize expected_len) -{ - gs_free char *expected_base64 = NULL; - int r; - nm_auto_free guint8 *exp2_arr = NULL; - nm_auto_free guint8 *exp3_arr = NULL; - gsize exp2_len; - gsize exp3_len; - gsize i; - - expected_base64 = g_base64_encode(expected_arr, expected_len); - - for (i = 0; expected_base64[i]; i++) - _test_unbase64char(expected_base64[i], FALSE); - - r = nm_sd_utils_unbase64mem(expected_base64, - strlen(expected_base64), - TRUE, - &exp2_arr, - &exp2_len); - g_assert_cmpint(r, ==, 0); - g_assert_cmpmem(expected_arr, expected_len, exp2_arr, exp2_len); - - if (!nm_streq(base64, expected_base64)) { - r = nm_sd_utils_unbase64mem(base64, strlen(base64), TRUE, &exp3_arr, &exp3_len); - g_assert_cmpint(r, ==, 0); - g_assert_cmpmem(expected_arr, expected_len, exp3_arr, exp3_len); - } -} - -#define _test_unbase64mem(base64, expected_str) \ - _test_unbase64mem_mem(base64, (const guint8 *) "" expected_str "", NM_STRLEN(expected_str)) - -static void -_test_unbase64mem_inval(const char *base64) -{ - gs_free guint8 *exp_arr = NULL; - gsize exp_len = 0; - int r; - - r = nm_sd_utils_unbase64mem(base64, strlen(base64), TRUE, &exp_arr, &exp_len); - g_assert_cmpint(r, <, 0); - g_assert(!exp_arr); - g_assert(exp_len == 0); -} - -static void -test_nm_sd_utils_unbase64mem(void) -{ - gs_free char *rnd_base64 = NULL; - guint8 rnd_buf[30]; - guint i, rnd_len; - - _test_unbase64mem("", ""); - _test_unbase64mem(" ", ""); - _test_unbase64mem(" Y Q == ", "a"); - _test_unbase64mem(" Y WJjZGV mZ 2g = ", "abcdefgh"); - _test_unbase64mem_inval(" Y %WJjZGV mZ 2g = "); - _test_unbase64mem_inval(" Y %WJjZGV mZ 2g = a"); - _test_unbase64mem("YQ==", "a"); - _test_unbase64mem_inval("YQ==a"); - - rnd_len = nmtst_get_rand_uint32() % sizeof(rnd_buf); - for (i = 0; i < rnd_len; i++) - rnd_buf[i] = nmtst_get_rand_uint32() % 256; - rnd_base64 = g_base64_encode(rnd_buf, rnd_len); - _test_unbase64mem_mem(rnd_base64, rnd_buf, rnd_len); - - _test_unbase64char('=', FALSE); - for (i = 0; i < 10; i++) { - char ch = nmtst_get_rand_uint32() % 256; - - if (ch != '=') - _test_unbase64char(ch, TRUE); - } -} - -/*****************************************************************************/ - NMTST_DEFINE(); int @@ -261,11 +105,8 @@ main(int argc, char **argv) { nmtst_init(&argc, &argv, TRUE); - g_test_add_func("/systemd/dhcp/create", test_dhcp_create); g_test_add_func("/systemd/lldp/create", test_lldp_create); g_test_add_func("/systemd/sd-event", test_sd_event); - g_test_add_func("/systemd/test_path_equal", test_path_equal); - g_test_add_func("/systemd/test_nm_sd_utils_unbase64mem", test_nm_sd_utils_unbase64mem); return g_test_run(); } diff --git a/src/core/tests/test-utils.c b/src/core/tests/test-utils.c index ad9950dd..2bcb6f69 100644 --- a/src/core/tests/test-utils.c +++ b/src/core/tests/test-utils.c @@ -234,8 +234,10 @@ test_shorten_hostname(void) * system configuration (`getconf HOST_NAME_MAX`). On Linux * it's typically 64 characters, but POSIX allows up to * 255 characters. + * + * We use our own define NM_HOST_NAME_MAX, which is always 64. */ - maxhost = g_strnfill(HOST_NAME_MAX, 'a'); + maxhost = g_strnfill(NM_HOST_NAME_MAX, 'a'); do_test_shorten_hostname("name1", TRUE, NULL); |