diff options
Diffstat (limited to 'src/core/devices/nm-device.c')
-rw-r--r-- | src/core/devices/nm-device.c | 7365 |
1 files changed, 2917 insertions, 4448 deletions
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 7faec23c8e..a9b77fe05c 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -34,6 +34,8 @@ #include "nm-device-private.h" #include "nm-l3cfg.h" #include "nm-l3-config-data.h" +#include "nm-l3-ipv4ll.h" +#include "nm-l3-ipv6ll.h" #include "NetworkManagerUtils.h" #include "nm-manager.h" #include "libnm-platform/nm-platform.h" @@ -42,14 +44,13 @@ #include "libnm-platform/nmp-rules-manager.h" #include "ndisc/nm-ndisc.h" #include "ndisc/nm-lndp-ndisc.h" + #include "dhcp/nm-dhcp-manager.h" #include "dhcp/nm-dhcp-utils.h" #include "nm-act-request.h" -#include "nm-proxy-config.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" #include "nm-pacrunner-manager.h" #include "dnsmasq/nm-dnsmasq-manager.h" +#include "nm-ip-config.h" #include "nm-dhcp-config.h" #include "nm-rfkill-manager.h" #include "nm-firewall-utils.h" @@ -66,7 +67,6 @@ #include "nm-config.h" #include "c-list/src/c-list.h" #include "dns/nm-dns-manager.h" -#include "nm-acd-manager.h" #include "libnm-core-intern/nm-core-internal.h" #include "libnm-systemd-core/nm-sd.h" #include "nm-lldp-listener.h" @@ -86,19 +86,7 @@ #define DEFAULT_AUTOCONNECT TRUE -static guint32 -dhcp_grace_period_from_timeout(guint32 timeout) -{ -#define DHCP_GRACE_PERIOD_MULTIPLIER 2U - - nm_assert(timeout > 0); - nm_assert(timeout < G_MAXINT32); - - if (timeout < G_MAXUINT32 / DHCP_GRACE_PERIOD_MULTIPLIER) - return timeout * DHCP_GRACE_PERIOD_MULTIPLIER; - - return G_MAXUINT32; -} +#define GRACE_PERIOD_MULTIPLIER 2U #define CARRIER_WAIT_TIME_MS 6000 #define CARRIER_WAIT_TIME_AFTER_MTU_MS 10000 @@ -117,6 +105,13 @@ typedef enum { CLEANUP_TYPE_DECONFIGURE, } CleanupType; +typedef enum _nm_packed { + ADDR_METHOD_STATE_DISABLED, + ADDR_METHOD_STATE_PENDING, + ADDR_METHOD_STATE_GOOD, + ADDR_METHOD_STATE_FAILED, +} AddrMethodState; + typedef struct { CList lst_slave; NMDevice *slave; @@ -139,28 +134,61 @@ typedef struct { NMOptionBool autoprobe; } SriovOp; -typedef void (*AcdCallback)(NMDevice *, NMIP4Config **, gboolean); - typedef enum { /* The various NML3ConfigData types that we track explicitly. Note that * their relative order matters: higher numbers in this enum means more * important (and during merge overwrites other settings). */ + L3_CONFIG_DATA_TYPE_LL_4, + L3_CONFIG_DATA_TYPE_LL_6, + +#define L3_CONFIG_DATA_TYPE_LL_X(IS_IPv4) \ + ((IS_IPv4) ? L3_CONFIG_DATA_TYPE_LL_4 : L3_CONFIG_DATA_TYPE_LL_6) + L3_CONFIG_DATA_TYPE_AC_6, + L3_CONFIG_DATA_TYPE_PD_6, + L3_CONFIG_DATA_TYPE_DHCP_4, L3_CONFIG_DATA_TYPE_DHCP_6, - L3_CONFIG_DATA_TYPE_DEV_4, - L3_CONFIG_DATA_TYPE_DEV_6, - L3_CONFIG_DATA_TYPE_SETTING, + +#define L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4) \ + ((IS_IPv4) ? L3_CONFIG_DATA_TYPE_DHCP_4 : L3_CONFIG_DATA_TYPE_DHCP_6) + + L3_CONFIG_DATA_TYPE_SHARED_4, + L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC, + L3_CONFIG_DATA_TYPE_DEVIP_4, + L3_CONFIG_DATA_TYPE_DEVIP_6, + +#define L3_CONFIG_DATA_TYPE_DEVIP(addr_family) \ + ({ \ + L3ConfigDataType _t; \ + \ + switch (addr_family) { \ + case AF_INET: \ + _t = L3_CONFIG_DATA_TYPE_DEVIP_4; \ + break; \ + case AF_INET6: \ + _t = L3_CONFIG_DATA_TYPE_DEVIP_6; \ + break; \ + default: \ + nm_assert_not_reached(); \ + /* fall-through */ \ + case AF_UNSPEC: \ + _t = L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC; \ + break; \ + } \ + \ + _t; \ + }) + + L3_CONFIG_DATA_TYPE_MANUALIP, + _L3_CONFIG_DATA_TYPE_NUM, _L3_CONFIG_DATA_TYPE_NONE, + _L3_CONFIG_DATA_TYPE_ACD_ONLY, } L3ConfigDataType; -typedef struct { - AcdCallback callback; - NMDevice * device; - NMIP4Config **configs; -} AcdData; +G_STATIC_ASSERT(NM_L3CFG_CONFIG_PRIORITY_IPV4LL == L3_CONFIG_DATA_TYPE_LL_4); typedef enum { HW_ADDR_TYPE_UNSET = 0, @@ -177,19 +205,73 @@ typedef enum { } FirewallState; typedef struct { - NMIPConfig *orig; /* the original configuration applied to the device */ - NMIPConfig *current; /* configuration after external changes. NULL means - * that the original configuration didn't change. */ -} AppliedConfig; + NMIPConfig *ip_config; +} L3IPData; + +typedef struct { + GSource *check_async_source; + GSource *req_timeout_source; + union { + const NMDeviceIPState state; + NMDeviceIPState state_; + }; + bool wait_for_carrier : 1; + bool wait_for_ports : 1; + bool is_disabled : 1; + bool is_ignore : 1; + bool do_reapply : 1; +} IPStateData; typedef struct { - NMDhcpClient *client; - NMDhcpConfig *config; - gulong notify_sigid; - guint grace_id; - bool grace_pending : 1; - bool was_active : 1; -} DhcpData; + NMDhcpClient * client; + NMDhcpConfig * config; + gulong notify_sigid; + NMDeviceIPState state; + union { + struct { + } v4; + struct { + guint needed_prefixes; + NMNDiscDHCPLevel mode; + } v6; + }; +} IPDhcpStateData; + +typedef struct { + NMDeviceIPState state; + NMDeviceStateReason failed_reason; +} IPDevStateData; + +typedef struct { + NMDeviceIPState state; + union { + struct { + NMDnsMasqManager * dnsmasq_manager; + NMNetnsSharedIPHandle *shared_ip_handle; + NMFirewallConfig * firewall_config; + gulong dnsmasq_state_id; + } v4; + struct { + } v6; + }; +} IPSharedStateData; + +typedef struct { + NMDeviceIPState state; + union { + struct { + NML3IPv4LL * ipv4ll; + NML3IPv4LLRegistration *ipv4ll_registation; + GSource * timeout_source; + } v4; + struct { + NML3IPv6LL * ipv6ll; + GSource * retry_source; + NML3IPv6LLState llstate; + struct in6_addr lladdr; + } v6; + }; +} IPLLStateData; struct _NMDeviceConnectivityHandle { CList concheck_lst; @@ -234,8 +316,7 @@ typedef struct { enum { STATE_CHANGED, AUTOCONNECT_ALLOWED, - IP4_CONFIG_CHANGED, - IP6_CONFIG_CHANGED, + L3CD_CHANGED, IP6_PREFIX_DELEGATED, IP6_SUBNET_NEEDED, REMOVED, @@ -260,6 +341,9 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice, PROP_IP4_ADDRESS, PROP_IP4_CONFIG, PROP_DHCP4_CONFIG, + +#define PROP_DHCPX_CONFIG(IS_IPv4) ((IS_IPv4) ? PROP_DHCP4_CONFIG : PROP_DHCP6_CONFIG) + PROP_IP6_CONFIG, PROP_DHCP6_CONFIG, PROP_STATE, @@ -293,8 +377,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice, PROP_INTERFACE_FLAGS, ); typedef struct _NMDevicePrivate { - bool in_state_changed; - guint device_link_changed_id; guint device_ip_link_changed_id; @@ -308,22 +390,12 @@ typedef struct _NMDevicePrivate { NMDeviceStateReason reason; } queued_state; - union { - struct { - guint queued_ip_config_id_6; - guint queued_ip_config_id_4; - }; - guint queued_ip_config_id_x[2]; - }; - struct { const char **arr; guint len; guint alloc; } pending_actions; - GSList *dad6_failed_addrs; - NMDBusTrackObjPath parent_device; char *udi; @@ -339,15 +411,33 @@ typedef struct _NMDevicePrivate { }; union { + NML3Cfg *const l3cfg; + NML3Cfg * l3cfg_; + }; + + union { + struct { + L3IPData l3ipdata_6; + L3IPData l3ipdata_4; + }; + L3IPData l3ipdata_x[2]; + }; + + NML3CfgCommitTypeHandle *l3cfg_commit_type; + + union { const int ifindex; int ifindex_; }; + union { const int ip_ifindex; int ip_ifindex_; }; - NMNetnsSharedIPHandle *shared_ip_handle; + union { + const NML3ConfigData *d; + } l3cds[_L3_CONFIG_DATA_TYPE_NUM]; int parent_ifindex; @@ -370,9 +460,6 @@ typedef struct _NMDevicePrivate { bool real : 1; - bool update_ip_config_completed_v4 : 1; - bool update_ip_config_completed_v6 : 1; - NMDeviceType type; char * type_desc; NMLinkType link_type; @@ -386,6 +473,8 @@ typedef struct _NMDevicePrivate { bool hw_addr_perm_fake : 1; /* whether the permanent HW address could not be read and is a fake */ + guint8 in_state_changed : 4; + NMUtilsStableType current_stable_id_type : 3; bool nm_owned : 1; /* whether the device is a device owned and created by NM */ @@ -413,21 +502,8 @@ typedef struct _NMDevicePrivate { bool queued_act_request_is_waiting_for_carrier : 1; NMDBusTrackObjPath act_request; - union { - struct { - guint activation_source_id_6; - guint activation_source_id_4; /* for layer2 and IPv4. */ - }; - guint activation_source_id_x[2]; - }; - - union { - struct { - ActivationHandleFunc activation_source_func_6; - ActivationHandleFunc activation_source_func_4; /* for layer2 and IPv4. */ - }; - ActivationHandleFunc activation_source_func_x[2]; - }; + GSource * activation_idle_source; + ActivationHandleFunc activation_func; guint recheck_assume_id; @@ -451,7 +527,7 @@ typedef struct _NMDevicePrivate { gulong config_changed_id; gulong ifindex_changed_id; guint32 mtu; - guint32 ip6_mtu; + guint32 ip6_mtu; /* FIXME(l3cfg) */ guint32 mtu_initial; guint32 ip6_mtu_initial; NMDeviceMtuSource mtu_source; @@ -468,6 +544,14 @@ typedef struct _NMDevicePrivate { gint64 carrier_wait_until_ms; union { + struct { + NML3ConfigMergeFlags l3config_merge_flags_6; + NML3ConfigMergeFlags l3config_merge_flags_4; + }; + NML3ConfigMergeFlags l3config_merge_flags_x[2]; + }; + + union { const NMDeviceSysIfaceState sys_iface_state; NMDeviceSysIfaceState sys_iface_state_; }; @@ -477,9 +561,6 @@ typedef struct _NMDevicePrivate { bool up : 1; /* IFF_UP */ - bool v4_commit_first_time : 1; - bool v6_commit_first_time : 1; - bool default_route_metric_penalty_ip4_has : 1; bool default_route_metric_penalty_ip6_has : 1; @@ -495,102 +576,86 @@ typedef struct _NMDevicePrivate { bool is_enslaved : 1; - bool ipv6ll_handle : 1; /* TRUE if NM handles the device's IPv6LL address */ - bool ipv6ll_has : 1; - bool ndisc_started : 1; bool device_link_changed_down : 1; bool concheck_rp_filter_checked : 1; - NMDeviceStageState stage1_sriov_state : 3; - - bool ip_config_started : 1; bool tc_committed : 1; - char *current_stable_id; + NMDeviceStageState stage1_sriov_state : 3; - union { - struct { - GSource *ip_req_timeout_source_6; - GSource *ip_req_timeout_source_4; - }; - GSource *ip_req_timeout_source_x[2]; - }; + char *current_stable_id; - /* Proxy Configuration */ - NMProxyConfig * proxy_config; NMPacrunnerConfId *pacrunner_conf_id; - /* IP configuration info. Combined config from VPN, settings, and device */ + struct { + union { + const NMDeviceIPState state; + NMDeviceIPState state_; + }; + } ip_data; + union { struct { - NMIP6Config *ip_config_6; - NMIP4Config *ip_config_4; + IPStateData ip_data_6; + IPStateData ip_data_4; }; - NMIPConfig *ip_config_x[2]; + IPStateData ip_data_x[2]; }; - /* Config from DHCP, PPP, LLv4, etc */ - AppliedConfig dev_ip_config_4; + struct { + NMDeviceIPState state; + } ipmanual_data; - /* config from the setting */ union { struct { - NMIP6Config *con_ip_config_6; - NMIP4Config *con_ip_config_4; + IPDhcpStateData ipdhcp_data_6; + IPDhcpStateData ipdhcp_data_4; }; - NMIPConfig *con_ip_config_x[2]; + IPDhcpStateData ipdhcp_data_x[2]; }; - /* Stuff added outside NM */ + struct { + NMNDisc * ndisc; + GSource * ndisc_grace_source; + gulong ndisc_changed_id; + gulong ndisc_timeout_id; + NMDeviceIPState state; + } ipac6_data; + union { struct { - NMIP6Config *ext_ip_config_6; - NMIP4Config *ext_ip_config_4; + IPLLStateData ipll_data_6; + IPLLStateData ipll_data_4; }; - NMIPConfig *ext_ip_config_x[2]; + IPLLStateData ipll_data_x[2]; }; - /* VPNs which use this device */ union { struct { - GSList *vpn_configs_6; - GSList *vpn_configs_4; + IPSharedStateData ipshared_data_6; + IPSharedStateData ipshared_data_4; }; - GSList *vpn_configs_x[2]; + IPSharedStateData ipshared_data_x[2]; }; - /* Extra device configuration, injected by the subclass of NMDevice. - * This is used for example by NMDeviceModem for WWAN configuration. */ union { struct { - AppliedConfig dev2_ip_config_6; - AppliedConfig dev2_ip_config_4; + IPDevStateData ipdev_data_6; + IPDevStateData ipdev_data_4; }; - AppliedConfig dev2_ip_config_x[2]; + IPDevStateData ipdev_data_x[2]; }; - /* DHCPv4 tracking */ - struct { - char *pac_url; - } dhcp4; + IPDevStateData ipdev_data_unspec; struct { - /* IP6 config from DHCP */ - AppliedConfig ip6_config; - /* Event ID of the current IP6 config from DHCP */ - char * event_id; - NMNDiscDHCPLevel mode; - guint needed_prefixes; - } dhcp6; + /* If we set the addrgenmode6, this records the previously set value. */ + guint8 previous_mode_val; - union { - struct { - DhcpData dhcp_data_6; - DhcpData dhcp_data_4; - }; - DhcpData dhcp_data_x[2]; - }; + /* whether @previous_mode_val is set. */ + bool previous_mode_has : 1; + } addrgenmode6_data; struct { NMLogDomain log_domain; @@ -602,58 +667,15 @@ typedef struct _NMDevicePrivate { guint deadline; } gw_ping; - /* dnsmasq stuff for shared connections */ - NMDnsMasqManager *dnsmasq_manager; - gulong dnsmasq_state_id; - /* Firewall */ FirewallState fw_state : 4; NMFirewalldManager * fw_mgr; NMFirewalldManagerCallId *fw_call; - /* IPv4LL stuff */ - sd_ipv4ll *ipv4ll; - guint ipv4ll_timeout; - guint rt6_temporary_not_available_id; - - /* IPv4 DAD stuff */ - struct { - GSList * dad_list; - NMAcdManager *announcing; - } acd; - - union { - struct { - const NMDeviceIPState ip_state_6; - const NMDeviceIPState ip_state_4; - }; - union { - const NMDeviceIPState ip_state_x[2]; - NMDeviceIPState ip_state_x_[2]; - }; - }; - - AppliedConfig ac_ip6_config; /* config from IPv6 autoconfiguration */ - NMIP6Config * ext_ip6_config_captured; /* Configuration captured from platform. */ - NMIP6Config * dad6_ip6_config; - struct in6_addr ipv6ll_addr; - - GHashTable *rt6_temporary_not_available; - - NMNDisc * ndisc; - gulong ndisc_changed_id; - gulong ndisc_timeout_id; - NMSettingIP6ConfigPrivacy ndisc_use_tempaddr; - - guint linklocal6_timeout_id; - guint8 linklocal6_dad_counter; - GHashTable *ip6_saved_properties; EthtoolState *ethtool_state; - gboolean needs_ip6_subnet; - /* master interface for bridge/bond/team slave */ NMDevice *master; gulong master_ready_id; @@ -709,6 +731,8 @@ typedef struct _NMDevicePrivate { bool mtu_force_set_done : 1; + bool needs_ip6_subnet : 1; + NMOptionBool promisc_reset; } NMDevicePrivate; @@ -718,59 +742,83 @@ G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_DBUS_OBJECT) /*****************************************************************************/ +static NMSettingConnectionMdns _prop_get_connection_mdns(NMDevice *self); +static NMSettingConnectionLlmnr _prop_get_connection_llmnr(NMDevice *self); + static const NMDBusInterfaceInfoExtended interface_info_device; static const GDBusSignalInfo signal_info_state_changed; -static void nm_device_set_proxy_config(NMDevice *self, const char *pac_url); +static void _dev_l3_cfg_commit(NMDevice *self, gboolean do_sync); -static gboolean update_ext_ip_config(NMDevice *self, int addr_family, gboolean intersect_configs); - -static gboolean nm_device_set_ip_config(NMDevice * self, - int addr_family, - NMIPConfig *config, - gboolean commit, - GPtrArray * ip4_dev_route_blacklist); - -static gboolean ip_config_merge_and_apply(NMDevice *self, int addr_family, gboolean commit); +static void _dev_l3_cfg_commit_type_reset(NMDevice *self); static gboolean nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure); static void nm_device_slave_notify_enslave(NMDevice *self, gboolean success); static void nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason); -static void addrconf6_start_with_link_ready(NMDevice *self); -static gboolean linklocal6_start(NMDevice *self); +static void _dev_ipll6_start(NMDevice *self); -static guint32 default_route_metric_penalty_get(NMDevice *self, int addr_family); +static void _dev_ipac6_start_continue(NMDevice *self); -static guint _prop_get_ipv4_dad_timeout(NMDevice *self); +static guint32 _dev_default_route_metric_penalty_get(NMDevice *self, int addr_family); -static NMIP6Config *dad6_get_pending_addresses(NMDevice *self); +static guint32 _prop_get_ipv4_dad_timeout(NMDevice *self); static void _carrier_wait_check_queued_act_request(NMDevice *self); static gint64 _get_carrier_wait_ms(NMDevice *self); +static GBytes *_prop_get_ipv6_dhcp_duid(NMDevice * self, + NMConnection *connection, + GBytes * hwaddr, + gboolean * out_enforce); + static const char *_activation_func_to_string(ActivationHandleFunc func); static void _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, gboolean quitting); -static void queued_state_clear(NMDevice *device); -static gboolean queued_ip4_config_change(gpointer user_data); -static gboolean queued_ip6_config_change(gpointer user_data); -static void ip_check_ping_watch_cb(GPid pid, int status, gpointer user_data); -static gboolean ip_config_valid(NMDeviceState state); -static NMActStageReturn dhcp4_start(NMDevice *self); -static gboolean dhcp6_start(NMDevice *self, gboolean wait_for_ll); -static void nm_device_start_ip_check(NMDevice *self); -static void realize_start_setup(NMDevice * self, - const NMPlatformLink *plink, - gboolean assume_state_guess_assume, - const char * assume_state_connection_uuid, - gboolean set_nm_owned, - NMUnmanFlagOp unmanaged_user_explicit, - gboolean force_platform_init); -static void _set_mtu(NMDevice *self, guint32 mtu); -static void _commit_mtu(NMDevice *self, const NMIP4Config *config); -static void _cancel_activation(NMDevice *self); +static void queued_state_clear(NMDevice *device); +static void ip_check_ping_watch_cb(GPid pid, int status, gpointer user_data); +static void nm_device_start_ip_check(NMDevice *self); +static void realize_start_setup(NMDevice * self, + const NMPlatformLink *plink, + gboolean assume_state_guess_assume, + const char * assume_state_connection_uuid, + gboolean set_nm_owned, + NMUnmanFlagOp unmanaged_user_explicit, + gboolean force_platform_init); +static void _set_mtu(NMDevice *self, guint32 mtu); +static void _commit_mtu(NMDevice *self); +static void _cancel_activation(NMDevice *self); + +static void _dev_ipll4_notify_event(NMDevice *self); + +static void _dev_ip_state_check(NMDevice *self, int addr_family); + +static void _dev_ipmanual_check_ready(NMDevice *self); + +static void +_dev_ipdhcpx_cleanup(NMDevice *self, int addr_family, gboolean reset_dhcp_config, gboolean release); + +static void _dev_ip_state_check_async(NMDevice *self, int addr_family); + +static void _dev_ipdhcpx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state); + +static void _dev_ipdhcpx_restart(NMDevice *self, int addr_family, gboolean release); + +static void _dev_ipdhcpx_handle_accept(NMDevice *self, int addr_family, const NML3ConfigData *l3cd); + +static gboolean +_dev_ipac6_grace_period_start(NMDevice *self, guint32 timeout_sec, gboolean force_restart); + +static void _dev_ipac6_start(NMDevice *self); + +static void _dev_unamanged_check_external_down(NMDevice *self, gboolean only_if_unmanaged); + +static void _dev_ipshared4_start(NMDevice *self); + +static void _dev_ipshared6_start(NMDevice *self); + +static void _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type); static void concheck_update_state(NMDevice * self, int addr_family, @@ -780,8 +828,79 @@ static void concheck_update_state(NMDevice * self, static void sriov_op_cb(GError *error, gpointer user_data); static void device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice *self); -static gboolean device_link_changed(NMDevice *self); -static void check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update); +static gboolean device_link_changed(gpointer user_data); + +/*****************************************************************************/ + +#define _NMLOG_addr_family(level, prefix, addr_family, fmt, ...) \ + G_STMT_START \ + { \ + const int _addr_family2 = (addr_family); \ + \ + _NMLOG(level, \ + (_addr_family2 == AF_UNSPEC ? LOGD_IP : LOGD_IPX(NM_IS_IPv4(_addr_family2))), \ + "" prefix "%s: " fmt, \ + nm_utils_addr_family_to_str(_addr_family2), \ + ##__VA_ARGS__); \ + } \ + G_STMT_END + +#define _NMLOG_ip(level, ...) _NMLOG_addr_family(level, "ip", __VA_ARGS__) +#define _LOGT_ip(...) _NMLOG_ip(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ip(...) _NMLOG_ip(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ip(...) _NMLOG_ip(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ip(...) _NMLOG_ip(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipll(level, ...) _NMLOG_addr_family(level, "ip:ll", __VA_ARGS__) +#define _LOGT_ipll(...) _NMLOG_ipll(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipll(...) _NMLOG_ipll(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipll(...) _NMLOG_ipll(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipll(...) _NMLOG_ipll(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipdev(level, ...) _NMLOG_addr_family(level, "ip:dev", __VA_ARGS__) +#define _LOGT_ipdev(...) _NMLOG_ipdev(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipdev(...) _NMLOG_ipdev(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipdev(...) _NMLOG_ipdev(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipdev(...) _NMLOG_ipdev(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipdhcp(level, ...) _NMLOG_addr_family(level, "ip:dhcp", __VA_ARGS__) +#define _LOGT_ipdhcp(...) _NMLOG_ipdhcp(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipdhcp(...) _NMLOG_ipdhcp(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipdhcp(...) _NMLOG_ipdhcp(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipdhcp(...) _NMLOG_ipdhcp(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipshared(level, ...) _NMLOG_addr_family(level, "ip:shared", __VA_ARGS__) +#define _LOGT_ipshared(...) _NMLOG_ipshared(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipshared(...) _NMLOG_ipshared(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipshared(...) _NMLOG_ipshared(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipshared(...) _NMLOG_ipshared(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipac6(level, ...) _NMLOG_addr_family(level, "ip:ac6", AF_UNSPEC, __VA_ARGS__) +#define _LOGT_ipac6(...) _NMLOG_ipac6(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipac6(...) _NMLOG_ipac6(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipac6(...) _NMLOG_ipac6(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipac6(...) _NMLOG_ipac6(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipmanual(level, ...) _NMLOG_addr_family(level, "ip:manual", AF_UNSPEC, __VA_ARGS__) +#define _LOGT_ipmanual(...) _NMLOG_ipmanual(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipmanual(...) _NMLOG_ipmanual(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipmanual(...) _NMLOG_ipmanual(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipmanual(...) _NMLOG_ipmanual(LOGL_WARN, __VA_ARGS__) + +/*****************************************************************************/ + +#define _CACHED_BOOL(cached_value, cmd) \ + ({ \ + NMTernary *const _cached_value = (cached_value); \ + \ + nm_assert(_cached_value); \ + nm_assert_is_ternary(*_cached_value); \ + \ + if (*_cached_value == NM_TERNARY_DEFAULT) \ + *_cached_value = !!(cmd); \ + \ + !!(*_cached_value); \ + }) /*****************************************************************************/ @@ -1309,7 +1428,7 @@ _prop_get_connection_lldp(NMDevice *self) return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX; } -static guint +static guint32 _prop_get_ipv4_dad_timeout(NMDevice *self) { NMConnection * connection; @@ -1321,6 +1440,9 @@ _prop_get_ipv4_dad_timeout(NMDevice *self) s_ip4 = nm_connection_get_setting_ip4_config(connection); if (s_ip4) timeout = nm_setting_ip_config_get_dad_timeout(s_ip4); + + nm_assert(timeout >= -1 && timeout <= NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX); + if (timeout >= 0) return timeout; @@ -1337,8 +1459,8 @@ _prop_get_ipvx_dhcp_timeout(NMDevice *self, int addr_family) { NMDeviceClass *klass; NMConnection * connection; - int timeout_i; guint32 timeout; + int timeout_i; nm_assert(NM_IS_DEVICE(self)); nm_assert_addr_family(addr_family); @@ -1381,6 +1503,37 @@ out: } static guint32 +_prop_get_ipvx_dns_priority(NMDevice *self, int addr_family) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip; + int prio = 0; + + connection = nm_device_get_applied_connection(self); + s_ip = nm_connection_get_setting_ip_config(connection, addr_family); + if (s_ip) + prio = nm_setting_ip_config_get_dns_priority(s_ip); + + if (prio == 0) { + prio = nm_config_data_get_connection_default_int64( + NM_CONFIG_GET_DATA, + NM_IS_IPv4(addr_family) ? NM_CON_DEFAULT("ipv4.dns-priority") + : NM_CON_DEFAULT("ipv6.dns-priority"), + self, + G_MININT32, + G_MAXINT32, + 0); + if (prio == 0) { + prio = nm_device_is_vpn(self) ? NM_DNS_PRIORITY_DEFAULT_VPN + : NM_DNS_PRIORITY_DEFAULT_NORMAL; + } + } + + nm_assert(prio != 0); + return prio; +} + +static guint32 _prop_get_ipvx_required_timeout(NMDevice *self, int addr_family) { NMConnection * connection; @@ -1414,6 +1567,25 @@ _prop_get_ipvx_required_timeout(NMDevice *self, int addr_family) 0); } +static gboolean +_prop_get_ipvx_may_fail(NMDevice *self, int addr_family) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip = NULL; + + connection = nm_device_get_applied_connection(self); + if (connection) + s_ip = nm_connection_get_setting_ip_config(connection, addr_family); + + return !s_ip || nm_setting_ip_config_get_may_fail(s_ip); +} + +static gboolean +_prop_get_ipvx_may_fail_cached(NMDevice *self, int addr_family, NMTernary *cache) +{ + return _CACHED_BOOL(cache, _prop_get_ipvx_may_fail(self, addr_family)); +} + /** * _prop_get_ipvx_dhcp_iaid: * @self: the #NMDevice @@ -2390,27 +2562,6 @@ concheck_get_mgr(NMDevice *self) return priv->concheck_mgr; } -NMIP4Config * -nm_device_ip4_config_new(NMDevice *self) -{ - return nm_ip4_config_new(nm_device_get_multi_index(self), nm_device_get_ip_ifindex(self)); -} - -NMIP6Config * -nm_device_ip6_config_new(NMDevice *self) -{ - return nm_ip6_config_new(nm_device_get_multi_index(self), nm_device_get_ip_ifindex(self)); -} - -NMIPConfig * -nm_device_ip_config_new(NMDevice *self, int addr_family) -{ - nm_assert_addr_family(addr_family); - - return NM_IS_IPv4(addr_family) ? (gpointer) nm_device_ip4_config_new(self) - : (gpointer) nm_device_ip6_config_new(self); -} - NML3ConfigData * nm_device_create_l3_config_data(NMDevice *self, NMIPConfigSource source) { @@ -2425,98 +2576,27 @@ nm_device_create_l3_config_data(NMDevice *self, NMIPConfigSource source) return nm_l3_config_data_new(nm_device_get_multi_index(self), ifindex, source); } -static void -applied_config_clear(AppliedConfig *config) -{ - g_clear_object(&config->current); - g_clear_object(&config->orig); -} - -static void -applied_config_init(AppliedConfig *config, gpointer ip_config) +const NML3ConfigData * +nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *connection) { - nm_assert(!ip_config || (!config->orig && !config->current) - || nm_ip_config_get_addr_family(ip_config) - == nm_ip_config_get_addr_family(config->orig ?: config->current)); - nm_assert(!ip_config || NM_IS_IP_CONFIG(ip_config)); + NML3ConfigData *l3cd; + int ifindex; - nm_g_object_ref(ip_config); - applied_config_clear(config); - config->orig = ip_config; -} - -static void -applied_config_init_new(AppliedConfig *config, NMDevice *self, int addr_family) -{ - gs_unref_object NMIPConfig *c = nm_device_ip_config_new(self, addr_family); - - applied_config_init(config, c); -} - -static NMIPConfig * -applied_config_get_current(AppliedConfig *config) -{ - return config->current ?: config->orig; -} - -static void -applied_config_add_address(AppliedConfig *config, const NMPlatformIPAddress *address) -{ - if (config->orig) - nm_ip_config_add_address(config->orig, address); - else - nm_assert(!config->current); - - if (config->current) - nm_ip_config_add_address(config->current, address); -} - -static void -applied_config_add_nameserver(AppliedConfig *config, const NMIPAddr *ns) -{ - if (config->orig) - nm_ip_config_add_nameserver(config->orig, ns); - else - nm_assert(!config->current); - - if (config->current) - nm_ip_config_add_nameserver(config->current, ns); -} - -static void -applied_config_add_search(AppliedConfig *config, const char *new) -{ - if (config->orig) - nm_ip_config_add_search(config->orig, new); - else - nm_assert(!config->current); - - if (config->current) - nm_ip_config_add_search(config->current, new); -} - -static void -applied_config_reset_searches(AppliedConfig *config) -{ - if (config->orig) - nm_ip_config_reset_searches(config->orig); - else - nm_assert(!config->current); + nm_assert(NM_IS_DEVICE(self)); + nm_assert(!connection || NM_IS_CONNECTION(connection)); - if (config->current) - nm_ip_config_reset_searches(config->current); -} + if (!connection) + return NULL; -static void -applied_config_reset_nameservers(AppliedConfig *config) -{ - if (config->orig) - nm_ip_config_reset_nameservers(config->orig); - else - nm_assert(!config->current); + ifindex = nm_device_get_ip_ifindex(self); + if (ifindex <= 0) + g_return_val_if_reached(NULL); - if (config->current) - nm_ip_config_reset_nameservers(config->current); + l3cd = + nm_l3_config_data_new_from_connection(nm_device_get_multi_index(self), ifindex, connection); + nm_l3_config_data_set_mdns(l3cd, _prop_get_connection_mdns(self)); + nm_l3_config_data_set_llmnr(l3cd, _prop_get_connection_llmnr(self)); + return l3cd; } /*****************************************************************************/ @@ -2562,6 +2642,7 @@ nm_device_sys_iface_state_set(NMDevice *self, NMDeviceSysIfaceState sys_iface_st nm_device_sys_iface_state_to_string(priv->sys_iface_state), nm_device_sys_iface_state_to_string(sys_iface_state)); priv->sys_iface_state_ = sys_iface_state; + _dev_l3_cfg_commit_type_reset(self); } /* this function only sets a flag, no immediate actions are initiated. @@ -2674,33 +2755,6 @@ nm_device_assume_state_reset(NMDevice *self) /*****************************************************************************/ -static void -init_ip_config_dns_priority(NMDevice *self, NMIPConfig *config) -{ - const char *property; - int priority; - - property = (nm_ip_config_get_addr_family(config) == AF_INET) - ? NM_CON_DEFAULT("ipv4.dns-priority") - : NM_CON_DEFAULT("ipv6.dns-priority"); - - priority = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, - property, - self, - G_MININT, - G_MAXINT, - 0); - - if (priority == 0) { - priority = - nm_device_is_vpn(self) ? NM_DNS_PRIORITY_DEFAULT_VPN : NM_DNS_PRIORITY_DEFAULT_NORMAL; - } - - nm_ip_config_set_dns_priority(config, priority); -} - -/*****************************************************************************/ - static char * nm_device_sysctl_ip_conf_get(NMDevice *self, int addr_family, const char *property) { @@ -2837,93 +2891,878 @@ _add_capabilities(NMDevice *self, NMDeviceCapabilities capabilities) /*****************************************************************************/ +static void +_dev_ip_state_req_timeout_cancel(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + if (addr_family == AF_UNSPEC) { + _dev_ip_state_req_timeout_cancel(self, AF_INET); + _dev_ip_state_req_timeout_cancel(self, AF_INET6); + return; + } + + if (nm_clear_g_source_inst(&priv->ip_data_x[NM_IS_IPv4(addr_family)].req_timeout_source)) + _LOGD_ip(addr_family, "required-timeout: cancelled"); +} + static gboolean -ip_required_timeout_x(NMDevice *self, int addr_family) +_dev_ip_state_req_timeout_cb_x(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - _LOGD(LOGD_CORE, - "required-timeout expired for IPv%c", - nm_utils_addr_family_to_char(addr_family)); - nm_clear_g_source_inst(&priv->ip_req_timeout_source_x[NM_IS_IPv4(addr_family)]); - check_ip_state(self, FALSE, TRUE); + _LOGD_ip(addr_family, "required-timeout: expired"); + nm_clear_g_source_inst(&priv->ip_data_x[NM_IS_IPv4(addr_family)].req_timeout_source); + _dev_ip_state_check(self, addr_family); return G_SOURCE_CONTINUE; } static gboolean -ip_required_timeout_4(gpointer data) +_dev_ip_state_req_timeout_cb_4(gpointer user_data) { - return ip_required_timeout_x(data, AF_INET); + return _dev_ip_state_req_timeout_cb_x(user_data, AF_INET); } static gboolean -ip_required_timeout_6(gpointer data) +_dev_ip_state_req_timeout_cb_6(gpointer user_data) { - return ip_required_timeout_x(data, AF_INET6); + return _dev_ip_state_req_timeout_cb_x(user_data, AF_INET6); } static void -_set_ip_state(NMDevice *self, int addr_family, NMDeviceIPState new_state) +_dev_ip_state_req_timeout_schedule(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); - guint timeout_msec; - int v4; + guint32 timeout_msec; + char buf[32]; - nm_assert_addr_family(addr_family); + nm_assert(!priv->ip_data_x[IS_IPv4].req_timeout_source); - if (new_state == NM_DEVICE_IP_STATE_CONF && !priv->ip_config_started) { - /* Start the required-timeout timers when one of IPv4/IPv6 - * enters the CONF state. This means that if there is no carrier and - * ipv4.method=auto,ipv6.method=manual, the timeout for IPv4 will - * start as soon as connection is activated, even if DHCPv4 did not - * start yet. - */ - priv->ip_config_started = TRUE; + timeout_msec = _prop_get_ipvx_required_timeout(self, addr_family); + if (timeout_msec == 0) { + _LOGD_ip(addr_family, "required-timeout: disabled"); + return; + } - for (v4 = 1; v4 >= 0; v4--) { - char buf[32]; + _LOGD_ip(addr_family, + "required-timeout: started (%s msec)", + timeout_msec == G_MAXINT32 ? "∞" : nm_sprintf_buf(buf, "%u", timeout_msec)); - nm_assert(!priv->ip_req_timeout_source_x[v4]); - if ((timeout_msec = _prop_get_ipvx_required_timeout(self, v4 ? AF_INET : AF_INET6))) { - _LOGD(LOGD_CORE, - "required-timeout in %s msec for IPv%c", - timeout_msec == G_MAXINT32 ? "∞" : nm_sprintf_buf(buf, "%u", timeout_msec), - v4 ? '4' : '6'); + if (timeout_msec == G_MAXINT32) { + priv->ip_data_x[IS_IPv4].req_timeout_source = g_source_ref(nm_g_source_sentinel_get(0)); + } else { + priv->ip_data_x[IS_IPv4].req_timeout_source = nm_g_timeout_add_source( + timeout_msec, + IS_IPv4 ? _dev_ip_state_req_timeout_cb_4 : _dev_ip_state_req_timeout_cb_6, + self); + } +} - if (timeout_msec == G_MAXINT32) { - priv->ip_req_timeout_source_x[v4] = g_source_ref(nm_g_source_sentinel_get(0)); - } else { - priv->ip_req_timeout_source_x[v4] = - nm_g_timeout_add_source(timeout_msec, - v4 ? ip_required_timeout_4 : ip_required_timeout_6, - self); +static gboolean +_dev_ip_state_set_state(NMDevice * self, + int addr_family, + NMDeviceIPState ip_state, + const char * reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int IS_IPv4; + + if (addr_family == AF_UNSPEC) { + if (priv->ip_data.state == ip_state) + return FALSE; + _LOGD_ip(addr_family, + "set (combined) state %s (was %s, reason: %s)", + nm_device_ip_state_to_string(ip_state), + nm_device_ip_state_to_string(priv->ip_data.state), + reason); + priv->ip_data.state_ = ip_state; + return TRUE; + } + + IS_IPv4 = NM_IS_IPv4(addr_family); + + if (priv->ip_data_x[IS_IPv4].state_ == ip_state) + return FALSE; + + _LOGD_ip(addr_family, + "set state %s (was %s, reason: %s)", + nm_device_ip_state_to_string(ip_state), + nm_device_ip_state_to_string(priv->ip_data_x[IS_IPv4].state), + reason); + priv->ip_data_x[IS_IPv4].state_ = ip_state; + return TRUE; +} + +static void +_device_ip_state_accumulate(NMDeviceIPState state, + gboolean * out_is_started, + gboolean * out_is_pending, + gboolean * out_is_failed) +{ + switch (state) { + case NM_DEVICE_IP_STATE_NONE: + return; + case NM_DEVICE_IP_STATE_PENDING: + *out_is_started = TRUE; + *out_is_pending = TRUE; + return; + case NM_DEVICE_IP_STATE_READY: + *out_is_started = TRUE; + return; + case NM_DEVICE_IP_STATE_FAILED: + *out_is_started = TRUE; + *out_is_failed = TRUE; + return; + } + nm_assert_not_reached(); + return; +} + +static void +_dev_ip_state_check(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + gboolean s_is_started = FALSE; + gboolean s_is_failed = FALSE; + gboolean s_is_pending = FALSE; + gboolean v_bool; + NMDeviceIPState ip_state; + NMDeviceIPState ip_state_other; + NMDeviceIPState combinedip_state; + NMTernary may_fail = NM_TERNARY_DEFAULT; + NMTernary may_fail_other = NM_TERNARY_DEFAULT; + + /* State handling in NMDevice: + * + * NMDevice manages a lot of state, that is for the various IP addressing methods, the state + * of the interface (controller/port), and a overall nm_device_get_state(). + * + * The idea is to compartmentalize these states into smaller units, and combine them as appropriate. + * + * For example, NMDhcpClient already provides an API that hides most of the complexity. But it still + * needs to expose some state, like whether we are still trying to get a lease (PENDING), whether there + * was a critical failure (FAILED) or we have a lease (READY). This state is grouped in NMDevice + * under priv->ipdhcp_data_x. Most important is priv->ipdhcp_data_x[].state, which distills all of this + * into 4 values of type NMDeviceIPState. This state is cached, so whenever something changes (e.g. + * an event from NMDhcpClient), we determine the new state and compare it with what is cached. If + * the cached state is as the new state, we are done. Otherwise, the change gets escalated (which + * means to call _dev_ip_state_check_async()). + * + * Then, the various sub-states escalate their changes to this function (_dev_ip_state_check). This + * function first takes the sub-states related to one IP address family, and combines them into + * priv->ip_data_x[] (and in particular priv->ip_data_x[].state). The same repeats. The current + * state is cached in priv->ip_data_x[].state, and _dev_ip_state_check() determines the new state. + * If there is no change, it ends here. Otherwise, it gets escalated. In this case, the escaplation + * happens in _dev_ip_state_check() below by combining the combined per-address-family into + * priv->ip_data. In particular this step needs to take into account settings like "may-fail" + * and "required-timeout". + * + * The escalation and compartmentalization priv->ip_data repeats. This time it escalates + * to the overall device state (nm_device_state_changed() and nm_device_get_state()), which then + * triggers larger state changes (e.g. the activation might fail). + */ + + if (priv->l3cfg && nm_l3cfg_commit_on_idle_is_scheduled(priv->l3cfg)) { + /* we have an update on NML3Cfg scheduled. We first process that, before + * progressing the IP state. When that's done, we will be called again. */ + _dev_ip_state_check_async(self, addr_family); + return; + } + + if (priv->ip_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_NONE) { + ip_state = NM_DEVICE_IP_STATE_NONE; + goto got_ip_state; + } + + if (nm_device_sys_iface_state_is_external(self)) { + ip_state = NM_DEVICE_IP_STATE_READY; + goto got_ip_state; + } + + if (priv->ip_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_PENDING + && (priv->state < NM_DEVICE_STATE_IP_CONFIG || priv->state > NM_DEVICE_STATE_ACTIVATED)) { + /* we can only leave pending state, if we are between (including) IP_CONFIG and ACTIVATED states. */ + ip_state = NM_DEVICE_IP_STATE_PENDING; + goto got_ip_state; + } + + if (priv->ip_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_PENDING + && nm_active_connection_get_master(NM_ACTIVE_CONNECTION(priv->act_request.obj)) + && !priv->is_enslaved) { + /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the + * master to enslave us. */ + ip_state = NM_DEVICE_IP_STATE_PENDING; + goto got_ip_state; + } + + if (priv->ip_data_x[IS_IPv4].wait_for_carrier || priv->ip_data_x[IS_IPv4].wait_for_ports) { + ip_state = NM_DEVICE_IP_STATE_PENDING; + goto got_ip_state; + } + + if (priv->ip_data_x[IS_IPv4].is_disabled || priv->ip_data_x[IS_IPv4].is_ignore) { + ip_state = NM_DEVICE_IP_STATE_READY; + goto got_ip_state; + } + + _device_ip_state_accumulate(priv->ipmanual_data.state, + &s_is_started, + &s_is_pending, + &s_is_failed); + + _device_ip_state_accumulate(priv->ipll_data_x[IS_IPv4].state, + &s_is_started, + &s_is_pending, + &s_is_failed); + + if (!IS_IPv4) { + _device_ip_state_accumulate(priv->ipac6_data.state, + &s_is_started, + &s_is_pending, + &s_is_failed); + } + + v_bool = FALSE; + _device_ip_state_accumulate(priv->ipdhcp_data_x[IS_IPv4].state, + &s_is_started, + &s_is_pending, + &v_bool); + if (v_bool) { + if (!IS_IPv4 && priv->ipdhcp_data_6.v6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) { + /* DHCPv6 is best-effort and not required. */ + } else + s_is_failed = TRUE; + } + + _device_ip_state_accumulate(priv->ipdev_data_x[IS_IPv4].state, + &s_is_started, + &s_is_pending, + &s_is_failed); + + _device_ip_state_accumulate(priv->ipdev_data_unspec.state, + &s_is_started, + &s_is_pending, + &s_is_failed); + + if (s_is_failed) + ip_state = NM_DEVICE_IP_STATE_FAILED; + else if (s_is_pending) + ip_state = NM_DEVICE_IP_STATE_PENDING; + else if (s_is_started) + ip_state = NM_DEVICE_IP_STATE_READY; + else + ip_state = NM_DEVICE_IP_STATE_PENDING; + +got_ip_state: + +#define _state_str_a(state, name) \ + ({ \ + const NMDeviceIPState _state = (state); \ + char * _s = ""; \ + \ + if (_state != NM_DEVICE_IP_STATE_NONE) { \ + _s = nm_sprintf_bufa(NM_STRLEN(name) + 11, \ + " " name "=%s", \ + nm_device_ip_state_to_string(_state)); \ + } \ + _s; \ + }) + + nm_assert(!priv->ip_data_4.is_ignore); + + _LOGT_ip(addr_family, + "check-state: state %s => %s, is_failed=%d, is_pending=%d, is_started=%d, " + "may-fail-4=%d, may-fail-6=%d;" + "%s;%s%s%s%s%s;%s%s%s%s%s%s%s", + nm_device_ip_state_to_string(priv->ip_data_x[IS_IPv4].state), + nm_device_ip_state_to_string(ip_state), + s_is_failed, + s_is_pending, + s_is_started, + _prop_get_ipvx_may_fail_cached(self, AF_INET, IS_IPv4 ? &may_fail : &may_fail_other), + _prop_get_ipvx_may_fail_cached(self, AF_INET6, !IS_IPv4 ? &may_fail : &may_fail_other), + _state_str_a(priv->ipmanual_data.state, "manualip"), + priv->ip_data_4.is_disabled ? " disabled4" : "", + _state_str_a(priv->ipll_data_4.state, "ll4"), + _state_str_a(priv->ipdhcp_data_4.state, "dhcp4"), + _state_str_a(priv->ipdev_data_4.state, "dev4"), + _state_str_a(priv->ipshared_data_4.state, "shared4"), + priv->ip_data_6.is_disabled ? " disabled6" : "", + priv->ip_data_6.is_ignore ? " ignore6" : "", + _state_str_a(priv->ipll_data_6.state, "ll6"), + _state_str_a(priv->ipac6_data.state, "ac6"), + _state_str_a(priv->ipdhcp_data_6.state, "dhcp6"), + _state_str_a(priv->ipdev_data_6.state, "dev6"), + _state_str_a(priv->ipshared_data_6.state, "shared6")); + + if (priv->ip_data_x[IS_IPv4].state == ip_state) { + /* no change. We can stop here. However, we also cancel the pending check, if any, + * because we just determined that there is no change. */ + } else { + _dev_ip_state_set_state(self, addr_family, ip_state, "check-ip-state"); + } + + if (ip_state == NM_DEVICE_IP_STATE_NONE) { + /* Nothing to do. This almost cannot happen, and there is probably nothing + * to do about this case. */ + goto out_done; + } + + ip_state_other = priv->ip_data_x[!IS_IPv4].state; + + if (ip_state == NM_DEVICE_IP_STATE_READY) { + /* we only set NM_ACTIVATION_STATE_FLAG_IP_READY_X() flag once we reach NM_DEVICE_IP_STATE_READY state. + * We don't ever clear it, even if we later enter NM_DEVICE_IP_STATE_FAILED state. + * + * This is not documented/guaranteed behavior, but seems to make sense for now. */ + _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_IP_READY_X(IS_IPv4)); + } + + if (ip_state == NM_DEVICE_IP_STATE_READY && ip_state_other == NM_DEVICE_IP_STATE_READY) + combinedip_state = NM_DEVICE_IP_STATE_READY; + else if (ip_state == NM_DEVICE_IP_STATE_READY && ip_state_other == NM_DEVICE_IP_STATE_PENDING + && (priv->ip_data_x[IS_IPv4].is_disabled || priv->ip_data_x[IS_IPv4].is_ignore)) { + /* This IP method is disabled/ignore, but the other family is still pending. + * Regardless of ipvx.may-fail, this means that we always require the other IP family + * to get ready too. */ + combinedip_state = NM_DEVICE_IP_STATE_PENDING; + } else if (ip_state == NM_DEVICE_IP_STATE_READY && ip_state_other == NM_DEVICE_IP_STATE_PENDING + && (priv->ip_data_x[!IS_IPv4].req_timeout_source + || !_prop_get_ipvx_may_fail_cached(self, + nm_utils_addr_family_other(addr_family), + &may_fail_other))) + combinedip_state = NM_DEVICE_IP_STATE_PENDING; + else if (ip_state == NM_DEVICE_IP_STATE_READY && ip_state_other == NM_DEVICE_IP_STATE_PENDING + && _prop_get_ipvx_may_fail_cached(self, + nm_utils_addr_family_other(addr_family), + &may_fail_other)) + combinedip_state = NM_DEVICE_IP_STATE_READY; + else if (ip_state == NM_DEVICE_IP_STATE_FAILED + && !_prop_get_ipvx_may_fail_cached(self, addr_family, &may_fail)) + combinedip_state = NM_DEVICE_IP_STATE_FAILED; + else { + if (priv->ip_data.state == NM_DEVICE_IP_STATE_NONE) + combinedip_state = NM_DEVICE_IP_STATE_PENDING; + else + combinedip_state = priv->ip_data.state; + } + + _LOGT_ip(AF_UNSPEC, + "check-state: (combined) state %s => %s", + nm_device_ip_state_to_string(priv->ip_data.state), + nm_device_ip_state_to_string(combinedip_state)); + + if (!_dev_ip_state_set_state(self, AF_UNSPEC, combinedip_state, "check-ip-state")) + goto out_done; + + switch (combinedip_state) { + case NM_DEVICE_IP_STATE_PENDING: + break; + case NM_DEVICE_IP_STATE_READY: + _dev_ip_state_req_timeout_cancel(self, AF_UNSPEC); + if (priv->state == NM_DEVICE_STATE_IP_CONFIG) { + nm_device_state_changed(self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); + } + break; + case NM_DEVICE_IP_STATE_FAILED: + nm_device_state_changed(self, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + break; + case NM_DEVICE_IP_STATE_NONE: + default: + nm_assert_not_reached(); + } + +out_done: + /* we just checked the state. We can cancel the pending async check. */ + nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].check_async_source); +} + +static gboolean +_dev_ip_state_check_async_cb(NMDevice *self, int addr_family) +{ + _dev_ip_state_check(self, addr_family); + return G_SOURCE_CONTINUE; +} + +static gboolean +_dev_ip_state_check_async_cb_4(gpointer user_data) +{ + return _dev_ip_state_check_async_cb(user_data, AF_INET); +} + +static gboolean +_dev_ip_state_check_async_cb_6(gpointer user_data) +{ + return _dev_ip_state_check_async_cb(user_data, AF_INET6); +} + +static void +_dev_ip_state_check_async(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int IS_IPv4; + + if (addr_family == AF_UNSPEC) { + _dev_ip_state_check_async(self, AF_INET); + _dev_ip_state_check_async(self, AF_INET6); + return; + } + + IS_IPv4 = NM_IS_IPv4(addr_family); + if (!priv->ip_data_x[IS_IPv4].check_async_source) { + priv->ip_data_x[IS_IPv4].check_async_source = nm_g_idle_add_source( + (IS_IPv4 ? _dev_ip_state_check_async_cb_4 : _dev_ip_state_check_async_cb_6), + self); + } +} + +static void +_dev_ip_state_cleanup(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int IS_IPv4; + + if (addr_family == AF_UNSPEC) { + _dev_ip_state_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE, "ip-state-clear"); + return; + } + + IS_IPv4 = NM_IS_IPv4(addr_family); + + nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].check_async_source); + nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].req_timeout_source); + _dev_ip_state_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE, "ip-state-clear"); + priv->ip_data_x[IS_IPv4].wait_for_carrier = FALSE; + priv->ip_data_x[IS_IPv4].wait_for_ports = FALSE; + priv->ip_data_x[IS_IPv4].is_disabled = FALSE; + priv->ip_data_x[IS_IPv4].is_ignore = FALSE; + priv->ip_data_x[IS_IPv4].do_reapply = FALSE; +} + +/*****************************************************************************/ + +static gpointer +_dev_l3_config_data_tag_get(NMDevicePrivate *priv, L3ConfigDataType l3cd_type) +{ + nm_assert(_NM_INT_NOT_NEGATIVE(l3cd_type) && l3cd_type < G_N_ELEMENTS(priv->l3cds)); + + return &priv->l3cds[l3cd_type]; +} + +static L3ConfigDataType +_dev_l3_config_data_tag_to_type(NMDevice *self, gconstpointer tag) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int d; + + /* In C it is undefined behavior to compare unrelated pointers. + * Work around that by using nm_ptr_to_uintptr(), which casts the pointers + * to integers. + * + * I guess, theoretically it's still a problem to assume that if tag pointers + * somewhere inside priv->l3cds, that the uintptr_t case would also yield + * a value in that range. In practice, I couldn't imaging this not not work + * reliably. */ + + if (nm_ptr_to_uintptr(tag) < nm_ptr_to_uintptr(&priv->l3cds[0]) + || nm_ptr_to_uintptr(tag) >= nm_ptr_to_uintptr(&priv->l3cds[G_N_ELEMENTS(priv->l3cds)])) + return _L3_CONFIG_DATA_TYPE_NONE; + + d = ((typeof(priv->l3cds[0]) *) tag) - (&priv->l3cds[0]); + + nm_assert(d >= 0); + nm_assert(d < _L3_CONFIG_DATA_TYPE_NUM); + nm_assert(tag == &priv->l3cds[d]); + nm_assert(tag == _dev_l3_config_data_tag_get(priv, d)); + return d; +} + +static L3ConfigDataType +_dev_l3_config_data_acd_addr_info_to_type(NMDevice * self, + const NML3AcdAddrInfo *addr_info, + guint i_track_infos) +{ + nm_assert(NM_IS_DEVICE(self)); + nm_assert(addr_info); + nm_assert(i_track_infos < addr_info->n_track_infos); + + return _dev_l3_config_data_tag_to_type(self, addr_info->track_infos[i_track_infos].tag); +} + +// FIXME(l3cfg): unused function?? +_nm_unused static const NML3AcdAddrTrackInfo * +_dev_l3_config_data_acd_addr_info_has_by_type(NMDevice * self, + const NML3AcdAddrInfo *addr_info, + L3ConfigDataType l3cd_type) +{ + guint i; + + nm_assert(NM_IS_DEVICE(self)); + nm_assert(addr_info); + nm_assert(_NM_INT_NOT_NEGATIVE(l3cd_type) && l3cd_type < _L3_CONFIG_DATA_TYPE_NUM); + + for (i = 0; i < addr_info->n_track_infos; i++) { + if (l3cd_type == _dev_l3_config_data_acd_addr_info_to_type(self, addr_info, i)) + return &addr_info->track_infos[i]; + } + return NULL; +} + +static void +_dev_l3_get_config_settings(NMDevice * self, + L3ConfigDataType type, + NML3ConfigMergeFlags *out_merge_flags, + NML3AcdDefendType * out_acd_defend_type, + guint32 * out_acd_timeout_msec) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NML3ConfigMergeFlags flags; + NMConnection * connection; + NMSettingIPConfig * s_ip; + + nm_assert(_NM_INT_NOT_NEGATIVE(type) && type < _L3_CONFIG_DATA_TYPE_NUM); + + if (G_UNLIKELY(!priv->l3config_merge_flags_has)) { + int IS_IPv4; + + connection = nm_device_get_applied_connection(self); + + for (IS_IPv4 = 0; IS_IPv4 < 2; IS_IPv4++) { + flags = NM_L3_CONFIG_MERGE_FLAGS_NONE; + + if (connection + && (s_ip = nm_connection_get_setting_ip_config(connection, + IS_IPv4 ? AF_INET : AF_INET6))) { + if (nm_setting_ip_config_get_ignore_auto_routes(s_ip)) + flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES; + + if (nm_setting_ip_config_get_ignore_auto_dns(s_ip)) + flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DNS; + + if (nm_setting_ip_config_get_never_default(s_ip) + || nm_setting_ip_config_get_gateway(s_ip)) { + /* if the connection has an explicit gateway, we also ignore + * the default routes from other sources. */ + flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES; } } + + priv->l3config_merge_flags_x[IS_IPv4] = flags; + } + priv->l3config_merge_flags_has = TRUE; + } + + switch (type) { + case L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC: + case L3_CONFIG_DATA_TYPE_MANUALIP: + case L3_CONFIG_DATA_TYPE_LL_4: + case L3_CONFIG_DATA_TYPE_LL_6: + case L3_CONFIG_DATA_TYPE_PD_6: + case L3_CONFIG_DATA_TYPE_SHARED_4: + case L3_CONFIG_DATA_TYPE_DEVIP_4: + case L3_CONFIG_DATA_TYPE_AC_6: + case L3_CONFIG_DATA_TYPE_DHCP_6: + case L3_CONFIG_DATA_TYPE_DEVIP_6: + *out_acd_timeout_msec = _prop_get_ipv4_dad_timeout(self); + goto after_acd_timeout; + + case L3_CONFIG_DATA_TYPE_DHCP_4: + /* For DHCP, we perform ACD separately, because we want to decline the + * lease in case of a conflict. */ + *out_acd_timeout_msec = 0; + goto after_acd_timeout; + + case _L3_CONFIG_DATA_TYPE_NUM: + case _L3_CONFIG_DATA_TYPE_NONE: + case _L3_CONFIG_DATA_TYPE_ACD_ONLY: + break; + } + *out_acd_timeout_msec = nm_assert_unreachable_val(0); + +after_acd_timeout: + switch (type) { + case L3_CONFIG_DATA_TYPE_LL_4: + *out_acd_defend_type = NM_L3_ACD_DEFEND_TYPE_ONCE; + goto after_acd_defend_type; + + case L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC: + case L3_CONFIG_DATA_TYPE_MANUALIP: + case L3_CONFIG_DATA_TYPE_LL_6: + case L3_CONFIG_DATA_TYPE_PD_6: + case L3_CONFIG_DATA_TYPE_SHARED_4: + case L3_CONFIG_DATA_TYPE_DHCP_4: + case L3_CONFIG_DATA_TYPE_DEVIP_4: + case L3_CONFIG_DATA_TYPE_AC_6: + case L3_CONFIG_DATA_TYPE_DHCP_6: + case L3_CONFIG_DATA_TYPE_DEVIP_6: + *out_acd_defend_type = NM_L3_ACD_DEFEND_TYPE_ALWAYS; + goto after_acd_defend_type; + + case _L3_CONFIG_DATA_TYPE_NUM: + case _L3_CONFIG_DATA_TYPE_NONE: + case _L3_CONFIG_DATA_TYPE_ACD_ONLY: + break; + } + *out_acd_defend_type = nm_assert_unreachable_val(NM_L3_ACD_DEFEND_TYPE_ALWAYS); + +after_acd_defend_type: + switch (type) { + case L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC: + case L3_CONFIG_DATA_TYPE_MANUALIP: + case L3_CONFIG_DATA_TYPE_LL_4: + case L3_CONFIG_DATA_TYPE_LL_6: + case L3_CONFIG_DATA_TYPE_PD_6: + case L3_CONFIG_DATA_TYPE_SHARED_4: + *out_merge_flags = NM_L3_CONFIG_MERGE_FLAGS_NONE; + goto after_merge_flags; + + case L3_CONFIG_DATA_TYPE_DHCP_4: + case L3_CONFIG_DATA_TYPE_DEVIP_4: + *out_merge_flags = priv->l3config_merge_flags_4; + goto after_merge_flags; + + case L3_CONFIG_DATA_TYPE_AC_6: + case L3_CONFIG_DATA_TYPE_DHCP_6: + case L3_CONFIG_DATA_TYPE_DEVIP_6: + *out_merge_flags = priv->l3config_merge_flags_6; + goto after_merge_flags; + + case _L3_CONFIG_DATA_TYPE_NUM: + case _L3_CONFIG_DATA_TYPE_NONE: + case _L3_CONFIG_DATA_TYPE_ACD_ONLY: + break; + } + *out_merge_flags = nm_assert_unreachable_val(NM_L3_CONFIG_MERGE_FLAGS_NONE); + +after_merge_flags: + return; +} + +static gboolean +_dev_l3_register_l3cds_add_config(NMDevice *self, L3ConfigDataType l3cd_type) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NML3ConfigMergeFlags merge_flags; + NML3AcdDefendType acd_defend_type; + guint32 acd_timeout_msec; + + _dev_l3_get_config_settings(self, l3cd_type, &merge_flags, &acd_defend_type, &acd_timeout_msec); + return nm_l3cfg_add_config(priv->l3cfg, + _dev_l3_config_data_tag_get(priv, l3cd_type), + FALSE, + priv->l3cds[l3cd_type].d, + l3cd_type, + nm_device_get_route_table(self, AF_INET), + nm_device_get_route_table(self, AF_INET6), + nm_device_get_route_metric(self, AF_INET), + nm_device_get_route_metric(self, AF_INET6), + _dev_default_route_metric_penalty_get(self, AF_INET), + _dev_default_route_metric_penalty_get(self, AF_INET6), + _prop_get_ipvx_dns_priority(self, AF_INET), + _prop_get_ipvx_dns_priority(self, AF_INET6), + acd_defend_type, + acd_timeout_msec, + NM_L3CFG_CONFIG_FLAGS_NONE, + merge_flags); +} + +static gboolean +_dev_l3_register_l3cds_set_one(NMDevice * self, + L3ConfigDataType l3cd_type, + const NML3ConfigData *l3cd, + NMTernary commit_sync) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; + gboolean changed = FALSE; + + if (priv->l3cds[l3cd_type].d != l3cd) { + if (nm_l3_config_data_equal(priv->l3cds[l3cd_type].d, l3cd)) { + /* we would set to a different instance, but the same content! + * We keep the previous one and ignore the new @l3cd. + * + * Warning: this means, that after calling this function, + * priv->l3cds[l3cd_type].d still might point to a different + * (though semantically equal) l3cd instance. */ + } else { + l3cd_old = g_steal_pointer(&priv->l3cds[l3cd_type].d); + if (l3cd) + priv->l3cds[l3cd_type].d = nm_l3_config_data_ref_and_seal(l3cd); + } + } + + if (priv->l3cfg) { + if (priv->l3cds[l3cd_type].d) { + if (_dev_l3_register_l3cds_add_config(self, l3cd_type)) + changed = TRUE; + } + + if (l3cd_old) { + if (nm_l3cfg_remove_config(priv->l3cfg, + _dev_l3_config_data_tag_get(priv, l3cd_type), + l3cd_old)) + changed = TRUE; + } + } + + if (changed && commit_sync != NM_TERNARY_DEFAULT) + _dev_l3_cfg_commit(self, !!commit_sync); + + return changed; +} + +static gboolean +_dev_l3_register_l3cds(NMDevice *self, + NML3Cfg * l3cfg, + gboolean do_add /* else remove */, + NMTernary do_commit) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean is_external; + gboolean changed; + int i; + + if (!l3cfg) + return FALSE; + + is_external = nm_device_sys_iface_state_is_external(self); + + changed = FALSE; + for (i = 0; i < (int) G_N_ELEMENTS(priv->l3cds); i++) { + if (!priv->l3cds[i].d) + continue; + if (!do_add) { + if (nm_l3cfg_remove_config(l3cfg, + _dev_l3_config_data_tag_get(priv, i), + priv->l3cds[i].d)) + changed = TRUE; + continue; } + if (is_external) + continue; + if (_dev_l3_register_l3cds_add_config(self, i)) + changed = TRUE; } - if (priv->ip_state_x[IS_IPv4] == new_state) + if (do_commit == NM_TERNARY_DEFAULT) + do_commit = changed; + if (do_commit) + _dev_l3_cfg_commit(self, TRUE); + + return changed; +} + +/*****************************************************************************/ + +void +nm_device_l3cfg_commit(NMDevice *self, NML3CfgCommitType commit_type, gboolean do_sync) +{ + NMDevicePrivate *priv; + + g_return_if_fail(NM_IS_DEVICE(self)); + + priv = NM_DEVICE_GET_PRIVATE(self); + + if (!priv->l3cfg) return; - _LOGT(LOGD_DEVICE, - "ip%c-state: set to %d (%s)", - nm_utils_addr_family_to_char(addr_family), - (int) new_state, - nm_device_ip_state_to_string(new_state)); + if (!do_sync) { + nm_l3cfg_commit_on_idle_schedule(priv->l3cfg, commit_type); + return; + } - priv->ip_state_x_[IS_IPv4] = new_state; + nm_l3cfg_commit(priv->l3cfg, commit_type); +} - if (new_state == NM_DEVICE_IP_STATE_DONE) { - /* we only set the IPx_READY flag once we reach NM_DEVICE_IP_STATE_DONE state. We don't - * ever clear it, even if we later enter NM_DEVICE_IP_STATE_FAIL state. - * - * This is not documented/guaranteed behavior, but seems to make sense for now. */ - _active_connection_set_state_flags(self, - NM_IS_IPv4(addr_family) - ? NM_ACTIVATION_STATE_FLAG_IP4_READY - : NM_ACTIVATION_STATE_FLAG_IP6_READY); +static void +_dev_l3_cfg_commit(NMDevice *self, gboolean do_sync) +{ + nm_device_l3cfg_commit(self, NM_L3_CFG_COMMIT_TYPE_AUTO, do_sync); +} + +static void +_dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + nm_assert(l3cfg == priv->l3cfg); + + switch (notify_data->notify_type) { + case NM_L3_CONFIG_NOTIFY_TYPE_L3CD_CHANGED: + if (notify_data->l3cd_changed.commited) { + g_signal_emit(self, + signals[L3CD_CHANGED], + 0, + notify_data->l3cd_changed.l3cd_old, + notify_data->l3cd_changed.l3cd_new); + } + return; + case NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT: + { + const NML3AcdAddrInfo *addr_info = ¬ify_data->acd_event.info; + + if (addr_info->state > NM_L3_ACD_ADDR_STATE_PROBING) + _dev_ipmanual_check_ready(self); + return; + } + case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT: + _dev_ipmanual_check_ready(self); + return; + case NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT: + nm_assert(NM_IS_L3_IPV4LL(notify_data->ipv4ll_event.ipv4ll)); + if (priv->ipll_data_4.v4.ipv4ll == notify_data->ipv4ll_event.ipv4ll) + _dev_ipll4_notify_event(self); + return; + case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE: + return; + case NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED: + /* we commit again. This way we try to configure the routes.*/ + _dev_l3_cfg_commit(self, FALSE); + return; + case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE: + if (NM_FLAGS_ANY(notify_data->platform_change_on_idle.obj_type_flags, + nmp_object_type_to_flags(NMP_OBJECT_TYPE_LINK))) + _dev_unamanged_check_external_down(self, TRUE); + _dev_ipmanual_check_ready(self); + return; + + case _NM_L3_CONFIG_NOTIFY_TYPE_NUM: + break; } + nm_assert_not_reached(); +} + +static void +_dev_l3_cfg_commit_type_reset(NMDevice *self) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NML3CfgCommitType commit_type; + + if (!priv->l3cfg) + return; + + switch (priv->sys_iface_state) { + case NM_DEVICE_SYS_IFACE_STATE_EXTERNAL: + case NM_DEVICE_SYS_IFACE_STATE_REMOVED: + commit_type = NM_L3_CFG_COMMIT_TYPE_NONE; + goto do_set; + case NM_DEVICE_SYS_IFACE_STATE_ASSUME: + commit_type = NM_L3_CFG_COMMIT_TYPE_ASSUME; + goto do_set; + case NM_DEVICE_SYS_IFACE_STATE_MANAGED: + commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE; + goto do_set; + } + nm_assert_not_reached(); + return; + +do_set: + priv->l3cfg_commit_type = + nm_l3cfg_commit_type_register(priv->l3cfg, commit_type, priv->l3cfg_commit_type, "device"); } /*****************************************************************************/ @@ -2947,11 +3786,18 @@ nm_device_get_iface(NMDevice *self) static gboolean _set_ifindex(NMDevice *self, int ifindex, gboolean is_ip_ifindex) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - int * p_ifindex; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gs_unref_object NML3Cfg *l3cfg_old = NULL; + NML3CfgCommitTypeHandle *l3cfg_commit_type_old = NULL; + gboolean l3_changed; + int ip_ifindex_new; + int * p_ifindex; + gboolean l3cfg_was_reset = FALSE; - if (ifindex < 0) + if (ifindex < 0) { + nm_assert_not_reached(); ifindex = 0; + } p_ifindex = is_ip_ifindex ? &priv->ip_ifindex_ : &priv->ifindex_; @@ -2960,13 +3806,74 @@ _set_ifindex(NMDevice *self, int ifindex, gboolean is_ip_ifindex) *p_ifindex = ifindex; - _LOGD(LOGD_DEVICE, "ifindex: set %sifindex %d", is_ip_ifindex ? "ip-" : "", ifindex); + ip_ifindex_new = nm_device_get_ip_ifindex(self); - if (!is_ip_ifindex) - _notify(self, PROP_IFINDEX); + if (priv->l3cfg) { + if (ip_ifindex_new <= 0 || ip_ifindex_new != nm_l3cfg_get_ifindex(priv->l3cfg)) { + g_signal_handlers_disconnect_by_func(priv->l3cfg, + G_CALLBACK(_dev_l3_cfg_notify_cb), + self); + l3cfg_old = g_steal_pointer(&priv->l3cfg_); + l3cfg_commit_type_old = g_steal_pointer(&priv->l3cfg_commit_type); + l3cfg_was_reset = TRUE; + } + } + if (!priv->l3cfg && ip_ifindex_new > 0) { + priv->l3cfg_ = nm_netns_access_l3cfg(priv->netns, ip_ifindex_new); + + g_signal_connect(priv->l3cfg, + NM_L3CFG_SIGNAL_NOTIFY, + G_CALLBACK(_dev_l3_cfg_notify_cb), + self); + + _dev_l3_cfg_commit_type_reset(self); + l3cfg_was_reset = TRUE; + } + + _LOGD(LOGD_DEVICE, + "ifindex: set %sifindex %d%s%s%s%s%s%s", + is_ip_ifindex ? "ip-" : "", + ifindex, + NM_PRINT_FMT_QUOTED(l3cfg_old && l3cfg_old != priv->l3cfg, + " (old-l3cfg: ", + nm_hash_obfuscated_ptr_str_a(l3cfg_old), + ")", + ""), + NM_PRINT_FMT_QUOTED(priv->l3cfg && l3cfg_old != priv->l3cfg, + " (l3cfg: ", + nm_hash_obfuscated_ptr_str_a(priv->l3cfg), + ")", + "")); if (priv->manager) nm_manager_emit_device_ifindex_changed(priv->manager, self); + + if (!is_ip_ifindex) + _notify(self, PROP_IFINDEX); + + if (l3cfg_was_reset) { + nm_ip_config_take_and_unexport_on_idle(g_steal_pointer(&priv->l3ipdata_4.ip_config)); + nm_ip_config_take_and_unexport_on_idle(g_steal_pointer(&priv->l3ipdata_6.ip_config)); + if (priv->l3cfg) { + priv->l3ipdata_4.ip_config = nm_ip_config_new(AF_INET, priv->l3cfg, FALSE); + priv->l3ipdata_6.ip_config = nm_ip_config_new(AF_INET6, priv->l3cfg, FALSE); + } + _notify(self, PROP_IP4_CONFIG); + _notify(self, PROP_IP6_CONFIG); + } + + l3_changed = FALSE; + if (_dev_l3_register_l3cds(self, priv->l3cfg, TRUE, FALSE)) + l3_changed = TRUE; + if (_dev_l3_register_l3cds(self, l3cfg_old, FALSE, FALSE)) + l3_changed = TRUE; + + if (l3_changed) + _dev_l3_cfg_commit(self, TRUE); + + if (l3cfg_commit_type_old) + nm_l3cfg_commit_type_unregister(l3cfg_old, l3cfg_commit_type_old); + return TRUE; } @@ -3703,8 +4610,10 @@ nm_device_get_route_metric_default(NMDeviceType device_type) return 11000; } -static gboolean -default_route_metric_penalty_detect(NMDevice *self, int addr_family) +/* FIXME(l3cfg): we currently never call this function. We need to react + * to changes and re-commit the IP configuration with updated penalty. */ +_nm_unused static gboolean +_dev_default_route_metric_penalty_detect(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); @@ -3719,7 +4628,7 @@ default_route_metric_penalty_detect(NMDevice *self, int addr_family) } static guint32 -default_route_metric_penalty_get(NMDevice *self, int addr_family) +_dev_default_route_metric_penalty_get(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -3787,28 +4696,23 @@ nm_device_get_route_table(NMDevice *self, int addr_family) return route_table ?: (guint32) RT_TABLE_MAIN; } -static NMIPRouteTableSyncMode +/* FIXME(l3cfg): need to properly handle the route-table sync mode and + * use it during commit. */ +_nm_unused static NMIPRouteTableSyncMode _get_route_table_sync_mode_stateful(NMDevice *self, int addr_family) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMDedupMultiIter ipconf_iter; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); gboolean all_sync_now; gboolean all_sync_eff; all_sync_now = _prop_get_ipvx_route_table(self, addr_family) != 0u; if (!all_sync_now) { - const NMPlatformIPRoute *route; + const NML3ConfigData *l3cd = priv->l3cds[L3_CONFIG_DATA_TYPE_MANUALIP].d; /* If there's a local route switch to all-sync in order * to properly manage the local table */ - nm_ip_config_iter_ip_route_for_each (&ipconf_iter, priv->con_ip_config_x[IS_IPv4], &route) { - if (nm_platform_route_type_uncoerce(route->type_coerced) == RTN_LOCAL) { - all_sync_now = TRUE; - break; - } - } + all_sync_now = l3cd && nm_l3_config_data_has_routes_with_type_local(l3cd, addr_family); } if (all_sync_now) @@ -3840,18 +4744,20 @@ nm_device_get_best_default_route(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - switch (addr_family) { - case AF_INET: - return priv->ip_config_4 ? nm_ip4_config_best_default_route_get(priv->ip_config_4) : NULL; - case AF_INET6: - return priv->ip_config_6 ? nm_ip6_config_best_default_route_get(priv->ip_config_6) : NULL; - case AF_UNSPEC: - return (priv->ip_config_4 ? nm_ip4_config_best_default_route_get(priv->ip_config_4) : NULL) - ?: (priv->ip_config_6 ? nm_ip6_config_best_default_route_get(priv->ip_config_6) - : NULL); - default: - g_return_val_if_reached(NULL); - } + if (!priv->l3cfg) + return NULL; + + /* FIXME(l3cfg): this function returns the best default route that we + * *want* to configure. What is the meaning of that? Possibly the caller + * cares whether there *is* a default route configured, for which they + * should ask platform. + * + * Check callers why they call this. Quite possibly this whole notion of + * "has a default route" is wrong to being with, regardless whether we + * look at the desired or actual configuration. That is, because "has a default route" + * does not do justice to the complexity of routing (with policy routing, + * etc.). */ + return nm_l3cfg_get_best_default_route(priv->l3cfg, addr_family, TRUE); } const char * @@ -4406,14 +5312,8 @@ concheck_update_state(NMDevice * self, _notify(self, IS_IPv4 ? PROP_IP4_CONNECTIVITY : PROP_IP6_CONNECTIVITY); - if (priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_sys_iface_state_is_external(self)) { - if (nm_device_get_best_default_route(self, AF_INET) - && !ip_config_merge_and_apply(self, AF_INET, TRUE)) - _LOGW(LOGD_IP4, "Failed to update IPv4 route metric"); - if (nm_device_get_best_default_route(self, AF_INET6) - && !ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "Failed to update IPv6 route metric"); - } + if (priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_sys_iface_state_is_external(self)) + _dev_l3_register_l3cds(self, priv->l3cfg, TRUE, NM_TERNARY_DEFAULT); } static const char * @@ -4748,16 +5648,14 @@ find_slave_info(NMDevice *self, NMDevice *slave) static gboolean nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *connection) { - NMDevicePrivate *priv; - SlaveInfo * info; - gboolean success = FALSE; - gboolean configure; + 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); - priv = NM_DEVICE_GET_PRIVATE(self); info = find_slave_info(self, slave); if (!info) return FALSE; @@ -4780,22 +5678,17 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co */ nm_device_update_hw_address(self); + /* Since slave devices don't have their own IP configuration, + * set the MTU here. + */ + _commit_mtu(slave); + /* Restart IP configuration if we're waiting for slaves. Do this * after updating the hardware address as IP config may need the * new address. */ - if (success) { - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_WAIT) - nm_device_activate_stage3_ip_start(self, AF_INET); - - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_WAIT) - nm_device_activate_stage3_ip_start(self, AF_INET6); - } - - /* Since slave devices don't have their own IP configuration, - * set the MTU here. - */ - _commit_mtu(slave, NM_DEVICE_GET_PRIVATE(slave)->ip_config_4); + if (success) + nm_device_activate_schedule_stage3_ip_config(self, FALSE); return success; } @@ -4882,6 +5775,8 @@ nm_device_master_release_one_slave(NMDevice * self, NM_DEVICE_STATE_REASON_REMOVED); } +/*****************************************************************************/ + /** * can_unmanaged_external_down: * @self: the device @@ -4896,7 +5791,7 @@ can_unmanaged_external_down(NMDevice *self) } static NMUnmanFlagOp -is_unmanaged_external_down(NMDevice *self, gboolean consider_can) +_dev_unmanaged_is_external_down(NMDevice *self, gboolean consider_can) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -4913,7 +5808,7 @@ is_unmanaged_external_down(NMDevice *self, gboolean consider_can) } static void -set_unmanaged_external_down(NMDevice *self, gboolean only_if_unmanaged) +_dev_unamanged_check_external_down(NMDevice *self, gboolean only_if_unmanaged) { NMUnmanFlagOp ext_flags; @@ -4925,7 +5820,7 @@ set_unmanaged_external_down(NMDevice *self, gboolean only_if_unmanaged) return; } - ext_flags = is_unmanaged_external_down(self, FALSE); + ext_flags = _dev_unmanaged_is_external_down(self, FALSE); if (ext_flags != NM_UNMAN_FLAG_OP_SET_UNMANAGED) { /* Ensure the assume check is queued before any queued state changes * from the transition to UNAVAILABLE. @@ -4953,26 +5848,15 @@ nm_device_update_dynamic_ip_setup(NMDevice *self) g_hash_table_remove_all(priv->ip6_saved_properties); - if (priv->dhcp_data_4.client) { - if (!nm_device_dhcp4_renew(self, FALSE)) { - nm_device_state_changed(self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_DHCP_FAILED); - return; - } - } - if (priv->dhcp_data_6.client) { - if (!nm_device_dhcp6_renew(self, FALSE)) { - nm_device_state_changed(self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_DHCP_FAILED); - return; - } - } - if (priv->ndisc) { + if (priv->ipdhcp_data_4.state != NM_DEVICE_IP_STATE_NONE) + _dev_ipdhcpx_restart(self, AF_INET, FALSE); + if (priv->ipdhcp_data_6.state != NM_DEVICE_IP_STATE_NONE) + _dev_ipdhcpx_restart(self, AF_INET6, FALSE); + + if (priv->ipac6_data.ndisc) { /* FIXME: todo */ } - if (priv->dnsmasq_manager) { + if (priv->ipshared_data_4.v4.dnsmasq_manager) { /* FIXME: todo */ } } @@ -5007,10 +5891,7 @@ carrier_changed(NMDevice *self, gboolean carrier) nm_device_update_dynamic_ip_setup(self); /* If needed, also resume IP configuration that is * waiting for carrier. */ - if (nm_device_activate_ip4_state_in_wait(self)) - nm_device_activate_stage3_ip_start(self, AF_INET); - if (nm_device_activate_ip6_state_in_wait(self)) - nm_device_activate_stage3_ip_start(self, AF_INET6); + nm_device_activate_schedule_stage3_ip_config(self, FALSE); return; } /* fall-through and change state of device */ @@ -5108,10 +5989,6 @@ nm_device_set_carrier(NMDevice *self, gboolean carrier) nm_device_remove_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); _carrier_wait_check_queued_act_request(self); } - - /* Send ARP announcements if did not yet and have carrier. */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !priv->acd.announcing) - nm_device_arp_announce(self); } else { if (priv->carrier_wait_id) nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); @@ -5243,85 +6120,6 @@ device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice } static void -ndisc_set_router_config(NMNDisc *ndisc, NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gs_unref_array GArray *addresses = NULL; - gs_unref_array GArray *dns_servers = NULL; - gs_unref_array GArray * dns_domains = NULL; - guint len; - guint i; - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter ipconf_iter; - - if (nm_ndisc_get_node_type(ndisc) != NM_NDISC_NODE_TYPE_ROUTER) - return; - - head_entry = nm_ip6_config_lookup_addresses(priv->ip_config_6); - addresses = - g_array_sized_new(FALSE, TRUE, sizeof(NMNDiscAddress), head_entry ? head_entry->len : 0); - nm_dedup_multi_iter_for_each (&ipconf_iter, head_entry) { - const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS(ipconf_iter.current->obj); - NMNDiscAddress * ndisc_addr; - guint32 lifetime; - guint32 preferred; - - if (IN6_IS_ADDR_UNSPECIFIED(&addr->address) || IN6_IS_ADDR_LINKLOCAL(&addr->address)) - continue; - - if (addr->n_ifa_flags & IFA_F_TENTATIVE || addr->n_ifa_flags & IFA_F_DADFAILED) - continue; - - if (addr->plen != 64) - continue; - - lifetime = nmp_utils_lifetime_get(addr->timestamp, - addr->lifetime, - addr->preferred, - NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000, - &preferred); - if (!lifetime) - continue; - - g_array_set_size(addresses, addresses->len + 1); - ndisc_addr = &g_array_index(addresses, NMNDiscAddress, addresses->len - 1); - ndisc_addr->address = addr->address; - ndisc_addr->expiry_msec = - _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, lifetime); - ndisc_addr->expiry_preferred_msec = - _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, preferred); - } - - len = nm_ip6_config_get_num_nameservers(priv->ip_config_6); - dns_servers = g_array_sized_new(FALSE, TRUE, sizeof(NMNDiscDNSServer), len); - g_array_set_size(dns_servers, len); - for (i = 0; i < len; i++) { - const struct in6_addr *nameserver = nm_ip6_config_get_nameserver(priv->ip_config_6, i); - NMNDiscDNSServer * ndisc_nameserver; - - ndisc_nameserver = &g_array_index(dns_servers, NMNDiscDNSServer, i); - ndisc_nameserver->address = *nameserver; - ndisc_nameserver->expiry_msec = - _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, NM_NDISC_ROUTER_LIFETIME); - } - - len = nm_ip6_config_get_num_searches(priv->ip_config_6); - dns_domains = g_array_sized_new(FALSE, TRUE, sizeof(NMNDiscDNSDomain), len); - g_array_set_size(dns_domains, len); - for (i = 0; i < len; i++) { - const char * search = nm_ip6_config_get_search(priv->ip_config_6, i); - NMNDiscDNSDomain *ndisc_search; - - ndisc_search = &g_array_index(dns_domains, NMNDiscDNSDomain, i); - ndisc_search->domain = (char *) search; - ndisc_search->expiry_msec = - _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, NM_NDISC_ROUTER_LIFETIME); - } - - nm_ndisc_set_config(ndisc, addresses, dns_servers, dns_domains); -} - -static void device_update_interface_flags(NMDevice *self, const NMPlatformLink *plink) { NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); @@ -5352,8 +6150,9 @@ device_update_interface_flags(NMDevice *self, const NMPlatformLink *plink) } static gboolean -device_link_changed(NMDevice *self) +device_link_changed(gpointer user_data) { + NMDevice * self = user_data; NMDeviceClass * klass = NM_DEVICE_GET_CLASS(self); NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); gboolean ip_ifname_changed = FALSE; @@ -5438,8 +6237,8 @@ device_link_changed(NMDevice *self) nm_device_emit_recheck_auto_activate(self); } - if (priv->ndisc && pllink->inet6_token.id) { - if (nm_ndisc_set_iid(priv->ndisc, pllink->inet6_token)) + if (priv->ipac6_data.ndisc && pllink->inet6_token.id) { + if (nm_ndisc_set_iid(priv->ipac6_data.ndisc, pllink->inet6_token)) _LOGD(LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); } @@ -5486,23 +6285,14 @@ device_link_changed(NMDevice *self) nm_device_set_unmanaged_by_flags(self, NM_UNMANAGED_PLATFORM_INIT, FALSE, reason); } - set_unmanaged_external_down(self, FALSE); + _dev_unamanged_check_external_down(self, FALSE); device_recheck_slave_status(self, pllink); if (priv->up && (!was_up || seen_down)) { /* the link was down and just came up. That happens for example, while changing MTU. * We must restore IP configuration. */ - if (NM_IN_SET(priv->ip_state_4, NM_DEVICE_IP_STATE_CONF, NM_DEVICE_IP_STATE_DONE)) { - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) - _LOGW(LOGD_IP4, "failed applying IP4 config after link comes up again"); - } - - priv->linklocal6_dad_counter = 0; - if (NM_IN_SET(priv->ip_state_6, NM_DEVICE_IP_STATE_CONF, NM_DEVICE_IP_STATE_DONE)) { - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "failed applying IP6 config after link comes up again"); - } + _dev_l3_cfg_commit(self, TRUE); } if (update_unmanaged_specs) @@ -5524,8 +6314,9 @@ device_link_changed(NMDevice *self) } static gboolean -device_ip_link_changed(NMDevice *self) +device_ip_link_changed(gpointer user_data) { + NMDevice * self = user_data; NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); const NMPlatformLink *pllink; const char * ip_iface; @@ -5588,13 +6379,12 @@ link_changed_cb(NMPlatform * platform, if (!(info->n_ifi_flags & IFF_UP)) priv->device_link_changed_down = TRUE; if (!priv->device_link_changed_id) { - priv->device_link_changed_id = g_idle_add((GSourceFunc) device_link_changed, self); + priv->device_link_changed_id = g_idle_add(device_link_changed, self); _LOGD(LOGD_DEVICE, "queued link change for ifindex %d", ifindex); } } else if (ifindex == nm_device_get_ip_ifindex(self)) { if (!priv->device_ip_link_changed_id) { - priv->device_ip_link_changed_id = - g_idle_add((GSourceFunc) device_ip_link_changed, self); + priv->device_ip_link_changed_id = g_idle_add(device_ip_link_changed, self); _LOGD(LOGD_DEVICE, "queued link change for ip-ifindex %d", ifindex); } } @@ -6045,8 +6835,6 @@ realize_start_setup(NMDevice * self, g_return_if_fail(nm_device_get_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT)); g_return_if_fail(priv->ip_ifindex <= 0); g_return_if_fail(priv->ip_iface == NULL); - g_return_if_fail(!priv->queued_ip_config_id_4); - g_return_if_fail(!priv->queued_ip_config_id_6); _LOGD(LOGD_DEVICE, "start setup of %s, kernel ifindex %d", @@ -6093,9 +6881,6 @@ realize_start_setup(NMDevice * self, if (priv->firmware_version) _notify(self, PROP_FIRMWARE_VERSION); - priv->ipv6ll_handle = (nm_platform_link_get_inet6_addr_gen_mode(platform, priv->ifindex) - == NM_IN6_ADDR_GEN_MODE_NONE); - if (nm_platform_link_supports_sriov(platform, priv->ifindex)) capabilities |= NM_DEVICE_CAP_SRIOV; } @@ -6153,7 +6938,7 @@ realize_start_setup(NMDevice * self, * or have IP addressing */ nm_device_set_unmanaged_flags(self, NM_UNMANAGED_EXTERNAL_DOWN, - is_unmanaged_external_down(self, TRUE)); + _dev_unmanaged_is_external_down(self, TRUE)); /* Unmanaged the loopback device with an explicit NM_UNMANAGED_BY_TYPE flag. * Later we might want to manage 'lo' too. Currently, that doesn't work because @@ -6192,9 +6977,6 @@ nm_device_realize_finish(NMDevice *self, const NMPlatformLink *plink) if (plink) device_recheck_slave_status(self, plink); - priv->update_ip_config_completed_v4 = FALSE; - priv->update_ip_config_completed_v6 = FALSE; - priv->real = TRUE; _notify(self, PROP_REAL); @@ -6290,9 +7072,6 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error) } } - nm_clear_g_source(&priv->queued_ip_config_id_4); - nm_clear_g_source(&priv->queued_ip_config_id_6); - g_object_freeze_notify(G_OBJECT(self)); NM_DEVICE_GET_CLASS(self)->unrealize_notify(self); @@ -6656,117 +7435,6 @@ nm_device_get_master(NMDevice *self) return NULL; } -static gboolean -get_ip_config_may_fail(NMDevice *self, int addr_family) -{ - NMConnection * connection; - NMSettingIPConfig *s_ip; - - connection = nm_device_get_applied_connection(self); - - s_ip = nm_connection_get_setting_ip_config(connection, addr_family); - - return !s_ip || nm_setting_ip_config_get_may_fail(s_ip); -} - -/* - * check_ip_state - * - * When @full_state_update is TRUE, transition the device from IP_CONFIG to the - * next state according to the outcome of IPv4 and IPv6 configuration. @may_fail - * indicates that we are called just after the initial configuration and thus - * IPv4/IPv6 are allowed to fail if the ipvx.may-fail properties say so, because - * the IP methods couldn't even be started. - * If @full_state_update is FALSE, just check if the connection should be failed - * due to the state of both ip families and the ipvx.may-fail settings. - */ -static void -check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - gboolean ip4_disabled = FALSE, ip6_disabled = FALSE; - NMSettingIPConfig *s_ip4, *s_ip6; - NMDeviceState state; - int IS_IPv4; - - if (full_state_update && nm_device_get_state(self) != NM_DEVICE_STATE_IP_CONFIG) - return; - - /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the - * master to enslave us. */ - if (nm_active_connection_get_master(NM_ACTIVE_CONNECTION(priv->act_request.obj)) - && !priv->is_enslaved) - return; - - s_ip4 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG); - if (s_ip4 - && nm_streq0(nm_setting_ip_config_get_method(s_ip4), NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - ip4_disabled = TRUE; - - s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG); - if (s_ip6 - && NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip6), - NM_SETTING_IP6_CONFIG_METHOD_IGNORE, - NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) - ip6_disabled = TRUE; - - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE - && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { - /* Both method completed (or disabled), proceed with activation */ - nm_device_state_changed(self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } - - for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { - if (priv->ip_state_x[IS_IPv4] == NM_DEVICE_IP_STATE_CONF - && priv->ip_req_timeout_source_x[IS_IPv4]) { - return; - } - } - - if ((priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL - || (ip4_disabled && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE)) - && (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL - || (ip6_disabled && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE))) { - /* Either both methods failed, or only one failed and the other is - * disabled */ - if (nm_device_sys_iface_state_is_external_or_assume(self)) { - /* We have assumed configuration, but couldn't redo it. No problem, - * move to check state. */ - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_DONE); - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_DONE); - state = NM_DEVICE_STATE_IP_CHECK; - } else if (may_fail && get_ip_config_may_fail(self, AF_INET) - && get_ip_config_may_fail(self, AF_INET6)) { - /* Couldn't start either IPv6 and IPv4 autoconfiguration, - * but both are allowed to fail. */ - state = NM_DEVICE_STATE_SECONDARIES; - } else { - /* Autoconfiguration attempted without success. */ - state = NM_DEVICE_STATE_FAILED; - } - - if (full_state_update || state == NM_DEVICE_STATE_FAILED) { - nm_device_state_changed(self, state, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } - return; - } - - /* If a method is still pending but required, wait */ - if (priv->ip_state_4 != NM_DEVICE_IP_STATE_DONE && !get_ip_config_may_fail(self, AF_INET)) - return; - if (priv->ip_state_6 != NM_DEVICE_IP_STATE_DONE && !get_ip_config_may_fail(self, AF_INET6)) - return; - - /* If at least a method has completed, proceed with activation */ - if ((priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !ip4_disabled) - || (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE && !ip6_disabled)) { - if (full_state_update) - nm_device_state_changed(self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } -} - /** * nm_device_slave_notify_enslave: * @self: the slave device @@ -6804,13 +7472,17 @@ nm_device_slave_notify_enslave(NMDevice *self, gboolean success) } } - if (activating) { - if (success) - check_ip_state(self, FALSE, TRUE); - else - nm_device_queue_state(self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN); - } else + if (!activating) { nm_device_queue_recheck_assume(self); + return; + } + + if (!success) { + nm_device_queue_state(self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN); + return; + } + + nm_device_activate_schedule_stage3_ip_config(self, FALSE); } /** @@ -6881,6 +7553,9 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config) g_return_if_fail(NM_IS_DEVICE(self)); + _dev_ipdhcpx_cleanup(self, AF_INET, TRUE, FALSE); + _dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, FALSE); + priv = NM_DEVICE_GET_PRIVATE(self); if (priv->master) { /* this is called when something externally messes with the slave or during shut-down. @@ -6892,15 +7567,7 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config) NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } - if (unconfigure_ip_config) { - nm_device_set_ip_config(self, AF_INET, NULL, FALSE, NULL); - nm_device_set_ip_config(self, AF_INET6, NULL, FALSE, NULL); - } else { - if (priv->dhcp_data_4.client) - nm_dhcp_client_stop(priv->dhcp_data_4.client, FALSE); - if (priv->dhcp_data_6.client) - nm_dhcp_client_stop(priv->dhcp_data_6.client, FALSE); - } + _dev_l3_register_l3cds(self, priv->l3cfg, FALSE, TRUE); } static gboolean @@ -7158,22 +7825,36 @@ nm_device_can_auto_connect(NMDevice *self, NMSettingsConnection *sett_conn, char static gboolean device_has_config(NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + const NMDedupMultiHeadEntry *head_entry; + const NMPlatformLink * pllink; + NMPLookup lookup; + + pllink = nm_l3cfg_get_pllink(priv->l3cfg, TRUE); + if (!pllink) + return FALSE; - /* Check for IP configuration. */ - if (priv->ip_config_4 && nm_ip4_config_get_num_addresses(priv->ip_config_4)) + if (pllink->master > 0) { + /* Master-slave relationship is also a configuration */ return TRUE; - if (priv->ip_config_6 && nm_ip6_config_get_num_addresses(priv->ip_config_6)) + } + + head_entry = nm_platform_lookup( + nm_device_get_platform(self), + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, pllink->ifindex)); + if (head_entry) return TRUE; - /* The existence of a software device is good enough. */ - if (nm_device_is_software(self) && nm_device_is_real(self)) + head_entry = nm_platform_lookup( + nm_device_get_platform(self), + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, pllink->ifindex)); + if (head_entry) return TRUE; - /* Master-slave relationship is also a configuration */ - if (!c_list_is_empty(&priv->slaves) - || nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) > 0) + if (nm_device_is_software(self) && nm_device_is_real(self)) { + /* The existence of a software device is good enough. */ return TRUE; + } return FALSE; } @@ -7331,10 +8012,16 @@ nm_device_generate_connection(NMDevice *self, } } else { /* Only regular and master devices get IP configuration; slaves do not */ - s_ip4 = nm_ip4_config_create_setting(priv->ip_config_4); + s_ip4 = nm_utils_platform_capture_ip_setting(nm_device_get_platform(self), + AF_INET, + nm_device_get_ip_ifindex(self), + FALSE); nm_connection_add_setting(connection, s_ip4); - s_ip6 = nm_ip6_config_create_setting(priv->ip_config_6, _get_maybe_ipv6_disabled(self)); + s_ip6 = nm_utils_platform_capture_ip_setting(nm_device_get_platform(self), + AF_INET6, + nm_device_get_ip_ifindex(self), + _get_maybe_ipv6_disabled(self)); nm_connection_add_setting(connection, s_ip6); nm_connection_add_setting(connection, nm_setting_proxy_new()); @@ -7800,20 +8487,6 @@ nm_device_emit_recheck_auto_activate(NMDevice *self) g_signal_emit(self, signals[RECHECK_AUTO_ACTIVATE], 0); } -static void -dnsmasq_state_changed_cb(NMDnsMasqManager *manager, guint32 status, gpointer user_data) -{ - NMDevice *self = NM_DEVICE(user_data); - - switch (status) { - case NM_DNSMASQ_STATUS_DEAD: - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); - break; - default: - break; - } -} - void nm_device_auth_request(NMDevice * self, GDBusMethodInvocation * context, @@ -7838,148 +8511,112 @@ nm_device_auth_request(NMDevice * self, /*****************************************************************************/ static void -activation_source_clear(NMDevice *self, int addr_family) +activation_source_clear(NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->activation_source_id_x[IS_IPv4] != 0) { + if (nm_clear_g_source_inst(&priv->activation_idle_source)) { _LOGD(LOGD_DEVICE, - "activation-stage: clear %s,v%c (id %u)", - _activation_func_to_string(priv->activation_source_func_x[IS_IPv4]), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4]); - nm_clear_g_source(&priv->activation_source_id_x[IS_IPv4]); - priv->activation_source_func_x[IS_IPv4] = NULL; + "activation-stage: clear %s", + _activation_func_to_string(priv->activation_func)); + priv->activation_func = NULL; } } static gboolean -activation_source_handle_cb(NMDevice *self, int addr_family) +activation_source_handle_cb(gpointer user_data) { + NMDevice * self = user_data; NMDevicePrivate * priv; - const int IS_IPv4 = NM_IS_IPv4(addr_family); - ActivationHandleFunc activation_source_func; - guint activation_source_id; + ActivationHandleFunc activation_func; g_return_val_if_fail(NM_IS_DEVICE(self), G_SOURCE_REMOVE); priv = NM_DEVICE_GET_PRIVATE(self); - activation_source_func = priv->activation_source_func_x[IS_IPv4]; - activation_source_id = priv->activation_source_id_x[IS_IPv4]; + g_return_val_if_fail(priv->activation_idle_source, G_SOURCE_REMOVE); - g_return_val_if_fail(activation_source_id != 0, G_SOURCE_REMOVE); - nm_assert(activation_source_func); + nm_assert(priv->activation_func); - priv->activation_source_func_x[IS_IPv4] = NULL; - priv->activation_source_id_x[IS_IPv4] = 0; + activation_func = priv->activation_func; + priv->activation_func = NULL; - _LOGD(LOGD_DEVICE, - "activation-stage: invoke %s,v%c (id %u)", - _activation_func_to_string(activation_source_func), - nm_utils_addr_family_to_char(addr_family), - activation_source_id); + nm_clear_g_source_inst(&priv->activation_idle_source); - activation_source_func(self); + _LOGD(LOGD_DEVICE, "activation-stage: invoke %s", _activation_func_to_string(activation_func)); - _LOGT(LOGD_DEVICE, - "activation-stage: complete %s,v%c (id %u)", - _activation_func_to_string(activation_source_func), - nm_utils_addr_family_to_char(addr_family), - activation_source_id); + activation_func(self); - return G_SOURCE_REMOVE; -} - -static gboolean -activation_source_handle_cb_4(gpointer user_data) -{ - return activation_source_handle_cb(user_data, AF_INET); -} - -static gboolean -activation_source_handle_cb_6(gpointer user_data) -{ - return activation_source_handle_cb(user_data, AF_INET6); + return G_SOURCE_CONTINUE; } static void -activation_source_schedule(NMDevice *self, ActivationHandleFunc func, int addr_family) +activation_source_schedule(NMDevice *self, ActivationHandleFunc func) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); - guint new_id = 0; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->activation_source_id_x[IS_IPv4] != 0 - && priv->activation_source_func_x[IS_IPv4] == func) { + if (priv->activation_idle_source && priv->activation_func == func) { /* Scheduling the same stage multiple times is fine. */ _LOGT(LOGD_DEVICE, - "activation-stage: already scheduled %s,v%c (id %u)", - _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4]); + "activation-stage: already scheduled %s", + _activation_func_to_string(func)); return; } - new_id = - g_idle_add(IS_IPv4 ? activation_source_handle_cb_4 : activation_source_handle_cb_6, self); - - if (priv->activation_source_id_x[IS_IPv4] != 0) { + if (priv->activation_idle_source) { _LOGD(LOGD_DEVICE, - "activation-stage: schedule %s,v%c which replaces %s,v%c (id %u -> %u)", + "activation-stage: schedule %s (which replaces %s)", _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - _activation_func_to_string(priv->activation_source_func_x[IS_IPv4]), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4], - new_id); - nm_clear_g_source(&priv->activation_source_id_x[IS_IPv4]); + _activation_func_to_string(priv->activation_func)); + nm_clear_g_source_inst(&priv->activation_idle_source); } else { - _LOGD(LOGD_DEVICE, - "activation-stage: schedule %s,v%c (id %u)", - _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - new_id); + _LOGD(LOGD_DEVICE, "activation-stage: schedule %s", _activation_func_to_string(func)); } - priv->activation_source_func_x[IS_IPv4] = func; - priv->activation_source_id_x[IS_IPv4] = new_id; + priv->activation_idle_source = nm_g_idle_add_source(activation_source_handle_cb, self); + priv->activation_func = func; } static void -activation_source_invoke_sync(NMDevice *self, ActivationHandleFunc func, int addr_family) +activation_source_invoke_sync(NMDevice *self, ActivationHandleFunc func) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->activation_source_id_x[IS_IPv4] == 0) { + if (!priv->activation_idle_source) { _LOGD(LOGD_DEVICE, - "activation-stage: synchronously invoke %s,v%c", - _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family)); - } else if (priv->activation_source_func_x[IS_IPv4] == func) { + "activation-stage: synchronously invoke %s", + _activation_func_to_string(func)); + } else if (priv->activation_func == func) { _LOGD(LOGD_DEVICE, - "activation-stage: synchronously invoke %s,v%c which was already scheduled (id %u)", - _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4]); + "activation-stage: synchronously invoke %s (which was already scheduled)", + _activation_func_to_string(func)); } else { _LOGD(LOGD_DEVICE, - "activation-stage: synchronously invoke %s,v%c which replaces %s,v%c (id %u)", + "activation-stage: synchronously invoke %s (which replaces %s)", _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - _activation_func_to_string(priv->activation_source_func_x[IS_IPv4]), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4]); + _activation_func_to_string(priv->activation_func)); } - nm_clear_g_source(&priv->activation_source_id_x[IS_IPv4]); - priv->activation_source_func_x[IS_IPv4] = NULL; + nm_clear_g_source_inst(&priv->activation_idle_source); + priv->activation_func = NULL; func(self); } +static void +activation_source_invoke_or_schedule(NMDevice *self, ActivationHandleFunc func, gboolean do_sync) +{ + nm_assert(NM_IS_DEVICE(self)); + nm_assert(NM_DEVICE_GET_PRIVATE(self)->act_request.obj); + nm_assert(func); + + if (do_sync) { + activation_source_invoke_sync(self, func); + return; + } + activation_source_schedule(self, func); +} + /*****************************************************************************/ static void @@ -8148,14 +8785,20 @@ activate_stage1_device_prepare(NMDevice *self) NMActiveConnection *master; NMDeviceClass * klass; - priv->v4_route_table_initialized = FALSE; - priv->v6_route_table_initialized = FALSE; + nm_assert((priv->ip_data_4.state == NM_DEVICE_IP_STATE_NONE) + == (priv->ip_data_6.state == NM_DEVICE_IP_STATE_NONE)); - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_NONE); - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_NONE); + if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_NONE) { + _dev_ip_state_set_state(self, AF_INET, NM_DEVICE_IP_STATE_PENDING, "stage1"); + _dev_ip_state_set_state(self, AF_INET6, NM_DEVICE_IP_STATE_PENDING, "stage1"); - /* Notify the new ActiveConnection along with the state change */ - nm_dbus_track_obj_path_set(&priv->act_request, priv->act_request.obj, TRUE); + /* Notify the new ActiveConnection along with the state change */ + nm_dbus_track_obj_path_set(&priv->act_request, priv->act_request.obj, TRUE); + + priv->v4_route_table_initialized = FALSE; + priv->v6_route_table_initialized = FALSE; + priv->l3config_merge_flags_has = FALSE; + } nm_device_state_changed(self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); @@ -8216,6 +8859,7 @@ activate_stage1_device_prepare(NMDevice *self) priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_PENDING; return; } + priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_COMPLETED; } @@ -8291,15 +8935,7 @@ activate_stage1_device_prepare(NMDevice *self) void nm_device_activate_schedule_stage1_device_prepare(NMDevice *self, gboolean do_sync) { - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(NM_DEVICE_GET_PRIVATE(self)->act_request.obj); - - if (!do_sync) { - activation_source_schedule(self, activate_stage1_device_prepare, AF_INET); - return; - } - - activation_source_invoke_sync(self, activate_stage1_device_prepare, AF_INET); + activation_source_invoke_or_schedule(self, activate_stage1_device_prepare, do_sync); } static NMActStageReturn @@ -8591,1084 +9227,732 @@ activate_stage2_device_config(NMDevice *self) lldp_setup(self, NM_TERNARY_DEFAULT); - _commit_mtu(self, NULL); + _commit_mtu(self); - nm_device_activate_schedule_stage3_ip_config_start(self); + nm_device_activate_schedule_stage3_ip_config(self, TRUE); } void nm_device_activate_schedule_stage2_device_config(NMDevice *self, gboolean do_sync) { - g_return_if_fail(NM_IS_DEVICE(self)); + activation_source_invoke_or_schedule(self, activate_stage2_device_config, do_sync); +} - if (!do_sync) { - activation_source_schedule(self, activate_stage2_device_config, AF_INET); - return; - } +/*****************************************************************************/ - activation_source_invoke_sync(self, activate_stage2_device_config, AF_INET); +static void +_dev_ipllx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + if (priv->ipll_data_x[IS_IPv4].state != state) { + _LOGD_ipll(addr_family, + "set state %s (was %s)", + nm_device_ip_state_to_string(state), + nm_device_ip_state_to_string(priv->ipll_data_x[IS_IPv4].state)); + priv->ipll_data_x[IS_IPv4].state = state; + } } -void -nm_device_ip_method_failed(NMDevice *self, int addr_family, NMDeviceStateReason reason) +static void +_dev_ipllx_cleanup(NMDevice *self, int addr_family) { - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_FAIL); + if (IS_IPv4) { + if (nm_clear_pointer(&priv->ipll_data_4.v4.ipv4ll, nm_l3_ipv4ll_unref)) + nm_clear_pointer(&priv->ipll_data_4.v4.ipv4ll_registation, + nm_l3_ipv4ll_register_remove); + else + nm_assert(!priv->ipll_data_4.v4.ipv4ll_registation); - if (get_ip_config_may_fail(self, addr_family)) - check_ip_state(self, FALSE, (nm_device_get_state(self) == NM_DEVICE_STATE_IP_CONFIG)); - else - nm_device_state_changed(self, NM_DEVICE_STATE_FAILED, reason); + nm_clear_g_source_inst(&priv->ipll_data_4.v4.timeout_source); + } else { + nm_clear_pointer(&priv->ipll_data_6.v6.ipv6ll, nm_l3_ipv6ll_destroy); + priv->ipll_data_6.v6.llstate = NM_L3_IPV6LL_STATE_NONE; + priv->ipll_data_6.v6.lladdr = nm_ip_addr_zero.addr6; + nm_clear_g_source_inst(&priv->ipll_data_6.v6.retry_source); + } + + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_LL_X(IS_IPv4), NULL, FALSE); + + _dev_ipllx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); } /*****************************************************************************/ static void -acd_data_destroy(gpointer ptr) +_dev_ipll4_notify_event(NMDevice *self) { - AcdData *data = ptr; - int i; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NML3IPv4LLState ipv4ll_state; + const NML3ConfigData *l3cd; + NMDeviceIPState state; + + nm_assert(NM_IS_L3_IPV4LL(priv->ipll_data_4.v4.ipv4ll)); + nm_assert(priv->ipll_data_4.state >= NM_DEVICE_IP_STATE_PENDING); + + ipv4ll_state = nm_l3_ipv4ll_get_state(priv->ipll_data_4.v4.ipv4ll); + + if (nm_l3_ipv4ll_state_is_good(ipv4ll_state)) { + l3cd = nm_l3_ipv4ll_get_l3cd(priv->ipll_data_4.v4.ipv4ll); + nm_assert(NM_IS_L3_CONFIG_DATA(l3cd)); + nm_assert(!nm_l3_ipv4ll_is_timed_out(priv->ipll_data_4.v4.ipv4ll)); + state = NM_DEVICE_IP_STATE_READY; + } else if (priv->ipll_data_4.v4.ipv4ll + && nm_l3_ipv4ll_is_timed_out(priv->ipll_data_4.v4.ipv4ll)) { + l3cd = NULL; + state = NM_DEVICE_IP_STATE_FAILED; + } else { + l3cd = NULL; + state = (priv->ipll_data_4.state == NM_DEVICE_IP_STATE_PENDING) ? NM_DEVICE_IP_STATE_PENDING + : NM_DEVICE_IP_STATE_FAILED; + } + + _dev_ipllx_set_state(self, AF_INET, state); + + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_LL_4, l3cd, FALSE); - for (i = 0; data->configs && data->configs[i]; i++) - g_object_unref(data->configs[i]); - g_free(data->configs); - g_slice_free(AcdData, data); + _dev_ip_state_check_async(self, AF_INET); } static void -ipv4_manual_method_apply(NMDevice *self, NMIP4Config **configs, gboolean success) +_dev_ipll4_start(NMDevice *self) { - NMConnection *connection; - const char * method; - - connection = nm_device_get_applied_connection(self); - nm_assert(connection); - method = nm_utils_get_ip_config_method(connection, AF_INET); - nm_assert(NM_IN_STRSET(method, - NM_SETTING_IP4_CONFIG_METHOD_MANUAL, - NM_SETTING_IP4_CONFIG_METHOD_AUTO)); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + guint32 timeout_msec; - if (!success) { - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE); + if (priv->ipll_data_4.state >= NM_DEVICE_IP_STATE_PENDING) return; - } - if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) - nm_device_activate_schedule_ip_config_result(self, AF_INET, NULL); - else { - if (NM_DEVICE_GET_PRIVATE(self)->ip_state_4 != NM_DEVICE_IP_STATE_DONE) - ip_config_merge_and_apply(self, AF_INET, TRUE); - } + _dev_ipllx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_PENDING); + + timeout_msec = _prop_get_ipv4_dad_timeout(self); + if (timeout_msec == 0) + timeout_msec = NM_ACD_TIMEOUT_RFC5227_MSEC; + + priv->ipll_data_4.v4.ipv4ll = nm_l3cfg_access_ipv4ll(priv->l3cfg); + priv->ipll_data_4.v4.ipv4ll_registation = + nm_l3_ipv4ll_register_new(priv->ipll_data_4.v4.ipv4ll, timeout_msec); } -static void -acd_manager_probe_terminated(NMAcdManager *acd_manager, gpointer user_data) +/*****************************************************************************/ + +static const char * +_device_get_dhcp_anycast_address(NMDevice *self) { - AcdData * data = user_data; - NMDevice * self; - NMDevicePrivate * priv; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Address *address; - gboolean result, success = TRUE; - int i; + NMDeviceClass *klass; - g_assert(data); - self = data->device; - priv = NM_DEVICE_GET_PRIVATE(self); + nm_assert(NM_IS_DEVICE(self)); - for (i = 0; data->configs && data->configs[i]; i++) { - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, data->configs[i], &address) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + klass = NM_DEVICE_GET_CLASS(self); - result = nm_acd_manager_check_address(acd_manager, address->address); - success &= result; + if (klass->get_dhcp_anycast_address) + return klass->get_dhcp_anycast_address(self); - _NMLOG(result ? LOGL_DEBUG : LOGL_WARN, - LOGD_DEVICE, - "IPv4 DAD result: address %s is %s", - _nm_utils_inet4_ntop(address->address, sbuf), - result ? "unique" : "duplicate"); - } - } + return NULL; +} - data->callback(self, data->configs, success); +/*****************************************************************************/ - priv->acd.dad_list = g_slist_remove(priv->acd.dad_list, acd_manager); - nm_acd_manager_free(acd_manager); +static IPDevStateData * +_dev_ipdev_data(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + switch (addr_family) { + case AF_INET: + return &priv->ipdev_data_4; + case AF_INET6: + return &priv->ipdev_data_6; + default: + nm_assert_not_reached(); + /* fall-through */ + case AF_UNSPEC: + return &priv->ipdev_data_unspec; + } } -/** - * ipv4_dad_start: - * @self: device instance - * @configs: NULL-terminated array of IPv4 configurations - * @cb: callback function - * - * Start IPv4 DAD on device @self, check addresses in @configs and call @cb - * when the procedure ends. @cb will be called in any case, even if DAD can't - * be started. @configs will be unreferenced after @cb has been called. - */ static void -ipv4_dad_start(NMDevice *self, NMIP4Config **configs, AcdCallback cb) +_dev_ipdev_cleanup(NMDevice *self, int addr_family) { - static const NMAcdCallbacks acd_callbacks = { - .probe_terminated_callback = acd_manager_probe_terminated, - .user_data_destroy = acd_data_destroy, - }; - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMAcdManager * acd_manager; - const NMPlatformIP4Address *address; - NMDedupMultiIter ipconf_iter; - AcdData * data; - guint timeout; - gboolean addr_found; - int r; - const guint8 * hwaddr_arr; - size_t length; - guint i; + IPDevStateData *p; - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(configs); - g_return_if_fail(cb); - - for (i = 0, addr_found = FALSE; configs[i]; i++) { - if (nm_ip4_config_get_num_addresses(configs[i]) > 0) { - addr_found = TRUE; - break; - } + p = _dev_ipdev_data(self, addr_family); + if (p->state != NM_DEVICE_IP_STATE_NONE) { + _LOGD_ipdev(addr_family, "reset state"); + p->state = NM_DEVICE_IP_STATE_NONE; + p->failed_reason = NM_DEVICE_STATE_REASON_NONE; } +} - timeout = _prop_get_ipv4_dad_timeout(self); - hwaddr_arr = nm_platform_link_get_address(nm_device_get_platform(self), - nm_device_get_ip_ifindex(self), - &length); +NMDeviceIPState +nm_device_devip_get_state(NMDevice *self, int addr_family) +{ + g_return_val_if_fail(NM_IS_DEVICE(self), NM_DEVICE_IP_STATE_NONE); - if (!timeout || !hwaddr_arr || !addr_found || length != ETH_ALEN - || nm_device_sys_iface_state_is_external_or_assume(self)) { - /* DAD not needed, signal success */ - cb(self, configs, TRUE); + return _dev_ipdev_data(self, addr_family)->state; +} - for (i = 0; configs[i]; i++) - g_object_unref(configs[i]); - g_free(configs); +void +nm_device_devip_set_state_full(NMDevice * self, + int addr_family, + NMDeviceIPState ip_state, + const NML3ConfigData *l3cd, + NMDeviceStateReason failed_reason) +{ + NMDevicePrivate *priv; + IPDevStateData * p; - return; - } + g_return_if_fail(NM_IS_DEVICE(self)); - data = g_slice_new0(AcdData); - data->configs = configs; - data->callback = cb; - data->device = self; + priv = NM_DEVICE_GET_PRIVATE(self); - acd_manager = nm_acd_manager_new(nm_device_get_ip_ifindex(self), - hwaddr_arr, - length, - &acd_callbacks, - data); - priv->acd.dad_list = g_slist_append(priv->acd.dad_list, acd_manager); + nm_assert_addr_family_or_unspec(addr_family); + nm_assert(NM_IN_SET(ip_state, + NM_DEVICE_IP_STATE_PENDING, + NM_DEVICE_IP_STATE_READY, + NM_DEVICE_IP_STATE_FAILED)); + nm_assert(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd)); - for (i = 0; configs[i]; i++) { - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, configs[i], &address) - nm_acd_manager_add_address(acd_manager, address->address); - } + nm_assert((ip_state != NM_DEVICE_IP_STATE_FAILED) + == (failed_reason == NM_DEVICE_STATE_REASON_NONE)); + nm_assert((ip_state != NM_DEVICE_IP_STATE_FAILED) || !l3cd); - r = nm_acd_manager_start_probe(acd_manager, timeout); - if (r < 0) { - _LOGW(LOGD_DEVICE, "acd probe failed"); + p = _dev_ipdev_data(self, addr_family); - /* DAD could not be started, signal success */ - cb(self, configs, TRUE); + if (p->state == ip_state && p->failed_reason == failed_reason + && priv->l3cds[L3_CONFIG_DATA_TYPE_DEVIP(addr_family)].d == l3cd) + return; - priv->acd.dad_list = g_slist_remove(priv->acd.dad_list, acd_manager); - nm_acd_manager_free(acd_manager); + if (ip_state == NM_DEVICE_IP_STATE_FAILED) { + _LOGD_ipdev(addr_family, + "set state=failed (reason %s)", + nm_device_state_reason_to_string_a(failed_reason)); + } else { + _LOGD_ipdev(addr_family, + "set state=%s%s", + nm_device_ip_state_to_string(ip_state), + l3cd ? " (has extra IP configuration)" : ""); } + p->state = ip_state; + p->failed_reason = failed_reason; + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DEVIP(addr_family), l3cd, FALSE); + _dev_ip_state_check_async(self, addr_family); } /*****************************************************************************/ -/* IPv4LL stuff */ static void -ipv4ll_cleanup(NMDevice *self) +_dev_ipmanual_set_state(NMDevice *self, NMDeviceIPState state) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->ipv4ll) { - sd_ipv4ll_set_callback(priv->ipv4ll, NULL, NULL); - sd_ipv4ll_stop(priv->ipv4ll); - priv->ipv4ll = sd_ipv4ll_unref(priv->ipv4ll); + if (priv->ipmanual_data.state != state) { + _LOGD_ipmanual("set state %s", nm_device_ip_state_to_string(state)); + priv->ipmanual_data.state = state; } - - nm_clear_g_source(&priv->ipv4ll_timeout); } -static NMIP4Config * -ipv4ll_get_ip4_config(NMDevice *self, guint32 lla) +static void +_dev_ipmanual_cleanup(NMDevice *self) { - NMIP4Config * config = NULL; - NMPlatformIP4Address address; - NMPlatformIP4Route route; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - config = nm_device_ip4_config_new(self); - g_assert(config); + if (priv->ipmanual_data.state == NM_DEVICE_IP_STATE_NONE) { + nm_assert(!priv->l3cds[L3_CONFIG_DATA_TYPE_MANUALIP].d); + return; + } - memset(&address, 0, sizeof(address)); - nm_platform_ip4_address_set_addr(&address, lla, 16); - address.addr_source = NM_IP_CONFIG_SOURCE_IP4LL; - nm_ip4_config_add_address(config, &address); + _dev_ipmanual_set_state(self, NM_DEVICE_IP_STATE_NONE); - /* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */ - memset(&route, 0, sizeof(route)); - route.network = htonl(0xE0000000L); - route.plen = 4; - route.rt_source = NM_IP_CONFIG_SOURCE_IP4LL; - route.table_coerced = nm_platform_route_table_coerce(nm_device_get_route_table(self, AF_INET)); - route.metric = nm_device_get_route_metric(self, AF_INET); - nm_ip4_config_add_route(config, &route, NULL); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_MANUALIP, NULL, FALSE); - return config; + _dev_ip_state_check_async(self, AF_INET); + _dev_ip_state_check_async(self, AF_INET6); } static void -nm_device_handle_ipv4ll_event(sd_ipv4ll *ll, int event, void *data) +_dev_ipmanual_check_ready(NMDevice *self) { - NMDevice * self = data; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - struct in_addr address; - NMIP4Config * config; - int r; - if (priv->act_request.obj == NULL) + if (priv->ipmanual_data.state != NM_DEVICE_IP_STATE_PENDING) { + /* we only care about PENDING to get it READY. Currently not other + * conditions are implemented. That is, we cannot get to FAILED + * (maybe we should, if DAD fails) and we cannot get from anything + * once we are READY. */ return; + } - nm_assert(nm_streq(nm_device_get_effective_ip_config_method(self, AF_INET), - NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)); + if (!nm_l3cfg_check_ready(priv->l3cfg, + priv->l3cds[L3_CONFIG_DATA_TYPE_MANUALIP].d, + NM_L3CFG_CHECK_READY_FLAGS_IP4_ACD_READY + | NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY)) + return; - switch (event) { - case SD_IPV4LL_EVENT_BIND: - r = sd_ipv4ll_get_address(ll, &address); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "invalid IPv4 link-local address received, error %d.", r); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED); - return; - } + _dev_ipmanual_set_state(self, NM_DEVICE_IP_STATE_READY); + _dev_ip_state_check_async(self, AF_UNSPEC); +} - if (!nm_utils_ip4_address_is_link_local(address.s_addr)) { - _LOGE(LOGD_AUTOIP4, "invalid address %08x received (not link-local).", address.s_addr); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); - return; - } +static void +_dev_ipmanual_start(NMDevice *self) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; - config = ipv4ll_get_ip4_config(self, address.s_addr); - if (config == NULL) { - _LOGE(LOGD_AUTOIP4, "failed to get IPv4LL config"); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); - return; - } + if (priv->ipmanual_data.state != NM_DEVICE_IP_STATE_NONE) + return; - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) { - nm_clear_g_source(&priv->ipv4ll_timeout); - nm_device_activate_schedule_ip_config_result(self, AF_INET, NM_IP_CONFIG_CAST(config)); - } else if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { - applied_config_init(&priv->dev_ip_config_4, config); - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) { - _LOGE(LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); - } - } else - g_assert_not_reached(); + l3cd = nm_device_create_l3_config_data_from_connection(self, + nm_device_get_applied_connection(self)); - g_object_unref(config); - break; - default: - _LOGW(LOGD_AUTOIP4, "IPv4LL address no longer valid after event %d.", event); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); + if (!l3cd) { + _dev_ipmanual_cleanup(self); + return; } -} - -static gboolean -ipv4ll_timeout_cb(gpointer user_data) -{ - NMDevice * self = NM_DEVICE(user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->ipv4ll_timeout) { - _LOGI(LOGD_AUTOIP4, "IPv4LL configuration timed out."); - priv->ipv4ll_timeout = 0; - ipv4ll_cleanup(self); + /* Initially we set the state to pending, because we (maybe) have to perform ACD first. */ + _dev_ipmanual_set_state(self, NM_DEVICE_IP_STATE_PENDING); - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) - nm_device_activate_schedule_ip_config_timeout(self, AF_INET); - } + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_MANUALIP, l3cd, FALSE); - return FALSE; + _dev_ip_state_check_async(self, AF_INET); + _dev_ip_state_check_async(self, AF_INET6); } -static NMActStageReturn -ipv4ll_start(NMDevice *self) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - const struct ether_addr *addr; - int ifindex, r; - size_t addr_len; +/*****************************************************************************/ - ipv4ll_cleanup(self); +static void +_dev_ipdhcpx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - r = sd_ipv4ll_new(&priv->ipv4ll); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: new() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; + if (priv->ipdhcp_data_x[IS_IPv4].state != state) { + _LOGD_ipdhcp(addr_family, + "set state %s (was %s)", + nm_device_ip_state_to_string(state), + nm_device_ip_state_to_string(priv->ipdhcp_data_x[IS_IPv4].state)); + priv->ipdhcp_data_x[IS_IPv4].state = state; } +} - r = sd_ipv4ll_attach_event(priv->ipv4ll, NULL, 0); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: attach_event() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; - } +static void +_dev_ipdhcpx_cleanup(NMDevice *self, int addr_family, gboolean full_cleanup, gboolean release) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - ifindex = nm_device_get_ip_ifindex(self); - addr = nm_platform_link_get_address(nm_device_get_platform(self), ifindex, &addr_len); - if (!addr || addr_len != ETH_ALEN) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: can't retrieve hardware address"); - return NM_ACT_STAGE_RETURN_FAILURE; - } + _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); - r = sd_ipv4ll_set_mac(priv->ipv4ll, addr); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: set_mac() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; + if (full_cleanup && !IS_IPv4) { + priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_NONE; + priv->ipdhcp_data_6.v6.needed_prefixes = 0; } - r = sd_ipv4ll_set_ifindex(priv->ipv4ll, ifindex); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: set_ifindex() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; - } + if (full_cleanup) + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), NULL, FALSE); - r = sd_ipv4ll_set_callback(priv->ipv4ll, nm_device_handle_ipv4ll_event, self); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: set_callback() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; + if (priv->ipdhcp_data_x[IS_IPv4].client) { + nm_clear_g_signal_handler(priv->ipdhcp_data_x[IS_IPv4].client, + &priv->ipdhcp_data_x[IS_IPv4].notify_sigid); + nm_dhcp_client_stop(priv->ipdhcp_data_x[IS_IPv4].client, release); + g_clear_object(&priv->ipdhcp_data_x[IS_IPv4].client); } - r = sd_ipv4ll_start(priv->ipv4ll); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: start() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; - } + if (full_cleanup && priv->ipdhcp_data_x[IS_IPv4].config) { + gs_unref_object NMDhcpConfig *config = + g_steal_pointer(&priv->ipdhcp_data_x[IS_IPv4].config); - _LOGI(LOGD_DEVICE | LOGD_AUTOIP4, "IPv4LL: started"); + _notify(self, PROP_DHCPX_CONFIG(IS_IPv4)); + nm_dbus_object_unexport_on_idle(g_steal_pointer(&config)); + } - /* Start a timeout to bound the address attempt */ - priv->ipv4ll_timeout = g_timeout_add_seconds(20, ipv4ll_timeout_cb, self); - return NM_ACT_STAGE_RETURN_POSTPONE; + _dev_ip_state_check_async(self, addr_family); } -/*****************************************************************************/ - static void -ensure_con_ip_config(NMDevice *self, int addr_family) +_dev_ipdhcpx_handle_fail(NMDevice *self, int addr_family, const char *reason) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMConnection * connection; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMIPConfig * con_ip_config; - if (priv->con_ip_config_x[IS_IPv4]) + if (priv->ipdhcp_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_FAILED) return; - connection = nm_device_get_applied_connection(self); - if (!connection) - return; + _LOGT_ipdhcp(addr_family, "DHCP failing: %s", reason ?: "unknown reason"); - con_ip_config = nm_device_ip_config_new(self, addr_family); + _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_FAILED); - if (IS_IPv4) { - nm_ip4_config_merge_setting(NM_IP4_CONFIG(con_ip_config), - nm_connection_get_setting_ip4_config(connection), - _prop_get_connection_mdns(self), - _prop_get_connection_llmnr(self), - nm_device_get_route_table(self, addr_family), - nm_device_get_route_metric(self, addr_family)); - } else { - nm_ip6_config_merge_setting(NM_IP6_CONFIG(con_ip_config), - nm_connection_get_setting_ip6_config(connection), - nm_device_get_route_table(self, addr_family), - nm_device_get_route_metric(self, addr_family)); - } + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), NULL, FALSE); - if (nm_device_sys_iface_state_is_external_or_assume(self)) { - /* For assumed connections ignore all addresses and routes. */ - nm_ip_config_reset_addresses(con_ip_config); - nm_ip_config_reset_routes(con_ip_config); - } + if (priv->ipdhcp_data_x[IS_IPv4].config) + nm_dhcp_config_set_lease(priv->ipdhcp_data_x[IS_IPv4].config, NULL); - priv->con_ip_config_x[IS_IPv4] = con_ip_config; -} - -/*****************************************************************************/ - -static const char * -_device_get_dhcp_anycast_address(NMDevice *self) -{ - NMDeviceClass *klass; - - nm_assert(NM_IS_DEVICE(self)); - - klass = NM_DEVICE_GET_CLASS(self); - - if (klass->get_dhcp_anycast_address) - return klass->get_dhcp_anycast_address(self); - - return NULL; + _dev_ip_state_check_async(self, addr_family); } static void -dhcp4_cleanup(NMDevice *self, CleanupType cleanup_type, gboolean release) +_dev_ipdhcpx_handle_accept(NMDevice *self, int addr_family, const NML3ConfigData *l3cd) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - priv->dhcp_data_4.was_active = FALSE; - nm_clear_g_source(&priv->dhcp_data_4.grace_id); - priv->dhcp_data_4.grace_pending = FALSE; - nm_clear_g_free(&priv->dhcp4.pac_url); + nm_assert(NM_IS_L3_CONFIG_DATA(l3cd)); - if (priv->dhcp_data_4.client) { - /* Stop any ongoing DHCP transaction on this device */ - nm_clear_g_signal_handler(priv->dhcp_data_4.client, &priv->dhcp_data_4.notify_sigid); + _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_READY); - if (cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED) - nm_dhcp_client_stop(priv->dhcp_data_4.client, release); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), l3cd, FALSE); - g_clear_object(&priv->dhcp_data_4.client); - } + nm_dhcp_config_set_lease(priv->ipdhcp_data_x[IS_IPv4].config, l3cd); - if (priv->dhcp_data_4.config) { - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_4.config); - _notify(self, PROP_DHCP4_CONFIG); - } + nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_X(IS_IPv4), + self, + NULL, + NULL, + NULL, + NULL); + + _dev_ip_state_check_async(self, addr_family); } -static gboolean -ip_config_merge_and_apply(NMDevice *self, int addr_family, gboolean commit) +static void +_dev_ipdhcpx_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_data, NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gboolean success; - gs_unref_object NMIPConfig *composite = NULL; - NMIPConfig * config; - gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; - NMConnection * connection; - gboolean ignore_auto_routes = FALSE; - gboolean ignore_auto_dns = FALSE; - gboolean ignore_default_routes = FALSE; - GSList * iter; - const char * ip6_addr_gen_token = NULL; - const int IS_IPv4 = NM_IS_IPv4(addr_family); - - if (nm_device_sys_iface_state_is_external(self)) - commit = FALSE; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int addr_family = nm_dhcp_client_get_addr_family(client); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - connection = nm_device_get_applied_connection(self); + nm_assert(notify_data); + nm_assert(priv->ipdhcp_data_x[IS_IPv4].state > NM_DEVICE_IP_STATE_NONE); + nm_assert(client && priv->ipdhcp_data_x[IS_IPv4].client == client); - /* Apply ignore-auto-routes and ignore-auto-dns settings */ - if (connection) { - NMSettingIPConfig *s_ip; + switch (notify_data->notify_type) { + case NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED: + nm_assert(!IS_IPv4); + /* Just re-emit. The device just contributes the prefix to the + * pool in NMPolicy, which decides about subnet allocation + * on the shared devices. */ + g_signal_emit(self, signals[IP6_PREFIX_DELEGATED], 0, notify_data->prefix_delegated.prefix); + return; - s_ip = nm_connection_get_setting_ip_config(connection, addr_family); - if (s_ip) { - ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes(s_ip); - ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns(s_ip); + case NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT: + /* Here we also fail if we had a lease and it expired. Maybe, + * ipv[46].dhcp-timeout should only cover the time until we get + * a lease for the first time. How it is here, it means that a + * connection can fail after being connected successfully for a + * longer time. */ + _dev_ipdhcpx_handle_fail(self, addr_family, "timeout getting lease"); + return; - /* if the connection has an explicit gateway, we also ignore - * the default routes from other sources. */ - ignore_default_routes = nm_setting_ip_config_get_never_default(s_ip) - || nm_setting_ip_config_get_gateway(s_ip); + case NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD: + /* Like NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT, this does not + * apply only if we never got a lease, but also after being fully + * connected. We can also fail then. */ + _dev_ipdhcpx_handle_fail(self, addr_family, notify_data->it_looks_bad.reason); + return; - if (!IS_IPv4) { - NMSettingIP6Config *s_ip6 = NM_SETTING_IP6_CONFIG(s_ip); + case NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE: - if (nm_setting_ip6_config_get_addr_gen_mode(s_ip6) - == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) - ip6_addr_gen_token = nm_setting_ip6_config_get_token(s_ip6); - } + if (!notify_data->lease_update.l3cd) { + _LOGT_ipdhcp(addr_family, "lease lost"); + _dev_ipdhcpx_handle_fail(self, addr_family, "lease lost"); + return; } - } - - composite = nm_device_ip_config_new(self, addr_family); - if (!IS_IPv4) { - nm_ip6_config_set_privacy(NM_IP6_CONFIG(composite), - priv->ndisc ? priv->ndisc_use_tempaddr - : NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + _LOGT_ipdhcp(addr_family, "lease update"); + _dev_ipdhcpx_handle_accept(self, addr_family, notify_data->lease_update.l3cd); + return; } - init_ip_config_dns_priority(self, composite); + nm_assert_not_reached(); +} + +/*****************************************************************************/ - if (commit) { - if (priv->queued_ip_config_id_x[IS_IPv4]) - update_ext_ip_config(self, addr_family, FALSE); - ensure_con_ip_config(self, addr_family); +static void +_dev_ipdhcpx_start(NMDevice *self, int addr_family) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingIPConfig * s_ip; + const NML3ConfigData *previous_lease; + gs_unref_bytes GBytes *hwaddr = NULL; + gboolean enforce_duid = FALSE; + gs_free_error GError *error = NULL; + const NMPlatformLink *pllink; + guint no_lease_timeout_sec; + int ifindex; + const char * str; + gboolean request_broadcast; + const char * fail_reason; + + if (priv->ipdhcp_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_NONE) + _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_PENDING); + else if (priv->ipdhcp_data_x[IS_IPv4].state > NM_DEVICE_IP_STATE_PENDING) { + /* already started. Nothing to do. */ + return; } - if (!IS_IPv4) { - if (commit && priv->ipv6ll_has) { - const NMPlatformIP6Address ll_a = { - .address = priv->ipv6ll_addr, - .plen = 64, - .addr_source = NM_IP_CONFIG_SOURCE_IP6LL, - }; - const NMPlatformIP6Route ll_r = { - .network.s6_addr16[0] = htons(0xfe80u), - .plen = 64, - .metric = nm_device_get_route_metric(self, addr_family), - .rt_source = NM_IP_CONFIG_SOURCE_IP6LL, - }; - - nm_assert(IN6_IS_ADDR_LINKLOCAL(&priv->ipv6ll_addr)); - - nm_ip6_config_add_address(NM_IP6_CONFIG(composite), &ll_a); - nm_ip6_config_add_route(NM_IP6_CONFIG(composite), &ll_r, NULL); - } + if (nm_device_sys_iface_state_is_external(self)) { + fail_reason = nm_assert_unreachable_val("cannot run DHCP on external interface"); + goto out_fail; } - if (commit) { - gboolean v; - - v = default_route_metric_penalty_detect(self, addr_family); - if (IS_IPv4) - priv->default_route_metric_penalty_ip4_has = v; - else - priv->default_route_metric_penalty_ip6_has = v; + connection = nm_device_get_applied_connection(self); + if (!connection) { + fail_reason = nm_assert_unreachable_val("no applied connection for starting DHCP"); + goto out_fail; } - /* Merge all the IP configs into the composite config */ + s_con = nm_connection_get_setting_connection(connection); + s_ip = nm_connection_get_setting_ip_config(connection, addr_family); + nm_assert(s_con); + nm_assert(s_ip); - if (IS_IPv4) { - config = applied_config_get_current(&priv->dev_ip_config_4); - if (config) { - nm_ip4_config_merge( - NM_IP4_CONFIG(composite), - NM_IP4_CONFIG(config), - (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), - default_route_metric_penalty_get(self, addr_family)); - } + ifindex = 0; + pllink = nm_l3cfg_get_pllink(priv->l3cfg, TRUE); + if (pllink) { + ifindex = pllink->ifindex; + nm_assert(ifindex > 0); + nm_assert(ifindex == nm_device_get_ip_ifindex(self)); } - - if (!IS_IPv4) { - config = applied_config_get_current(&priv->ac_ip6_config); - if (config) { - nm_ip6_config_merge( - NM_IP6_CONFIG(composite), - NM_IP6_CONFIG(config), - (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), - default_route_metric_penalty_get(self, addr_family)); - } + if (ifindex <= 0) { + fail_reason = "cannot start DHCP without interface"; + goto out_fail; } + hwaddr = nmp_link_address_get_as_bytes(&pllink->l_address); + if (!IS_IPv4) { - config = applied_config_get_current(&priv->dhcp6.ip6_config); - if (config) { - nm_ip6_config_merge( - NM_IP6_CONFIG(composite), - NM_IP6_CONFIG(config), - (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), - default_route_metric_penalty_get(self, addr_family)); + if (!hwaddr) { + fail_reason = "interface has no MAC address to start DHCPv6"; + goto out_fail; } } - for (iter = priv->vpn_configs_x[IS_IPv4]; iter; iter = iter->next) - nm_ip_config_merge(composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0); - - if (priv->ext_ip_config_x[IS_IPv4]) - nm_ip_config_merge(composite, - priv->ext_ip_config_x[IS_IPv4], - NM_IP_CONFIG_MERGE_EXTERNAL, - 0); - - /* Merge WWAN config *last* to ensure modem-given settings overwrite - * any external stuff set by pppd or other scripts. - */ - config = applied_config_get_current(&priv->dev2_ip_config_x[IS_IPv4]); - if (config) { - nm_ip_config_merge(composite, - config, - (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), - default_route_metric_penalty_get(self, addr_family)); - } - - if (!IS_IPv4) { - if (priv->rt6_temporary_not_available) { - const NMPObject *o; - GHashTableIter hiter; - - g_hash_table_iter_init(&hiter, priv->rt6_temporary_not_available); - while (g_hash_table_iter_next(&hiter, (gpointer *) &o, NULL)) { - nm_ip6_config_add_route(NM_IP6_CONFIG(composite), - NMP_OBJECT_CAST_IP6_ROUTE(o), - NULL); - } + request_broadcast = FALSE; + if (pllink) { + str = nmp_object_link_udev_device_get_property_value(NMP_OBJECT_UP_CAST(pllink), + "ID_NET_DHCP_BROADCAST"); + if (str && _nm_utils_ascii_str_to_bool(str, FALSE)) { + /* Use the device property ID_NET_DHCP_BROADCAST setting, which may be set for interfaces + * requiring that the DHCPOFFER message is being broadcast because they can't handle unicast + * messages while not fully configured. + */ + request_broadcast = TRUE; } } - /* Merge user overrides into the composite config. For assumed connections, - * con_ip_config_x is empty. */ - if (priv->con_ip_config_x[IS_IPv4]) { - nm_ip_config_merge(composite, - priv->con_ip_config_x[IS_IPv4], - NM_IP_CONFIG_MERGE_DEFAULT, - default_route_metric_penalty_get(self, addr_family)); + if (!IS_IPv4 + && NM_IN_SET(priv->ipll_data_6.state, + NM_DEVICE_IP_STATE_NONE, + NM_DEVICE_IP_STATE_PENDING)) { + _dev_ipll6_start(self); + return; } - if (commit) { - gboolean is_vrf; + no_lease_timeout_sec = _prop_get_ipvx_dhcp_timeout(self, addr_family); + + if (IS_IPv4) { + NMDhcpClientConfig config; + gs_unref_bytes GBytes *bcast_hwaddr = NULL; + gs_unref_bytes GBytes *client_id = NULL; + gs_unref_bytes GBytes *vendor_class_identifier = NULL; + const char *const * reject_servers; + const char * hostname; + gboolean hostname_is_fqdn; + + client_id = _prop_get_ipv4_dhcp_client_id(self, connection, hwaddr); + vendor_class_identifier = + _prop_get_ipv4_dhcp_vendor_class_identifier(self, NM_SETTING_IP4_CONFIG(s_ip)); + reject_servers = nm_setting_ip_config_get_dhcp_reject_servers(s_ip, NULL); - is_vrf = priv->master && nm_device_get_device_type(priv->master) == NM_DEVICE_TYPE_VRF; + bcast_hwaddr = nmp_link_address_get_as_bytes(&pllink->l_broadcast); - if (IS_IPv4) { - nm_ip4_config_add_dependent_routes(NM_IP4_CONFIG(composite), - nm_device_get_route_table(self, addr_family), - nm_device_get_route_metric(self, addr_family), - is_vrf, - &ip4_dev_route_blacklist); + hostname = nm_setting_ip4_config_get_dhcp_fqdn(NM_SETTING_IP4_CONFIG(s_ip)); + if (hostname) { + hostname_is_fqdn = TRUE; } else { - nm_ip6_config_add_dependent_routes(NM_IP6_CONFIG(composite), - nm_device_get_route_table(self, addr_family), - nm_device_get_route_metric(self, addr_family), - is_vrf); + hostname = nm_setting_ip_config_get_dhcp_hostname(s_ip); } - } - if (IS_IPv4) { - if (commit) { - if (NM_DEVICE_GET_CLASS(self)->ip4_config_pre_commit) - NM_DEVICE_GET_CLASS(self)->ip4_config_pre_commit(self, NM_IP4_CONFIG(composite)); - } - } + config = (NMDhcpClientConfig){ + .addr_family = AF_INET, + .l3cfg = nm_device_get_l3cfg(self), + .iface = nm_device_get_ip_iface(self), + .uuid = nm_connection_get_uuid(connection), + .hwaddr = hwaddr, + .bcast_hwaddr = bcast_hwaddr, + .send_hostname = nm_setting_ip_config_get_dhcp_send_hostname(s_ip), + .hostname = hostname, + .hostname_flags = _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET), + .client_id = client_id, + .mud_url = _prop_get_connection_mud_url(self, s_con), + .timeout = no_lease_timeout_sec, + .anycast_address = _device_get_dhcp_anycast_address(self), + .vendor_class_identifier = vendor_class_identifier, + .use_fqdn = hostname_is_fqdn, + .reject_servers = reject_servers, + .v4.request_broadcast = request_broadcast, + }; - if (!IS_IPv4) { - NMUtilsIPv6IfaceId iid; + priv->ipdhcp_data_4.client = + nm_dhcp_manager_start_client(nm_dhcp_manager_get(), &config, &error); + } else { + gs_unref_bytes GBytes *duid = NULL; + gboolean iaid_explicit; + guint32 iaid; + NMDhcpClientConfig config; - if (commit && priv->ndisc_started && ip6_addr_gen_token - && nm_utils_ipv6_interface_identifier_get_from_token(&iid, ip6_addr_gen_token)) { - set_ipv6_token(self, &iid, ip6_addr_gen_token); + if (!IN6_IS_ADDR_LINKLOCAL(&priv->ipll_data_6.v6.lladdr)) { + /* FIXME(l3cfg:dhcp): NMDhcpClient should monitor the IPv6 link local addresses. + * NMDevice kicks off NML3IPv6LL to generate an LL6 address, thereby trying + * that we have an LL address, but NMDhcpClient needs automatically monitor those + * addresses and choose a suitable one. + * + * This also means, NMDhcpClientConfig.v6.ll_addr needs to go away. */ + fail_reason = "interface has no IPv6 link local address to start DHCPv6"; + goto out_fail; } - } - success = - nm_device_set_ip_config(self, addr_family, composite, commit, ip4_dev_route_blacklist); - if (commit) { - if (IS_IPv4) - priv->v4_commit_first_time = FALSE; - else - priv->v6_commit_first_time = FALSE; - } - - return success; -} + iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET6, connection, FALSE, &iaid_explicit); + 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.ll_addr = &priv->ipll_data_6.v6.lladdr, + .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, + }; -static gboolean -dhcp4_lease_change(NMDevice *self, NMIP4Config *config, gboolean bound) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gs_free_error GError *error = NULL; + priv->ipdhcp_data_6.client = + nm_dhcp_manager_start_client(nm_dhcp_manager_get(), &config, &error); + } - g_return_val_if_fail(config, FALSE); + if (!priv->ipdhcp_data_x[IS_IPv4].client) { + fail_reason = error->message; + goto out_fail; + } - applied_config_init(&priv->dev_ip_config_4, config); + priv->ipdhcp_data_x[IS_IPv4].notify_sigid = + g_signal_connect(priv->ipdhcp_data_x[IS_IPv4].client, + NM_DHCP_CLIENT_NOTIFY, + G_CALLBACK(_dev_ipdhcpx_notify), + self); - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) { - _LOGW(LOGD_DHCP4, "failed to update IPv4 config for DHCP change."); - return FALSE; - } + /* FIXME(l3cfg:dhcp:previous-lease): take the NML3ConfigData from the previous lease (if any) + * and pass it on to NMDhcpClient. This is a fake lease that we use initially (until + * NMDhcpClient got a real lease). Note that NMDhcpClient needs to check whether the + * lease already expired. */ - /* TODO: we should perform DAD again whenever we obtain a - * new lease after an expiry. But what should we do if - * a duplicate address is detected? Fail the connection; - * restart DHCP; continue without an address? */ - if (bound && !nm_dhcp_client_accept(priv->dhcp_data_4.client, &error)) { - _LOGW(LOGD_DHCP4, "error accepting lease: %s", error->message); - return FALSE; + previous_lease = nm_dhcp_client_get_lease(priv->ipdhcp_data_x[IS_IPv4].client); + if (!priv->ipdhcp_data_x[IS_IPv4].config) { + priv->ipdhcp_data_x[IS_IPv4].config = nm_dhcp_config_new(addr_family, previous_lease); + _notify(self, PROP_DHCPX_CONFIG(IS_IPv4)); } + if (previous_lease) + _dev_ipdhcpx_handle_accept(self, addr_family, previous_lease); - nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_4, self, NULL, NULL, NULL, NULL); + return; - return TRUE; +out_fail: + _dev_ipdhcpx_handle_fail(self, addr_family, fail_reason); } -static gboolean -dhcp_grace_period_expired(NMDevice *self, int addr_family) +static void +_dev_ipdhcpx_start_continue(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); - priv->dhcp_data_x[IS_IPv4].grace_id = 0; - priv->dhcp_data_x[IS_IPv4].grace_pending = FALSE; - - _LOGI(LOGD_DHCPX(IS_IPv4), - "DHCPv%c: grace period expired", - nm_utils_addr_family_to_char(addr_family)); - - nm_device_ip_method_failed(self, addr_family, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - /* If the device didn't fail, the DHCP client will continue */ - - return G_SOURCE_REMOVE; + if (priv->ipdhcp_data_x[IS_IPv4].state != NM_DEVICE_IP_STATE_NONE) + _dev_ipdhcpx_start(self, addr_family); } -static gboolean -dhcp_grace_period_expired_4(gpointer user_data) -{ - return dhcp_grace_period_expired(user_data, AF_INET); -} - -static gboolean -dhcp_grace_period_expired_6(gpointer user_data) -{ - return dhcp_grace_period_expired(user_data, AF_INET6); -} - -static gboolean -dhcp_grace_period_start(NMDevice *self, int addr_family) +static void +_dev_ipdhcpx_restart(NMDevice *self, int addr_family, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); - guint32 timeout; - /* In any other case (expired lease, assumed connection, etc.), - * wait for some time before failing the IP method. - */ - if (priv->dhcp_data_x[IS_IPv4].grace_pending) { - /* already pending. */ - return FALSE; + if (priv->ipdhcp_data_x[IS_IPv4].state != NM_DEVICE_IP_STATE_NONE) { + _LOGI_ipdhcp(addr_family, "restarting%s", release ? " (release lease)" : ""); + _dev_ipdhcpx_cleanup(self, addr_family, FALSE, release); } - /* Start a grace period equal to the DHCP timeout multiplied - * by a constant factor. */ - timeout = _prop_get_ipvx_dhcp_timeout(self, addr_family); - if (timeout == NM_DHCP_TIMEOUT_INFINITY) - _LOGI(LOGD_DHCPX(IS_IPv4), - "DHCPv%c: trying to acquire a new lease", - nm_utils_addr_family_to_char(addr_family)); - else { - timeout = dhcp_grace_period_from_timeout(timeout); - _LOGI(LOGD_DHCPX(IS_IPv4), - "DHCPv%c: trying to acquire a new lease within %u seconds", - nm_utils_addr_family_to_char(addr_family), - timeout); - nm_assert(!priv->dhcp_data_x[IS_IPv4].grace_id); - priv->dhcp_data_x[IS_IPv4].grace_id = g_timeout_add_seconds( - timeout, - IS_IPv4 ? dhcp_grace_period_expired_4 : dhcp_grace_period_expired_6, - self); - } - - priv->dhcp_data_x[IS_IPv4].grace_pending = TRUE; - - return TRUE; -} -static void -dhcp4_fail(NMDevice *self, NMDhcpState dhcp_state) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - _LOGD(LOGD_DHCP4, - "DHCPv4 failed (ip_state %s, was_active %d)", - nm_device_ip_state_to_string(priv->ip_state_4), - priv->dhcp_data_4.was_active); - - /* The client is always left running after a failure. */ - - /* Nothing to do if we failed before... */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL) - goto clear_config; - - /* ... and also if there are static addresses configured - * on the interface. - */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && priv->con_ip_config_4 - && nm_ip4_config_get_num_addresses(priv->con_ip_config_4) > 0) - goto clear_config; - - /* Fail the method when one of the following is true: - * 1) the DHCP client terminated: it does not make sense to start a grace - * period without a client running; - * 2) we failed to get an initial lease AND the client was - * not active before. - */ - if (dhcp_state == NM_DHCP_STATE_TERMINATED - || (!priv->dhcp_data_4.was_active && priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF)) { - nm_device_activate_schedule_ip_config_timeout(self, AF_INET); - return; - } - - if (dhcp_grace_period_start(self, AF_INET)) - goto clear_config; - - return; - -clear_config: - /* The previous configuration is no longer valid */ - if (priv->dhcp_data_4.config) { - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_4.config); - priv->dhcp_data_4.config = nm_dhcp_config_new(AF_INET); - _notify(self, PROP_DHCP4_CONFIG); - } -} - -static void -dhcp4_dad_cb(NMDevice *self, NMIP4Config **configs, gboolean success) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - if (success) { - nm_device_activate_schedule_ip_config_result(self, AF_INET, NM_IP_CONFIG_CAST(configs[1])); - } else { - nm_dhcp_client_decline(priv->dhcp_data_4.client, "Address conflict detected", NULL); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE); - } + _dev_ipdhcpx_start(self, addr_family); } static void -dhcp4_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_data, NMDevice *self) +_dev_ipdhcp6_set_dhcp_level(NMDevice *self, NMNDiscDHCPLevel dhcp_level) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMIP4Config * manual; - NMIP4Config ** configs; - NMConnection * connection; - NMDhcpState state; - NMIP4Config * ip4_config; - GHashTable * options; - - nm_assert(nm_dhcp_client_get_addr_family(client) == AF_INET); - nm_assert(notify_data); - nm_assert(notify_data->notify_type == NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED); - - state = notify_data->state_changed.dhcp_state; - ip4_config = NM_IP4_CONFIG(notify_data->state_changed.ip_config); - options = notify_data->state_changed.options; - - nm_assert(!ip4_config || NM_IS_IP4_CONFIG(ip4_config)); - - _LOGD(LOGD_DHCP4, "new DHCPv4 client state %d", (int) state); - - switch (state) { - case NM_DHCP_STATE_BOUND: - case NM_DHCP_STATE_EXTENDED: - if (!ip4_config) { - _LOGW(LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event."); - dhcp4_fail(self, state); - break; - } - - nm_clear_g_source(&priv->dhcp_data_4.grace_id); - priv->dhcp_data_4.grace_pending = FALSE; - - /* After some failures, we have been able to renew the lease: - * update the ip state - */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL) - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_CONF); - - g_free(priv->dhcp4.pac_url); - priv->dhcp4.pac_url = g_strdup(g_hash_table_lookup(options, "wpad")); - nm_device_set_proxy_config(self, priv->dhcp4.pac_url); - - nm_dhcp_config_set_options(priv->dhcp_data_4.config, options); - _notify(self, PROP_DHCP4_CONFIG); - - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) { - connection = nm_device_get_applied_connection(self); - g_assert(connection); - - manual = nm_device_ip4_config_new(self); - nm_ip4_config_merge_setting(manual, - nm_connection_get_setting_ip4_config(connection), - NM_SETTING_CONNECTION_MDNS_DEFAULT, - NM_SETTING_CONNECTION_LLMNR_DEFAULT, - nm_device_get_route_table(self, AF_INET), - nm_device_get_route_metric(self, AF_INET)); - - configs = g_new0(NMIP4Config *, 3); - configs[0] = manual; - configs[1] = g_object_ref(ip4_config); - - ipv4_dad_start(self, configs, dhcp4_dad_cb); - } else if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { - if (dhcp4_lease_change(self, ip4_config, state == NM_DHCP_STATE_BOUND)) - nm_device_update_metered(self); - else - dhcp4_fail(self, state); - } - break; - case NM_DHCP_STATE_TIMEOUT: - dhcp4_fail(self, state); - break; - case NM_DHCP_STATE_EXPIRE: - /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) - break; - /* fall-through */ - case NM_DHCP_STATE_DONE: - case NM_DHCP_STATE_FAIL: - case NM_DHCP_STATE_TERMINATED: - dhcp4_fail(self, state); - break; - default: - break; - } -} -static NMActStageReturn -dhcp4_start(NMDevice *self) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMSettingIPConfig *s_ip4; - gs_unref_bytes GBytes *vendor_class_identifier = NULL; - gs_unref_bytes GBytes *hwaddr = NULL; - gs_unref_bytes GBytes *bcast_hwaddr = NULL; - gs_unref_bytes GBytes *client_id = NULL; - NMConnection * connection; - NMSettingConnection * s_con; - GError * error = NULL; - const NMPlatformLink * pllink; - const char *const * reject_servers; - gboolean request_broadcast; - const char * str; + nm_assert(NM_IN_SET(dhcp_level, + NM_NDISC_DHCP_LEVEL_NONE, + NM_NDISC_DHCP_LEVEL_OTHERCONF, + NM_NDISC_DHCP_LEVEL_MANAGED)); - connection = nm_device_get_applied_connection(self); - g_return_val_if_fail(connection, FALSE); + if (dhcp_level == NM_NDISC_DHCP_LEVEL_NONE && priv->ipdhcp_data_6.v6.needed_prefixes > 0) + dhcp_level = NM_NDISC_DHCP_LEVEL_OTHERCONF; - s_ip4 = nm_connection_get_setting_ip4_config(connection); - - s_con = nm_connection_get_setting_connection(connection); - nm_assert(s_con); - - /* Clear old exported DHCP options */ - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_4.config); - priv->dhcp_data_4.config = nm_dhcp_config_new(AF_INET); - - request_broadcast = FALSE; + if (priv->ipdhcp_data_6.v6.mode == dhcp_level) + return; - pllink = nm_platform_link_get(nm_device_get_platform(self), nm_device_get_ip_ifindex(self)); - if (pllink) { - hwaddr = nmp_link_address_get_as_bytes(&pllink->l_address); - bcast_hwaddr = nmp_link_address_get_as_bytes(&pllink->l_broadcast); + _LOGD_ipdhcp(AF_INET6, "level: set to %s", nm_ndisc_dhcp_level_to_string(dhcp_level)); - str = nmp_object_link_udev_device_get_property_value(NMP_OBJECT_UP_CAST(pllink), - "ID_NET_DHCP_BROADCAST"); - if (str && _nm_utils_ascii_str_to_bool(str, FALSE)) { - /* Use the device property ID_NET_DHCP_BROADCAST setting, which may be set for interfaces - * requiring that the DHCPOFFER message is being broadcast because they can't handle unicast - * messages while not fully configured. - */ - request_broadcast = TRUE; - } + if (dhcp_level == NM_NDISC_DHCP_LEVEL_NONE) { + _dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, TRUE); + return; } - client_id = _prop_get_ipv4_dhcp_client_id(self, connection, hwaddr); - vendor_class_identifier = - _prop_get_ipv4_dhcp_vendor_class_identifier(self, NM_SETTING_IP4_CONFIG(s_ip4)); - reject_servers = nm_setting_ip_config_get_dhcp_reject_servers(s_ip4, NULL); - - g_warn_if_fail(priv->dhcp_data_4.client == NULL); - priv->dhcp_data_4.client = nm_dhcp_manager_start_ip4( - nm_dhcp_manager_get(), - nm_netns_get_multi_idx(nm_device_get_netns(self)), - nm_device_get_ip_iface(self), - nm_device_get_ip_ifindex(self), - hwaddr, - bcast_hwaddr, - nm_connection_get_uuid(connection), - nm_device_get_route_table(self, AF_INET), - nm_device_get_route_metric(self, AF_INET), - request_broadcast ? NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST : NM_DHCP_CLIENT_FLAGS_NONE, - nm_setting_ip_config_get_dhcp_send_hostname(s_ip4), - nm_setting_ip_config_get_dhcp_hostname(s_ip4), - nm_setting_ip4_config_get_dhcp_fqdn(NM_SETTING_IP4_CONFIG(s_ip4)), - _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET), - _prop_get_connection_mud_url(self, s_con), - client_id, - _prop_get_ipvx_dhcp_timeout(self, AF_INET), - _device_get_dhcp_anycast_address(self), - NULL, - vendor_class_identifier, - reject_servers, - &error); - if (!priv->dhcp_data_4.client) { - _LOGW(LOGD_DHCP4, "failure to start DHCP: %s", error->message); - g_clear_error(&error); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - priv->dhcp_data_4.notify_sigid = g_signal_connect(priv->dhcp_data_4.client, - NM_DHCP_CLIENT_NOTIFY, - G_CALLBACK(dhcp4_notify), - self); - - if (nm_device_sys_iface_state_is_external_or_assume(self)) - priv->dhcp_data_4.was_active = TRUE; - - /* DHCP devices will be notified by the DHCP manager when stuff happens */ - return NM_ACT_STAGE_RETURN_POSTPONE; + priv->ipdhcp_data_6.v6.mode = dhcp_level; + _dev_ipdhcpx_restart(self, AF_INET6, TRUE); } -gboolean -nm_device_dhcp4_renew(NMDevice *self, gboolean release) +/* + * Called on the requesting interface when a subnet can't be obtained + * from known prefixes for a newly active shared connection. + */ +void +nm_device_request_ip6_prefixes(NMDevice *self, guint needed_prefixes) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - g_return_val_if_fail(priv->dhcp_data_4.client != NULL, FALSE); - - _LOGI(LOGD_DHCP4, "DHCPv4 lease renewal requested"); - - /* Terminate old DHCP instance and release the old lease */ - dhcp4_cleanup(self, CLEANUP_TYPE_DECONFIGURE, release); - - /* Start DHCP again on the interface */ - return dhcp4_start(self) != NM_ACT_STAGE_RETURN_FAILURE; -} - -/*****************************************************************************/ - -static NMIP4Config * -shared4_new_config(NMDevice *self, NMConnection *connection) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMIP4Config * config; - NMSettingIPConfig * s_ip4; - NMPlatformIP4Address address = { - .addr_source = NM_IP_CONFIG_SOURCE_SHARED, - }; + if (priv->ipdhcp_data_6.v6.needed_prefixes == needed_prefixes) + return; - g_return_val_if_fail(self, NULL); - g_return_val_if_fail(connection, NULL); + _LOGD(LOGD_IP6, "ipv6-pd: asking DHCPv6 for %u prefixes", needed_prefixes); - s_ip4 = nm_connection_get_setting_ip4_config(connection); - if (s_ip4 && nm_setting_ip_config_get_num_addresses(s_ip4) > 0) { - /* Use the first user-supplied address */ - NMIPAddress *user = nm_setting_ip_config_get_address(s_ip4, 0); - in_addr_t a; + priv->ipdhcp_data_6.v6.needed_prefixes = needed_prefixes; - nm_ip_address_get_address_binary(user, &a); - nm_platform_ip4_address_set_addr(&address, a, nm_ip_address_get_prefix(user)); - nm_clear_pointer(&priv->shared_ip_handle, nm_netns_shared_ip_release); - } else { - if (!priv->shared_ip_handle) - priv->shared_ip_handle = nm_netns_shared_ip_reserve(nm_device_get_netns(self)); - nm_platform_ip4_address_set_addr(&address, priv->shared_ip_handle->addr, 24); + if (priv->ipdhcp_data_6.v6.mode == NM_NDISC_DHCP_LEVEL_NONE) { + priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_OTHERCONF; + _LOGD_ipdhcp(AF_INET6, + "level: set to %s", + nm_ndisc_dhcp_level_to_string(NM_NDISC_DHCP_LEVEL_OTHERCONF)); } - config = nm_device_ip4_config_new(self); - nm_ip4_config_add_address(config, &address); - - return config; + _dev_ipdhcpx_restart(self, AF_INET6, TRUE); } /*****************************************************************************/ @@ -9766,392 +10050,6 @@ have_any_ready_slaves(NMDevice *self) } /*****************************************************************************/ -/* DHCPv6 stuff */ - -static void -dhcp6_cleanup(NMDevice *self, CleanupType cleanup_type, gboolean release) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - priv->dhcp_data_6.was_active = FALSE; - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; - applied_config_clear(&priv->dhcp6.ip6_config); - nm_clear_g_free(&priv->dhcp6.event_id); - nm_clear_g_source(&priv->dhcp_data_6.grace_id); - priv->dhcp_data_6.grace_pending = FALSE; - - if (priv->dhcp_data_6.client) { - nm_clear_g_signal_handler(priv->dhcp_data_6.client, &priv->dhcp_data_6.notify_sigid); - - if (cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED) - nm_dhcp_client_stop(priv->dhcp_data_6.client, release); - - g_clear_object(&priv->dhcp_data_6.client); - } - - if (priv->dhcp_data_6.config) { - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_6.config); - _notify(self, PROP_DHCP6_CONFIG); - } -} - -static gboolean -dhcp6_lease_change(NMDevice *self) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMSettingsConnection *settings_connection; - - if (!applied_config_get_current(&priv->dhcp6.ip6_config)) { - _LOGW(LOGD_DHCP6, "failed to get DHCPv6 config for rebind"); - return FALSE; - } - - g_assert(priv->dhcp_data_6.client); /* sanity check */ - - settings_connection = nm_device_get_settings_connection(self); - g_assert(settings_connection); - - /* Apply the updated config */ - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) { - _LOGW(LOGD_DHCP6, "failed to update IPv6 config in response to DHCP event"); - return FALSE; - } - - nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_6, self, NULL, NULL, NULL, NULL); - - return TRUE; -} - -static void -dhcp6_fail(NMDevice *self, NMDhcpState dhcp_state) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gboolean is_dhcp_managed; - - _LOGD(LOGD_DHCP6, - "DHCPv6 failed (ip_state %s, was_active %d)", - nm_device_ip_state_to_string(priv->ip_state_6), - priv->dhcp_data_6.was_active); - - /* The client is always left running after a failure. */ - - /* Nothing to do if we failed before... */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL) - goto clear_config; - - is_dhcp_managed = (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED); - - if (is_dhcp_managed) { - /* ... and also if there are static addresses configured - * on the interface. - */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE && priv->con_ip_config_6 - && nm_ip6_config_get_num_addresses(priv->con_ip_config_6)) - goto clear_config; - - /* Fail the method when one of the following is true: - * 1) the DHCP client terminated: it does not make sense to start a grace - * period without a client running; - * 2) we failed to get an initial lease AND the client was - * not active before. - */ - if (dhcp_state == NM_DHCP_STATE_TERMINATED - || (!priv->dhcp_data_6.was_active && priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF)) { - nm_device_activate_schedule_ip_config_timeout(self, AF_INET6); - return; - } - - if (dhcp_grace_period_start(self, AF_INET6)) - goto clear_config; - } else { - /* not a hard failure; just live with the RA info */ - dhcp6_cleanup(self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - } - return; - -clear_config: - /* The previous configuration is no longer valid */ - if (priv->dhcp_data_6.config) { - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_6.config); - priv->dhcp_data_6.config = nm_dhcp_config_new(AF_INET6); - _notify(self, PROP_DHCP6_CONFIG); - } -} - -static void -dhcp6_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_data, NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gs_free char * event_id = NULL; - NMDhcpState state; - NMIP6Config * ip6_config; - GHashTable * options; - - nm_assert(nm_dhcp_client_get_addr_family(client) == AF_INET6); - nm_assert(notify_data); - - if (notify_data->notify_type == NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED) { - /* Just re-emit. The device just contributes the prefix to the - * pool in NMPolicy, which decides about subnet allocation - * on the shared devices. */ - g_signal_emit(self, signals[IP6_PREFIX_DELEGATED], 0, notify_data->prefix_delegated.prefix); - return; - } - - nm_assert(notify_data->notify_type == NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED); - - state = notify_data->state_changed.dhcp_state; - ip6_config = NM_IP6_CONFIG(notify_data->state_changed.ip_config); - options = notify_data->state_changed.options; - - nm_assert(!ip6_config || NM_IS_IP6_CONFIG(ip6_config)); - - _LOGD(LOGD_DHCP6, "new DHCPv6 client state %d", (int) state); - - switch (state) { - case NM_DHCP_STATE_BOUND: - case NM_DHCP_STATE_EXTENDED: - nm_clear_g_source(&priv->dhcp_data_6.grace_id); - priv->dhcp_data_6.grace_pending = FALSE; - /* If the server sends multiple IPv6 addresses, we receive a state - * changed event for each of them. Use the event ID to merge IPv6 - * addresses from the same transaction into a single configuration. - */ - - event_id = nm_dhcp_utils_get_dhcp6_event_id(options); - - if (ip6_config && event_id && priv->dhcp6.event_id - && nm_streq(event_id, priv->dhcp6.event_id)) { - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *a; - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6_config, &a) - applied_config_add_address(&priv->dhcp6.ip6_config, NM_PLATFORM_IP_ADDRESS_CAST(a)); - } else { - nm_clear_g_free(&priv->dhcp6.event_id); - if (ip6_config) { - applied_config_init(&priv->dhcp6.ip6_config, ip6_config); - priv->dhcp6.event_id = g_strdup(event_id); - nm_dhcp_config_set_options(priv->dhcp_data_6.config, options); - _notify(self, PROP_DHCP6_CONFIG); - } else - applied_config_clear(&priv->dhcp6.ip6_config); - } - - /* After long time we have been able to renew the lease: - * update the ip state - */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL) - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_CONF); - - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) { - if (!applied_config_get_current(&priv->dhcp6.ip6_config)) { - nm_device_ip_method_failed(self, AF_INET6, NM_DEVICE_STATE_REASON_DHCP_FAILED); - break; - } - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - } else if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) - if (!dhcp6_lease_change(self)) - dhcp6_fail(self, state); - break; - case NM_DHCP_STATE_TIMEOUT: - if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED) - dhcp6_fail(self, state); - else { - /* not a hard failure; just live with the RA info */ - dhcp6_cleanup(self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - } - break; - case NM_DHCP_STATE_EXPIRE: - /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ - if (priv->ip_state_6 != NM_DEVICE_IP_STATE_CONF) - dhcp6_fail(self, state); - break; - case NM_DHCP_STATE_TERMINATED: - /* In IPv6 info-only mode, the client doesn't handle leases so it - * may exit right after getting a response from the server. That's - * normal. In that case we just ignore the exit. - */ - if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) - break; - /* fall-through */ - case NM_DHCP_STATE_DONE: - case NM_DHCP_STATE_FAIL: - dhcp6_fail(self, state); - break; - default: - break; - } -} - -/*****************************************************************************/ - -static gboolean -dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMSettingIPConfig *s_ip6; - gs_unref_bytes GBytes *hwaddr = NULL; - gs_unref_bytes GBytes * duid = NULL; - gboolean enforce_duid = FALSE; - const NMPlatformLink * pllink; - GError * error = NULL; - guint32 iaid; - gboolean iaid_explicit; - NMSettingConnection * s_con; - const NMPlatformIP6Address *ll_addr = NULL; - int ip_ifindex; - - g_return_val_if_fail(connection, FALSE); - - s_ip6 = nm_connection_get_setting_ip6_config(connection); - nm_assert(s_ip6); - s_con = nm_connection_get_setting_connection(connection); - nm_assert(s_con); - - if (priv->ext_ip6_config_captured) { - ll_addr = nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL); - } - - if (!ll_addr) { - _LOGW(LOGD_DHCP6, "can't start DHCPv6: no link-local address"); - return FALSE; - } - - ip_ifindex = nm_device_get_ip_ifindex(self); - if (ip_ifindex <= 0) { - _LOGD(LOGD_DHCP6, "can't start DHCPv6: interface is gone"); - return FALSE; - } - - pllink = nm_platform_link_get(nm_device_get_platform(self), ip_ifindex); - if (pllink) - hwaddr = nmp_link_address_get_as_bytes(&pllink->l_address); - - iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET6, connection, TRUE, &iaid_explicit); - duid = _prop_get_ipv6_dhcp_duid(self, connection, hwaddr, &enforce_duid); - - priv->dhcp_data_6.client = nm_dhcp_manager_start_ip6( - nm_dhcp_manager_get(), - nm_device_get_multi_index(self), - nm_device_get_ip_iface(self), - ip_ifindex, - &ll_addr->address, - nm_connection_get_uuid(connection), - nm_device_get_route_table(self, AF_INET6), - nm_device_get_route_metric(self, AF_INET6), - (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? NM_DHCP_CLIENT_FLAGS_INFO_ONLY - : NM_DHCP_CLIENT_FLAGS_NONE, - nm_setting_ip_config_get_dhcp_send_hostname(s_ip6), - nm_setting_ip_config_get_dhcp_hostname(s_ip6), - _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET6), - _prop_get_connection_mud_url(self, s_con), - duid, - enforce_duid, - iaid, - iaid_explicit, - _prop_get_ipvx_dhcp_timeout(self, AF_INET6), - _device_get_dhcp_anycast_address(self), - nm_setting_ip6_config_get_ip6_privacy(NM_SETTING_IP6_CONFIG(s_ip6)), - priv->dhcp6.needed_prefixes, - &error); - if (!priv->dhcp_data_6.client) { - _LOGW(LOGD_DHCP6, "failure to start DHCPv6: %s", error->message); - g_clear_error(&error); - if (nm_device_sys_iface_state_is_external_or_assume(self)) - priv->dhcp_data_6.was_active = TRUE; - return FALSE; - } - - priv->dhcp_data_6.notify_sigid = g_signal_connect(priv->dhcp_data_6.client, - NM_DHCP_CLIENT_NOTIFY, - G_CALLBACK(dhcp6_notify), - self); - - if (nm_device_sys_iface_state_is_external_or_assume(self)) - priv->dhcp_data_6.was_active = TRUE; - - return TRUE; -} - -static gboolean -dhcp6_start(NMDevice *self, gboolean wait_for_ll) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMConnection * connection; - - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_6.config); - priv->dhcp_data_6.config = nm_dhcp_config_new(AF_INET6); - - nm_assert(!applied_config_get_current(&priv->dhcp6.ip6_config)); - applied_config_clear(&priv->dhcp6.ip6_config); - nm_clear_g_free(&priv->dhcp6.event_id); - - connection = nm_device_get_applied_connection(self); - g_return_val_if_fail(connection, FALSE); - - if (wait_for_ll) { - /* ensure link local is ready... */ - if (!linklocal6_start(self)) { - /* wait for the LL address to show up */ - return TRUE; - } - /* already have the LL address; kick off DHCP */ - } - - if (!dhcp6_start_with_link_ready(self, connection)) - return FALSE; - - return TRUE; -} - -gboolean -nm_device_dhcp6_renew(NMDevice *self, gboolean release) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMNDiscDHCPLevel mode; - - g_return_val_if_fail(priv->dhcp_data_6.client != NULL, FALSE); - - _LOGI(LOGD_DHCP6, "DHCPv6 lease renewal requested"); - - /* Terminate old DHCP instance and release the old lease */ - mode = priv->dhcp6.mode; - dhcp6_cleanup(self, CLEANUP_TYPE_DECONFIGURE, release); - priv->dhcp6.mode = mode; - - /* Start DHCP again on the interface */ - return dhcp6_start(self, FALSE); -} - -/*****************************************************************************/ - -/* - * Called on the requesting interface when a subnet can't be obtained - * from known prefixes for a newly active shared connection. - */ -void -nm_device_request_ip6_prefixes(NMDevice *self, int needed_prefixes) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - priv->dhcp6.needed_prefixes = needed_prefixes; - - if (priv->dhcp_data_6.client) { - _LOGD(LOGD_IP6, "ipv6-pd: asking DHCPv6 for %d prefixes", needed_prefixes); - nm_device_dhcp6_renew(self, FALSE); - } else { - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_OTHERCONF; - _LOGD(LOGD_DEVICE | LOGD_DHCP6, "ipv6-pd: starting DHCPv6 to request a prefix"); - dhcp6_start(self, FALSE); - } -} gboolean nm_device_needs_ip6_subnet(NMDevice *self) @@ -10166,25 +10064,24 @@ nm_device_needs_ip6_subnet(NMDevice *self) void nm_device_use_ip6_subnet(NMDevice *self, const NMPlatformIP6Address *subnet) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMPlatformIP6Address address = *subnet; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + char sbuf[sizeof(_nm_utils_to_string_buffer)]; + NMPlatformIP6Address address; - if (!applied_config_get_current(&priv->ac_ip6_config)) - applied_config_init_new(&priv->ac_ip6_config, self, AF_INET6); + l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED); /* Assign a ::1 address in the subnet for us. */ + address = *subnet; address.address.s6_addr32[3] |= htonl(1); - applied_config_add_address(&priv->ac_ip6_config, NM_PLATFORM_IP_ADDRESS_CAST(&address)); + + nm_l3_config_data_add_address_6(l3cd, &address); _LOGD(LOGD_IP6, - "ipv6-pd: using %s address (preferred for %u seconds)", - _nm_utils_inet6_ntop(&address.address, sbuf), - subnet->preferred); + "ipv6-pd: using %s", + nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf))); - /* This also updates the ndisc if there are actual changes. */ - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "ipv6-pd: failed applying IP6 config for connection sharing"); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_PD_6, l3cd, FALSE); + _dev_l3_cfg_commit(self, TRUE); } /* @@ -10194,137 +10091,184 @@ nm_device_use_ip6_subnet(NMDevice *self, const NMPlatformIP6Address *subnet) void nm_device_copy_ip6_dns_config(NMDevice *self, NMDevice *from_device) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMIP6Config * from_config = NULL; - guint i, len; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NMDevicePrivate * priv_src; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + const NML3ConfigData * l3cd_src; - if (applied_config_get_current(&priv->ac_ip6_config)) { - applied_config_reset_nameservers(&priv->ac_ip6_config); - applied_config_reset_searches(&priv->ac_ip6_config); + /* FIXME(l3cfg): this entire code an approach seems flawed. It's flawed, because the + * very next RA will reset the changes. */ + + if (priv->l3cds[L3_CONFIG_DATA_TYPE_AC_6].d) { + l3cd = nm_l3_config_data_new_clone(priv->l3cds[L3_CONFIG_DATA_TYPE_AC_6].d, 0); + nm_l3_config_data_clear_nameservers(l3cd, AF_INET6); + nm_l3_config_data_clear_searches(l3cd, AF_INET6); } else - applied_config_init_new(&priv->ac_ip6_config, self, AF_INET6); + l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED); - if (from_device) - from_config = nm_device_get_ip6_config(from_device); - if (!from_config) - return; + priv_src = NM_DEVICE_GET_PRIVATE(from_device); + l3cd_src = priv_src->l3cds[L3_CONFIG_DATA_TYPE_AC_6].d; + if (l3cd_src) { + const char *const * strvarr; + const struct in6_addr *const *addrs; + guint n; + guint i; - len = nm_ip6_config_get_num_nameservers(from_config); - for (i = 0; i < len; i++) { - applied_config_add_nameserver( - &priv->ac_ip6_config, - (const NMIPAddr *) nm_ip6_config_get_nameserver(from_config, i)); - } + addrs = nm_l3_config_data_get_nameservers(l3cd_src, AF_INET6, &n); + for (i = 0; i < n; i++) + nm_l3_config_data_add_nameserver(l3cd, AF_INET6, addrs[i]); - len = nm_ip6_config_get_num_searches(from_config); - for (i = 0; i < len; i++) { - applied_config_add_search(&priv->ac_ip6_config, nm_ip6_config_get_search(from_config, i)); + strvarr = nm_l3_config_data_get_searches(l3cd_src, AF_INET6, &n); + for (i = 0; i < n; i++) + nm_l3_config_data_add_search(l3cd, AF_INET6, strvarr[i]); } - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "ipv6-pd: failed applying DNS config for connection sharing"); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_AC_6, l3cd, FALSE); + + _dev_l3_cfg_commit(self, TRUE); } /*****************************************************************************/ -static void -linklocal6_failed(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - nm_clear_g_source(&priv->linklocal6_timeout_id); - nm_device_activate_schedule_ip_config_timeout(self, AF_INET6); -} - static gboolean -linklocal6_timeout_cb(gpointer user_data) +_dev_ipll6_state_retry_cb(gpointer user_data) { - NMDevice *self = user_data; + NMDevice * self = user_data; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - _LOGD(LOGD_DEVICE, "linklocal6: waiting for link-local addresses failed due to timeout"); - linklocal6_failed(self); - return G_SOURCE_REMOVE; + nm_clear_g_source_inst(&priv->ipll_data_6.v6.retry_source); + _dev_ipll6_start(self); + return G_SOURCE_CONTINUE; } static void -linklocal6_check_complete(NMDevice *self) +_dev_ipll6_set_llstate(NMDevice *self, NML3IPv6LLState llstate, const struct in6_addr *lladdr) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMConnection * connection; - const char * method; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean changed = FALSE; + NMDeviceIPState state; + NMDeviceIPState old_state; + + if (!lladdr) + lladdr = &nm_ip_addr_zero.addr6; + + if (priv->ipll_data_6.v6.llstate != llstate + || !IN6_ARE_ADDR_EQUAL(&priv->ipll_data_6.v6.lladdr, lladdr)) { + changed = TRUE; + priv->ipll_data_6.v6.llstate = llstate; + priv->ipll_data_6.v6.lladdr = *lladdr; + } + + nm_assert((priv->ipll_data_6.v6.ipv6ll + && NM_IN_SET(priv->ipll_data_6.v6.llstate, + NM_L3_IPV6LL_STATE_STARTING, + NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, + NM_L3_IPV6LL_STATE_READY, + NM_L3_IPV6LL_STATE_DAD_FAILED)) + || (!priv->ipll_data_6.v6.ipv6ll + && NM_IN_SET(priv->ipll_data_6.v6.llstate, + NM_L3_IPV6LL_STATE_NONE, + NM_L3_IPV6LL_STATE_DEFUNCT))); + + switch (priv->ipll_data_6.v6.llstate) { + case NM_L3_IPV6LL_STATE_NONE: + state = NM_DEVICE_IP_STATE_NONE; + break; + case NM_L3_IPV6LL_STATE_DEFUNCT: + case NM_L3_IPV6LL_STATE_DAD_FAILED: + state = NM_DEVICE_IP_STATE_FAILED; + break; + case NM_L3_IPV6LL_STATE_READY: + state = NM_DEVICE_IP_STATE_READY; + break; + case NM_L3_IPV6LL_STATE_STARTING: + case NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS: + state = NM_DEVICE_IP_STATE_PENDING; + break; + default: + state = nm_assert_unreachable_val(NM_DEVICE_IP_STATE_FAILED); + break; + } - if (!priv->linklocal6_timeout_id) { - /* we are not waiting for linklocal to complete. Nothing to do. */ - return; + old_state = priv->ipll_data_6.state; + if (priv->ipll_data_6.state != state) { + priv->ipll_data_6.state = state; + changed = TRUE; } - if (!priv->ext_ip6_config_captured - || !nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) { - /* we don't have a non-tentative link local address yet. Wait longer. */ - return; + if (priv->ipll_data_6.v6.llstate != NM_L3_IPV6LL_STATE_DEFUNCT) + nm_clear_g_source_inst(&priv->ipll_data_6.v6.retry_source); + else if (!priv->ipll_data_6.v6.retry_source) { + /* we schedule a timer to try to recover from this... Possibly some higher layer + * will however fail the activation... */ + priv->ipll_data_6.v6.retry_source = + nm_g_timeout_add_source(10000, _dev_ipll6_state_retry_cb, self); } - nm_clear_g_source(&priv->linklocal6_timeout_id); + if (changed) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - connection = nm_device_get_applied_connection(self); - g_assert(connection); + _LOGT_ipll(AF_INET6, + "set state %s (was %s, llstate=%s, lladdr=%s)", + nm_device_ip_state_to_string(priv->ipll_data_6.state), + nm_device_ip_state_to_string(old_state), + nm_l3_ipv6ll_state_to_string(priv->ipll_data_6.v6.llstate), + nm_ip_addr_is_null(AF_INET6, &priv->ipll_data_6.v6.lladdr) + ? "(none)" + : _nm_utils_inet6_ntop(&priv->ipll_data_6.v6.lladdr, sbuf)); + } - method = nm_device_get_effective_ip_config_method(self, AF_INET6); + if (changed) + _dev_ip_state_check_async(self, AF_INET6); - _LOGD(LOGD_DEVICE, - "linklocal6: waiting for link-local addresses successful, continue with method %s", - method); - - if (NM_IN_STRSET(method, - NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_METHOD_SHARED)) - addrconf6_start_with_link_ready(self); - else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { - if (!dhcp6_start_with_link_ready(self, connection)) { - /* Time out IPv6 instead of failing the entire activation */ - nm_device_activate_schedule_ip_config_timeout(self, AF_INET6); - } - } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - else - g_return_if_fail(FALSE); + if (priv->ipll_data_6.v6.llstate == NM_L3_IPV6LL_STATE_READY) { + /* if we got an IPv6LL address, we might poke some other methods + * to progress... */ + _dev_ipac6_start_continue(self); + _dev_ipdhcpx_start_continue(self, AF_INET6); + } } static void -check_and_add_ipv6ll_addr(NMDevice *self) +_dev_ipll6_state_change_cb(NML3IPv6LL * ipv6ll, + NML3IPv6LLState llstate, + const struct in6_addr *lladdr, + gpointer user_data) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - struct in6_addr lladdr; - NMConnection * connection; - NMSettingIP6Config *s_ip6 = NULL; - GError * error = NULL; - const char * addr_type; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + _dev_ipll6_set_llstate(user_data, llstate, lladdr); +} - if (!priv->ipv6ll_handle) - return; +static void +_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; + const struct in6_addr *lladdr; - if (priv->ext_ip6_config_captured - && nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) { - /* Already have an LL address, nothing to do */ + if (priv->ipll_data_6.v6.ipv6ll) return; - } - priv->ipv6ll_has = FALSE; - memset(&priv->ipv6ll_addr, 0, sizeof(priv->ipv6ll_addr)); + if (!priv->l3cfg) { + _LOGD(LOGD_IP6, "linklocal6: no IP link for IPv6"); + goto out_fail; + } - memset(&lladdr, 0, sizeof(lladdr)); - lladdr.s6_addr16[0] = htons(0xfe80); + ifname = nm_device_get_ip_iface(self); + if (!ifname) { + _LOGD(LOGD_IP6, "linklocal6: no interface name for IPv6"); + goto out_fail; + } 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) { @@ -10332,73 +10276,31 @@ check_and_add_ipv6ll_addr(NMDevice *self) const char * stable_id; stable_id = _prop_get_connection_stable_id(self, connection, &stable_type); - if (!nm_utils_ipv6_addr_set_stable_privacy_may_fail(stable_type, - &lladdr, - nm_device_get_iface(self), - stable_id, - priv->linklocal6_dad_counter++, - &error)) { - _LOGW(LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message); - g_clear_error(&error); - linklocal6_failed(self); - return; - } - addr_type = "stable-privacy"; + priv->ipll_data_6.v6.ipv6ll = nm_l3_ipv6ll_new_stable_privacy(priv->l3cfg, + assume, + stable_type, + ifname, + stable_id, + _dev_ipll6_state_change_cb, + self); } else { NMUtilsIPv6IfaceId iid; - if (priv->linklocal6_timeout_id) { - /* We already started and attempt to add a LL address. For the EUI-64 - * mode we can't pick a new one, we'll just fail. */ - _LOGW(LOGD_IP6, "linklocal6: DAD failed for an EUI-64 address"); - linklocal6_failed(self); - return; - } - if (!nm_device_get_ip_iface_identifier(self, &iid, TRUE)) { _LOGW(LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue"); - return; + goto out_fail; } - nm_utils_ipv6_addr_set_interface_identifier(&lladdr, &iid); - addr_type = "EUI-64"; - } - - _LOGD(LOGD_IP6, - "linklocal6: generated %s IPv6LL address %s", - addr_type, - _nm_utils_inet6_ntop(&lladdr, sbuf)); - priv->ipv6ll_has = TRUE; - priv->ipv6ll_addr = lladdr; - ip_config_merge_and_apply(self, AF_INET6, TRUE); -} - -static gboolean -linklocal6_start(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - nm_clear_g_source(&priv->linklocal6_timeout_id); - - if (priv->ext_ip6_config_captured - && nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) - return TRUE; - - _LOGD(LOGD_DEVICE, - "linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses " - "configured. Wait.", - nm_device_get_effective_ip_config_method(self, AF_INET6)); + priv->ipll_data_6.v6.ipv6ll = + nm_l3_ipv6ll_new_token(priv->l3cfg, assume, &iid, _dev_ipll6_state_change_cb, self); + } - check_and_add_ipv6ll_addr(self); + llstate = nm_l3_ipv6ll_get_state(priv->ipll_data_6.v6.ipv6ll, &lladdr); + _dev_ipll6_set_llstate(self, llstate, lladdr); + return; - /* Depending on the network and what the 'dad_transmits' and 'retrans_time_ms' - * sysctl values are, DAD for the IPv6LL address may take quite a while. - * FIXME: use dad/retrans sysctl values if they are higher than a minimum time. - * (rh #1101809) - */ - priv->linklocal6_timeout_id = g_timeout_add_seconds(15, linklocal6_timeout_cb, self); - return FALSE; +out_fail: + _dev_ipll6_set_llstate(self, NM_L3_IPV6LL_STATE_DEFUNCT, NULL); } /*****************************************************************************/ @@ -10561,13 +10463,16 @@ set_platform_mtu(NMDevice *self, guint32 mtu) } static void -_commit_mtu(NMDevice *self, const NMIP4Config *config) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMDeviceMtuSource source = NM_DEVICE_MTU_SOURCE_NONE; - guint32 ip6_mtu, ip6_mtu_orig; - guint32 mtu_desired, mtu_desired_orig; - guint32 mtu_plat; +_commit_mtu(NMDevice *self) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NMDeviceMtuSource source = NM_DEVICE_MTU_SOURCE_NONE; + const NML3ConfigData *l3cd; + guint32 ip6_mtu_orig; + guint32 ip6_mtu; + guint32 mtu_desired_orig; + guint32 mtu_desired; + guint32 mtu_plat; struct { gboolean initialized; guint32 value; @@ -10575,7 +10480,9 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config) 0, }; int ifindex; - char sbuf[64], sbuf1[64], sbuf2[64]; + char sbuf[64]; + char sbuf1[64]; + char sbuf2[64]; gboolean success = TRUE; ifindex = nm_device_get_ip_ifindex(self); @@ -10589,8 +10496,11 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config) return; } + l3cd = nm_l3cfg_get_combined_l3cd(priv->l3cfg, FALSE); + { - guint32 mtu = 0; + guint32 mtu = 0; + guint32 mtu2; gboolean force = FALSE; /* We take the MTU from various sources: (in order of increasing @@ -10617,9 +10527,9 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config) if (NM_DEVICE_GET_CLASS(self)->get_configured_mtu) mtu = NM_DEVICE_GET_CLASS(self)->get_configured_mtu(self, &source, &force); - if (config && !force && source < NM_DEVICE_MTU_SOURCE_IP_CONFIG - && nm_ip4_config_get_mtu(config)) { - mtu = nm_ip4_config_get_mtu(config); + if (l3cd && !force && source < NM_DEVICE_MTU_SOURCE_IP_CONFIG + && (mtu2 = nm_l3_config_data_get_mtu(l3cd)) > 0) { + mtu = mtu2; source = NM_DEVICE_MTU_SOURCE_IP_CONFIG; } @@ -10808,239 +10718,181 @@ nm_device_commit_mtu(NMDevice *self) state = nm_device_get_state(self); if (state >= NM_DEVICE_STATE_CONFIG && state < NM_DEVICE_STATE_DEACTIVATING) { _LOGT(LOGD_DEVICE, "mtu: commit-mtu..."); - _commit_mtu(self, NM_DEVICE_GET_PRIVATE(self)->ip_config_4); + _commit_mtu(self); } else _LOGT(LOGD_DEVICE, "mtu: commit-mtu... skip due to state %s", nm_device_state_to_string(state)); } +/*****************************************************************************/ + static void -ndisc_config_changed(NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, NMDevice *self) +_dev_ipac6_ndisc_set_router_config(NMDevice *self) { - NMNDiscConfigMap changed = changed_int; - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - guint i; - - g_return_if_fail(priv->act_request.obj); - - if (!applied_config_get_current(&priv->ac_ip6_config)) - applied_config_init_new(&priv->ac_ip6_config, self, AF_INET6); - - if (changed & NM_NDISC_CONFIG_ADDRESSES) { - guint32 ifa_flags; - - /* Check, whether kernel is recent enough to help user space handling RA. - * If it's not supported, we have no ipv6-privacy and must add autoconf - * addresses as /128. The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. */ - ifa_flags = IFA_F_NOPREFIXROUTE; - if (NM_IN_SET(priv->ndisc_use_tempaddr, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) - ifa_flags |= IFA_F_MANAGETEMPADDR; - - nm_ip6_config_reset_addresses_ndisc((NMIP6Config *) priv->ac_ip6_config.orig, - rdata->addresses, - rdata->addresses_n, - 64, - ifa_flags); - if (priv->ac_ip6_config.current) { - nm_ip6_config_reset_addresses_ndisc((NMIP6Config *) priv->ac_ip6_config.current, - rdata->addresses, - rdata->addresses_n, - 64, - ifa_flags); - } - } + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + const NML3ConfigData *l3cd; - if (NM_FLAGS_ANY(changed, NM_NDISC_CONFIG_ROUTES | NM_NDISC_CONFIG_GATEWAYS)) { - nm_ip6_config_reset_routes_ndisc((NMIP6Config *) priv->ac_ip6_config.orig, - rdata->gateways, - rdata->gateways_n, - rdata->routes, - rdata->routes_n, - nm_device_get_route_table(self, AF_INET6), - nm_device_get_route_metric(self, AF_INET6)); - if (priv->ac_ip6_config.current) { - nm_ip6_config_reset_routes_ndisc((NMIP6Config *) priv->ac_ip6_config.current, - rdata->gateways, - rdata->gateways_n, - rdata->routes, - rdata->routes_n, - nm_device_get_route_table(self, AF_INET6), - nm_device_get_route_metric(self, AF_INET6)); - } - } + if (!priv->ipac6_data.ndisc) + return; - if (changed & NM_NDISC_CONFIG_DNS_SERVERS) { - /* Rebuild DNS server list from neighbor discovery cache. */ - applied_config_reset_nameservers(&priv->ac_ip6_config); + if (nm_ndisc_get_node_type(priv->ipac6_data.ndisc) != NM_NDISC_NODE_TYPE_ROUTER) + return; - for (i = 0; i < rdata->dns_servers_n; i++) - applied_config_add_nameserver(&priv->ac_ip6_config, - (const NMIPAddr *) &rdata->dns_servers[i].address); - } + /* FIXME(l3cfg): this doesn't seem right. What is the meaning of the l3cd at this + * point? Also, when do we need to reset the config (and call this function again?). */ + l3cd = nm_l3cfg_get_combined_l3cd(priv->l3cfg, FALSE); + if (l3cd) + nm_ndisc_set_config(priv->ipac6_data.ndisc, l3cd); +} - if (changed & NM_NDISC_CONFIG_DNS_DOMAINS) { - /* Rebuild domain list from neighbor discovery cache. */ - applied_config_reset_searches(&priv->ac_ip6_config); +static void +_dev_ipac6_set_state(NMDevice *self, NMDeviceIPState state) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - for (i = 0; i < rdata->dns_domains_n; i++) - applied_config_add_search(&priv->ac_ip6_config, rdata->dns_domains[i].domain); + if (priv->ipac6_data.state != state) { + _LOGD_ipac6("set state: %s (was %s)", + nm_device_ip_state_to_string(state), + nm_device_ip_state_to_string(priv->ipac6_data.state)); + priv->ipac6_data.state = state; } +} - if (changed & NM_NDISC_CONFIG_DHCP_LEVEL) { - dhcp6_cleanup(self, CLEANUP_TYPE_DECONFIGURE, TRUE); - - priv->dhcp6.mode = rdata->dhcp_level; - if (priv->dhcp6.mode != NM_NDISC_DHCP_LEVEL_NONE) { - _LOGD(LOGD_DEVICE | LOGD_DHCP6, - "Activation: Stage 3 of 5 (IP Configure Start) starting DHCPv6" - " as requested by IPv6 router..."); - if (!dhcp6_start(self, FALSE)) { - if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED) { - nm_device_state_changed(self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_DHCP_START_FAILED); - return; - } - } - } - } +static void +_dev_ipac6_ndisc_config_changed(NMNDisc * ndisc, + const NMNDiscData * rdata, + guint changed_i, + const NML3ConfigData *l3cd, + NMDevice * self) +{ + _dev_ipac6_grace_period_start(self, 0, TRUE); - if (changed & NM_NDISC_CONFIG_HOP_LIMIT) - nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe(nm_device_get_platform(self), - nm_device_get_ip_iface(self), - rdata->hop_limit); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_AC_6, l3cd, FALSE); - if (changed & NM_NDISC_CONFIG_REACHABLE_TIME) { - nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time(nm_device_get_platform(self), - nm_device_get_ip_iface(self), - rdata->reachable_time_ms); - } + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); - if (changed & NM_NDISC_CONFIG_RETRANS_TIMER) { - nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time(nm_device_get_platform(self), - nm_device_get_ip_iface(self), - rdata->retrans_timer_ms); - } + _dev_ipdhcp6_set_dhcp_level(self, rdata->dhcp_level); - if (changed & NM_NDISC_CONFIG_MTU) { - if (priv->ip6_mtu != rdata->mtu) { - _LOGD(LOGD_DEVICE, "mtu: set IPv6 MTU to %u", (guint) rdata->mtu); - priv->ip6_mtu = rdata->mtu; - } - } + _dev_l3_cfg_commit(self, FALSE); - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); + _dev_ip_state_check_async(self, AF_INET6); } static void -ndisc_ra_timeout(NMNDisc *ndisc, NMDevice *self) +_dev_ipac6_handle_timeout(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - /* We don't want to stop listening for router advertisements completely, - * but instead let device activation continue activating. If an RA - * shows up later, we'll use it as long as the device is not disconnected. - */ + _LOGD_ipac6("timeout for autoconf (IPv6 router advertisement) reached"); - _LOGD(LOGD_IP6, "timed out waiting for IPv6 router advertisement"); - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) { - /* If RA is our only source of addressing information and we don't - * ever receive one, then time out IPv6. But if there is other - * IPv6 configuration, like manual IPv6 addresses or external IPv6 - * config, consider that sufficient for IPv6 success. - * - * FIXME: it doesn't seem correct to determine this based on which - * addresses we find inside priv->ip_config_6. - */ - if (priv->ip_config_6 - && nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ip_config_6), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - else - nm_device_activate_schedule_ip_config_timeout(self, AF_INET6); - } + nm_clear_g_source_inst(&priv->ipac6_data.ndisc_grace_source); + + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_FAILED); + + _dev_ip_state_check_async(self, AF_INET6); } static void -addrconf6_start_with_link_ready(NMDevice *self) +_dev_ipac6_ndisc_ra_timeout(NMNDisc *ndisc, NMDevice *self) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMUtilsIPv6IfaceId iid; + _dev_ipac6_handle_timeout(self); +} - g_assert(priv->ndisc); +static gboolean +_dev_ipac6_grace_period_expired(gpointer user_data) +{ + _dev_ipac6_handle_timeout(user_data); + return G_SOURCE_REMOVE; +} - if (nm_device_get_ip_iface_identifier(self, &iid, FALSE)) { - _LOGD(LOGD_IP6, "addrconf6: using the device EUI-64 identifier"); - nm_ndisc_set_iid(priv->ndisc, iid); - } else { - /* Don't abort the addrconf at this point -- if ndisc needs the iid - * it will notice this itself. */ - _LOGI(LOGD_IP6, "addrconf6: no interface identifier; IPv6 address creation may fail"); +static gboolean +_dev_ipac6_grace_period_start(NMDevice *self, guint32 timeout_sec, gboolean force_restart) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean stopped; + + /* In any other case (expired lease, assumed connection, etc.), + * wait for some time before failing the IP method. + */ + if (!force_restart && priv->ipac6_data.ndisc_grace_source) { + /* already pending. */ + return FALSE; } - /* Apply any manual configuration before starting RA */ - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "failed to apply manual IPv6 configuration"); + /* Start a grace period equal to the RA timeout multiplied + * by a constant factor. */ + + stopped = nm_clear_g_source_inst(&priv->ipac6_data.ndisc_grace_source); - if (nm_ndisc_get_node_type(priv->ndisc) == NM_NDISC_NODE_TYPE_ROUTER) { - nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "1"); - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - priv->needs_ip6_subnet = TRUE; - g_signal_emit(self, signals[IP6_SUBNET_NEEDED], 0); + if (timeout_sec == 0) { + if (stopped) + _LOGD_ipac6("grace period stopped"); + return FALSE; } - priv->ndisc_changed_id = g_signal_connect(priv->ndisc, - NM_NDISC_CONFIG_RECEIVED, - G_CALLBACK(ndisc_config_changed), - self); - priv->ndisc_timeout_id = g_signal_connect(priv->ndisc, - NM_NDISC_RA_TIMEOUT_SIGNAL, - G_CALLBACK(ndisc_ra_timeout), - self); + nm_assert(timeout_sec <= G_MAXINT32); - ndisc_set_router_config(priv->ndisc, self); - nm_ndisc_start(priv->ndisc); - priv->ndisc_started = TRUE; - return; + if (timeout_sec >= G_MAXUINT / (GRACE_PERIOD_MULTIPLIER * 1000u)) + timeout_sec = NM_RA_TIMEOUT_INFINITY; + + if (timeout_sec == NM_RA_TIMEOUT_INFINITY) { + _LOGD_ipac6("grace period starts with infinity timeout"); + priv->ipac6_data.ndisc_grace_source = g_source_ref(nm_g_source_sentinel_get(0)); + } else { + _LOGD_ipac6("grace period starts with %u seconds", timeout_sec); + priv->ipac6_data.ndisc_grace_source = + nm_g_timeout_add_source(timeout_sec * (GRACE_PERIOD_MULTIPLIER * 1000u), + _dev_ipac6_grace_period_expired, + self); + } + + return TRUE; } -static gboolean -addrconf6_start(NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) +static void +_dev_ipac6_start(NMDevice *self) { NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); NMConnection * connection; - NMSettingIP6Config *s_ip6 = NULL; - GError * error = NULL; + NMSettingIP6Config *s_ip = NULL; + NMNDiscNodeType node_type; NMUtilsStableType stable_type; const char * stable_id; - NMNDiscNodeType node_type; int max_addresses; int router_solicitations; int router_solicitation_interval; guint32 ra_timeout; guint32 default_ra_timeout; + NMUtilsIPv6IfaceId iid; - if (!g_file_test("/proc/sys/net/ipv6", G_FILE_TEST_IS_DIR)) { - _LOGI(LOGD_IP6, "addrconf6: kernel does not support IPv6"); - return FALSE; + if (priv->ipac6_data.state == NM_DEVICE_IP_STATE_NONE) { + if (!g_file_test("/proc/sys/net/ipv6", G_FILE_TEST_IS_DIR)) { + _LOGI_ipac6("addrconf6: kernel does not support IPv6"); + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_INET6); + return; + } + + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_PENDING); } - connection = nm_device_get_applied_connection(self); - g_assert(connection); + if (NM_IN_SET(priv->ipll_data_6.state, NM_DEVICE_IP_STATE_NONE, NM_DEVICE_IP_STATE_PENDING)) { + _dev_ipac6_grace_period_start(self, 30, TRUE); + _dev_ipll6_start(self); + return; + } - nm_assert(!applied_config_get_current(&priv->ac_ip6_config)); - applied_config_clear(&priv->ac_ip6_config); + if (priv->ipac6_data.ndisc) { + /* we already started. Nothing to do. */ + return; + } - nm_clear_pointer(&priv->rt6_temporary_not_available, g_hash_table_unref); - nm_clear_g_source(&priv->rt6_temporary_not_available_id); + connection = nm_device_get_applied_connection(self); + if (connection) + s_ip = NM_SETTING_IP6_CONFIG(nm_connection_get_setting_ip6_config(connection)); - s_ip6 = NM_SETTING_IP6_CONFIG(nm_connection_get_setting_ip6_config(connection)); - g_assert(s_ip6); + g_return_if_fail(s_ip); if (nm_streq(nm_device_get_effective_ip_config_method(self, AF_INET6), NM_SETTING_IP6_CONFIG_METHOD_SHARED)) @@ -11063,60 +10915,95 @@ addrconf6_start(NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) ra_timeout = default_ra_timeout; } - stable_id = _prop_get_connection_stable_id(self, connection, &stable_type); - priv->ndisc = nm_lndp_ndisc_new(nm_device_get_platform(self), - nm_device_get_ip_ifindex(self), - nm_device_get_ip_iface(self), - stable_type, - stable_id, - nm_setting_ip6_config_get_addr_gen_mode(s_ip6), - node_type, - max_addresses, - router_solicitations, - router_solicitation_interval, - ra_timeout, - &error); - if (!priv->ndisc) { - _LOGE(LOGD_IP6, "addrconf6: failed to start neighbor discovery: %s", error->message); - g_error_free(error); - return FALSE; + stable_id = _prop_get_connection_stable_id(self, connection, &stable_type); + + { + const NMNDiscConfig config = { + .l3cfg = nm_device_get_l3cfg(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), + .node_type = node_type, + .max_addresses = max_addresses, + .router_solicitations = router_solicitations, + .router_solicitation_interval = router_solicitation_interval, + .ra_timeout = ra_timeout, + .ip6_privacy = _prop_get_ipv6_ip6_privacy(self), + }; + + priv->ipac6_data.ndisc = nm_lndp_ndisc_new(&config); + + priv->ipac6_data.ndisc_changed_id = + g_signal_connect(priv->ipac6_data.ndisc, + NM_NDISC_CONFIG_RECEIVED, + G_CALLBACK(_dev_ipac6_ndisc_config_changed), + self); + priv->ipac6_data.ndisc_timeout_id = + g_signal_connect(priv->ipac6_data.ndisc, + NM_NDISC_RA_TIMEOUT_SIGNAL, + G_CALLBACK(_dev_ipac6_ndisc_ra_timeout), + self); } - priv->ndisc_use_tempaddr = use_tempaddr; + if (nm_device_get_ip_iface_identifier(self, &iid, FALSE)) { + _LOGD_ipac6("using the device EUI-64 identifier"); + nm_ndisc_set_iid(priv->ipac6_data.ndisc, iid); + } else { + /* Don't abort the addrconf at this point -- if ndisc needs the iid + * it will notice this itself. */ + _LOGD_ipac6("no interface identifier; IPv6 address creation may fail"); + } - /* ensure link local is ready... */ - if (!linklocal6_start(self)) { - /* wait for the LL address to show up */ - return TRUE; + if (nm_ndisc_get_node_type(priv->ipac6_data.ndisc) == NM_NDISC_NODE_TYPE_ROUTER) { + nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "1"); + priv->needs_ip6_subnet = TRUE; + g_signal_emit(self, signals[IP6_SUBNET_NEEDED], 0); } - /* already have the LL address; kick off neighbor discovery */ - addrconf6_start_with_link_ready(self); - return TRUE; + _dev_ipac6_ndisc_set_router_config(self); + + if (node_type == NM_NDISC_NODE_TYPE_ROUTER) + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); + else + _dev_ipac6_grace_period_start(self, ra_timeout, TRUE); + + nm_ndisc_start(priv->ipac6_data.ndisc); } static void -addrconf6_cleanup(NMDevice *self) +_dev_ipac6_start_continue(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - priv->ndisc_started = FALSE; - nm_clear_g_signal_handler(priv->ndisc, &priv->ndisc_changed_id); - nm_clear_g_signal_handler(priv->ndisc, &priv->ndisc_timeout_id); + if (priv->ipac6_data.state != NM_DEVICE_IP_STATE_NONE) + _dev_ipac6_start(self); +} + +static void +_dev_ipac6_cleanup(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - applied_config_clear(&priv->ac_ip6_config); - nm_clear_pointer(&priv->rt6_temporary_not_available, g_hash_table_unref); - nm_clear_g_source(&priv->rt6_temporary_not_available_id); - if (priv->ndisc) { - nm_ndisc_stop(priv->ndisc); - g_clear_object(&priv->ndisc); + nm_clear_g_source_inst(&priv->ipac6_data.ndisc_grace_source); + + nm_clear_g_signal_handler(priv->ipac6_data.ndisc, &priv->ipac6_data.ndisc_changed_id); + nm_clear_g_signal_handler(priv->ipac6_data.ndisc, &priv->ipac6_data.ndisc_timeout_id); + + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_AC_6, NULL, FALSE); + + if (priv->ipac6_data.ndisc) { + nm_ndisc_stop(priv->ipac6_data.ndisc); + g_clear_object(&priv->ipac6_data.ndisc); } + + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_NONE); } /*****************************************************************************/ static void -save_ip6_properties(NMDevice *self) +_dev_sysctl_save_ip6_properties(NMDevice *self) { static const char *const ip6_properties_to_save[] = { "accept_ra", @@ -11149,61 +11036,80 @@ save_ip6_properties(NMDevice *self) } static void -restore_ip6_properties(NMDevice *self) +_dev_sysctl_restore_ip6_properties(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); GHashTableIter iter; - gpointer key, value; + gpointer key; + gpointer value; g_hash_table_iter_init(&iter, priv->ip6_saved_properties); - while (g_hash_table_iter_next(&iter, &key, &value)) { - /* Don't touch "disable_ipv6" if we're doing userland IPv6LL */ - if (priv->ipv6ll_handle && nm_streq(key, "disable_ipv6")) - continue; + while (g_hash_table_iter_next(&iter, &key, &value)) nm_device_sysctl_ip_conf_set(self, AF_INET6, key, value); - } } static void -set_disable_ipv6(NMDevice *self, const char *value) +_dev_sysctl_set_disable_ipv6(NMDevice *self, gboolean do_disable) { - /* We only touch disable_ipv6 when NM is not managing the IPv6LL address */ - if (!NM_DEVICE_GET_PRIVATE(self)->ipv6ll_handle) - nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", value); + nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", do_disable ? "1" : "0"); } +/*****************************************************************************/ + static void -set_nm_ipv6ll(NMDevice *self, gboolean enable) +_dev_addrgenmode6_set(NMDevice *self, guint8 addr_gen_mode) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - int ifindex = nm_device_get_ip_ifindex(self); + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + int ifindex = nm_device_get_ip_ifindex(self); + const NMPlatformLink *plink; + int r; + int cur_addr_gen_mode; + char sbuf[100]; - priv->ipv6ll_handle = enable; - if (ifindex > 0) { - int r; - - _LOGD(LOGD_IP6, "will %s userland IPv6LL", enable ? "enable" : "disable"); - r = nm_platform_link_set_inet6_addr_gen_mode(nm_device_get_platform(self), - ifindex, - enable ? NM_IN6_ADDR_GEN_MODE_NONE - : NM_IN6_ADDR_GEN_MODE_EUI64); - if (r < 0) { - _NMLOG(NM_IN_SET(r, -NME_PL_NOT_FOUND, -NME_PL_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN, - LOGD_IP6, - "failed to %s userspace IPv6LL address handling (%s)", - enable ? "enable" : "disable", - nm_strerror(r)); - } + if (ifindex <= 0) + return; - if (enable) { - gs_free char *value = NULL; + plink = nm_platform_link_get(nm_device_get_platform(self), ifindex); + if (!plink) + return; - /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ - value = nm_device_sysctl_ip_conf_get(self, AF_INET6, "disable_ipv6"); - if (nm_streq0(value, "0")) - nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); + cur_addr_gen_mode = _nm_platform_link_get_inet6_addr_gen_mode(plink); + nm_assert(cur_addr_gen_mode >= 0 && cur_addr_gen_mode <= 255); + + if (!priv->addrgenmode6_data.previous_mode_has) { + priv->addrgenmode6_data.previous_mode_has = TRUE; + priv->addrgenmode6_data.previous_mode_val = cur_addr_gen_mode; + nm_assert(priv->addrgenmode6_data.previous_mode_val == cur_addr_gen_mode); + } + + _LOGD_ip(AF_INET6, + "addrgenmode6: set %s%s", + nm_platform_link_inet6_addrgenmode2str(addr_gen_mode, sbuf, sizeof(sbuf)), + (cur_addr_gen_mode == addr_gen_mode) ? " (already set)" : ""); + + if (cur_addr_gen_mode == addr_gen_mode) + return; + + r = nm_platform_link_set_inet6_addr_gen_mode(nm_device_get_platform(self), + ifindex, + addr_gen_mode); + if (r < 0) { + _NMLOG_ip(NM_IN_SET(r, -NME_PL_NOT_FOUND, -NME_PL_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN, + AF_INET6, + "addrgenmode6: failed to set %s: (%s)", + nm_platform_link_inet6_addrgenmode2str(addr_gen_mode, sbuf, sizeof(sbuf)), + nm_strerror(r)); + } - /* Ensure IPv6 is enabled */ + if (addr_gen_mode == NM_IN6_ADDR_GEN_MODE_NONE) { + gs_free char *value = NULL; + + /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ + _LOGD_ip(AF_INET6, + "addrgenmode6: toggle disable_ipv6 sysctl after disabling addr-gen-mode"); + value = nm_device_sysctl_ip_conf_get(self, AF_INET6, "disable_ipv6"); + if (nm_streq0(value, "0")) { + nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "0"); } } @@ -11229,30 +11135,44 @@ ip_requires_slaves(NMDevice *self, int addr_family) NM_SETTING_IP6_CONFIG_METHOD_DHCP); } -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * self, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) +static void +activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family) { const int IS_IPv4 = NM_IS_IPv4(addr_family); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDeviceClass * klass = NM_DEVICE_GET_CLASS(self); NMConnection * connection; - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + int ip_ifindex; const char * method; - nm_assert_addr_family(addr_family); + if (nm_device_sys_iface_state_is_external(self)) + goto out; connection = nm_device_get_applied_connection(self); + g_return_if_fail(connection); - g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); + ip_ifindex = nm_device_get_ip_ifindex(self); + + if (ip_ifindex >= 0 && !nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex) + && !nm_device_sys_iface_state_is_external(self)) { + nm_platform_link_change_flags(nm_device_get_platform(self), ip_ifindex, IFF_UP, TRUE); + if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex)) + _LOGW(LOGD_DEVICE, + "interface %s not up for IP configuration", + nm_device_get_ip_iface(self)); + } if (connection_ip_method_requires_carrier(connection, addr_family, NULL) && nm_device_is_master(self) && !priv->carrier) { - _LOGI(LOGD_IP | LOGD_DEVICE, - "IPv%c config waiting until carrier is on", - nm_utils_addr_family_to_char(addr_family)); - return NM_ACT_STAGE_RETURN_IP_WAIT; + if (!priv->ip_data_x[IS_IPv4].wait_for_carrier) { + _LOGT_ip(addr_family, "waiting until carrier is on"); + priv->ip_data_x[IS_IPv4].wait_for_carrier = TRUE; + } + goto out; + } + if (priv->ip_data_x[IS_IPv4].wait_for_carrier) { + _LOGT_ip(addr_family, "waiting until carrier completed"); + priv->ip_data_x[IS_IPv4].wait_for_carrier = FALSE; } if (nm_device_is_master(self) && ip_requires_slaves(self, addr_family)) { @@ -11260,93 +11180,46 @@ act_stage3_ip_config_start(NMDevice * self, * a successful IP configuration attempt, then postpone IP addressing. */ if (!have_any_ready_slaves(self)) { - _LOGI(LOGD_DEVICE | LOGD_IP, - "IPv%c config waiting until slaves are ready", - nm_utils_addr_family_to_char(addr_family)); - return NM_ACT_STAGE_RETURN_IP_WAIT; + if (!priv->ip_data_x[IS_IPv4].wait_for_ports) { + _LOGT_ip(addr_family, "waiting for ports"); + priv->ip_data_x[IS_IPv4].wait_for_ports = TRUE; + } + goto out; } } - - if (!IS_IPv4) - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; + if (priv->ip_data_x[IS_IPv4].wait_for_ports) { + _LOGT_ip(addr_family, "waiting until ports completed"); + priv->ip_data_x[IS_IPv4].wait_for_ports = FALSE; + } method = nm_device_get_effective_ip_config_method(self, addr_family); - _LOGD(LOGD_IP | LOGD_DEVICE, - "IPv%c config method is %s", - nm_utils_addr_family_to_char(addr_family), - method); + _dev_ipmanual_start(self); if (IS_IPv4) { - if (NM_IN_STRSET(method, - NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { - NMSettingIPConfig *s_ip4; - NMIP4Config ** configs, *config; - guint num_addresses; - - s_ip4 = nm_connection_get_setting_ip4_config(connection); - g_return_val_if_fail(s_ip4, NM_ACT_STAGE_RETURN_FAILURE); - num_addresses = nm_setting_ip_config_get_num_addresses(s_ip4); - - if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - ret = dhcp4_start(self); - if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED); - return ret; - } - } else { - g_return_val_if_fail(num_addresses != 0, NM_ACT_STAGE_RETURN_FAILURE); - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } - - if (num_addresses) { - config = nm_device_ip4_config_new(self); - nm_ip4_config_merge_setting(config, - nm_connection_get_setting_ip4_config(connection), - NM_SETTING_CONNECTION_MDNS_DEFAULT, - NM_SETTING_CONNECTION_LLMNR_DEFAULT, - nm_device_get_route_table(self, AF_INET), - nm_device_get_route_metric(self, AF_INET)); - configs = g_new0(NMIP4Config *, 2); - configs[0] = config; - ipv4_dad_start(self, configs, ipv4_manual_method_apply); - } - } else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) { - ret = ipv4ll_start(self); - if (ret == NM_ACT_STAGE_RETURN_FAILURE) - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED); - } else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { - if (out_config) { - *out_config = shared4_new_config(self, connection); - if (*out_config) { - priv->dnsmasq_manager = nm_dnsmasq_manager_new(nm_device_get_ip_iface(self)); - ret = NM_ACT_STAGE_RETURN_SUCCESS; - } else { - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - ret = NM_ACT_STAGE_RETURN_FAILURE; - } - } else - g_return_val_if_reached(NM_ACT_STAGE_RETURN_FAILURE); - } else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - ret = NM_ACT_STAGE_RETURN_SUCCESS; - else - _LOGW(LOGD_IP4, "unhandled IPv4 config method '%s'; will fail", method); - - return ret; - } else { - NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; - const char * ip6_privacy_str = "0"; - NMPlatform * platform; - int ifindex; + 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)) + _dev_ipshared4_start(self); + else if (NM_IN_STRSET(method, + NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + /* pass */ + } else + nm_assert_not_reached(); + } + if (!IS_IPv4) { if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { - nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); - return NM_ACT_STAGE_RETURN_IP_DONE; - } - - if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { - if (!nm_device_sys_iface_state_is_external(self)) { + if (!priv->ip_data_x[IS_IPv4].is_disabled) { + priv->ip_data_x[IS_IPv4].is_disabled = TRUE; + nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); + } + } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + if (!priv->ip_data_x[IS_IPv4].is_ignore) { + priv->ip_data_x[IS_IPv4].is_ignore = TRUE; if (priv->master) { /* If a device only has an IPv6 link-local address, * we don't generate an assumed connection. Therefore, @@ -11356,165 +11229,62 @@ act_stage3_ip_config_start(NMDevice * self, * slave should not depend on the previous state. Flush * addresses and routes on activation. */ - ifindex = nm_device_get_ip_ifindex(self); - platform = nm_device_get_platform(self); - - if (ifindex > 0) { - gs_unref_object NMIP6Config *config = nm_device_ip6_config_new(self); - - nm_platform_ip_route_flush(platform, AF_INET6, ifindex); - nm_platform_ip_address_flush(platform, AF_INET6, ifindex); - nm_device_set_ip_config(self, AF_INET6, (NMIPConfig *) config, FALSE, NULL); + if (ip_ifindex > 0) { + nm_platform_ip_route_flush(nm_device_get_platform(self), + AF_INET6, + ip_ifindex); + nm_platform_ip_address_flush(nm_device_get_platform(self), + AF_INET6, + ip_ifindex); } } else { - gboolean ipv6ll_handle_old = priv->ipv6ll_handle; - /* When activating an IPv6 'ignore' connection we need to revert back * to kernel IPv6LL, but the kernel won't actually assign an address * to the interface until disable_ipv6 is bounced. */ - set_nm_ipv6ll(self, FALSE); - if (ipv6ll_handle_old) - nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); - restore_ip6_properties(self); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_EUI64); + _dev_sysctl_set_disable_ipv6(self, TRUE); + _dev_sysctl_restore_ip6_properties(self); } } - return NM_ACT_STAGE_RETURN_IP_DONE; - } - - /* Ensure the MTU makes sense. If it was below 1280 the kernel would not - * expose any ipv6 sysctls or allow presence of any addresses on the interface, - * including LL, which * would make it impossible to autoconfigure MTU to a - * correct value. */ - _commit_mtu(self, priv->ip_config_4); - - /* Any method past this point requires an IPv6LL address. Use NM-controlled - * IPv6LL if this is not an assumed connection, since assumed connections - * will already have IPv6 set up. - */ - if (!nm_device_sys_iface_state_is_external_or_assume(self)) - set_nm_ipv6ll(self, TRUE); - - /* Re-enable IPv6 on the interface */ - nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); - set_disable_ipv6(self, "0"); - - /* Synchronize external IPv6 configuration with kernel, since - * linklocal6_start() uses the information there to determine if we can - * proceed with the selected method (SLAAC, DHCP, link-local). - */ - nm_platform_process_events(nm_device_get_platform(self)); - g_clear_object(&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = - nm_ip6_config_capture(nm_device_get_multi_index(self), - nm_device_get_platform(self), - nm_device_get_ip_ifindex(self), - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - - ip6_privacy = _prop_get_ipv6_ip6_privacy(self); - - if (NM_IN_STRSET(method, - NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { - if (!addrconf6_start(self, ip6_privacy)) { - /* IPv6 might be disabled; allow IPv4 to proceed */ - ret = NM_ACT_STAGE_RETURN_IP_FAIL; - } else - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { - ret = - linklocal6_start(self) ? NM_ACT_STAGE_RETURN_SUCCESS : NM_ACT_STAGE_RETURN_POSTPONE; - } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_MANAGED; - if (!dhcp6_start(self, TRUE)) { - /* IPv6 might be disabled; allow IPv4 to proceed */ - ret = NM_ACT_STAGE_RETURN_IP_FAIL; + } else { + /* Ensure the MTU makes sense. If it was below 1280 the kernel would not + * expose any ipv6 sysctls or allow presence of any addresses on the interface, + * including LL, which * would make it impossible to autoconfigure MTU to a + * correct value. */ + _commit_mtu(self); + + /* Any method past this point requires an IPv6LL address. Use NM-controlled + * IPv6LL if this is not an assumed connection, since assumed connections + * will already have IPv6 set up. + */ + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); + + /* Re-enable IPv6 on the interface */ + nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); + _dev_sysctl_set_disable_ipv6(self, FALSE); + + if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) + _dev_ipll6_start(self); + else if (NM_IN_STRSET(method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) + _dev_ipac6_start(self); + else if (NM_IN_STRSET(method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) + _dev_ipshared6_start(self); + else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { + priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_MANAGED; + _dev_ipdhcpx_start(self, AF_INET6); + } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + /* pass */ } else - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) - ret = NM_ACT_STAGE_RETURN_SUCCESS; - else - _LOGW(LOGD_IP6, "unhandled IPv6 config method '%s'; will fail", method); - - if (ret != NM_ACT_STAGE_RETURN_FAILURE - && !nm_device_sys_iface_state_is_external_or_assume(self)) { - switch (ip6_privacy) { - case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN: - case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: - ip6_privacy_str = "0"; - break; - case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: - ip6_privacy_str = "1"; - break; - case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: - ip6_privacy_str = "2"; - break; - } - nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", ip6_privacy_str); + nm_assert_not_reached(); } - - return ret; } -} -gboolean -nm_device_activate_stage3_ip_start(NMDevice *self, int addr_family) -{ - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMActStageReturn ret; - NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - gs_unref_object NMIPConfig *ip_config = NULL; - - g_assert(priv->ip_state_x[IS_IPv4] == NM_DEVICE_IP_STATE_WAIT); + if (klass->act_stage3_ip_config) + klass->act_stage3_ip_config(self, addr_family); - if (nm_device_sys_iface_state_is_external(self)) { - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - return TRUE; - } - - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_CONF); - - ret = NM_DEVICE_GET_CLASS(self)->act_stage3_ip_config_start(self, - addr_family, - (gpointer *) &ip_config, - &failure_reason); - - switch (ret) { - case NM_ACT_STAGE_RETURN_SUCCESS: - if (!IS_IPv4) { - /* Here we get a static IPv6 config, like for Shared where it's - * autogenerated or from modems where it comes from ModemManager. - */ - if (!ip_config) - ip_config = nm_device_ip_config_new(self, addr_family); - nm_assert(!applied_config_get_current(&priv->ac_ip6_config)); - applied_config_init(&priv->ac_ip6_config, ip_config); - ip_config = NULL; - } - nm_device_activate_schedule_ip_config_result(self, addr_family, ip_config); - break; - case NM_ACT_STAGE_RETURN_IP_DONE: - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - break; - case NM_ACT_STAGE_RETURN_FAILURE: - nm_device_state_changed(self, NM_DEVICE_STATE_FAILED, failure_reason); - return FALSE; - case NM_ACT_STAGE_RETURN_IP_FAIL: - /* Activation not wanted */ - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_FAIL); - break; - case NM_ACT_STAGE_RETURN_IP_WAIT: - /* Wait for something to try IP config again */ - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_WAIT); - break; - default: - g_assert(ret == NM_ACT_STAGE_RETURN_POSTPONE); - } - - return TRUE; +out: + _dev_ip_state_check_async(self, addr_family); } static void @@ -11541,12 +11311,12 @@ fw_change_zone_cb(NMFirewalldManager * firewalld_manager, switch (priv->fw_state) { case FIREWALL_STATE_WAIT_STAGE_3: priv->fw_state = FIREWALL_STATE_INITIALIZED; - nm_device_activate_schedule_stage3_ip_config_start(self); + nm_device_activate_schedule_stage3_ip_config(self, TRUE); break; case FIREWALL_STATE_WAIT_IP_CONFIG: priv->fw_state = FIREWALL_STATE_INITIALIZED; - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE - || priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) + if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY + || priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY) nm_device_start_ip_check(self); break; case FIREWALL_STATE_INITIALIZED: @@ -11598,24 +11368,48 @@ fw_change_zone(NMDevice *self) self); } -/* - * activate_stage3_ip_config_start - * - * Begin automatic/manual IP configuration - * - */ static void -activate_stage3_ip_config_start(NMDevice *self) +activate_stage3_ip_config(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); int ifindex; + /* stage3 is different from stage1+2. + * + * What is true in all cases is that when we start a stage, we call the corresponding + * nm_device_activate_schedule_stage*() function. But usually the stage cannot complete + * right away but needs to wait for some things to happen. So the activate_stage*() function + * returns, and will be later proceeded by calling *the same* stage again. That means, + * activate_stage*() must be re-entrant and be called repeatedly until we can proceed + * to the next stage. Only when the stage is completed, we schedule the next one. + * + * stage3 is different. It does IP configuration and as such (the stage handling itself) + * cannot fail. If a failure happens (for example for DHCP), we remember that (in priv->ipdhcp_data_x) + * and issue _dev_ip_state_check_async(). That one combines the DHCP state to determine the + * overall per-address-family state (priv->ip_data_x). Those states are then combined + * further into priv->combinedip_state, which then leads to nm_device_state_changed() + * (which for example can make the device fully ACTIVATED or FAILED). + * + * The difference between stage1+2 and stage3 is that IP configuration is running continuously + * while the device is active. As such the activate_stage3_ip_config() does not fail directly, + * unlike the other stages which can abort via NM_ACT_STAGE_RETURN_FAILURE. */ + g_return_if_fail(priv->act_request.obj); ifindex = nm_device_get_ip_ifindex(self); + if (priv->ip_data_4.do_reapply) { + _LOGD_ip(AF_INET, "reapply..."); + _cleanup_ip_pre(self, AF_INET, CLEANUP_TYPE_DECONFIGURE); + } + if (priv->ip_data_6.do_reapply) { + _LOGD_ip(AF_INET6, "reapply..."); + _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE); + } + /* Add the interface to the specified firewall zone */ - if (priv->fw_state == FIREWALL_STATE_UNMANAGED) { + switch (priv->fw_state) { + case FIREWALL_STATE_UNMANAGED: if (nm_device_sys_iface_state_is_external(self)) { /* fake success */ priv->fw_state = FIREWALL_STATE_INITIALIZED; @@ -11625,143 +11419,140 @@ activate_stage3_ip_config_start(NMDevice *self) return; } /* no ifindex, nothing to do for now */ - } else if (priv->fw_state == FIREWALL_STATE_WAIT_STAGE_3) { + break; + case FIREWALL_STATE_WAIT_STAGE_3: /* a firewall call for stage3 is pending. Return and wait. */ return; + default: + nm_assert(NM_IN_SET((FirewallState) priv->fw_state, + FIREWALL_STATE_INITIALIZED, + FIREWALL_STATE_WAIT_IP_CONFIG)); + break; } - nm_assert(ifindex <= 0 || priv->fw_state == FIREWALL_STATE_INITIALIZED); - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_WAIT); - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_WAIT); - - _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_LAYER2_READY); - - nm_device_state_changed(self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); - - /* Device should be up before we can do anything with it */ - if (!nm_device_sys_iface_state_is_external(self) - && (ifindex = nm_device_get_ip_ifindex(self)) > 0 - && !nm_platform_link_is_up(nm_device_get_platform(self), ifindex)) - _LOGW(LOGD_DEVICE, - "interface %s not up for IP configuration", - nm_device_get_ip_iface(self)); + if (priv->state < NM_DEVICE_STATE_IP_CONFIG) { + _dev_ip_state_req_timeout_schedule(self, AF_INET); + _dev_ip_state_req_timeout_schedule(self, AF_INET6); - if (nm_device_activate_ip4_state_in_wait(self) - && !nm_device_activate_stage3_ip_start(self, AF_INET)) - return; + _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_LAYER2_READY); - if (nm_device_activate_ip6_state_in_wait(self) - && !nm_device_activate_stage3_ip_start(self, AF_INET6)) - return; + nm_device_state_changed(self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); - /* Proxy */ - nm_device_set_proxy_config(self, NULL); + /* Device should be up before we can do anything with it */ + if (!nm_device_sys_iface_state_is_external(self) && ifindex > 0 + && !nm_platform_link_is_up(nm_device_get_platform(self), ifindex)) + _LOGW(LOGD_DEVICE, + "interface %s not up for IP configuration", + nm_device_get_ip_iface(self)); + } - check_ip_state(self, TRUE, TRUE); + activate_stage3_ip_config_for_addr_family(self, AF_INET); + activate_stage3_ip_config_for_addr_family(self, AF_INET6); } -/* - * nm_device_activate_schedule_stage3_ip_config_start - * - * Schedule IP configuration start - */ void -nm_device_activate_schedule_stage3_ip_config_start(NMDevice *self) +nm_device_activate_schedule_stage3_ip_config(NMDevice *self, gboolean do_sync) { - NMDevicePrivate *priv; - - g_return_if_fail(NM_IS_DEVICE(self)); - - priv = NM_DEVICE_GET_PRIVATE(self); - - g_return_if_fail(priv->act_request.obj); - - activation_source_schedule(self, activate_stage3_ip_config_start, AF_INET); + activation_source_invoke_or_schedule(self, activate_stage3_ip_config, do_sync); } -static NMActStageReturn -act_stage4_ip_config_timeout(NMDevice * self, - int addr_family, - NMDeviceStateReason *out_failure_reason) +/*****************************************************************************/ + +static void +_dev_ipsharedx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state) { - nm_assert_addr_family(addr_family); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - if (!get_ip_config_may_fail(self, addr_family)) { - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return NM_ACT_STAGE_RETURN_FAILURE; + if (priv->ipshared_data_x[IS_IPv4].state != state) { + _LOGD_ipshared(addr_family, + "set state %s (was %s)", + nm_device_ip_state_to_string(state), + nm_device_ip_state_to_string(priv->ipshared_data_x[IS_IPv4].state)); + priv->ipshared_data_x[IS_IPv4].state = state; } - - return NM_ACT_STAGE_RETURN_SUCCESS; } static void -activate_stage4_ip_config_timeout_x(NMDevice *self, int addr_family) +_dev_ipsharedx_cleanup(NMDevice *self, int addr_family) { - NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - NMActStageReturn ret; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - ret = - NM_DEVICE_GET_CLASS(self)->act_stage4_ip_config_timeout(self, addr_family, &failure_reason); + if (IS_IPv4) { + if (priv->ipshared_data_4.v4.dnsmasq_manager) { + nm_clear_g_signal_handler(priv->ipshared_data_4.v4.dnsmasq_manager, + &priv->ipshared_data_4.v4.dnsmasq_state_id); + nm_dnsmasq_manager_stop(priv->ipshared_data_4.v4.dnsmasq_manager); + g_clear_object(&priv->ipshared_data_4.v4.dnsmasq_manager); + } - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) - return; + if (priv->ipshared_data_4.v4.firewall_config) { + nm_firewall_config_apply(priv->ipshared_data_4.v4.firewall_config, FALSE); + nm_clear_pointer(&priv->ipshared_data_4.v4.firewall_config, nm_firewall_config_free); + } - if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - nm_device_state_changed(self, NM_DEVICE_STATE_FAILED, failure_reason); - return; + nm_clear_pointer(&priv->ipshared_data_4.v4.shared_ip_handle, nm_netns_shared_ip_release); } - g_assert(ret == NM_ACT_STAGE_RETURN_SUCCESS); - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_FAIL); - check_ip_state(self, FALSE, TRUE); + _dev_ipsharedx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); } -static void -activate_stage4_ip_config_timeout_4(NMDevice *self) -{ - activate_stage4_ip_config_timeout_x(self, AF_INET); -} +/*****************************************************************************/ -static void -activate_stage4_ip_config_timeout_6(NMDevice *self) +static const NML3ConfigData * +_dev_ipshared4_new_l3cd(NMDevice *self, NMConnection *connection, NMPlatformIP4Address *out_addr4) { - activate_stage4_ip_config_timeout_x(self, AF_INET6); -} + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + NMSettingIPConfig * s_ip4; + NMPlatformIP4Address address = { + .addr_source = NM_IP_CONFIG_SOURCE_SHARED, + }; -#define activate_stage4_ip_config_timeout_x_fcn(addr_family) \ - (NM_IS_IPv4(addr_family) ? activate_stage4_ip_config_timeout_4 \ - : activate_stage4_ip_config_timeout_6) + g_return_val_if_fail(self, NULL); + g_return_val_if_fail(connection, NULL); -void -nm_device_activate_schedule_ip_config_timeout(NMDevice *self, int addr_family) -{ - NMDevicePrivate *priv; + s_ip4 = nm_connection_get_setting_ip4_config(connection); + if (s_ip4 && nm_setting_ip_config_get_num_addresses(s_ip4) > 0) { + /* Use the first user-supplied address */ + NMIPAddress *user = nm_setting_ip_config_get_address(s_ip4, 0); + in_addr_t a; - g_return_if_fail(NM_IS_DEVICE(self)); + nm_ip_address_get_address_binary(user, &a); + nm_platform_ip4_address_set_addr(&address, a, nm_ip_address_get_prefix(user)); + nm_clear_pointer(&priv->ipshared_data_4.v4.shared_ip_handle, nm_netns_shared_ip_release); + } else { + if (!priv->ipshared_data_4.v4.shared_ip_handle) + priv->ipshared_data_4.v4.shared_ip_handle = + nm_netns_shared_ip_reserve(nm_device_get_netns(self)); + nm_platform_ip4_address_set_addr(&address, + priv->ipshared_data_4.v4.shared_ip_handle->addr, + 24); + } - priv = NM_DEVICE_GET_PRIVATE(self); + l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED); + nm_l3_config_data_add_address_4(l3cd, &address); - g_return_if_fail(priv->act_request.obj); + NM_SET_OUT(out_addr4, address); - activation_source_schedule(self, - activate_stage4_ip_config_timeout_x_fcn(addr_family), - addr_family); + return nm_l3_config_data_seal(g_steal_pointer(&l3cd)); } static gboolean -share_init(NMDevice *self, GError **error) -{ - const char *const modules[] = {"ip_tables", - "iptable_nat", - "nf_nat_ftp", - "nf_nat_irc", - "nf_nat_sip", - "nf_nat_tftp", - "nf_nat_pptp", - "nf_nat_h323"}; - guint i; - int errsv; +_dev_ipshared4_init(NMDevice *self) +{ + static const char *const modules[] = {"ip_tables", + "iptable_nat", + "nf_nat_ftp", + "nf_nat_irc", + "nf_nat_sip", + "nf_nat_tftp", + "nf_nat_pptp", + "nf_nat_h323"}; + int errsv; + guint i; if (nm_platform_sysctl_get_int32(nm_device_get_platform(self), NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"), @@ -11772,15 +11563,7 @@ share_init(NMDevice *self, GError **error) NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"), "1")) { errsv = errno; - _LOGD(LOGD_SHARING, - "share: error enabling IPv4 forwarding: (%d) %s", - errsv, - nm_strerror_native(errsv)); - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "cannot set ipv4/ip_forward: %s", - nm_strerror_native(errsv)); + _LOGW_ipshared(AF_INET, "error enabling IPv4 forwarding: %s", nm_strerror_native(errsv)); return FALSE; } @@ -11793,10 +11576,9 @@ share_init(NMDevice *self, GError **error) NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_dynaddr"), "1")) { errsv = errno; - _LOGD(LOGD_SHARING, - "share: error enabling dynamic addresses: (%d) %s", - errsv, - nm_strerror_native(errsv)); + _LOGD_ipshared(AF_INET, + "share: error enabling dynamic addresses: %s", + nm_strerror_native(errsv)); } for (i = 0; i < G_N_ELEMENTS(modules); i++) @@ -11805,48 +11587,59 @@ share_init(NMDevice *self, GError **error) return TRUE; } -static gboolean -start_sharing(NMDevice *self, NMIP4Config *config, GError **error) +static void +_dev_ipshared4_dnsmasq_state_changed_cb(NMDnsMasqManager *manager, guint status, gpointer user_data) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMActRequest * req; - const NMPlatformIP4Address *ip4_addr = NULL; - const char * ip_iface; - GError * local = NULL; - NMConnection * conn; - NMSettingConnection * s_con; - gboolean announce_android_metered; - NMFirewallConfig * firewall_config; + NMDevice *self = NM_DEVICE(user_data); - g_return_val_if_fail(config, FALSE); + if (status != NM_DNSMASQ_STATUS_DEAD) + return; + + _dev_ipsharedx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_INET); +} + +static void +_dev_ipshared4_start(NMDevice *self) +{ + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + NMPlatformIP4Address ip4_addr; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + const char * ip_iface; + gs_free_error GError *error = NULL; + NMSettingConnection * s_con; + gboolean announce_android_metered; + NMConnection * applied; + + if (priv->ipshared_data_4.state != NM_DEVICE_IP_STATE_NONE) + return; + + nm_assert(!priv->ipshared_data_4.v4.firewall_config); + nm_assert(!priv->ipshared_data_4.v4.dnsmasq_manager); + nm_assert(priv->ipshared_data_4.v4.dnsmasq_state_id == 0); ip_iface = nm_device_get_ip_iface(self); - if (!ip_iface) { - g_set_error(error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "device has no ip interface"); - return FALSE; - } + g_return_if_fail(ip_iface); - ip4_addr = nm_ip4_config_get_first_address(config); - if (!ip4_addr || !ip4_addr->address) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "could not determine IPv4 address"); - return FALSE; - } + applied = nm_device_get_applied_connection(self); + g_return_if_fail(applied); - if (!share_init(self, error)) - return FALSE; + _dev_ipsharedx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_PENDING); - req = nm_device_get_act_request(self); - g_return_val_if_fail(req, FALSE); + l3cd = _dev_ipshared4_new_l3cd(self, applied, &ip4_addr); + if (!l3cd) { + nm_assert_not_reached(); + goto out_fail; + } - firewall_config = nm_firewall_config_new(ip_iface, ip4_addr->address, ip4_addr->plen); + if (!_dev_ipshared4_init(self)) + goto out_fail; - nm_act_request_set_shared(req, firewall_config); + priv->ipshared_data_4.v4.firewall_config = + nm_firewall_config_new(ip_iface, ip4_addr.address, ip4_addr.plen); + nm_firewall_config_apply(priv->ipshared_data_4.v4.firewall_config, TRUE); - conn = nm_act_request_get_applied_connection(req); - s_con = nm_connection_get_setting_connection(conn); + s_con = nm_connection_get_setting_connection(applied); switch (nm_setting_connection_get_metered(s_con)) { case NM_METERED_YES: @@ -11868,353 +11661,55 @@ start_sharing(NMDevice *self, NMIP4Config *config, GError **error) break; } - if (!nm_dnsmasq_manager_start(priv->dnsmasq_manager, - config, + priv->ipshared_data_4.v4.dnsmasq_manager = nm_dnsmasq_manager_new(ip_iface); + + if (!nm_dnsmasq_manager_start(priv->ipshared_data_4.v4.dnsmasq_manager, + l3cd, announce_android_metered, - &local)) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "could not start dnsmasq due to %s", - local->message); - g_error_free(local); - nm_act_request_set_shared(req, NULL); - return FALSE; + &error)) { + _LOGW_ipshared(AF_INET, "could not start dnsmasq: %s", error->message); + goto out_fail; } - priv->dnsmasq_state_id = g_signal_connect(priv->dnsmasq_manager, - NM_DNS_MASQ_MANAGER_STATE_CHANGED, - G_CALLBACK(dnsmasq_state_changed_cb), - self); - return TRUE; -} + priv->ipshared_data_4.v4.dnsmasq_state_id = + g_signal_connect(priv->ipshared_data_4.v4.dnsmasq_manager, + NM_DNS_MASQ_MANAGER_STATE_CHANGED, + G_CALLBACK(_dev_ipshared4_dnsmasq_state_changed_cb), + self); -static void -arp_cleanup(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_SHARED_4, l3cd, FALSE); + _dev_ip_state_check_async(self, AF_INET); + return; - nm_clear_pointer(&priv->acd.announcing, nm_acd_manager_free); +out_fail: + _dev_ipsharedx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_INET); } -void -nm_device_arp_announce(NMDevice *self) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMConnection * connection; - NMSettingIPConfig *s_ip4; - guint num, i; - const guint8 * hw_addr; - size_t hw_addr_len = 0; - - arp_cleanup(self); - - hw_addr = nm_platform_link_get_address(nm_device_get_platform(self), - nm_device_get_ip_ifindex(self), - &hw_addr_len); - - if (!hw_addr || hw_addr_len != ETH_ALEN) - return; - - /* We only care about manually-configured addresses; DHCP- and autoip-configured - * ones should already have been seen on the network at this point. - */ - connection = nm_device_get_applied_connection(self); - if (!connection) - return; - s_ip4 = nm_connection_get_setting_ip4_config(connection); - if (!s_ip4) - return; - num = nm_setting_ip_config_get_num_addresses(s_ip4); - if (num == 0) - return; - - priv->acd.announcing = - nm_acd_manager_new(nm_device_get_ip_ifindex(self), hw_addr, hw_addr_len, NULL, NULL); - - for (i = 0; i < num; i++) { - NMIPAddress *ip = nm_setting_ip_config_get_address(s_ip4, i); - in_addr_t addr; - - if (inet_pton(AF_INET, nm_ip_address_get_address(ip), &addr) == 1) - nm_acd_manager_add_address(priv->acd.announcing, addr); - else - g_warn_if_reached(); - } - - nm_acd_manager_announce_addresses(priv->acd.announcing); -} +/*****************************************************************************/ static void -activate_stage5_ip_config_result_x(NMDevice *self, int addr_family) +_dev_ipshared6_start(NMDevice *self) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMActRequest * req; - const char * method; - int ip_ifindex; - int errsv; - - req = nm_device_get_act_request(self); - g_assert(req); - - nm_clear_g_source_inst(&priv->ip_req_timeout_source_x[IS_IPv4]); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - /* Interface must be IFF_UP before IP config can be applied */ - ip_ifindex = nm_device_get_ip_ifindex(self); - g_return_if_fail(ip_ifindex); + _dev_ipac6_start(self); - if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex) - && !nm_device_sys_iface_state_is_external(self)) { - nm_platform_link_change_flags(nm_device_get_platform(self), ip_ifindex, IFF_UP, TRUE); - if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex)) - _LOGW(LOGD_DEVICE, - "interface %s not up for IP configuration", - nm_device_get_ip_iface(self)); - } - - if (!ip_config_merge_and_apply(self, addr_family, TRUE)) { - _LOGD(LOGD_DEVICE | LOGD_IPX(IS_IPv4), - "Activation: Stage 5 of 5 (IPv%c Commit) failed", - nm_utils_addr_family_to_char(addr_family)); - nm_device_ip_method_failed(self, addr_family, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + if (priv->ipshared_data_6.state != NM_DEVICE_IP_STATE_NONE) return; - } - - if (!IS_IPv4) { - if (priv->dhcp6.mode != NM_NDISC_DHCP_LEVEL_NONE - && priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) { - if (applied_config_get_current(&priv->dhcp6.ip6_config)) { - /* If IPv6 wasn't the first IP to complete, and DHCP was used, - * then ensure dispatcher scripts get the DHCP lease information. - */ - nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_6, - self, - NULL, - NULL, - NULL, - NULL); - } else { - /* still waiting for first dhcp6 lease. */ - return; - } - } - } - - /* Start IPv4 sharing/IPv6 forwarding if we need it */ - method = nm_device_get_effective_ip_config_method(self, addr_family); - if (IS_IPv4) { - if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { - gs_free_error GError *error = NULL; - - if (!start_sharing(self, priv->ip_config_4, &error)) { - _LOGW(LOGD_SHARING, - "Activation: Stage 5 of 5 (IPv4 Commit) start sharing failed: %s", - error->message); - nm_device_ip_method_failed(self, - AF_INET, - NM_DEVICE_STATE_REASON_SHARED_START_FAILED); - return; - } - } - } else { - if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { - if (!nm_platform_sysctl_set( - nm_device_get_platform(self), - NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv6/conf/all/forwarding"), - "1")) { - errsv = errno; - _LOGE(LOGD_SHARING, - "share: error enabling IPv6 forwarding: (%d) %s", - errsv, - nm_strerror_native(errsv)); - nm_device_ip_method_failed(self, - AF_INET6, - NM_DEVICE_STATE_REASON_SHARED_START_FAILED); - return; - } - } - } - - if (IS_IPv4) { - if (priv->dhcp_data_4.client) { - gs_free_error GError *error = NULL; - - if (!nm_dhcp_client_accept(priv->dhcp_data_4.client, &error)) { - _LOGW(LOGD_DHCP4, - "Activation: Stage 5 of 5 (IPv4 Commit) error accepting lease: %s", - error->message); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_DHCP_ERROR); - return; - } - } - - /* If IPv4 wasn't the first to complete, and DHCP was used, then ensure - * dispatcher scripts get the DHCP lease information. - */ - if (priv->dhcp_data_4.client && nm_device_activate_ip4_state_in_conf(self) - && (nm_device_get_state(self) > NM_DEVICE_STATE_IP_CONFIG)) { - nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_4, - self, - NULL, - NULL, - NULL, - NULL); - } - } - - if (!IS_IPv4) { - /* Check if we have to wait for DAD */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF && !priv->dad6_ip6_config) { - if (!priv->carrier && priv->ignore_carrier && get_ip_config_may_fail(self, AF_INET6)) - _LOGI(LOGD_DEVICE | LOGD_IP6, - "IPv6 DAD: carrier missing and ignored, not delaying activation"); - else - priv->dad6_ip6_config = dad6_get_pending_addresses(self); - - if (priv->dad6_ip6_config) { - _LOGD(LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: awaiting termination"); - } else { - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - } - } - } - - if (IS_IPv4 && priv->carrier) { - /* We send ARP announcements only when the link gets carrier, - * otherwise the announcements would be lost. Furthermore, for - * controllers having carrier implies that there is at least one - * port and therefore the MAC address is the correct one. - */ - nm_device_arp_announce(self); - } - - if (IS_IPv4) { - /* Enter the IP_CHECK state if this is the first method to complete */ - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - } -} - -static void -activate_stage5_ip_config_result_4(NMDevice *self) -{ - activate_stage5_ip_config_result_x(self, AF_INET); -} - -static void -activate_stage5_ip_config_result_6(NMDevice *self) -{ - activate_stage5_ip_config_result_x(self, AF_INET6); -} - -#define activate_stage5_ip_config_result_x_fcn(addr_family) \ - (NM_IS_IPv4(addr_family) ? activate_stage5_ip_config_result_4 \ - : activate_stage5_ip_config_result_6) - -void -nm_device_activate_schedule_ip_config_result(NMDevice *self, int addr_family, NMIPConfig *config) -{ - NMDevicePrivate *priv; - const int IS_IPv4 = NM_IS_IPv4(addr_family); - - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(!config || (IS_IPv4 && nm_ip_config_get_addr_family(config) == AF_INET)); - - priv = NM_DEVICE_GET_PRIVATE(self); - - if (IS_IPv4) { - applied_config_init(&priv->dev_ip_config_4, config); - } else { - /* If IP had previously failed, move it back to NM_DEVICE_IP_STATE_CONF since we - * clearly now have configuration. - */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL) - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_CONF); - } - - activation_source_schedule(self, - activate_stage5_ip_config_result_x_fcn(addr_family), - addr_family); -} - -NMDeviceIPState -nm_device_activate_get_ip_state(NMDevice *self, int addr_family) -{ - const int IS_IPv4 = NM_IS_IPv4(addr_family); - - g_return_val_if_fail(NM_IS_DEVICE(self), NM_DEVICE_IP_STATE_NONE); - g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), NM_DEVICE_IP_STATE_NONE); - - return NM_DEVICE_GET_PRIVATE(self)->ip_state_x[IS_IPv4]; -} - -static void -dad6_add_pending_address(NMDevice * self, - NMPlatform * platform, - int ifindex, - const struct in6_addr *address, - NMIP6Config ** dad6_config) -{ - const NMPlatformIP6Address *pl_addr; - - pl_addr = nm_platform_ip6_address_get(platform, ifindex, address); - if (pl_addr && NM_FLAGS_HAS(pl_addr->n_ifa_flags, IFA_F_TENTATIVE) - && !NM_FLAGS_HAS(pl_addr->n_ifa_flags, IFA_F_DADFAILED) - && !NM_FLAGS_HAS(pl_addr->n_ifa_flags, IFA_F_OPTIMISTIC)) { - _LOGt(LOGD_DEVICE, - "IPv6 DAD: pending address %s", - nm_platform_ip6_address_to_string(pl_addr, NULL, 0)); - - if (!*dad6_config) - *dad6_config = nm_device_ip6_config_new(self); - - nm_ip6_config_add_address(*dad6_config, pl_addr); - } -} - -/* - * Returns a NMIP6Config containing NM-configured addresses which - * have the tentative flag, or NULL if none is present. - */ -static NMIP6Config * -dad6_get_pending_addresses(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMIP6Config * confs[] = {(NMIP6Config *) applied_config_get_current(&priv->ac_ip6_config), - (NMIP6Config *) applied_config_get_current(&priv->dhcp6.ip6_config), - priv->con_ip_config_6, - (NMIP6Config *) applied_config_get_current(&priv->dev2_ip_config_6)}; - const NMPlatformIP6Address *addr; - NMIP6Config * dad6_config = NULL; - NMDedupMultiIter ipconf_iter; - guint i; - int ifindex; - NMPlatform * platform; - - ifindex = nm_device_get_ip_ifindex(self); - g_return_val_if_fail(ifindex > 0, NULL); - - platform = nm_device_get_platform(self); - - if (priv->ipv6ll_has) { - dad6_add_pending_address(self, platform, ifindex, &priv->ipv6ll_addr, &dad6_config); - } - /* We are interested only in addresses that we have explicitly configured, - * not in externally added ones. - */ - for (i = 0; i < G_N_ELEMENTS(confs); i++) { - if (!confs[i]) - continue; - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, confs[i], &addr) { - dad6_add_pending_address(self, platform, ifindex, &addr->address, &dad6_config); - } + if (!nm_platform_sysctl_set( + nm_device_get_platform(self), + NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv6/conf/all/forwarding"), + "1")) { + _LOGW_ipshared(AF_INET6, "failure to enable ipv6 forwarding"); + _dev_ipsharedx_set_state(self, AF_INET6, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_INET6); + return; } - return dad6_config; + _dev_ipsharedx_set_state(self, AF_INET6, NM_DEVICE_IP_STATE_READY); + _dev_ip_state_check_async(self, AF_INET6); } /*****************************************************************************/ @@ -12255,21 +11750,6 @@ act_request_set(NMDevice *self, NMActRequest *act_request) } } -static void -dnsmasq_cleanup(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - if (!priv->dnsmasq_manager) - return; - - nm_clear_g_signal_handler(priv->dnsmasq_manager, &priv->dnsmasq_state_id); - - nm_dnsmasq_manager_stop(priv->dnsmasq_manager); - g_object_unref(priv->dnsmasq_manager); - priv->dnsmasq_manager = NULL; -} - gboolean nm_device_is_nm_owned(NMDevice *self) { @@ -12356,32 +11836,23 @@ delete_on_deactivate_check_and_schedule(NMDevice *self) static void _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); + _dev_ipsharedx_cleanup(self, addr_family); - if (nm_clear_g_source(&priv->queued_ip_config_id_x[IS_IPv4])) { - _LOGD(LOGD_DEVICE, - "clearing queued IP%c config change", - nm_utils_addr_family_to_char(addr_family)); - } + _dev_ipdev_cleanup(self, addr_family); - if (IS_IPv4) { - dhcp4_cleanup(self, cleanup_type, FALSE); - arp_cleanup(self); - dnsmasq_cleanup(self); - ipv4ll_cleanup(self); - g_slist_free_full(priv->acd.dad_list, (GDestroyNotify) nm_acd_manager_free); - priv->acd.dad_list = NULL; - } else { - g_slist_free_full(priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); - priv->dad6_failed_addrs = NULL; - g_clear_object(&priv->dad6_ip6_config); - dhcp6_cleanup(self, cleanup_type, FALSE); - nm_clear_g_source(&priv->linklocal6_timeout_id); - addrconf6_cleanup(self); - } + _dev_ipdhcpx_cleanup(self, addr_family, TRUE, FALSE); + + if (!IS_IPv4) + _dev_ipac6_cleanup(self); + + _dev_ipllx_cleanup(self, addr_family); + + _dev_ipmanual_cleanup(self); + + _dev_ip_state_cleanup(self, AF_UNSPEC); + _dev_ip_state_cleanup(self, addr_family); } gboolean @@ -12452,127 +11923,6 @@ _nm_device_hash_check_invalid_keys(GHashTable * hash, return FALSE; } -void -nm_device_reactivate_ip_config(NMDevice * self, - int addr_family, - NMSettingIPConfig *s_ip_old, - NMSettingIPConfig *s_ip_new) -{ - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMDevicePrivate *priv; - const char * method_old; - const char * method_new; - - g_return_if_fail(NM_IS_DEVICE(self)); - - priv = NM_DEVICE_GET_PRIVATE(self); - - if (priv->ip_state_x[IS_IPv4] == NM_DEVICE_IP_STATE_NONE) - return; - - g_clear_object(&priv->con_ip_config_x[IS_IPv4]); - g_clear_object(&priv->ext_ip_config_x[IS_IPv4]); - if (IS_IPv4) { - g_clear_object(&priv->dev_ip_config_4.current); - } else { - g_clear_object(&priv->ac_ip6_config.current); - g_clear_object(&priv->dhcp6.ip6_config.current); - } - g_clear_object(&priv->dev2_ip_config_x[IS_IPv4].current); - - if (!IS_IPv4) { - if (priv->ipv6ll_handle && !IN6_IS_ADDR_UNSPECIFIED(&priv->ipv6ll_addr)) - priv->ipv6ll_has = TRUE; - } - - priv->con_ip_config_x[IS_IPv4] = nm_device_ip_config_new(self, addr_family); - - if (IS_IPv4) { - nm_ip4_config_merge_setting(priv->con_ip_config_4, - s_ip_new, - _prop_get_connection_mdns(self), - _prop_get_connection_llmnr(self), - nm_device_get_route_table(self, AF_INET), - nm_device_get_route_metric(self, AF_INET)); - } else { - nm_ip6_config_merge_setting(priv->con_ip_config_6, - s_ip_new, - nm_device_get_route_table(self, AF_INET6), - nm_device_get_route_metric(self, AF_INET6)); - } - - method_old = (s_ip_old ? nm_setting_ip_config_get_method(s_ip_old) : NULL) - ?: (IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_DISABLED - : NM_SETTING_IP6_CONFIG_METHOD_IGNORE); - method_new = (s_ip_new ? nm_setting_ip_config_get_method(s_ip_new) : NULL) - ?: (IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_DISABLED - : NM_SETTING_IP6_CONFIG_METHOD_IGNORE); - - if (!nm_streq0(method_old, method_new)) { - _cleanup_ip_pre(self, addr_family, CLEANUP_TYPE_DECONFIGURE); - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_WAIT); - if (!nm_device_activate_stage3_ip_start(self, addr_family)) { - _LOGW(LOGD_IP4, - "Failed to apply IPv%c configuration", - nm_utils_addr_family_to_char(addr_family)); - } - return; - } - - if (s_ip_old && s_ip_new) { - gint64 metric_old, metric_new; - - /* For dynamic IP methods (DHCP, IPv4LL, WWAN) the route metric is - * set at activation/renewal time using the value from static - * configuration. To support runtime change we need to update the - * dynamic configuration in place and tell the DHCP client the new - * value to use for future renewals. - */ - metric_old = nm_setting_ip_config_get_route_metric(s_ip_old); - metric_new = nm_setting_ip_config_get_route_metric(s_ip_new); - - if (metric_old != metric_new) { - if (IS_IPv4) { - if (priv->dev_ip_config_4.orig) { - nm_ip4_config_update_routes_metric((NMIP4Config *) priv->dev_ip_config_4.orig, - nm_device_get_route_metric(self, AF_INET)); - } - if (priv->dev2_ip_config_4.orig) { - nm_ip4_config_update_routes_metric((NMIP4Config *) priv->dev2_ip_config_4.orig, - nm_device_get_route_metric(self, AF_INET)); - } - if (priv->dhcp_data_4.client) { - nm_dhcp_client_set_route_metric(priv->dhcp_data_4.client, - nm_device_get_route_metric(self, AF_INET)); - } - } else { - if (priv->ac_ip6_config.orig) { - nm_ip6_config_update_routes_metric((NMIP6Config *) priv->ac_ip6_config.orig, - nm_device_get_route_metric(self, AF_INET6)); - } - if (priv->dhcp6.ip6_config.orig) { - nm_ip6_config_update_routes_metric((NMIP6Config *) priv->dhcp6.ip6_config.orig, - nm_device_get_route_metric(self, AF_INET6)); - } - if (priv->dev2_ip_config_6.orig) { - nm_ip6_config_update_routes_metric((NMIP6Config *) priv->dev2_ip_config_6.orig, - nm_device_get_route_metric(self, AF_INET6)); - } - if (priv->dhcp_data_6.client) { - nm_dhcp_client_set_route_metric(priv->dhcp_data_6.client, - nm_device_get_route_metric(self, AF_INET6)); - } - } - } - } - - if (nm_device_get_ip_ifindex(self) > 0 && !ip_config_merge_and_apply(self, addr_family, TRUE)) { - _LOGW(LOGD_IPX(IS_IPv4), - "Failed to reapply IPv%c configuration", - nm_utils_addr_family_to_char(addr_family)); - } -} - static void _pacrunner_manager_add(NMDevice *self) { @@ -12581,10 +11931,8 @@ _pacrunner_manager_add(NMDevice *self) nm_pacrunner_manager_remove_clear(&priv->pacrunner_conf_id); priv->pacrunner_conf_id = nm_pacrunner_manager_add(nm_pacrunner_manager_get(), - priv->proxy_config, nm_device_get_ip_iface(self), - NULL, - NULL); + nm_device_get_l3cd(self, TRUE)); } static void @@ -12592,12 +11940,12 @@ reactivate_proxy_config(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (!priv->pacrunner_conf_id) - return; - nm_device_set_proxy_config(self, priv->dhcp4.pac_url); - _pacrunner_manager_add(self); + if (priv->pacrunner_conf_id) + _pacrunner_manager_add(self); } +/*****************************************************************************/ + static gboolean can_reapply_change(NMDevice * self, const char *setting_name, @@ -12692,9 +12040,8 @@ check_and_reapply_connection(NMDevice * self, NMConnection * applied = nm_device_get_applied_connection(self); gs_unref_object NMConnection *applied_clone = NULL; gs_unref_hashtable GHashTable *diffs = NULL; - NMConnection * con_old, *con_new; - NMSettingIPConfig * s_ip4_old, *s_ip4_new; - NMSettingIPConfig * s_ip6_old, *s_ip6_new; + NMConnection * con_old; + NMConnection * con_new; GHashTableIter iter; if (priv->state < NM_DEVICE_STATE_PREPARE || priv->state > NM_DEVICE_STATE_ACTIVATED) { @@ -12743,11 +12090,11 @@ check_and_reapply_connection(NMDevice * self, && version_id != nm_active_connection_version_id_get( (NMActiveConnection *) priv->act_request.obj)) { - g_set_error_literal( - error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_VERSION_ID_MISMATCH, - "Reapply failed because device changed in the meantime and the version-id mismatches"); + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_VERSION_ID_MISMATCH, + "Reapply failed because device changed in the meantime and the " + "version-id mismatches"); return FALSE; } @@ -12810,11 +12157,9 @@ check_and_reapply_connection(NMDevice * self, } else con_old = con_new = applied; - priv->v4_commit_first_time = TRUE; - priv->v6_commit_first_time = TRUE; - priv->v4_route_table_initialized = FALSE; priv->v6_route_table_initialized = FALSE; + priv->l3config_merge_flags_has = FALSE; /************************************************************************** * Reapply changes @@ -12831,20 +12176,21 @@ check_and_reapply_connection(NMDevice * self, lldp_setup(self, NM_TERNARY_DEFAULT); if (priv->state >= NM_DEVICE_STATE_IP_CONFIG) { - s_ip4_old = nm_connection_get_setting_ip4_config(con_old); - s_ip4_new = nm_connection_get_setting_ip4_config(con_new); - s_ip6_old = nm_connection_get_setting_ip6_config(con_old); - s_ip6_new = nm_connection_get_setting_ip6_config(con_new); - /* Allow reapply of MTU */ priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE; - nm_device_reactivate_ip_config(self, AF_INET, s_ip4_old, s_ip4_new); - nm_device_reactivate_ip_config(self, AF_INET6, s_ip6_old, s_ip6_new); + if (nm_g_hash_table_lookup(diffs, NM_SETTING_IP4_CONFIG_SETTING_NAME)) + priv->ip_data_4.do_reapply = TRUE; + if (nm_g_hash_table_lookup(diffs, NM_SETTING_IP6_CONFIG_SETTING_NAME)) + priv->ip_data_6.do_reapply = TRUE; + + nm_device_activate_schedule_stage3_ip_config(self, FALSE); _routing_rules_sync(self, NM_TERNARY_TRUE); reactivate_proxy_config(self); + + nm_device_l3cfg_commit(self, NM_L3_CFG_COMMIT_TYPE_REAPPLY, FALSE); } if (priv->state >= NM_DEVICE_STATE_IP_CHECK) @@ -13074,96 +12420,6 @@ impl_device_get_applied_connection(NMDBusObject * obj, /*****************************************************************************/ -typedef struct { - gint64 timestamp_ms; - bool dirty; -} IP6RoutesTemporaryNotAvailableData; - -static gboolean -_rt6_temporary_not_available_timeout(gpointer user_data) -{ - NMDevice * self = NM_DEVICE(user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - priv->rt6_temporary_not_available_id = 0; - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - - return G_SOURCE_REMOVE; -} - -static gboolean -_rt6_temporary_not_available_set(NMDevice *self, GPtrArray *temporary_not_available) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - IP6RoutesTemporaryNotAvailableData *data; - GHashTableIter iter; - gint64 now_ms, oldest_ms; - const gint64 MAX_AGE_MS = 20000; - guint i; - gboolean success = TRUE; - - if (!temporary_not_available || !temporary_not_available->len) { - /* nothing outstanding. Clear tracking the routes. */ - nm_clear_pointer(&priv->rt6_temporary_not_available, g_hash_table_unref); - nm_clear_g_source(&priv->rt6_temporary_not_available_id); - return success; - } - - if (priv->rt6_temporary_not_available) { - g_hash_table_iter_init(&iter, priv->rt6_temporary_not_available); - while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &data)) - data->dirty = TRUE; - } else { - priv->rt6_temporary_not_available = - g_hash_table_new_full((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal, - (GDestroyNotify) nmp_object_unref, - nm_g_slice_free_fcn(IP6RoutesTemporaryNotAvailableData)); - } - - now_ms = nm_utils_get_monotonic_timestamp_msec(); - oldest_ms = now_ms; - - for (i = 0; i < temporary_not_available->len; i++) { - const NMPObject *o = temporary_not_available->pdata[i]; - - data = g_hash_table_lookup(priv->rt6_temporary_not_available, o); - if (data) { - if (!data->dirty) - continue; - data->dirty = FALSE; - nm_assert(data->timestamp_ms > 0 && data->timestamp_ms <= now_ms); - if (now_ms > data->timestamp_ms + MAX_AGE_MS) { - /* timeout. Could not add this address. */ - _LOGW(LOGD_DEVICE, - "failure to add IPv6 route: %s", - nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - success = FALSE; - } else - oldest_ms = MIN(data->timestamp_ms, oldest_ms); - continue; - } - - data = g_slice_new0(IP6RoutesTemporaryNotAvailableData); - data->timestamp_ms = now_ms; - g_hash_table_insert(priv->rt6_temporary_not_available, (gpointer) nmp_object_ref(o), data); - } - - g_hash_table_iter_init(&iter, priv->rt6_temporary_not_available); - while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &data)) { - if (data->dirty) - g_hash_table_iter_remove(&iter); - } - - nm_clear_g_source(&priv->rt6_temporary_not_available_id); - priv->rt6_temporary_not_available_id = - g_timeout_add(oldest_ms + MAX_AGE_MS - now_ms, _rt6_temporary_not_available_timeout, self); - - return success; -} - -/*****************************************************************************/ - static void disconnect_cb(NMDevice * self, GDBusMethodInvocation *context, @@ -13543,324 +12799,44 @@ nm_device_is_activating(NMDevice *self) * handler is actually run. If there's an activation handler scheduled * we're activating anyway. */ - return priv->activation_source_id_4 != 0; -} - -NMProxyConfig * -nm_device_get_proxy_config(NMDevice *self) -{ - g_return_val_if_fail(NM_IS_DEVICE(self), NULL); - - return NM_DEVICE_GET_PRIVATE(self)->proxy_config; -} - -static void -nm_device_set_proxy_config(NMDevice *self, const char *pac_url) -{ - NMDevicePrivate *priv; - NMConnection * connection; - NMSettingProxy * s_proxy = NULL; - - g_return_if_fail(NM_IS_DEVICE(self)); - - priv = NM_DEVICE_GET_PRIVATE(self); - - g_clear_object(&priv->proxy_config); - priv->proxy_config = nm_proxy_config_new(); - - if (pac_url) { - nm_proxy_config_set_method(priv->proxy_config, NM_PROXY_CONFIG_METHOD_AUTO); - nm_proxy_config_set_pac_url(priv->proxy_config, pac_url); - _LOGD(LOGD_PROXY, "proxy: PAC url \"%s\"", pac_url); - } else - nm_proxy_config_set_method(priv->proxy_config, NM_PROXY_CONFIG_METHOD_NONE); - - connection = nm_device_get_applied_connection(self); - if (connection) - s_proxy = nm_connection_get_setting_proxy(connection); - - if (s_proxy) - nm_proxy_config_merge_setting(priv->proxy_config, s_proxy); + return !!priv->activation_idle_source; } -/* IP Configuration stuff */ NMDhcpConfig * nm_device_get_dhcp_config(NMDevice *self, int addr_family) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); - g_return_val_if_fail(NM_IS_DEVICE(self), NULL); - nm_assert_addr_family(addr_family); - - return NM_DEVICE_GET_PRIVATE(self)->dhcp_data_x[IS_IPv4].config; + return NM_DEVICE_GET_PRIVATE(self)->ipdhcp_data_x[NM_IS_IPv4(addr_family)].config; } -NMIP4Config * -nm_device_get_ip4_config(NMDevice *self) +NML3Cfg * +nm_device_get_l3cfg(NMDevice *self) { g_return_val_if_fail(NM_IS_DEVICE(self), NULL); - return NM_DEVICE_GET_PRIVATE(self)->ip_config_4; -} - -static gboolean -nm_device_set_ip_config(NMDevice * self, - int addr_family, - NMIPConfig *new_config, - gboolean commit, - GPtrArray * ip4_dev_route_blacklist) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMIPConfig * old_config; - gboolean has_changes = FALSE; - gboolean success = TRUE; - NMSettingsConnection * settings_connection; - NMIPRouteTableSyncMode route_table_sync_mode; - - nm_assert_addr_family(addr_family); - nm_assert(!new_config || nm_ip_config_get_addr_family(new_config) == addr_family); - nm_assert(!new_config - || (new_config && ({ - int ip_ifindex = nm_device_get_ip_ifindex(self); - - (ip_ifindex > 0 && ip_ifindex == nm_ip_config_get_ifindex(new_config)); - }))); - nm_assert(IS_IPv4 || !ip4_dev_route_blacklist); - - if (commit && new_config) - route_table_sync_mode = _get_route_table_sync_mode_stateful(self, addr_family); - else - route_table_sync_mode = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE; - - _LOGD(LOGD_IPX(IS_IPv4), - "ip%c-config: update (commit=%d, new-config=" NM_HASH_OBFUSCATE_PTR_FMT - ", route-table-sync-mode=%d)", - nm_utils_addr_family_to_char(addr_family), - commit, - NM_HASH_OBFUSCATE_PTR(new_config), - (int) route_table_sync_mode); - - /* Always commit to nm-platform to update lifetimes */ - if (commit && new_config) { - _commit_mtu(self, IS_IPv4 ? NM_IP4_CONFIG(new_config) : priv->ip_config_4); - - if (IS_IPv4) { - success = nm_ip4_config_commit(NM_IP4_CONFIG(new_config), - nm_device_get_platform(self), - route_table_sync_mode); - nm_platform_ip4_dev_route_blacklist_set(nm_device_get_platform(self), - nm_ip_config_get_ifindex(new_config), - ip4_dev_route_blacklist); - } else { - gs_unref_ptrarray GPtrArray *temporary_not_available = NULL; - - success = nm_ip6_config_commit(NM_IP6_CONFIG(new_config), - nm_device_get_platform(self), - route_table_sync_mode, - &temporary_not_available); - - if (!_rt6_temporary_not_available_set(self, temporary_not_available)) - success = FALSE; - } - } - - old_config = priv->ip_config_x[IS_IPv4]; - - if (new_config && old_config) { - /* has_changes is set only on relevant changes, because when the configuration changes, - * this causes a re-read and reset. This should only happen for relevant changes */ - nm_ip_config_replace(old_config, new_config, &has_changes); - if (has_changes) { - _LOGD(LOGD_IPX(IS_IPv4), - "ip%c-config: update IP Config instance (%s)", - nm_utils_addr_family_to_char(addr_family), - nm_dbus_object_get_path(NM_DBUS_OBJECT(old_config))); - } - } else if (new_config /*&& !old_config*/) { - has_changes = TRUE; - priv->ip_config_x[IS_IPv4] = g_object_ref(new_config); - if (!nm_dbus_object_is_exported(NM_DBUS_OBJECT(new_config))) - nm_dbus_object_export(NM_DBUS_OBJECT(new_config)); - - _LOGD(LOGD_IPX(IS_IPv4), - "ip%c-config: set IP Config instance (%s)", - nm_utils_addr_family_to_char(addr_family), - nm_dbus_object_get_path(NM_DBUS_OBJECT(new_config))); - } else if (old_config /*&& !new_config*/) { - has_changes = TRUE; - priv->ip_config_x[IS_IPv4] = NULL; - _LOGD(LOGD_IPX(IS_IPv4), - "ip%c-config: clear IP Config instance (%s)", - nm_utils_addr_family_to_char(addr_family), - nm_dbus_object_get_path(NM_DBUS_OBJECT(old_config))); - if (IS_IPv4) { - /* Device config is invalid if combined config is invalid */ - applied_config_clear(&priv->dev_ip_config_4); - } else - priv->needs_ip6_subnet = FALSE; - } - - if (has_changes) { - if (old_config != priv->ip_config_x[IS_IPv4]) - _notify(self, IS_IPv4 ? PROP_IP4_CONFIG : PROP_IP6_CONFIG); - - g_signal_emit(self, - signals[IS_IPv4 ? IP4_CONFIG_CHANGED : IP6_CONFIG_CHANGED], - 0, - priv->ip_config_x[IS_IPv4], - old_config); - - if (old_config != priv->ip_config_x[IS_IPv4]) - nm_dbus_object_clear_and_unexport(&old_config); - - if (nm_device_sys_iface_state_is_external(self) - && (settings_connection = nm_device_get_settings_connection(self)) - && NM_FLAGS_HAS(nm_settings_connection_get_flags(settings_connection), - NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL) - && nm_active_connection_get_activation_type(NM_ACTIVE_CONNECTION(priv->act_request.obj)) - == NM_ACTIVATION_TYPE_EXTERNAL) { - gs_unref_object NMConnection *new_connection = NULL; - - new_connection = nm_simple_connection_new_clone( - nm_settings_connection_get_connection(settings_connection)); - - nm_connection_add_setting( - new_connection, - IS_IPv4 ? nm_ip4_config_create_setting(priv->ip_config_4) - : nm_ip6_config_create_setting(priv->ip_config_6, - _get_maybe_ipv6_disabled(self))); - - nm_settings_connection_update(settings_connection, - new_connection, - NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, - NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, - NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, - NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET, - "update-external", - NULL); - } - - nm_device_queue_recheck_assume(self); - - if (!IS_IPv4) { - if (priv->ndisc) - ndisc_set_router_config(priv->ndisc, self); - } - } - - nm_assert(!old_config || old_config == priv->ip_config_x[IS_IPv4]); - - return success; + return NM_DEVICE_GET_PRIVATE(self)->l3cfg; } -static gboolean -_replace_vpn_config_in_list(GSList **plist, GObject *old, GObject *new) -{ - GSList *old_link; - - /* Below, assert that @new is not yet tracked, but still behave - * correctly in any case. Don't complain for missing @old since - * it could have been removed when the parent device became - * unmanaged. */ - - if (old && (old_link = g_slist_find(*plist, old))) { - if (old != new) { - if (new) - old_link->data = g_object_ref(new); - else - *plist = g_slist_delete_link(*plist, old_link); - g_object_unref(old); - } - return TRUE; - } - - if (new) { - if (!g_slist_find(*plist, new)) - *plist = g_slist_append(*plist, g_object_ref(new)); - else - g_return_val_if_reached(TRUE); - return TRUE; - } - - return FALSE; -} - -void -nm_device_replace_vpn4_config(NMDevice *self, NMIP4Config *old, NMIP4Config *config) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - nm_assert(!old || NM_IS_IP4_CONFIG(old)); - nm_assert(!config || NM_IS_IP4_CONFIG(config)); - nm_assert(!old || nm_ip4_config_get_ifindex(old) == nm_device_get_ip_ifindex(self)); - nm_assert(!config || nm_ip4_config_get_ifindex(config) == nm_device_get_ip_ifindex(self)); - - if (!_replace_vpn_config_in_list(&priv->vpn_configs_4, (GObject *) old, (GObject *) config)) - return; - - /* NULL to use existing configs */ - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) - _LOGW(LOGD_IP4, "failed to set VPN routes for device"); -} - -void -nm_device_set_dev2_ip_config(NMDevice *self, int addr_family, NMIPConfig *config) +const NML3ConfigData * +nm_device_get_l3cd(NMDevice *self, gboolean get_commited) { NMDevicePrivate *priv; - const int IS_IPv4 = NM_IS_IPv4(addr_family); - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - g_return_if_fail(!config || nm_ip_config_get_addr_family(config) == addr_family); + g_return_val_if_fail(NM_IS_DEVICE(self), NULL); priv = NM_DEVICE_GET_PRIVATE(self); - applied_config_init(&priv->dev2_ip_config_x[IS_IPv4], config); - if (!ip_config_merge_and_apply(self, addr_family, TRUE)) { - _LOGW(LOGD_IP, - "failed to set extra device IPv%c configuration", - nm_utils_addr_family_to_char(addr_family)); - } -} - -void -nm_device_replace_vpn6_config(NMDevice *self, NMIP6Config *old, NMIP6Config *config) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMDeviceState state; - - nm_assert(!old || NM_IS_IP6_CONFIG(old)); - nm_assert(!old || nm_ip6_config_get_ifindex(old) > 0); - nm_assert(!old || nm_device_get_ip_ifindex(self) == 0 - || nm_device_get_ip_ifindex(self) == nm_ip6_config_get_ifindex(old)); - nm_assert(!config || NM_IS_IP6_CONFIG(config)); - nm_assert(!config || nm_ip6_config_get_ifindex(config) > 0); - nm_assert(!config || nm_device_get_ip_ifindex(self) == nm_ip6_config_get_ifindex(config)); - - if (!_replace_vpn_config_in_list(&priv->vpn_configs_6, (GObject *) old, (GObject *) config)) - return; - - state = nm_device_get_state(self); - if (state >= NM_DEVICE_STATE_IP_CONFIG && state <= NM_DEVICE_STATE_ACTIVATED) { - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "failed to set VPN routes for device"); - } -} - -NMIP6Config * -nm_device_get_ip6_config(NMDevice *self) -{ - g_return_val_if_fail(NM_IS_DEVICE(self), NULL); + if (!priv->l3cfg) + return NULL; - return NM_DEVICE_GET_PRIVATE(self)->ip_config_6; + return nm_l3cfg_get_combined_l3cd(priv->l3cfg, get_commited); } /*****************************************************************************/ static gboolean -dispatcher_cleanup(NMDevice *self) +_dispatcher_cleanup(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -13874,7 +12850,7 @@ dispatcher_cleanup(NMDevice *self) } static void -dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_data) +_dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_data) { NMDevice * self = NM_DEVICE(user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -13894,7 +12870,7 @@ ip_check_pre_up(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (dispatcher_cleanup(self)) + if (_dispatcher_cleanup(self)) nm_assert_not_reached(); priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES; @@ -13902,11 +12878,11 @@ ip_check_pre_up(NMDevice *self) if (!nm_dispatcher_call_device(NM_DISPATCHER_ACTION_PRE_UP, self, NULL, - dispatcher_complete_proceed_state, + _dispatcher_complete_proceed_state, self, &priv->dispatcher.call_id)) { /* Just proceed on errors */ - dispatcher_complete_proceed_state(0, self); + _dispatcher_complete_proceed_state(0, self); } } @@ -14086,8 +13062,8 @@ nm_device_start_ip_check(NMDevice *self) g_return_if_fail(!priv->gw_ping.watch); g_return_if_fail(!priv->gw_ping.timeout); g_return_if_fail(!priv->gw_ping.pid); - g_return_if_fail(priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE - || priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE); + g_return_if_fail(priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY + || priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY); connection = nm_device_get_applied_connection(self); g_assert(connection); @@ -14098,17 +13074,21 @@ nm_device_start_ip_check(NMDevice *self) buf[0] = '\0'; if (timeout) { - const NMPObject *gw; + const NMPObject * gw; + const NML3ConfigData *l3cd; - if (priv->ip_config_4 && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { - gw = nm_ip4_config_best_default_route_get(priv->ip_config_4); + l3cd = priv->l3cfg ? nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE) : NULL; + if (!l3cd) { + /* pass */ + } else if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY) { + gw = nm_l3_config_data_get_best_default_route(l3cd, AF_INET); if (gw) { _nm_utils_inet4_ntop(NMP_OBJECT_CAST_IP4_ROUTE(gw)->gateway, buf); ping_binary = nm_utils_find_helper("ping", "/usr/bin/ping", NULL); log_domain = LOGD_IP4; } - } else if (priv->ip_config_6 && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { - gw = nm_ip6_config_best_default_route_get(priv->ip_config_6); + } else if (priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY) { + gw = nm_l3_config_data_get_best_default_route(l3cd, AF_INET6); if (gw) { _nm_utils_inet6_ntop(&NMP_OBJECT_CAST_IP6_ROUTE(gw)->gateway, buf); ping_binary = nm_utils_find_helper("ping6", "/usr/bin/ping6", NULL); @@ -14248,15 +13228,7 @@ nm_device_bring_up(NMDevice *self, gboolean block, gboolean *no_firmware) /* Can only get HW address of some devices when they are up */ nm_device_update_hw_address(self); - /* when the link comes up, we must restore IP configuration if necessary. */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) - _LOGW(LOGD_IP4, "failed applying IP4 config after bringing link up"); - } - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "failed applying IP6 config after bringing link up"); - } + _dev_l3_cfg_commit(self, TRUE); return TRUE; } @@ -14319,381 +13291,6 @@ nm_device_get_firmware_missing(NMDevice *self) return NM_DEVICE_GET_PRIVATE(self)->firmware_missing; } -static void -intersect_ext_config(NMDevice * self, - AppliedConfig *config, - gboolean intersect_addresses, - gboolean intersect_routes) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMIPConfig * ext; - guint32 penalty; - int family; - - if (!config->orig) - return; - - family = nm_ip_config_get_addr_family(config->orig); - penalty = default_route_metric_penalty_get(self, family); - ext = family == AF_INET ? (NMIPConfig *) priv->ext_ip_config_4 - : (NMIPConfig *) priv->ext_ip_config_6; - - if (config->current) { - nm_ip_config_intersect(config->current, - ext, - intersect_addresses, - intersect_routes, - penalty); - } else { - config->current = nm_ip_config_intersect_alloc(config->orig, - ext, - intersect_addresses, - intersect_routes, - penalty); - } -} - -static gboolean -update_ext_ip_config(NMDevice *self, int addr_family, gboolean intersect_configs) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - int ifindex; - GSList * iter; - gboolean is_up; - - nm_assert_addr_family(addr_family); - - ifindex = nm_device_get_ip_ifindex(self); - if (!ifindex) - return FALSE; - - is_up = nm_platform_link_is_up(nm_device_get_platform(self), ifindex); - - if (NM_IS_IPv4(addr_family)) { - g_clear_object(&priv->ext_ip_config_4); - priv->ext_ip_config_4 = nm_ip4_config_capture(nm_device_get_multi_index(self), - nm_device_get_platform(self), - ifindex); - if (priv->ext_ip_config_4) { - if (intersect_configs) { - /* This function was called upon external changes. Remove the configuration - * (addresses,routes) that is no longer present externally from the internal - * config. This way, we don't re-add addresses that were manually removed - * by the user. */ - if (priv->con_ip_config_4) { - nm_ip4_config_intersect(priv->con_ip_config_4, - priv->ext_ip_config_4, - TRUE, - is_up, - default_route_metric_penalty_get(self, AF_INET)); - } - - intersect_ext_config(self, &priv->dev_ip_config_4, TRUE, is_up); - intersect_ext_config(self, &priv->dev2_ip_config_4, TRUE, is_up); - - for (iter = priv->vpn_configs_4; iter; iter = iter->next) - nm_ip4_config_intersect(iter->data, priv->ext_ip_config_4, TRUE, is_up, 0); - } - - /* Remove parts from ext_ip_config_4 to only contain the information that - * was configured externally -- we already have the same configuration from - * internal origins. */ - if (priv->con_ip_config_4) { - nm_ip4_config_subtract(priv->ext_ip_config_4, - priv->con_ip_config_4, - default_route_metric_penalty_get(self, AF_INET)); - } - if (applied_config_get_current(&priv->dev_ip_config_4)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_4, - applied_config_get_current(&priv->dev_ip_config_4), - default_route_metric_penalty_get(self, AF_INET)); - } - if (applied_config_get_current(&priv->dev2_ip_config_4)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_4, - applied_config_get_current(&priv->dev2_ip_config_4), - default_route_metric_penalty_get(self, AF_INET)); - } - for (iter = priv->vpn_configs_4; iter; iter = iter->next) - nm_ip4_config_subtract(priv->ext_ip_config_4, iter->data, 0); - } - - } else { - nm_assert(!NM_IS_IPv4(addr_family)); - - g_clear_object(&priv->ext_ip_config_6); - g_clear_object(&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = - nm_ip6_config_capture(nm_device_get_multi_index(self), - nm_device_get_platform(self), - ifindex, - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - if (priv->ext_ip6_config_captured) { - priv->ext_ip_config_6 = nm_ip6_config_new_cloned(priv->ext_ip6_config_captured); - - if (intersect_configs) { - /* This function was called upon external changes. Remove the configuration - * (addresses,routes) that is no longer present externally from the internal - * config. This way, we don't re-add addresses that were manually removed - * by the user. */ - if (priv->con_ip_config_6) { - nm_ip6_config_intersect(priv->con_ip_config_6, - priv->ext_ip_config_6, - is_up, - is_up, - default_route_metric_penalty_get(self, AF_INET6)); - } - - intersect_ext_config(self, &priv->ac_ip6_config, is_up, is_up); - intersect_ext_config(self, &priv->dhcp6.ip6_config, is_up, is_up); - intersect_ext_config(self, &priv->dev2_ip_config_6, is_up, is_up); - - for (iter = priv->vpn_configs_6; iter; iter = iter->next) - nm_ip6_config_intersect(iter->data, priv->ext_ip_config_6, is_up, is_up, 0); - - if (is_up && priv->ipv6ll_has - && !nm_ip6_config_lookup_address(priv->ext_ip_config_6, &priv->ipv6ll_addr)) - priv->ipv6ll_has = FALSE; - } - - /* Remove parts from ext_ip_config_6 to only contain the information that - * was configured externally -- we already have the same configuration from - * internal origins. */ - if (priv->con_ip_config_6) { - nm_ip6_config_subtract(priv->ext_ip_config_6, - priv->con_ip_config_6, - default_route_metric_penalty_get(self, AF_INET6)); - } - if (applied_config_get_current(&priv->ac_ip6_config)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_6, - applied_config_get_current(&priv->ac_ip6_config), - default_route_metric_penalty_get(self, AF_INET6)); - } - if (applied_config_get_current(&priv->dhcp6.ip6_config)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_6, - applied_config_get_current(&priv->dhcp6.ip6_config), - default_route_metric_penalty_get(self, AF_INET6)); - } - if (applied_config_get_current(&priv->dev2_ip_config_6)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_6, - applied_config_get_current(&priv->dev2_ip_config_6), - default_route_metric_penalty_get(self, AF_INET6)); - } - for (iter = priv->vpn_configs_6; iter; iter = iter->next) - nm_ip6_config_subtract(priv->ext_ip_config_6, iter->data, 0); - } - } - - return TRUE; -} - -static void -update_ip_config(NMDevice *self, int addr_family) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - nm_assert_addr_family(addr_family); - - if (NM_IS_IPv4(addr_family)) - priv->update_ip_config_completed_v4 = TRUE; - else - priv->update_ip_config_completed_v6 = TRUE; - - if (update_ext_ip_config(self, addr_family, TRUE)) { - if (NM_IS_IPv4(addr_family)) { - if (priv->ext_ip_config_4) - ip_config_merge_and_apply(self, AF_INET, FALSE); - } else { - if (priv->ext_ip6_config_captured) - ip_config_merge_and_apply(self, AF_INET6, FALSE); - } - } -} - -void -nm_device_capture_initial_config(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - if (!priv->update_ip_config_completed_v4) - update_ip_config(self, AF_INET); - if (!priv->update_ip_config_completed_v6) - update_ip_config(self, AF_INET6); -} - -static gboolean -queued_ip_config_change(NMDevice *self, int addr_family) -{ - NMDevicePrivate *priv; - const int IS_IPv4 = NM_IS_IPv4(addr_family); - - g_return_val_if_fail(NM_IS_DEVICE(self), G_SOURCE_REMOVE); - - priv = NM_DEVICE_GET_PRIVATE(self); - - /* Wait for any queued state changes */ - if (priv->queued_state.id) - return G_SOURCE_CONTINUE; - - /* If a commit is scheduled, this function would potentially interfere with - * it changing IP configurations before they are applied. Postpone the - * update in such case. - */ - if (priv->activation_source_id_x[IS_IPv4] != 0 - && priv->activation_source_func_x[IS_IPv4] - == activate_stage5_ip_config_result_x_fcn(addr_family)) - return G_SOURCE_CONTINUE; - - priv->queued_ip_config_id_x[IS_IPv4] = 0; - - update_ip_config(self, addr_family); - - if (!IS_IPv4) { - /* Check whether we need to complete waiting for link-local. - * We are also called from an idle handler, so no problem doing state transitions - * now. */ - linklocal6_check_complete(self); - } - - if (!IS_IPv4) { - NMPlatform * platform; - GSList * dad6_failed_addrs, *iter; - const NMPlatformLink *pllink; - - dad6_failed_addrs = g_steal_pointer(&priv->dad6_failed_addrs); - - if (priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state < NM_DEVICE_STATE_DEACTIVATING - && priv->ifindex > 0 && !nm_device_sys_iface_state_is_external(self) - && (platform = nm_device_get_platform(self)) - && (pllink = nm_platform_link_get(platform, priv->ifindex)) - && (pllink->n_ifi_flags & IFF_UP)) { - gboolean need_ipv6ll = FALSE; - NMNDiscConfigMap ndisc_config_changed = NM_NDISC_CONFIG_NONE; - - /* Handle DAD failures */ - for (iter = dad6_failed_addrs; iter; iter = iter->next) { - const NMPObject * obj = iter->data; - const NMPlatformIP6Address *addr; - - if (!nm_ndisc_dad_addr_is_fail_candidate(platform, obj)) - continue; - - addr = NMP_OBJECT_CAST_IP6_ADDRESS(obj); - - _LOGI(LOGD_IP6, - "ipv6: duplicate address check failed for the %s address", - nm_platform_ip6_address_to_string(addr, NULL, 0)); - - if (IN6_IS_ADDR_LINKLOCAL(&addr->address)) - need_ipv6ll = TRUE; - else if (priv->ndisc) - ndisc_config_changed |= nm_ndisc_dad_failed(priv->ndisc, &addr->address, FALSE); - } - - if (ndisc_config_changed != NM_NDISC_CONFIG_NONE) - nm_ndisc_emit_config_change(priv->ndisc, ndisc_config_changed); - - /* If no IPv6 link-local address exists but other addresses do then we - * must add the LL address to remain conformant with RFC 3513 chapter 2.1 - * ("Addressing Model"): "All interfaces are required to have at least - * one link-local unicast address". - */ - if (priv->ip_config_6 && nm_ip6_config_get_num_addresses(priv->ip_config_6)) - need_ipv6ll = TRUE; - if (need_ipv6ll) - check_and_add_ipv6ll_addr(self); - } - - g_slist_free_full(dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); - } - - if (!IS_IPv4) { - /* Check if DAD is still pending */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF && priv->dad6_ip6_config - && priv->ext_ip6_config_captured - && !nm_ip6_config_has_any_dad_pending(priv->ext_ip6_config_captured, - priv->dad6_ip6_config)) { - _LOGD(LOGD_DEVICE | LOGD_IP6, "IPv6 DAD terminated"); - g_clear_object(&priv->dad6_ip6_config); - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - if (priv->rt6_temporary_not_available) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - } - } - - set_unmanaged_external_down(self, TRUE); - - return G_SOURCE_REMOVE; -} - -static gboolean -queued_ip4_config_change(gpointer user_data) -{ - return queued_ip_config_change(user_data, AF_INET); -} - -static gboolean -queued_ip6_config_change(gpointer user_data) -{ - return queued_ip_config_change(user_data, AF_INET6); -} - -static void -device_ipx_changed(NMPlatform * platform, - int obj_type_i, - int ifindex, - gconstpointer platform_object, - int change_type_i, - NMDevice * self) -{ - const NMPObjectType obj_type = obj_type_i; - const NMPlatformSignalChangeType change_type = change_type_i; - NMDevicePrivate * priv; - const NMPlatformIP6Address * addr; - - if (nm_device_get_ip_ifindex(self) != ifindex) - return; - - if (!nm_device_is_real(self)) - return; - - if (nm_device_get_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT)) { - /* ignore all platform signals until the link is initialized in platform. */ - return; - } - - priv = NM_DEVICE_GET_PRIVATE(self); - - switch (obj_type) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP4_ROUTE: - if (!priv->queued_ip_config_id_4) { - priv->queued_ip_config_id_4 = g_idle_add(queued_ip4_config_change, self); - _LOGD(LOGD_DEVICE, "queued IP4 config change"); - } - break; - case NMP_OBJECT_TYPE_IP6_ADDRESS: - addr = platform_object; - - if (priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state < NM_DEVICE_STATE_DEACTIVATING - && nm_ndisc_dad_addr_is_fail_candidate_event(change_type, addr)) { - priv->dad6_failed_addrs = - g_slist_prepend(priv->dad6_failed_addrs, - (gpointer) nmp_object_ref(NMP_OBJECT_UP_CAST(addr))); - } - - /* fall-through */ - case NMP_OBJECT_TYPE_IP6_ROUTE: - if (!priv->queued_ip_config_id_6) { - priv->queued_ip_config_id_6 = g_idle_add(queued_ip6_config_change, self); - _LOGD(LOGD_DEVICE, "queued IP6 config change"); - } - break; - default: - g_return_if_reached(); - } -} - /*****************************************************************************/ NM_UTILS_FLAGS2STR_DEFINE(nm_unmanaged_flags2str, @@ -14946,12 +13543,6 @@ _set_unmanaged_flags(NMDevice * self, nm_device_set_unmanaged_flags(self, NM_UNMANAGED_USER_SETTINGS, !!unmanaged); } - /* trigger an initial update of IP configuration. */ - nm_assert_se(!nm_clear_g_source(&priv->queued_ip_config_id_4)); - nm_assert_se(!nm_clear_g_source(&priv->queued_ip_config_id_6)); - priv->queued_ip_config_id_4 = g_idle_add(queued_ip4_config_change, self); - priv->queued_ip_config_id_6 = g_idle_add(queued_ip6_config_change, self); - if (priv->pending_actions.len == 0) { do_notify_has_pending_actions = TRUE; had_pending_actions = nm_device_has_pending_action(self); @@ -15303,9 +13894,13 @@ nm_device_update_metered(NMDevice *self) /* Try to guess a value using the metered flag in IP configuration */ if (value == NM_METERED_INVALID) { - if (priv->ip_config_4 && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE - && nm_ip4_config_get_metered(priv->ip_config_4)) - value = NM_METERED_GUESS_YES; + if (priv->l3cfg) { + const NML3ConfigData *l3cd; + + l3cd = nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE); + if (l3cd && nm_l3_config_data_get_metered(l3cd)) + value = NM_METERED_GUESS_YES; + } } /* Otherwise, look at connection type. For Bluetooth, we look at the type of @@ -15876,12 +14471,11 @@ _cancel_activation(NMDevice *self) priv->fw_state = FIREWALL_STATE_INITIALIZED; } - dispatcher_cleanup(self); + _dispatcher_cleanup(self); ip_check_gw_ping_cleanup(self); /* Break the activation chain */ - activation_source_clear(self, AF_INET); - activation_source_clear(self, AF_INET6); + activation_source_clear(self); } static void @@ -15911,17 +14505,13 @@ _cleanup_generic_pre(NMDevice *self, CleanupType cleanup_type) queued_state_clear(self); - nm_clear_pointer(&priv->shared_ip_handle, nm_netns_shared_ip_release); - for (i = 0; i < 2; i++) nm_clear_pointer(&priv->hostname_resolver_x[i], _hostname_resolver_free); _cleanup_ip_pre(self, AF_INET, cleanup_type); _cleanup_ip_pre(self, AF_INET6, cleanup_type); - priv->ip_config_started = FALSE; - nm_clear_g_source_inst(&priv->ip_req_timeout_source_4); - nm_clear_g_source_inst(&priv->ip_req_timeout_source_6); + _dev_ip_state_req_timeout_cancel(self, AF_UNSPEC); } static void @@ -15929,11 +14519,9 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - priv->v4_commit_first_time = TRUE; - priv->v6_commit_first_time = TRUE; - priv->v4_route_table_initialized = FALSE; priv->v6_route_table_initialized = FALSE; + priv->l3config_merge_flags_has = FALSE; priv->v4_route_table_all_sync_before = FALSE; priv->v6_route_table_all_sync_before = FALSE; @@ -15941,42 +14529,9 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type) priv->default_route_metric_penalty_ip4_has = FALSE; priv->default_route_metric_penalty_ip6_has = FALSE; - priv->linklocal6_dad_counter = 0; - priv->mtu_force_set_done = FALSE; - /* Clean up IP configs; this does not actually deconfigure the - * interface; the caller must flush routes and addresses explicitly. - */ - nm_device_set_ip_config(self, AF_INET, NULL, TRUE, NULL); - nm_device_set_ip_config(self, AF_INET6, NULL, TRUE, NULL); - g_clear_object(&priv->proxy_config); - g_clear_object(&priv->con_ip_config_4); - applied_config_clear(&priv->dev_ip_config_4); - applied_config_clear(&priv->dev2_ip_config_4); - g_clear_object(&priv->ext_ip_config_4); - g_clear_object(&priv->ip_config_4); - g_clear_object(&priv->con_ip_config_6); - applied_config_clear(&priv->ac_ip6_config); - g_clear_object(&priv->ext_ip_config_6); - g_clear_object(&priv->ext_ip6_config_captured); - applied_config_clear(&priv->dev2_ip_config_6); - g_clear_object(&priv->ip_config_6); - g_clear_object(&priv->dad6_ip6_config); - priv->ipv6ll_has = FALSE; - memset(&priv->ipv6ll_addr, 0, sizeof(priv->ipv6ll_addr)); - - nm_clear_pointer(&priv->rt6_temporary_not_available, g_hash_table_unref); - nm_clear_g_source(&priv->rt6_temporary_not_available_id); - - g_slist_free_full(priv->vpn_configs_4, g_object_unref); - priv->vpn_configs_4 = NULL; - g_slist_free_full(priv->vpn_configs_6, g_object_unref); - priv->vpn_configs_6 = NULL; - - /* We no longer accept the delegations. nm_device_set_ip_config(NULL) - * above disables them. */ - nm_assert(priv->needs_ip6_subnet == FALSE); + priv->needs_ip6_subnet = FALSE; if (priv->act_request.obj) { nm_active_connection_set_default(NM_ACTIVE_CONNECTION(priv->act_request.obj), @@ -15998,6 +14553,9 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type) * or ATM device). */ _set_ip_ifindex(self, 0, NULL); + + nm_clear_g_source_inst(&priv->ip_data_4.check_async_source); + nm_clear_g_source_inst(&priv->ip_data_6.check_async_source); } /* @@ -16029,7 +14587,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu /* Turn off kernel IPv6 */ if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { - set_disable_ipv6(self, "1"); + _dev_sysctl_set_disable_ipv6(self, TRUE); nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", "0"); } @@ -16146,18 +14704,19 @@ deactivate_reset_hw_addr(NMDevice *self) static char * find_dhcp4_address(NMDevice *self) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - const NMPlatformIP4Address *a; - NMDedupMultiIter ipconf_iter; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const NMPObject *obj; - if (!priv->ip_config_4) + if (!priv->l3cds[L3_CONFIG_DATA_TYPE_DHCP_4].d) return NULL; - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, priv->ip_config_4, &a) { - if (a->addr_source == NM_IP_CONFIG_SOURCE_DHCP) - return nm_utils_inet4_ntop_dup(a->address); - } - return NULL; + obj = nm_l3_config_data_get_first_obj(priv->l3cds[L3_CONFIG_DATA_TYPE_DHCP_4].d, + NMP_OBJECT_TYPE_IP4_ADDRESS, + NULL); + if (!obj) + return NULL; + + return nm_utils_inet4_ntop_dup(NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address); } void @@ -16169,7 +14728,6 @@ nm_device_spawn_iface_helper(NMDevice *self) GError * error = NULL; const char * method; GPtrArray * argv; - gs_free char * dhcp4_address = NULL; char * logging_backend; NMUtilsStableType stable_type; const char * stable_id; @@ -16214,10 +14772,9 @@ nm_device_spawn_iface_helper(NMDevice *self) g_ptr_array_add(argv, g_strdup("--log-domains")); g_ptr_array_add(argv, g_strdup(nm_logging_domains_to_string())); - dhcp4_address = find_dhcp4_address(self); - method = nm_device_get_effective_ip_config_method(self, AF_INET); if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + char * dhcp4_address; NMSettingIPConfig *s_ip4; s_ip4 = nm_connection_get_setting_ip4_config(connection); @@ -16226,34 +14783,36 @@ nm_device_spawn_iface_helper(NMDevice *self) g_ptr_array_add(argv, g_strdup("--priority4")); g_ptr_array_add(argv, g_strdup_printf("%u", nm_device_get_route_metric(self, AF_INET))); - g_ptr_array_add(argv, g_strdup("--dhcp4")); - g_ptr_array_add(argv, g_strdup(dhcp4_address)); + dhcp4_address = find_dhcp4_address(self); + if (dhcp4_address) { + g_ptr_array_add(argv, g_strdup("--dhcp4")); + g_ptr_array_add(argv, dhcp4_address); + } + if (nm_setting_ip_config_get_may_fail(s_ip4) == FALSE) g_ptr_array_add(argv, g_strdup("--dhcp4-required")); - if (priv->dhcp_data_4.client) { - const char *hostname; - GBytes * client_id; + if (priv->ipdhcp_data_4.client) { + const NMDhcpClientConfig *config; + + config = nm_dhcp_client_get_config(priv->ipdhcp_data_4.client); - client_id = nm_dhcp_client_get_client_id(priv->dhcp_data_4.client); - if (client_id) { + if (config->client_id) { g_ptr_array_add(argv, g_strdup("--dhcp4-clientid")); g_ptr_array_add(argv, - nm_utils_bin2hexstr_full(g_bytes_get_data(client_id, NULL), - g_bytes_get_size(client_id), + nm_utils_bin2hexstr_full(g_bytes_get_data(config->client_id, NULL), + g_bytes_get_size(config->client_id), ':', FALSE, NULL)); } - hostname = nm_dhcp_client_get_hostname(priv->dhcp_data_4.client); - if (hostname) { - if (NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(priv->dhcp_data_4.client), - NM_DHCP_CLIENT_FLAGS_USE_FQDN)) + if (config->hostname) { + if (config->use_fqdn) g_ptr_array_add(argv, g_strdup("--dhcp4-fqdn")); else g_ptr_array_add(argv, g_strdup("--dhcp4-hostname")); - g_ptr_array_add(argv, g_strdup(hostname)); + g_ptr_array_add(argv, g_strdup(config->hostname)); } } @@ -16277,7 +14836,7 @@ nm_device_spawn_iface_helper(NMDevice *self) g_ptr_array_add(argv, g_strdup("--slaac-required")); g_ptr_array_add(argv, g_strdup("--slaac-tempaddr")); - g_ptr_array_add(argv, g_strdup_printf("%d", priv->ndisc_use_tempaddr)); + g_ptr_array_add(argv, g_strdup_printf("%d", (int) _prop_get_ipv6_ip6_privacy(self))); if (nm_device_get_ip_iface_identifier(self, &iid, FALSE)) { g_ptr_array_add(argv, g_strdup("--iid")); @@ -16328,28 +14887,11 @@ nm_device_spawn_iface_helper(NMDevice *self) /*****************************************************************************/ -static gboolean -ip_config_valid(NMDeviceState state) -{ - return (state == NM_DEVICE_STATE_UNMANAGED) - || (state >= NM_DEVICE_STATE_IP_CHECK && state <= NM_DEVICE_STATE_DEACTIVATING); -} - -static void -notify_ip_properties(NMDevice *self) -{ - _notify(self, PROP_IP_IFACE); - _notify(self, PROP_IP4_CONFIG); - _notify(self, PROP_DHCP4_CONFIG); - _notify(self, PROP_IP6_CONFIG); - _notify(self, PROP_DHCP6_CONFIG); -} - static void ip6_managed_setup(NMDevice *self) { - set_nm_ipv6ll(self, TRUE); - set_disable_ipv6(self, "1"); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); + _dev_sysctl_set_disable_ipv6(self, TRUE); nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", "0"); nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "0"); @@ -16483,9 +15025,9 @@ deactivate_dispatcher_complete(NMDispatcherCallId *call_id, gpointer user_data) static void _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, gboolean quitting) { - NMDevicePrivate *priv; - NMDeviceState old_state; - gs_unref_object NMActRequest *req = NULL; + gs_unref_object NMActRequest *req = NULL; + NMDevicePrivate * priv; + NMDeviceState old_state; gboolean no_firmware = FALSE; NMSettingsConnection * sett_conn; NMSettingSriov * s_sriov; @@ -16495,8 +15037,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, priv = NM_DEVICE_GET_PRIVATE(self); - /* Track re-entry */ - g_warn_if_fail(priv->in_state_changed == FALSE); + g_return_if_fail(priv->in_state_changed == 0); old_state = priv->state; @@ -16533,14 +15074,14 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, * by the device not having any pending action anymore * we add one here that gets removed at the end of the function */ nm_device_add_pending_action(self, NM_PENDING_ACTION_IN_STATE_CHANGE, TRUE); - priv->in_state_changed = TRUE; + priv->in_state_changed++; priv->state = state; priv->state_reason = reason; queued_state_clear(self); - dispatcher_cleanup(self); + _dispatcher_cleanup(self); nm_clear_g_cancellable(&priv->deactivating_cancellable); @@ -16597,15 +15138,15 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, nm_device_cleanup(self, reason, CLEANUP_TYPE_DECONFIGURE); nm_device_take_down(self, TRUE); nm_device_hw_addr_reset(self, "unmanage"); - set_nm_ipv6ll(self, FALSE); - restore_ip6_properties(self); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_EUI64); + _dev_sysctl_restore_ip6_properties(self); } } nm_device_sys_iface_state_set(self, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL); break; case NM_DEVICE_STATE_UNAVAILABLE: if (old_state == NM_DEVICE_STATE_UNMANAGED) { - save_ip6_properties(self); + _dev_sysctl_save_ip6_properties(self); if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) ip6_managed_setup(self); device_init_static_sriov_num_vfs(self); @@ -16634,7 +15175,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, /* Ensure devices that previously assumed a connection now have * userspace IPv6LL enabled. */ - set_nm_ipv6ll(self, TRUE); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); nm_device_cleanup(self, reason, CLEANUP_TYPE_DECONFIGURE); } else if (old_state < NM_DEVICE_STATE_DISCONNECTED) { @@ -16747,9 +15288,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, _LOGI(LOGD_DEVICE, "Activation: successful, device activated."); nm_device_update_metered(self); nm_dispatcher_call_device(NM_DISPATCHER_ACTION_UP, self, req, NULL, NULL, NULL); - - if (priv->proxy_config) - _pacrunner_manager_add(self); + _pacrunner_manager_add(self); break; case NM_DEVICE_STATE_FAILED: /* Usually upon failure the activation chain is interrupted in @@ -16809,11 +15348,6 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, fw_change_zone(self); } else nm_device_start_ip_check(self); - - /* IP-related properties are only valid when the device has IP configuration; - * now that it does, ensure their change notifications are emitted. - */ - notify_ip_properties(self); break; } case NM_DEVICE_STATE_SECONDARIES: @@ -16836,18 +15370,13 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, } } - /* IP-related properties are only valid when the device has IP configuration. - * If it no longer does, ensure their change notifications are emitted. - */ - if (ip_config_valid(old_state) && !ip_config_valid(state)) - notify_ip_properties(self); - concheck_now = NM_IN_SET(state, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_DISCONNECTED) || old_state >= NM_DEVICE_STATE_ACTIVATED; concheck_update_interval(self, AF_INET, concheck_now); concheck_update_interval(self, AF_INET6, concheck_now); - priv->in_state_changed = FALSE; + priv->in_state_changed--; + nm_device_remove_pending_action(self, NM_PENDING_ACTION_IN_STATE_CHANGE, TRUE); if ((old_state > NM_DEVICE_STATE_UNMANAGED) != (state > NM_DEVICE_STATE_UNMANAGED)) @@ -17869,15 +16398,16 @@ nm_device_clear_dns_lookup_data(NMDevice *self) const char * nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean *out_wait) { - NMDevicePrivate * priv; const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate * priv; HostnameResolver *resolver; - NMIPConfig * ip_config; const char * method; + int ifindex; gboolean address_changed = FALSE; gs_unref_object GInetAddress *new_address = NULL; g_return_val_if_fail(NM_IS_DEVICE(self), NULL); + priv = NM_DEVICE_GET_PRIVATE(self); /* If the device is not supposed to have addresses, @@ -17917,39 +16447,29 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean /* Determine the most suitable address of the interface * and whether it changed from the previous lookup */ - ip_config = priv->ip_config_x[IS_IPv4]; - if (ip_config) { - const NMPlatformIPAddress *addr = NULL; - - if (IS_IPv4) { - addr = nm_ip_config_get_first_address(ip_config); - } else { - /* For IPv6 prefer, in order: - * - !link-local, !deprecated - * - !link-local, deprecated - * - link-local - */ - addr = nm_ip_config_find_first_address(ip_config, - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL); - if (!addr) { - addr = nm_ip_config_find_first_address( - ip_config, - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED); - } - if (!addr) { - addr = nm_ip_config_find_first_address(ip_config, - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY); + ifindex = nm_device_get_ip_ifindex(self); + if (ifindex > 0) { + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + const NMDedupMultiEntry * iter; + + /* FIXME(l3cfg): now we lookup the address from platform. Should we instead look + * it up from NML3Cfg? That is, take an address that we want to configure as + * 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)); + + if (head_entry) { + c_list_for_each_entry (iter, &head_entry->lst_entries_head, lst_entries) { + const NMPlatformIPAddress *addr = NMP_OBJECT_CAST_IP_ADDRESS(iter->obj); + + new_address = g_inet_address_new_from_bytes(addr->address_ptr, + IS_IPv4 ? G_SOCKET_FAMILY_IPV4 + : G_SOCKET_FAMILY_IPV6); + break; } } - - if (addr) { - new_address = g_inet_address_new_from_bytes(addr->address_ptr, - IS_IPv4 ? G_SOCKET_FAMILY_IPV4 - : G_SOCKET_FAMILY_IPV6); - } } if (new_address && resolver->address) { @@ -18023,11 +16543,7 @@ _activation_func_to_string(ActivationHandleFunc func) G_STMT_END FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage1_device_prepare); FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage2_device_config); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage3_ip_config_start); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage4_ip_config_timeout_4); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage4_ip_config_timeout_6); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage5_ip_config_result_4); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage5_ip_config_result_6); + FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage3_ip_config); g_return_val_if_reached("unknown"); } @@ -18059,13 +16575,10 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) nm_utils_str_utf8safe_escape_cp(priv->iface, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)); break; case PROP_IP_IFACE: - if (ip_config_valid(priv->state)) { - g_value_take_string( - value, - nm_utils_str_utf8safe_escape_cp(nm_device_get_ip_iface(self), - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)); - } else - g_value_set_string(value, NULL); + g_value_take_string( + value, + nm_utils_str_utf8safe_escape_cp(nm_device_get_ip_iface(self), + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)); break; case PROP_IFINDEX: g_value_set_int(value, priv->ifindex); @@ -18100,24 +16613,16 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) g_value_set_uint(value, priv->mtu); break; case PROP_IP4_CONFIG: - nm_dbus_utils_g_value_set_object_path(value, - ip_config_valid(priv->state) ? priv->ip_config_4 - : NULL); + nm_dbus_utils_g_value_set_object_path(value, priv->l3ipdata_4.ip_config); break; case PROP_DHCP4_CONFIG: - nm_dbus_utils_g_value_set_object_path( - value, - ip_config_valid(priv->state) ? priv->dhcp_data_4.config : NULL); + nm_dbus_utils_g_value_set_object_path(value, priv->ipdhcp_data_4.config); break; case PROP_IP6_CONFIG: - nm_dbus_utils_g_value_set_object_path(value, - ip_config_valid(priv->state) ? priv->ip_config_6 - : NULL); + nm_dbus_utils_g_value_set_object_path(value, priv->l3ipdata_6.ip_config); break; case PROP_DHCP6_CONFIG: - nm_dbus_utils_g_value_set_object_path( - value, - ip_config_valid(priv->state) ? priv->dhcp_data_6.config : NULL); + nm_dbus_utils_g_value_set_object_path(value, priv->ipdhcp_data_6.config); break; case PROP_STATE: g_value_set_uint(value, priv->state); @@ -18344,6 +16849,8 @@ nm_device_init(NMDevice *self) c_list_init(&self->devices_lst); c_list_init(&priv->slaves); + priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_NONE; + priv->concheck_x[0].state = NM_CONNECTIVITY_UNKNOWN; priv->concheck_x[1].state = NM_CONNECTIVITY_UNKNOWN; @@ -18369,9 +16876,6 @@ nm_device_init(NMDevice *self) priv->ip6_saved_properties = g_hash_table_new_full(nm_str_hash, g_str_equal, NULL, g_free); priv->sys_iface_state_ = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; - priv->v4_commit_first_time = TRUE; - priv->v6_commit_first_time = TRUE; - priv->promisc_reset = NM_OPTION_BOOL_DEFAULT; } @@ -18428,24 +16932,7 @@ constructed(GObject *object) if (NM_DEVICE_GET_CLASS(self)->get_generic_capabilities) priv->capabilities |= NM_DEVICE_GET_CLASS(self)->get_generic_capabilities(self); - /* Watch for external IP config changes */ platform = nm_device_get_platform(self); - g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - G_CALLBACK(device_ipx_changed), - self); - g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - G_CALLBACK(device_ipx_changed), - self); - g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - G_CALLBACK(device_ipx_changed), - self); - g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - G_CALLBACK(device_ipx_changed), - self); g_signal_connect(platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK(link_changed_cb), self); priv->manager = g_object_ref(NM_MANAGER_GET); @@ -18497,15 +16984,12 @@ dispose(GObject *object) _parent_set_ifindex(self, 0, FALSE); platform = nm_device_get_platform(self); - g_signal_handlers_disconnect_by_func(platform, G_CALLBACK(device_ipx_changed), self); g_signal_handlers_disconnect_by_func(platform, G_CALLBACK(link_changed_cb), self); - arp_cleanup(self); - nm_clear_g_signal_handler(nm_config_get(), &priv->config_changed_id); nm_clear_g_signal_handler(priv->manager, &priv->ifindex_changed_id); - dispatcher_cleanup(self); + _dispatcher_cleanup(self); nm_pacrunner_manager_remove_clear(&priv->pacrunner_conf_id); @@ -18514,7 +16998,7 @@ dispose(GObject *object) nm_assert(c_list_is_empty(&priv->slaves)); /* Let the kernel manage IPv6LL again */ - set_nm_ipv6ll(self, FALSE); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_EUI64); _cleanup_generic_post(self, CLEANUP_TYPE_KEEP); @@ -18583,7 +17067,6 @@ finalize(GObject *object) g_free(priv->hw_addr_perm); g_free(priv->hw_addr_initial); g_free(priv->pending_actions.arr); - g_slist_free_full(priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); nm_clear_g_free(&priv->physical_port_id); g_free(priv->udi); g_free(priv->path); @@ -18763,10 +17246,8 @@ nm_device_class_init(NMDeviceClass *klass) klass->link_changed = link_changed; - klass->is_available = is_available; - klass->act_stage2_config = act_stage2_config; - klass->act_stage3_ip_config_start = act_stage3_ip_config_start; - klass->act_stage4_ip_config_timeout = act_stage4_ip_config_timeout; + klass->is_available = is_available; + klass->act_stage2_config = act_stage2_config; klass->get_type_description = get_type_description; klass->can_auto_connect = can_auto_connect; @@ -19098,29 +17579,17 @@ nm_device_class_init(NMDeviceClass *klass) G_TYPE_BOOLEAN, 0); - signals[IP4_CONFIG_CHANGED] = g_signal_new(NM_DEVICE_IP4_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 2, - G_TYPE_OBJECT, - G_TYPE_OBJECT); - - signals[IP6_CONFIG_CHANGED] = g_signal_new(NM_DEVICE_IP6_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 2, - G_TYPE_OBJECT, - G_TYPE_OBJECT); + signals[L3CD_CHANGED] = g_signal_new(NM_DEVICE_L3CD_CHANGED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_POINTER, /* (const NML3ConfigData *l3cd_old) */ + G_TYPE_POINTER /* (const NML3ConfigData *l3cd_new) */); signals[IP6_PREFIX_DELEGATED] = g_signal_new(NM_DEVICE_IP6_PREFIX_DELEGATED, |