diff options
Diffstat (limited to 'src/core/nm-l3cfg.c')
| -rw-r--r-- | src/core/nm-l3cfg.c | 753 |
1 files changed, 544 insertions, 209 deletions
diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index aa56c13f..9258decf 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -12,9 +12,11 @@ #include "libnm-glib-aux/nm-time-utils.h" #include "libnm-platform/nm-platform.h" #include "libnm-platform/nmp-object.h" +#include "libnm-platform/nmp-route-manager.h" #include "nm-netns.h" #include "n-acd/src/n-acd.h" #include "nm-l3-ipv4ll.h" +#include "nm-ip-config.h" /*****************************************************************************/ @@ -203,6 +205,7 @@ typedef struct { guint32 acd_timeout_msec_confdata; NML3AcdDefendType acd_defend_type_confdata : 3; bool dirty_confdata : 1; + gboolean force_commit_once : 1; } L3ConfigData; /*****************************************************************************/ @@ -240,12 +243,20 @@ typedef struct _NML3CfgPrivate { CList acd_event_notify_lst_head; - NAcd * nacd; + NAcd *nacd; GSource *nacd_source; GSource *nacd_event_down_source; gint64 nacd_event_down_ratelimited_until_msec; + union { + struct { + NMIPConfig *ipconfig_6; + NMIPConfig *ipconfig_4; + }; + NMIPConfig *ipconfig_x[2]; + }; + /* This is for rate-limiting the creation of nacd instance. */ GSource *nacd_instance_ensure_retry; @@ -332,17 +343,17 @@ static void _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is static void _nm_l3cfg_emit_signal_notify_acd_event_all(NML3Cfg *self); static gboolean _acd_has_valid_link(const NMPObject *obj, - const guint8 ** out_addr_bin, - gboolean * out_acd_not_supported); + const guint8 **out_addr_bin, + gboolean *out_acd_not_supported); static void _l3_acd_nacd_instance_reset(NML3Cfg *self, NMTernary start_timer, gboolean acd_data_notify); -static void _l3_acd_data_state_change(NML3Cfg * self, - AcdData * acd_data, +static void _l3_acd_data_state_change(NML3Cfg *self, + AcdData *acd_data, AcdStateChangeMode mode, const NMEtherAddr *sender, - gint64 * p_now_msec); + gint64 *p_now_msec); static AcdData *_l3_acd_data_find(NML3Cfg *self, in_addr_t addr); @@ -400,6 +411,92 @@ static NM_UTILS_LOOKUP_DEFINE(_l3_acd_addr_state_to_string, "external-removed"), NM_UTILS_LOOKUP_ITEM(NM_L3_ACD_ADDR_STATE_USED, "used"), ); +static gboolean +_obj_is_route_nodev(const NMPObject *obj) +{ + gboolean has_ifindex; + + nm_assert(obj); + + has_ifindex = (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex > 0); + + nm_assert(has_ifindex + == !(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + && nm_platform_route_type_is_nodev(nm_platform_route_type_uncoerce( + NMP_OBJECT_CAST_IP_ROUTE(obj)->type_coerced)))); + + return !has_ifindex; +} + +/*****************************************************************************/ + +NMIPConfig * +nm_l3cfg_ipconfig_get(NML3Cfg *self, int addr_family) +{ + g_return_val_if_fail(NM_IS_L3CFG(self), NULL); + nm_assert_addr_family(addr_family); + + return self->priv.p->ipconfig_x[NM_IS_IPv4(addr_family)]; +} + +static void +_ipconfig_toggle_notify(gpointer data, GObject *object, gboolean is_last_ref) +{ + NML3Cfg *self = NM_L3CFG(data); + NMIPConfig *ipconfig = NM_IP_CONFIG(object); + + if (!is_last_ref) { + /* This happens while we take another ref below. Ignore the signal. */ + nm_assert(!NM_IN_SET(ipconfig, self->priv.p->ipconfig_4, self->priv.p->ipconfig_6)); + return; + } + + if (ipconfig == self->priv.p->ipconfig_4) + self->priv.p->ipconfig_4 = NULL; + else { + nm_assert(ipconfig == self->priv.p->ipconfig_6); + self->priv.p->ipconfig_6 = NULL; + } + + /* We take a second reference to keep the instance alive, while also removing the + * toggle ref. This will notify the function again, but we will ignore that. */ + g_object_ref(ipconfig); + + g_object_remove_toggle_ref(G_OBJECT(ipconfig), _ipconfig_toggle_notify, self); + + /* pass on the reference, and unexport on idle. */ + nm_ip_config_take_and_unexport_on_idle(g_steal_pointer(&ipconfig)); +} + +NMIPConfig * +nm_l3cfg_ipconfig_acquire(NML3Cfg *self, int addr_family) +{ + NMIPConfig *ipconfig; + + g_return_val_if_fail(NM_IS_L3CFG(self), NULL); + nm_assert_addr_family(addr_family); + + ipconfig = self->priv.p->ipconfig_x[NM_IS_IPv4(addr_family)]; + + if (ipconfig) + return g_object_ref(ipconfig); + + ipconfig = nm_ip_config_new(addr_family, self); + + self->priv.p->ipconfig_x[NM_IS_IPv4(addr_family)] = ipconfig; + + /* The ipconfig keeps self alive. We use a toggle reference + * to avoid a cycle. But we anyway wouldn't want a strong reference, + * because the user releases the instance by unrefing it, and we + * notice that via the weak reference. */ + g_object_add_toggle_ref(G_OBJECT(ipconfig), _ipconfig_toggle_notify, self); + + /* We keep the toggle reference, and return the other reference to the caller. */ + return g_steal_pointer(&ipconfig); +} + /*****************************************************************************/ gboolean @@ -415,13 +512,13 @@ nm_l3cfg_is_vrf(const NML3Cfg *self) static const char * _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data, - char * sbuf, + char *sbuf, gsize sbuf_size) { char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; char sbuf100[100]; char sbufobf[NM_HASH_OBFUSCATE_PTR_STR_BUF_SIZE]; - char * s = sbuf; + char *s = sbuf; gsize l = sbuf_size; in_addr_t addr4; @@ -509,7 +606,7 @@ _nm_l3cfg_emit_signal_notify_simple(NML3Cfg *self, NML3ConfigNotifyType notify_t } static void -_nm_l3cfg_emit_signal_notify_l3cd_changed(NML3Cfg * self, +_nm_l3cfg_emit_signal_notify_l3cd_changed(NML3Cfg *self, const NML3ConfigData *l3cd_old, const NML3ConfigData *l3cd_new, gboolean commited) @@ -538,7 +635,7 @@ _l3_changed_configs_set_dirty(NML3Cfg *self) /*****************************************************************************/ static void -_l3_acd_ipv4_addresses_on_link_update(NML3Cfg * self, +_l3_acd_ipv4_addresses_on_link_update(NML3Cfg *self, in_addr_t addr, gboolean add /* or else remove */) { @@ -596,7 +693,7 @@ static NAcdProbe * _nm_n_acd_data_probe_new(NML3Cfg *self, in_addr_t addr, guint32 timeout_msec, gpointer user_data) { nm_auto(n_acd_probe_config_freep) NAcdProbeConfig *probe_config = NULL; - NAcdProbe * probe; + NAcdProbe *probe; int r; nm_assert(self); @@ -628,7 +725,7 @@ _nm_n_acd_data_probe_new(NML3Cfg *self, in_addr_t addr, guint32 timeout_msec, gp G_STMT_START \ { \ if (NM_MORE_ASSERTS > 0) { \ - const NML3Cfg * _self = (self); \ + const NML3Cfg *_self = (self); \ const ObjStateData *_obj_state = (obj_state); \ \ nm_assert(_obj_state); \ @@ -866,7 +963,7 @@ _obj_states_update_all(NML3Cfg *self) for (i = 0; i < (int) G_N_ELEMENTS(obj_types); i++) { const NMPObjectType obj_type = obj_types[i]; NMDedupMultiIter o_iter; - const NMPObject * obj; + const NMPObject *obj; if (!self->priv.p->combined_l3cd_commited) continue; @@ -875,6 +972,11 @@ _obj_states_update_all(NML3Cfg *self) self->priv.p->combined_l3cd_commited, &obj, obj_type) { + if (_obj_is_route_nodev(obj)) { + /* this is a nodev route. We don't track an obj-state for this. */ + continue; + } + obj_state = g_hash_table_lookup(self->priv.p->obj_state_hash, &obj); if (!obj_state) { obj_state = @@ -926,21 +1028,16 @@ _obj_states_update_all(NML3Cfg *self) } typedef struct { - NML3Cfg * self; + NML3Cfg *self; NML3CfgCommitType commit_type; } ObjStatesSyncFilterData; static gboolean -_obj_states_sync_filter(/* const NMDedupMultiObj * */ gconstpointer o, gpointer user_data) +_obj_states_sync_filter(NML3Cfg *self, const NMPObject *obj, NML3CfgCommitType commit_type) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; - const NMPObject * obj = o; - const ObjStatesSyncFilterData *sync_filter_data = user_data; - NMPObjectType obj_type; - ObjStateData * obj_state; - - nm_assert(sync_filter_data); - nm_assert(NM_IS_L3CFG(sync_filter_data->self)); + char sbuf[sizeof(_nm_utils_to_string_buffer)]; + NMPObjectType obj_type; + ObjStateData *obj_state; obj_type = NMP_OBJECT_GET_TYPE(obj); @@ -948,22 +1045,19 @@ _obj_states_sync_filter(/* const NMDedupMultiObj * */ gconstpointer o, gpointer && NMP_OBJECT_CAST_IP4_ADDRESS(obj)->a_acd_not_ready) return FALSE; - obj_state = g_hash_table_lookup(sync_filter_data->self->priv.p->obj_state_hash, &obj); + obj_state = g_hash_table_lookup(self->priv.p->obj_state_hash, &obj); - nm_assert_obj_state(sync_filter_data->self, obj_state); + nm_assert_obj_state(self, obj_state); nm_assert(obj_state->obj == obj); nm_assert(c_list_is_empty(&obj_state->os_zombie_lst)); if (!obj_state->os_nm_configured) { - NML3Cfg *self; - - if (sync_filter_data->commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME + if (commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME && !_obj_state_data_get_assume_config_once(obj_state)) return FALSE; obj_state->os_nm_configured = TRUE; - self = sync_filter_data->self; _LOGD("obj-state: configure-first-time: %s", _obj_state_data_to_string(obj_state, sbuf, sizeof(sbuf))); return TRUE; @@ -975,14 +1069,80 @@ _obj_states_sync_filter(/* const NMDedupMultiObj * */ gconstpointer o, gpointer return TRUE; } - if (!obj_state->os_plobj && sync_filter_data->commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY) + if (!obj_state->os_plobj && commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY + && !nmp_object_get_force_commit(obj)) return FALSE; return TRUE; } +static gboolean +_obj_states_sync_filter_predicate(gconstpointer o, gpointer user_data) +{ + const NMPObject *obj = o; + const ObjStatesSyncFilterData *sync_filter_data = user_data; + + return _obj_states_sync_filter(sync_filter_data->self, obj, sync_filter_data->commit_type); +} + +static GPtrArray * +_commit_collect_addresses(NML3Cfg *self, int addr_family, NML3CfgCommitType commit_type) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + const NMDedupMultiHeadEntry *head_entry; + const ObjStatesSyncFilterData sync_filter_data = { + .self = self, + .commit_type = commit_type, + }; + + head_entry = nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited, + NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)); + return nm_dedup_multi_objs_to_ptr_array_head(head_entry, + _obj_states_sync_filter_predicate, + (gpointer) &sync_filter_data); +} + +static void +_commit_collect_routes(NML3Cfg *self, + int addr_family, + NML3CfgCommitType commit_type, + GPtrArray **routes, + GPtrArray **routes_nodev) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + const NMDedupMultiHeadEntry *head_entry; + const NMDedupMultiEntry *entry; + + nm_assert(routes && !*routes); + nm_assert(routes_nodev && !*routes_nodev); + + head_entry = nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited, + NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)); + + if (!head_entry) + return; + + c_list_for_each_entry (entry, &head_entry->lst_entries_head, lst_entries) { + const NMPObject *obj = entry->obj; + GPtrArray **r; + + if (_obj_is_route_nodev(obj)) + r = routes_nodev; + else { + if (!_obj_states_sync_filter(self, obj, commit_type)) + continue; + r = routes; + } + + if (!*r) + *r = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref); + + g_ptr_array_add(*r, (gpointer) nmp_object_ref(obj)); + } +} + static void -_obj_state_zombie_lst_get_prune_lists(NML3Cfg * self, +_obj_state_zombie_lst_get_prune_lists(NML3Cfg *self, int addr_family, GPtrArray **out_addresses_prune, GPtrArray **out_routes_prune) @@ -991,8 +1151,8 @@ _obj_state_zombie_lst_get_prune_lists(NML3Cfg * self, const NMPObjectType obj_type_route = NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4); const NMPObjectType obj_type_address = NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4); char sbuf[sizeof(_nm_utils_to_string_buffer)]; - ObjStateData * obj_state; - ObjStateData * obj_state_safe; + ObjStateData *obj_state; + ObjStateData *obj_state_safe; nm_assert(NM_IS_L3CFG(self)); nm_assert(out_addresses_prune && !*out_addresses_prune); @@ -1003,7 +1163,7 @@ _obj_state_zombie_lst_get_prune_lists(NML3Cfg * self, &self->priv.p->obj_state_zombie_lst_head, os_zombie_lst) { NMPObjectType obj_type; - GPtrArray ** p_a; + GPtrArray **p_a; nm_assert_obj_state(self, obj_state); nm_assert(obj_state->os_zombie_count > 0); @@ -1073,16 +1233,16 @@ static void _load_link(NML3Cfg *self, gboolean initial) { nm_auto_nmpobj const NMPObject *obj_old = NULL; - const NMPObject * obj; - const char * ifname; - const char * ifname_old; + const NMPObject *obj; + const char *ifname; + const char *ifname_old; gboolean nacd_changed; gboolean nacd_new_valid; gboolean nacd_old_valid; - const guint8 * nacd_old_addr = NULL; - const guint8 * nacd_new_addr = NULL; + const guint8 *nacd_old_addr = NULL; + const guint8 *nacd_new_addr = NULL; gboolean nacd_link_now_up; - AcdData * acd_data; + AcdData *acd_data; if (initial) { obj = nm_platform_link_get_obj(self->priv.platform, self->priv.ifindex, TRUE); @@ -1172,9 +1332,9 @@ _nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint32 obj_type_flags) } void -_nm_l3cfg_notify_platform_change(NML3Cfg * self, +_nm_l3cfg_notify_platform_change(NML3Cfg *self, NMPlatformSignalChangeType change_type, - const NMPObject * obj) + const NMPObject *obj) { NML3ConfigNotifyData notify_data; NMPObjectType obj_type; @@ -1247,9 +1407,9 @@ _acd_data_free(AcdData *acd_data) } static guint -_acd_data_collect_tracks_data(const AcdData * acd_data, +_acd_data_collect_tracks_data(const AcdData *acd_data, NMTernary dirty_selector, - guint32 * out_best_acd_timeout_msec, + guint32 *out_best_acd_timeout_msec, NML3AcdDefendType *out_best_acd_defend_type) { NML3AcdDefendType best_acd_defend_type = _NM_L3_ACD_DEFEND_TYPE_NONE; @@ -1257,6 +1417,15 @@ _acd_data_collect_tracks_data(const AcdData * acd_data, guint n = 0; guint i; + /* We do a simple search over all track-infos for the best, which determines + * our ACD state. That is, we prefer ACD disabled, and otherwise the + * shortest configured timeout. + * + * This linear search is probably fast enough, because we expect that each + * address/acd_data has few trackers. + * The alternative would be caching the best result, but that is more complicated, + * so not done. */ + for (i = 0; i < acd_data->info.n_track_infos; i++) { const NML3AcdAddrTrackInfo *acd_track = &acd_data->info.track_infos[i]; @@ -1280,9 +1449,9 @@ _acd_data_collect_tracks_data(const AcdData * acd_data, } static NML3AcdAddrTrackInfo * -_acd_data_find_track(const AcdData * acd_data, +_acd_data_find_track(const AcdData *acd_data, const NML3ConfigData *l3cd, - const NMPObject * obj, + const NMPObject *obj, gconstpointer tag) { guint i; @@ -1301,11 +1470,11 @@ _acd_data_find_track(const AcdData * acd_data, static gboolean _acd_has_valid_link(const NMPObject *obj, - const guint8 ** out_addr_bin, - gboolean * out_acd_not_supported) + const guint8 **out_addr_bin, + gboolean *out_acd_not_supported) { const NMPlatformLink *link; - const guint8 * addr_bin; + const guint8 *addr_bin; gsize addr_len; if (!obj) { @@ -1360,8 +1529,8 @@ _l3_acd_nacd_event(int fd, GIOCondition condition, gpointer user_data) while (TRUE) { NMEtherAddr sender_addr_data; const NMEtherAddr *sender_addr; - AcdData * acd_data; - NAcdEvent * event; + AcdData *acd_data; + NAcdEvent *event; if (!self->priv.p->nacd) { /* In the loop we emit signals, where *anything* might happen. @@ -1440,12 +1609,7 @@ _l3_acd_nacd_event(int fd, GIOCondition condition, gpointer user_data) "in %u msec)", timeout_msec); self->priv.p->nacd_event_down_source = - nm_g_timeout_source_new(timeout_msec, - G_PRIORITY_DEFAULT, - _l3_acd_nacd_event_down_timeout_cb, - self, - NULL); - g_source_attach(self->priv.p->nacd_event_down_source, NULL); + nm_g_timeout_add_source(timeout_msec, _l3_acd_nacd_event_down_timeout_cb, self); } break; default: @@ -1494,6 +1658,7 @@ _l3_acd_nacd_instance_reset(NML3Cfg *self, NMTernary start_timer, gboolean acd_d } nm_clear_g_source_inst(&self->priv.p->nacd_source); nm_clear_g_source_inst(&self->priv.p->nacd_instance_ensure_retry); + nm_clear_g_source_inst(&self->priv.p->nacd_event_down_source); if (c_list_is_empty(&self->priv.p->acd_lst_head)) start_timer = NM_TERNARY_DEFAULT; @@ -1505,12 +1670,9 @@ _l3_acd_nacd_instance_reset(NML3Cfg *self, NMTernary start_timer, gboolean acd_d break; case NM_TERNARY_TRUE: self->priv.p->nacd_instance_ensure_retry = - nm_g_timeout_source_new_seconds(ACD_ENSURE_RATELIMIT_MSEC / 1000u, - G_PRIORITY_DEFAULT, + nm_g_timeout_add_seconds_source(ACD_ENSURE_RATELIMIT_MSEC / 1000u, _l3_acd_nacd_instance_ensure_retry_cb, - self, - NULL); - g_source_attach(self->priv.p->nacd_instance_ensure_retry, NULL); + self); break; case NM_TERNARY_DEFAULT: break; @@ -1534,8 +1696,8 @@ static NAcd * _l3_acd_nacd_instance_ensure(NML3Cfg *self, gboolean *out_acd_not_supported) { nm_auto(n_acd_config_freep) NAcdConfig *config = NULL; - nm_auto(n_acd_unrefp) NAcd * nacd = NULL; - const guint8 * addr_bin; + nm_auto(n_acd_unrefp) NAcd *nacd = NULL; + const guint8 *addr_bin; gboolean acd_not_supported; gboolean valid; int fd; @@ -1599,11 +1761,11 @@ failed_create_acd: } static NAcdProbe * -_l3_acd_nacd_instance_create_probe(NML3Cfg * self, +_l3_acd_nacd_instance_create_probe(NML3Cfg *self, in_addr_t addr, guint32 timeout_msec, gpointer user_data, - gboolean * out_acd_not_supported, + gboolean *out_acd_not_supported, const char **out_failure_reason) { gboolean acd_not_supported; @@ -1699,17 +1861,17 @@ _l3_acd_data_defendconflict_warning_ratelimited(AcdData *acd_data, gint64 *p_now } static void -_l3_acd_data_add(NML3Cfg * self, +_l3_acd_data_add(NML3Cfg *self, const NML3ConfigData *l3cd, - const NMPObject * obj, + const NMPObject *obj, gconstpointer tag, NML3AcdDefendType acd_defend_type, guint32 acd_timeout_msec) { in_addr_t addr = NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address; NML3AcdAddrTrackInfo *acd_track; - AcdData * acd_data; - const char * track_mode; + AcdData *acd_data; + const char *track_mode; char sbuf100[100]; if (ACD_ADDR_SKIP(addr)) @@ -1795,7 +1957,7 @@ _l3_acd_data_add(NML3Cfg * self, } static void -_l3_acd_data_add_all(NML3Cfg * self, +_l3_acd_data_add_all(NML3Cfg *self, const L3ConfigData *const *infos, guint infos_len, gboolean reapply) @@ -1817,7 +1979,7 @@ _l3_acd_data_add_all(NML3Cfg * self, for (i_info = 0; i_info < infos_len; i_info++) { const L3ConfigData *info = infos[i_info]; NMDedupMultiIter iter; - const NMPObject * obj; + const NMPObject *obj; nm_l3_config_data_iter_obj_for_each (&iter, info->l3cd, &obj, NMP_OBJECT_TYPE_IP4_ADDRESS) { _l3_acd_data_add(self, @@ -1859,14 +2021,24 @@ _l3_acd_data_timeout_cb(gpointer user_data) static void _l3_acd_data_timeout_schedule(AcdData *acd_data, gint64 timeout_msec) { + /* in _l3_acd_data_state_set_full() we clear the timer. At the same time, + * in _l3_acd_data_state_change(ACD_STATE_CHANGE_MODE_TIMEOUT) we only + * expect timeouts in certain states. + * + * That means, scheduling a timeout is only correct if we are in a certain + * state, which allows to handle timeouts. This assert checks for that to + * ensure we don't call a timeout in an unexpected state. */ + nm_assert(NM_IN_SET(acd_data->info.state, + NM_L3_ACD_ADDR_STATE_PROBING, + NM_L3_ACD_ADDR_STATE_USED, + NM_L3_ACD_ADDR_STATE_DEFENDING, + NM_L3_ACD_ADDR_STATE_CONFLICT)); + nm_clear_g_source_inst(&acd_data->acd_data_timeout_source); acd_data->acd_data_timeout_source = - nm_g_timeout_source_new(NM_CLAMP((gint64) 0, timeout_msec, (gint64) G_MAXUINT), - G_PRIORITY_DEFAULT, + nm_g_timeout_add_source(NM_CLAMP((gint64) 0, timeout_msec, (gint64) G_MAXUINT), _l3_acd_data_timeout_cb, - acd_data, - NULL); - g_source_attach(acd_data->acd_data_timeout_source, NULL); + acd_data); } static void @@ -1899,7 +2071,7 @@ _nm_l3cfg_emit_signal_notify_acd_event(NML3Cfg *self, AcdData *acd_data) { gs_free NML3AcdAddrTrackInfo *track_infos_clone = NULL; NML3ConfigNotifyData notify_data; - NML3AcdAddrInfo * info; + NML3AcdAddrInfo *info; guint i; nm_assert(acd_data); @@ -1908,7 +2080,7 @@ _nm_l3cfg_emit_signal_notify_acd_event(NML3Cfg *self, AcdData *acd_data) notify_data.notify_type = NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT; notify_data.acd_event = (typeof(notify_data.acd_event)){ - .info = acd_data->info, + .info = acd_data->info, }; /* we need to clone the track-data, because the callee is allowed to add/remove @@ -1952,7 +2124,7 @@ static void _nm_l3cfg_emit_signal_notify_acd_event_all(NML3Cfg *self) { gs_unref_object NML3Cfg *self_keep_alive = NULL; - AcdData * acd_data; + AcdData *acd_data; while ((acd_data = c_list_first_entry(&self->priv.p->acd_event_notify_lst_head, AcdData, @@ -1964,11 +2136,11 @@ _nm_l3cfg_emit_signal_notify_acd_event_all(NML3Cfg *self) } } -_nm_printf(5, 6) static void _l3_acd_data_state_set_full(NML3Cfg * self, - AcdData * acd_data, +_nm_printf(5, 6) static void _l3_acd_data_state_set_full(NML3Cfg *self, + AcdData *acd_data, NML3AcdAddrState state, gboolean allow_commit, - const char * format, + const char *format, ...) { NML3AcdAddrState old_state; @@ -1993,17 +2165,19 @@ _nm_printf(5, 6) static void _l3_acd_data_state_set_full(NML3Cfg * self, else changed = FALSE; - if (format) { - gs_free char *msg = NULL; - va_list args; + if (_LOGT_ENABLED()) { + if (format) { + gs_free char *msg = NULL; + va_list args; - va_start(args, format); - msg = g_strdup_vprintf(format, args); - va_end(args); + va_start(args, format); + msg = g_strdup_vprintf(format, args); + va_end(args); - _LOGT_acd(acd_data, "set state to %s (%s)", _l3_acd_addr_state_to_string(state), msg); - } else - _LOGT_acd(acd_data, "set state to %s", _l3_acd_addr_state_to_string(state)); + _LOGT_acd(acd_data, "set state to %s (%s)", _l3_acd_addr_state_to_string(state), msg); + } else + _LOGT_acd(acd_data, "set state to %s", _l3_acd_addr_state_to_string(state)); + } if (changed && allow_commit) { /* The availability of an address just changed (and we are instructed to @@ -2014,8 +2188,8 @@ _nm_printf(5, 6) static void _l3_acd_data_state_set_full(NML3Cfg * self, } static void -_l3_acd_data_state_set(NML3Cfg * self, - AcdData * acd_data, +_l3_acd_data_state_set(NML3Cfg *self, + AcdData *acd_data, NML3AcdAddrState state, gboolean allow_commit) { @@ -2023,17 +2197,17 @@ _l3_acd_data_state_set(NML3Cfg * self, } static void -_l3_acd_data_state_change(NML3Cfg * self, - AcdData * acd_data, +_l3_acd_data_state_change(NML3Cfg *self, + AcdData *acd_data, AcdStateChangeMode state_change_mode, const NMEtherAddr *sender_addr, - gint64 * p_now_msec) + gint64 *p_now_msec) { guint32 acd_timeout_msec; NML3AcdDefendType acd_defend_type; gint64 now_msec; - const char * log_reason; + const char *log_reason; char sbuf256[256]; char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; @@ -2260,22 +2434,7 @@ handle_init: /* we are already probing. There is nothing to do for this timeout. */ return; } - - nm_utils_get_monotonic_timestamp_msec_cached(p_now_msec); - - if (acd_data->probing_timestamp_msec + ACD_WAIT_PROBING_EXTRA_TIME_MSEC - + ACD_WAIT_PROBING_EXTRA_TIME2_MSEC - >= (*p_now_msec)) { - /* hm. We failed to create a new probe too long. Something is really wrong - * internally, but let's ignore the issue and assume the address is good. What - * else would we do? Assume the address is USED? */ - _LOGT_acd(acd_data, - "probe-good (waiting for creating probe timed out. Assume good)"); - goto handle_start_defending; - } - - log_reason = "retry probing on timeout"; - goto handle_start_probing; + /* fall-through */ case NM_L3_ACD_ADDR_STATE_USED: case NM_L3_ACD_ADDR_STATE_CONFLICT: @@ -2295,7 +2454,10 @@ handle_init: acd_data->acd_defend_type_desired = acd_defend_type; if (acd_timeout_msec <= 0) { - log_reason = "acd disabled by configuration (restart after previous conflict)"; + if (acd_data->info.state == NM_L3_ACD_ADDR_STATE_PROBING) + log_reason = "acd disabled by configuration (timeout during probing)"; + else + log_reason = "acd disabled by configuration (restart after previous conflict)"; goto handle_probing_done; } @@ -2305,6 +2467,23 @@ handle_init: } nm_utils_get_monotonic_timestamp_msec_cached(p_now_msec); + + if (acd_data->info.state == NM_L3_ACD_ADDR_STATE_PROBING) { + if ((*p_now_msec) > acd_data->probing_timestamp_msec + + ACD_WAIT_PROBING_EXTRA_TIME_MSEC + + ACD_WAIT_PROBING_EXTRA_TIME2_MSEC) { + /* hm. We failed to create a new probe too long. Something is really wrong + * internally, but let's ignore the issue and assume the address is good. What + * else would we do? Assume the address is USED? */ + _LOGT_acd(acd_data, + "probe-good (waiting for creating probe timed out. Assume good)"); + goto handle_start_defending; + } + + log_reason = "retry probing on timeout"; + goto handle_start_probing; + } + acd_data->probing_timestamp_msec = (*p_now_msec); acd_data->probing_timeout_msec = acd_timeout_msec; if (acd_data->info.state == NM_L3_ACD_ADDR_STATE_USED) @@ -2525,40 +2704,23 @@ handle_init: return; case ACD_STATE_CHANGE_MODE_INSTANCE_RESET: + nm_assert(NM_IN_SET(acd_data->info.state, + NM_L3_ACD_ADDR_STATE_PROBING, + NM_L3_ACD_ADDR_STATE_DEFENDING, + NM_L3_ACD_ADDR_STATE_READY, + NM_L3_ACD_ADDR_STATE_USED, + NM_L3_ACD_ADDR_STATE_CONFLICT, + NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED)); - switch (acd_data->info.state) { - case NM_L3_ACD_ADDR_STATE_INIT: - nm_assert_not_reached(); - return; - case NM_L3_ACD_ADDR_STATE_PROBING: - case NM_L3_ACD_ADDR_STATE_DEFENDING: - - if (!acd_data->nacd_probe) { - /* we failed starting to probe before and have a timer running to - * restart. We don't do anything now, but let the timer handle it. - * This also implements some rate limiting for us. */ - _LOGT_acd(acd_data, - "n-acd instance reset. Ignore event while restarting %s", - (acd_data->info.state == NM_L3_ACD_ADDR_STATE_PROBING) ? "probing" - : "defending"); - return; - } - - _LOGT_acd(acd_data, - "n-acd instance reset. Trigger a restart of the %s", - (acd_data->info.state == NM_L3_ACD_ADDR_STATE_PROBING) ? "probing" - : "defending"); - acd_data->nacd_probe = n_acd_probe_free(acd_data->nacd_probe); - _l3_acd_data_timeout_schedule(acd_data, 0); - return; - case NM_L3_ACD_ADDR_STATE_READY: - case NM_L3_ACD_ADDR_STATE_USED: - case NM_L3_ACD_ADDR_STATE_CONFLICT: - case NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED: - nm_assert(!acd_data->nacd_probe); - return; - } - nm_assert_not_reached(); + /* an instance-reset is a dramatic event. We start over with probing. */ + _LOGT_acd(acd_data, + "n-acd instance reset. Reset to probing while in state %s", + _l3_acd_addr_state_to_string(acd_data->info.state)); + acd_data->nacd_probe = n_acd_probe_free(acd_data->nacd_probe); + acd_data->last_defendconflict_timestamp_msec = 0; + acd_data->probing_timestamp_msec = nm_utils_get_monotonic_timestamp_msec_cached(p_now_msec); + _l3_acd_data_state_set(self, acd_data, NM_L3_ACD_ADDR_STATE_PROBING, FALSE); + _l3_acd_data_timeout_schedule(acd_data, 0); return; } @@ -2569,7 +2731,7 @@ handle_start_probing: if (TRUE) { const NML3AcdAddrState orig_state = acd_data->info.state; nm_auto(n_acd_probe_freep) NAcdProbe *probe = NULL; - const char * failure_reason; + const char *failure_reason; gboolean acd_not_supported; nm_assert(NM_IN_SET(acd_data->info.state, @@ -2646,10 +2808,10 @@ handle_probing_done: goto handle_start_defending; case NM_L3_ACD_ADDR_STATE_READY: case NM_L3_ACD_ADDR_STATE_DEFENDING: + case NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED: goto handle_start_defending; case NM_L3_ACD_ADDR_STATE_CONFLICT: return; - case NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED: nm_assert_not_reached(); return; } @@ -2689,7 +2851,7 @@ handle_start_defending: if (!acd_data->nacd_probe) { const char *failure_reason; - NAcdProbe * probe; + NAcdProbe *probe; if (acd_data->acd_data_timeout_source) { /* we already failed to create a probe. We are ratelimited to retry, but @@ -2786,11 +2948,11 @@ nm_l3cfg_get_acd_addr_info(NML3Cfg *self, in_addr_t addr) /*****************************************************************************/ gboolean -nm_l3cfg_check_ready(NML3Cfg * self, - const NML3ConfigData * l3cd, +nm_l3cfg_check_ready(NML3Cfg *self, + const NML3ConfigData *l3cd, int addr_family, NML3CfgCheckReadyFlags flags, - gboolean * acd_used) + gboolean *acd_used) { NMDedupMultiIter iter; const NMPObject *obj; @@ -2869,7 +3031,7 @@ static gboolean _l3_commit_on_idle_cb(gpointer user_data) { _nm_unused gs_unref_object NML3Cfg *self_keep_alive = NULL; - NML3Cfg * self = user_data; + NML3Cfg *self = user_data; NML3CfgCommitType commit_type; commit_type = self->priv.p->commit_on_idle_type; @@ -2948,7 +3110,7 @@ nm_l3cfg_commit_on_idle_is_scheduled(NML3Cfg *self) (&g_array_index((l3_config_datas), L3ConfigData, (idx))) static gssize -_l3_config_datas_find_next(GArray * l3_config_datas, +_l3_config_datas_find_next(GArray *l3_config_datas, guint start_idx, gconstpointer needle_tag, const NML3ConfigData *needle_l3cd) @@ -2978,9 +3140,9 @@ _l3_config_datas_get_sorted_cmp(gconstpointer p_a, gconstpointer p_b, gpointer u nm_assert(b); nm_assert(nm_l3_config_data_get_ifindex(a->l3cd) == nm_l3_config_data_get_ifindex(b->l3cd)); - /* we sort the entries with higher priority (more important, lower numerical value) + /* we sort the entries with higher priority (higher numerical value, more important) * first. */ - NM_CMP_FIELD(a, b, priority_confdata); + NM_CMP_FIELD(b, a, priority_confdata); /* if the priority is not unique, we sort them in the order they were added, * with the oldest first (lower numerical value). */ @@ -3028,8 +3190,16 @@ nm_l3cfg_mark_config_dirty(NML3Cfg *self, gconstpointer tag, gboolean dirty) } } +/* + * nm_l3cfg_add_config: + * @priority: all l3cd get merged/combined. This merging requires that some + * l3cd are more important than others. For example, coming from static IP + * configuration needs to take precedence over DHCP. The @priority determines + * the order in which l3cds get merged (and thus the outcome). Higher numbers + * mean more important!! + */ gboolean -nm_l3cfg_add_config(NML3Cfg * self, +nm_l3cfg_add_config(NML3Cfg *self, gconstpointer tag, gboolean replace_same_tag, const NML3ConfigData *l3cd, @@ -3123,7 +3293,8 @@ nm_l3cfg_add_config(NML3Cfg * self, .acd_timeout_msec_confdata = acd_timeout_msec, .priority_confdata = priority, .pseudo_timestamp_confdata = ++self->priv.p->pseudo_timestamp_counter, - .dirty_confdata = FALSE, + .force_commit_once = NM_FLAGS_HAS(config_flags, NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE), + .dirty_confdata = FALSE, }; changed = TRUE; } else { @@ -3194,7 +3365,7 @@ nm_l3cfg_add_config(NML3Cfg * self, } static gboolean -_l3cfg_remove_config(NML3Cfg * self, +_l3cfg_remove_config(NML3Cfg *self, gconstpointer tag, gboolean only_dirty, const NML3ConfigData *l3cd) @@ -3263,22 +3434,70 @@ nm_l3cfg_remove_config_all_dirty(NML3Cfg *self, gconstpointer tag) /*****************************************************************************/ +#define _NODEV_ROUTES_TAG(self, IS_IPv4) ((gconstpointer) (&(&(self)->priv.route_manager)[IS_IPv4])) + +static gboolean +_nodev_routes_untrack(NML3Cfg *self, int addr_family) +{ + return nmp_route_manager_untrack_all(self->priv.route_manager, + _NODEV_ROUTES_TAG(self, NM_IS_IPv4(addr_family)), + FALSE, + TRUE); +} + +static void +_nodev_routes_sync(NML3Cfg *self, + int addr_family, + NML3CfgCommitType commit_type, + GPtrArray *routes_nodev) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + const NMPObjectType obj_type = NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4); + guint i; + gboolean changed = FALSE; + + if (!routes_nodev) + goto out_clear; + + for (i = 0; i < routes_nodev->len; i++) { + const NMPObject *obj = routes_nodev->pdata[i]; + + if (nmp_route_manager_track(self->priv.route_manager, + obj_type, + NMP_OBJECT_CAST_IP_ROUTE(obj), + 1, + _NODEV_ROUTES_TAG(self, IS_IPv4), + NULL)) + changed = TRUE; + } + +out_clear: + if (_nodev_routes_untrack(self, addr_family)) + changed = TRUE; + + if (changed || commit_type >= NM_L3_CFG_COMMIT_TYPE_REAPPLY) + nmp_route_manager_sync(self->priv.route_manager, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), FALSE); +} + +/*****************************************************************************/ + typedef struct { - NML3Cfg * self; + NML3Cfg *self; gconstpointer tag; bool assume_config_once; bool to_commit; + bool force_commit_once; } L3ConfigMergeHookAddObjData; static gboolean -_l3_hook_add_obj_cb(const NML3ConfigData * l3cd, - const NMPObject * obj, +_l3_hook_add_obj_cb(const NML3ConfigData *l3cd, + const NMPObject *obj, NML3ConfigMergeHookResult *hook_result, gpointer user_data) { const L3ConfigMergeHookAddObjData *hook_data = user_data; - NML3Cfg * self = hook_data->self; - AcdData * acd_data; + NML3Cfg *self = hook_data->self; + AcdData *acd_data; in_addr_t addr; gboolean acd_bad = FALSE; @@ -3286,8 +3505,10 @@ _l3_hook_add_obj_cb(const NML3ConfigData * l3cd, nm_assert(hook_result); nm_assert(hook_result->ip4acd_not_ready == NM_OPTION_BOOL_DEFAULT); nm_assert(hook_result->assume_config_once == NM_OPTION_BOOL_DEFAULT); + nm_assert(hook_result->force_commit == NM_OPTION_BOOL_DEFAULT); hook_result->assume_config_once = hook_data->assume_config_once; + hook_result->force_commit = hook_data->force_commit_once; switch (NMP_OBJECT_GET_TYPE(obj)) { case NMP_OBJECT_TYPE_IP4_ADDRESS: @@ -3327,7 +3548,8 @@ _l3_hook_add_obj_cb(const NML3ConfigData * l3cd, if (!NM_IN_SET(acd_data->info.state, NM_L3_ACD_ADDR_STATE_READY, - NM_L3_ACD_ADDR_STATE_DEFENDING)) { + NM_L3_ACD_ADDR_STATE_DEFENDING, + NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED)) { acd_bad = TRUE; goto out_ip4_address; } @@ -3347,21 +3569,21 @@ out_ip4_address: } static void -_l3cfg_update_combined_config(NML3Cfg * self, +_l3cfg_update_combined_config(NML3Cfg *self, gboolean to_commit, gboolean reapply, const NML3ConfigData **out_old /* transfer reference */, - gboolean * out_changed_combined_l3cd) + gboolean *out_changed_combined_l3cd) { - nm_auto_unref_l3cd const NML3ConfigData *l3cd_commited_old = NULL; - nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; - nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; - gs_free const L3ConfigData **l3_config_datas_free = NULL; - const L3ConfigData ** l3_config_datas_arr; - guint l3_config_datas_len; - guint i; - gboolean merged_changed = FALSE; - gboolean commited_changed = FALSE; + nm_auto_unref_l3cd const NML3ConfigData *l3cd_commited_old = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + gs_free const L3ConfigData **l3_config_datas_free = NULL; + const L3ConfigData **l3_config_datas_arr; + guint l3_config_datas_len; + guint i; + gboolean merged_changed = FALSE; + gboolean commited_changed = FALSE; nm_assert(NM_IS_L3CFG(self)); nm_assert(!out_old || !*out_old); @@ -3388,6 +3610,20 @@ _l3cfg_update_combined_config(NML3Cfg * self, l3_config_datas_arr[i] = _l3_config_datas_at(self->priv.p->l3_config_datas, i); if (l3_config_datas_len > 1) { + /* We are about to merge the l3cds. The order in which we do that matters. + * + * Below, we iterate over the l3cds and merge them into a new one. nm_l3_config_data_merge() + * uses "NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE" flag, which means to keep the first entry. + * + * Consider for example addresses/routes, which have a set of ID attributes (based on + * which no duplicates can be accepted) and additional attributes. For example, trying + * to add the same address twice ("same" according to their ID), only one can be added. + * If they differ in their lifetimes, we need to make a choice. + * We could merge the attributes in a sensible way. Instead, NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE + * takes care to only take the first one. + * + * So we want to sort the more important entries *first*, and this is based on + * the priority_confdata. */ g_qsort_with_data(l3_config_datas_arr, l3_config_datas_len, sizeof(l3_config_datas_arr[0]), @@ -3417,12 +3653,21 @@ _l3cfg_update_combined_config(NML3Cfg * self, for (i = 0; i < l3_config_datas_len; i++) { const L3ConfigData *l3cd_data = l3_config_datas_arr[i]; + /* more important entries must be sorted *first*. */ + nm_assert( + i == 0 + || (l3_config_datas_arr[i - 1]->priority_confdata > l3cd_data->priority_confdata) + || (l3_config_datas_arr[i - 1]->priority_confdata == l3cd_data->priority_confdata + && l3_config_datas_arr[i - 1]->pseudo_timestamp_confdata + < l3cd_data->pseudo_timestamp_confdata)); + if (NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ONLY_FOR_ACD)) continue; hook_data.tag = l3cd_data->tag_confdata; hook_data.assume_config_once = NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE); + hook_data.force_commit_once = l3cd_data->force_commit_once; nm_l3_config_data_merge(l3cd, l3cd_data->l3cd, @@ -3448,6 +3693,7 @@ _l3cfg_update_combined_config(NML3Cfg * self, IS_IPv4 ? AF_INET : AF_INET6, l3cd_data->default_route_table_x[IS_IPv4], l3cd_data->default_route_metric_x[IS_IPv4], + l3cd_data->force_commit_once, l3cd_data->l3cd); } } @@ -3524,7 +3770,7 @@ out: static gboolean _routes_temporary_not_available_timeout(gpointer user_data) { - NML3Cfg * self = NM_L3CFG(user_data); + NML3Cfg *self = NM_L3CFG(user_data); ObjStateData *obj_state; gint64 now_msec; gint64 expiry_msec; @@ -3563,12 +3809,12 @@ _routes_temporary_not_available_timeout(gpointer user_data) } static gboolean -_routes_temporary_not_available_update(NML3Cfg * self, +_routes_temporary_not_available_update(NML3Cfg *self, int addr_family, GPtrArray *routes_temporary_not_available_arr) { - ObjStateData * obj_state; - ObjStateData * obj_state_safe; + ObjStateData *obj_state; + ObjStateData *obj_state_safe; gint64 now_msec; gboolean prune_all = FALSE; gboolean success = TRUE; @@ -3673,6 +3919,7 @@ out_prune: } /*****************************************************************************/ + static const char * ip6_privacy_to_str(NMSettingIP6ConfigPrivacy ip6_privacy) { @@ -3699,7 +3946,7 @@ _l3_commit_ndisc_params(NML3Cfg *self, NML3CfgCommitType commit_type) guint32 reachable = 0; guint32 retrans = 0; int hop_limit = 0; - const char * ifname; + const char *ifname; if (commit_type < NM_L3_CFG_COMMIT_TYPE_UPDATE) { self->priv.p->ndisc_reachable_time_msec_set = FALSE; @@ -3757,7 +4004,7 @@ _l3_commit_ip6_privacy(NML3Cfg *self, NML3CfgCommitType commit_type) { NMSettingIP6ConfigPrivacy ip6_privacy; NMSettingIP6ConfigPrivacy ip6_privacy_set_before; - const char * ifname; + const char *ifname; if (commit_type < NM_L3_CFG_COMMIT_TYPE_UPDATE) ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; @@ -3861,16 +4108,79 @@ set: ip6_privacy_to_str(ip6_privacy)); } +static void +_l3_commit_ip6_token(NML3Cfg *self, NML3CfgCommitType commit_type) +{ + NMUtilsIPv6IfaceId token; + const NMPlatformLink *pllink; + int val; + + if (commit_type < NM_L3_CFG_COMMIT_TYPE_UPDATE || !self->priv.p->combined_l3cd_commited) + token.id = 0; + else + token = nm_l3_config_data_get_ip6_token(self->priv.p->combined_l3cd_commited); + + pllink = nm_l3cfg_get_pllink(self, TRUE); + if (!pllink || pllink->inet6_token.id == token.id) + return; + + if (_LOGT_ENABLED()) { + struct in6_addr addr = {}; + struct in6_addr addr_old = {}; + char addr_str[INET6_ADDRSTRLEN]; + char addr_str_old[INET6_ADDRSTRLEN]; + + nm_utils_ipv6_addr_set_interface_identifier(&addr, &token); + nm_utils_ipv6_addr_set_interface_identifier(&addr_old, &pllink->inet6_token); + + _LOGT("commit-ip6-token: set value %s (was %s)", + inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &addr_old, addr_str_old, INET6_ADDRSTRLEN)); + } + + /* The kernel allows setting a token only when 'accept_ra' + * is 1: temporarily flip it if necessary; unfortunately + * this will also generate an additional Router Solicitation + * from kernel. */ + val = nm_platform_sysctl_ip_conf_get_int_checked(self->priv.platform, + AF_INET6, + pllink->name, + "accept_ra", + 10, + G_MININT32, + G_MAXINT32, + 1); + + if (val != 1) { + nm_platform_sysctl_ip_conf_set(self->priv.platform, + AF_INET6, + pllink->name, + "accept_ra", + "1"); + } + + nm_platform_link_set_ipv6_token(self->priv.platform, self->priv.ifindex, &token); + + if (val != 1) { + nm_platform_sysctl_ip_conf_set_int64(self->priv.platform, + AF_INET6, + pllink->name, + "accept_ra", + val); + } +} + static gboolean -_l3_commit_one(NML3Cfg * self, +_l3_commit_one(NML3Cfg *self, int addr_family, NML3CfgCommitType commit_type, gboolean changed_combined_l3cd, const NML3ConfigData *l3cd_old) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); + const int IS_IPv4 = NM_IS_IPv4(addr_family); gs_unref_ptrarray GPtrArray *addresses = NULL; gs_unref_ptrarray GPtrArray *routes = NULL; + gs_unref_ptrarray GPtrArray *routes_nodev = NULL; gs_unref_ptrarray GPtrArray *addresses_prune = NULL; gs_unref_ptrarray GPtrArray *routes_prune = NULL; gs_unref_ptrarray GPtrArray *routes_temporary_not_available_arr = NULL; @@ -3892,23 +4202,9 @@ _l3_commit_one(NML3Cfg * self, _l3_cfg_commit_type_to_string(commit_type, sbuf_commit_type, sizeof(sbuf_commit_type))); if (self->priv.p->combined_l3cd_commited) { - const NMDedupMultiHeadEntry * head_entry; - const ObjStatesSyncFilterData sync_filter_data = { - .self = self, - .commit_type = commit_type, - }; - - head_entry = nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited, - NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)); - addresses = nm_dedup_multi_objs_to_ptr_array_head(head_entry, - _obj_states_sync_filter, - (gpointer) &sync_filter_data); + addresses = _commit_collect_addresses(self, addr_family, commit_type); - head_entry = nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited, - NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)); - routes = nm_dedup_multi_objs_to_ptr_array_head(head_entry, - _obj_states_sync_filter, - (gpointer) &sync_filter_data); + _commit_collect_routes(self, addr_family, commit_type, &routes, &routes_nodev); route_table_sync = nm_l3_config_data_get_route_table_sync(self->priv.p->combined_l3cd_commited, @@ -3918,10 +4214,11 @@ _l3_commit_one(NML3Cfg * self, if (!IS_IPv4) { _l3_commit_ip6_privacy(self, commit_type); _l3_commit_ndisc_params(self, commit_type); + _l3_commit_ip6_token(self, commit_type); } if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) - route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_ALL; + route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN; if (commit_type == NM_L3_CFG_COMMIT_TYPE_REAPPLY) { addresses_prune = nm_platform_ip_address_get_prune_list(self->priv.platform, @@ -3945,6 +4242,8 @@ _l3_commit_one(NML3Cfg * self, addresses, addresses_prune); + _nodev_routes_sync(self, addr_family, commit_type, routes_nodev); + if (!nm_platform_ip_route_sync(self->priv.platform, addr_family, self->priv.ifindex, @@ -3968,13 +4267,14 @@ _l3_commit_one(NML3Cfg * self, static void _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle) { - _nm_unused gs_unref_object NML3Cfg *self_keep_alive = NULL; - nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; + _nm_unused gs_unref_object NML3Cfg *self_keep_alive = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; NML3CfgCommitType commit_type_auto; gboolean commit_type_from_auto = FALSE; gboolean is_sticky_update = FALSE; char sbuf_ct[30]; gboolean changed_combined_l3cd; + guint i; g_return_if_fail(NM_IS_L3CFG(self)); nm_assert(NM_IN_SET(commit_type, @@ -4038,6 +4338,15 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle) _l3_acd_data_process_changes(self); + if (self->priv.p->l3_config_datas) { + for (i = 0; i < self->priv.p->l3_config_datas->len; i++) { + L3ConfigData *l3_config_data = _l3_config_datas_at(self->priv.p->l3_config_datas, i); + + if (l3_config_data->force_commit_once) + l3_config_data->force_commit_once = FALSE; + } + } + nm_assert(self->priv.p->commit_reentrant_count == 1); self->priv.p->commit_reentrant_count--; @@ -4083,10 +4392,10 @@ nm_l3cfg_commit_type_get(NML3Cfg *self) * is %NM_L3_CFG_COMMIT_TYPE_NONE. */ NML3CfgCommitTypeHandle * -nm_l3cfg_commit_type_register(NML3Cfg * self, +nm_l3cfg_commit_type_register(NML3Cfg *self, NML3CfgCommitType commit_type, NML3CfgCommitTypeHandle *existing_handle, - const char * source) + const char *source) { NML3CfgCommitTypeHandle *handle; NML3CfgCommitTypeHandle *h; @@ -4170,6 +4479,20 @@ nm_l3cfg_commit_type_unregister(NML3Cfg *self, NML3CfgCommitTypeHandle *handle) nm_g_slice_free(handle); } +void +nm_l3cfg_commit_type_reset_update(NML3Cfg *self) +{ + NML3CfgCommitTypeHandle *h; + + c_list_for_each_entry (h, &self->priv.p->commit_type_lst_head, commit_type_lst) { + if (h->commit_type >= NM_L3_CFG_COMMIT_TYPE_UPDATE) { + return; + } + } + + self->priv.p->commit_type_update_sticky = FALSE; +} + /*****************************************************************************/ const NML3ConfigData * @@ -4202,7 +4525,7 @@ gboolean nm_l3cfg_has_commited_ip6_addresses_pending_dad(NML3Cfg *self) { const NML3ConfigData *l3cd; - const NMPObject * plat_obj; + const NMPObject *plat_obj; NMPLookup plat_lookup; NMDedupMultiIter iter; @@ -4224,7 +4547,7 @@ nm_l3cfg_has_commited_ip6_addresses_pending_dad(NML3Cfg *self) nm_platform_iter_obj_for_each (&iter, self->priv.platform, &plat_lookup, &plat_obj) { const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj); - const NMDedupMultiEntry * l3cd_entry; + const NMDedupMultiEntry *l3cd_entry; if (!NM_FLAGS_HAS(plat_addr->n_ifa_flags, IFA_F_TENTATIVE) || NM_FLAGS_ANY(plat_addr->n_ifa_flags, IFA_F_DADFAILED | IFA_F_OPTIMISTIC)) @@ -4355,6 +4678,8 @@ constructed(GObject *object) self->priv.platform = g_object_ref(nm_netns_get_platform(self->priv.netns)); nm_assert(NM_IS_PLATFORM(self->priv.platform)); + self->priv.route_manager = nmp_route_manager_ref(nm_netns_get_route_manager(self->priv.netns)); + _LOGT("created (netns=" NM_HASH_OBFUSCATE_PTR_FMT ")", NM_HASH_OBFUSCATE_PTR(self->priv.netns)); G_OBJECT_CLASS(nm_l3cfg_parent_class)->constructed(object); @@ -4376,6 +4701,9 @@ finalize(GObject *object) { NML3Cfg *self = NM_L3CFG(object); + nm_assert(!self->priv.p->ipconfig_4); + nm_assert(!self->priv.p->ipconfig_6); + nm_assert(!self->priv.p->l3_config_datas); nm_assert(!self->priv.p->ipv4ll); @@ -4393,6 +4721,7 @@ finalize(GObject *object) nm_clear_pointer(&self->priv.p->nacd, n_acd_unref); nm_clear_g_source_inst(&self->priv.p->nacd_source); nm_clear_g_source_inst(&self->priv.p->nacd_instance_ensure_retry); + nm_clear_g_source_inst(&self->priv.p->nacd_event_down_source); nm_clear_g_source_inst(&self->priv.p->obj_state_temporary_not_available_timeout_source); @@ -4401,8 +4730,14 @@ finalize(GObject *object) nm_assert(c_list_is_empty(&self->priv.p->obj_state_temporary_not_available_lst_head)); nm_assert(c_list_is_empty(&self->priv.p->obj_state_zombie_lst_head)); + if (_nodev_routes_untrack(self, AF_INET)) + nmp_route_manager_sync(self->priv.route_manager, NMP_OBJECT_TYPE_IP4_ROUTE, FALSE); + if (_nodev_routes_untrack(self, AF_INET6)) + nmp_route_manager_sync(self->priv.route_manager, NMP_OBJECT_TYPE_IP6_ROUTE, FALSE); + g_clear_object(&self->priv.netns); g_clear_object(&self->priv.platform); + nm_clear_pointer(&self->priv.route_manager, nmp_route_manager_unref); nm_clear_l3cd(&self->priv.p->combined_l3cd_merged); nm_clear_l3cd(&self->priv.p->combined_l3cd_commited); |