2020-09-29 16:42:22 +02:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2020-07-18 19:01:04 +02:00
|
|
|
|
|
|
|
|
#include "nm-default.h"
|
|
|
|
|
|
|
|
|
|
#include "nm-l3cfg.h"
|
|
|
|
|
|
2020-08-03 17:33:31 +02:00
|
|
|
#include <net/if.h>
|
2020-09-16 13:18:54 +02:00
|
|
|
#include <linux/if_addr.h>
|
2020-09-23 15:55:28 +02:00
|
|
|
#include <linux/rtnetlink.h>
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-07-18 19:01:04 +02:00
|
|
|
#include "platform/nm-platform.h"
|
2020-07-21 11:57:31 +02:00
|
|
|
#include "platform/nmp-object.h"
|
2020-07-18 19:01:04 +02:00
|
|
|
#include "nm-netns.h"
|
2020-08-03 17:33:31 +02:00
|
|
|
#include "n-acd/src/n-acd.h"
|
2020-07-18 19:01:04 +02:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-08-03 17:33:31 +02:00
|
|
|
#define ACD_SUPPORTED_ETH_ALEN ETH_ALEN
|
|
|
|
|
#define ACD_ENSURE_RATELIMIT_MSEC ((guint32) 4000u)
|
2020-09-28 16:03:33 +02:00
|
|
|
#define ACD_WAIT_PROBING_EXTRA_TIME_MSEC ((guint32)(1000u + ACD_ENSURE_RATELIMIT_MSEC))
|
2020-08-03 17:33:31 +02:00
|
|
|
#define ACD_WAIT_PROBING_EXTRA_TIME2_MSEC ((guint32) 1000u)
|
|
|
|
|
#define ACD_WAIT_PROBING_RESTART_TIME_MSEC ((guint32) 8000u)
|
|
|
|
|
#define ACD_MAX_TIMEOUT_MSEC ((guint32) 30000u)
|
|
|
|
|
#define ACD_WAIT_TIME_PROBING_FULL_RESTART_MSEC ((guint32) 30000u)
|
|
|
|
|
#define ACD_WAIT_TIME_ANNOUNCE_RESTART_MSEC ((guint32) 20000u)
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
ACD_ADDR_SKIP(in_addr_t addr)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
return addr == 0u;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
#define ACD_TRACK_FMT \
|
|
|
|
|
"[l3cd=" NM_HASH_OBFUSCATE_PTR_FMT ",obj=" NM_HASH_OBFUSCATE_PTR_FMT \
|
|
|
|
|
",tag=" NM_HASH_OBFUSCATE_PTR_FMT "]"
|
|
|
|
|
#define ACD_TRACK_PTR2(l3cd, obj, tag) \
|
|
|
|
|
NM_HASH_OBFUSCATE_PTR(l3cd), NM_HASH_OBFUSCATE_PTR(obj), NM_HASH_OBFUSCATE_PTR(tag)
|
|
|
|
|
#define ACD_TRACK_PTR(acd_track) \
|
|
|
|
|
ACD_TRACK_PTR2((acd_track)->l3cd, (acd_track)->obj, (acd_track)->tag)
|
2020-08-03 17:33:31 +02:00
|
|
|
|
|
|
|
|
typedef enum {
|
2020-09-28 16:03:33 +02:00
|
|
|
ACD_STATE_CHANGE_MODE_INIT,
|
|
|
|
|
ACD_STATE_CHANGE_MODE_POST_COMMIT,
|
|
|
|
|
|
|
|
|
|
ACD_STATE_CHANGE_MODE_NACD_READY,
|
|
|
|
|
ACD_STATE_CHANGE_MODE_NACD_USED,
|
|
|
|
|
ACD_STATE_CHANGE_MODE_NACD_DOWN,
|
|
|
|
|
|
|
|
|
|
ACD_STATE_CHANGE_MODE_EXTERNAL_ADDED,
|
|
|
|
|
ACD_STATE_CHANGE_MODE_EXTERNAL_REMOVED,
|
|
|
|
|
ACD_STATE_CHANGE_MODE_LINK_NOW_UP,
|
|
|
|
|
ACD_STATE_CHANGE_MODE_INSTANCE_RESET,
|
|
|
|
|
ACD_STATE_CHANGE_MODE_TIMEOUT,
|
2020-08-03 17:33:31 +02:00
|
|
|
} AcdStateChangeMode;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2020-09-28 16:03:33 +02:00
|
|
|
CList acd_track_lst;
|
|
|
|
|
const NMPObject * obj;
|
|
|
|
|
const NML3ConfigData *l3cd;
|
|
|
|
|
gconstpointer tag;
|
|
|
|
|
guint32 acd_timeout_msec;
|
|
|
|
|
bool acd_dirty : 1;
|
|
|
|
|
bool acd_failed_notified : 1;
|
2020-08-03 17:33:31 +02:00
|
|
|
} AcdTrackData;
|
|
|
|
|
|
|
|
|
|
typedef enum _nm_packed {
|
2020-09-28 16:03:33 +02:00
|
|
|
ACD_STATE_INIT,
|
|
|
|
|
ACD_STATE_PROBING,
|
|
|
|
|
ACD_STATE_PROBE_DONE,
|
|
|
|
|
ACD_STATE_ANNOUNCING,
|
2020-08-03 17:33:31 +02:00
|
|
|
} AcdState;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2020-09-28 16:03:33 +02:00
|
|
|
in_addr_t addr;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* This is only relevant while in state ACD_STATE_PROBING. It's the
|
2020-09-28 14:50:01 +02:00
|
|
|
* duration for how long we probe, and @probing_timestamp_msec is the
|
|
|
|
|
* timestamp when we start probing. */
|
2020-09-28 16:03:33 +02:00
|
|
|
guint32 probing_timeout_msec;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
CList acd_lst;
|
|
|
|
|
CList acd_notify_complete_lst;
|
|
|
|
|
CList acd_track_lst_head;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
NML3Cfg *self;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
NAcdProbe *nacd_probe;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
GSource *acd_timeout_source;
|
|
|
|
|
gint64 acd_timeout_expiry_msec;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* see probing_timeout_msec. */
|
|
|
|
|
gint64 probing_timestamp_msec;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* the ACD state for this address. */
|
|
|
|
|
AcdState acd_state;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* The probe result. This is only relevant if @acd_state is ACD_STATE_PROBE_DONE.
|
2020-09-28 14:50:01 +02:00
|
|
|
* In state ACD_STATE_ANNOUNCING the @probe_result must be TRUE. */
|
2020-09-28 16:03:33 +02:00
|
|
|
bool probe_result : 1;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
bool announcing_failed_is_retrying : 1;
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
bool initializing : 1;
|
2020-08-03 17:33:31 +02:00
|
|
|
} AcdData;
|
|
|
|
|
|
2020-09-03 12:24:08 +02:00
|
|
|
struct _NML3CfgCommitTypeHandle {
|
2020-09-28 16:03:33 +02:00
|
|
|
CList commit_type_lst;
|
|
|
|
|
NML3CfgCommitType commit_type;
|
2020-09-03 12:24:08 +02:00
|
|
|
};
|
|
|
|
|
|
2020-07-23 16:15:26 +02:00
|
|
|
typedef struct {
|
2020-09-28 16:03:33 +02:00
|
|
|
const NML3ConfigData *l3cd;
|
|
|
|
|
NML3ConfigMergeFlags merge_flags;
|
2020-09-23 15:55:28 +02:00
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
guint32 default_route_table_6;
|
|
|
|
|
guint32 default_route_table_4;
|
|
|
|
|
};
|
|
|
|
|
guint32 default_route_table_x[2];
|
|
|
|
|
};
|
|
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
guint32 default_route_metric_6;
|
|
|
|
|
guint32 default_route_metric_4;
|
|
|
|
|
};
|
|
|
|
|
guint32 default_route_metric_x[2];
|
|
|
|
|
};
|
2020-09-28 16:03:33 +02:00
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
guint32 default_route_penalty_6;
|
|
|
|
|
guint32 default_route_penalty_4;
|
|
|
|
|
};
|
|
|
|
|
guint32 default_route_penalty_x[2];
|
|
|
|
|
};
|
|
|
|
|
gconstpointer tag;
|
|
|
|
|
guint64 pseudo_timestamp;
|
|
|
|
|
int priority;
|
|
|
|
|
guint32 acd_timeout_msec;
|
|
|
|
|
bool dirty : 1;
|
2020-07-23 16:15:26 +02:00
|
|
|
} L3ConfigData;
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
NM_GOBJECT_PROPERTIES_DEFINE(NML3Cfg, PROP_NETNS, PROP_IFINDEX, );
|
2020-07-18 19:01:04 +02:00
|
|
|
|
2020-07-28 13:29:36 +02:00
|
|
|
enum {
|
2020-09-28 16:03:33 +02:00
|
|
|
SIGNAL_NOTIFY,
|
|
|
|
|
LAST_SIGNAL,
|
2020-07-28 13:29:36 +02:00
|
|
|
};
|
|
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
static guint signals[LAST_SIGNAL] = {0};
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-07-21 12:39:31 +02:00
|
|
|
typedef struct _NML3CfgPrivate {
|
2020-09-28 16:03:33 +02:00
|
|
|
GArray *property_emit_list;
|
|
|
|
|
GArray *l3_config_datas;
|
2020-09-16 12:44:38 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
const NML3ConfigData *combined_l3cd_merged;
|
2020-09-16 12:44:38 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
const NML3ConfigData *combined_l3cd_commited;
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
CList commit_type_lst_head;
|
2020-09-03 12:24:08 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
GHashTable *routes_temporary_not_available_hash;
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
GHashTable *externally_removed_objs_hash;
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
GHashTable *acd_ipv4_addresses_on_link;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
GHashTable *acd_lst_hash;
|
|
|
|
|
CList acd_lst_head;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
CList acd_notify_complete_lst_head;
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
NAcd * nacd;
|
|
|
|
|
GSource *nacd_source;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* This is for rate-limiting the creation of nacd instance. */
|
|
|
|
|
GSource *nacd_instance_ensure_retry;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
GSource *commit_on_idle_source;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
guint64 pseudo_timestamp_counter;
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
guint externally_removed_objs_cnt_addresses_6;
|
|
|
|
|
guint externally_removed_objs_cnt_addresses_4;
|
|
|
|
|
};
|
|
|
|
|
guint externally_removed_objs_cnt_addresses_x[2];
|
|
|
|
|
};
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
guint externally_removed_objs_cnt_routes_6;
|
|
|
|
|
guint externally_removed_objs_cnt_routes_4;
|
|
|
|
|
};
|
|
|
|
|
guint externally_removed_objs_cnt_routes_x[2];
|
|
|
|
|
};
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
guint routes_temporary_not_available_id;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
bool commit_type_update_sticky : 1;
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
bool acd_is_pending : 1;
|
|
|
|
|
bool acd_is_announcing : 1;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
bool nacd_acd_not_supported : 1;
|
|
|
|
|
bool acd_ipv4_addresses_on_link_has : 1;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-07-21 12:39:31 +02:00
|
|
|
} NML3CfgPrivate;
|
|
|
|
|
|
2020-07-18 19:01:04 +02:00
|
|
|
struct _NML3CfgClass {
|
2020-09-28 16:03:33 +02:00
|
|
|
GObjectClass parent;
|
2020-07-18 19:01:04 +02:00
|
|
|
};
|
|
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
G_DEFINE_TYPE(NML3Cfg, nm_l3cfg, G_TYPE_OBJECT)
|
2020-07-18 19:01:04 +02:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define _NMLOG_DOMAIN LOGD_CORE
|
|
|
|
|
#define _NMLOG_PREFIX_NAME "l3cfg"
|
2020-09-28 16:03:33 +02:00
|
|
|
#define _NMLOG(level, ...) \
|
|
|
|
|
G_STMT_START \
|
|
|
|
|
{ \
|
|
|
|
|
nm_log((level), \
|
|
|
|
|
(_NMLOG_DOMAIN), \
|
|
|
|
|
NULL, \
|
|
|
|
|
NULL, \
|
|
|
|
|
"l3cfg[" NM_HASH_OBFUSCATE_PTR_FMT \
|
|
|
|
|
",ifindex=%d]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
|
|
|
|
|
NM_HASH_OBFUSCATE_PTR(self), \
|
|
|
|
|
nm_l3cfg_get_ifindex(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
|
|
|
|
|
} \
|
|
|
|
|
G_STMT_END
|
|
|
|
|
|
|
|
|
|
#define _LOGT_acd(acd_data, ...) \
|
|
|
|
|
G_STMT_START \
|
|
|
|
|
{ \
|
|
|
|
|
char _sbuf_acd[NM_UTILS_INET_ADDRSTRLEN]; \
|
|
|
|
|
\
|
|
|
|
|
_LOGT("acd[%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
|
|
|
|
|
_nm_utils_inet4_ntop((acd_data)->addr, _sbuf_acd) \
|
|
|
|
|
_NM_UTILS_MACRO_REST(__VA_ARGS__)); \
|
|
|
|
|
} \
|
|
|
|
|
G_STMT_END
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-07-18 19:01:04 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
static void _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle);
|
|
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
static void _property_emit_notify(NML3Cfg *self, NML3CfgPropertyEmitType emit_type);
|
l3cfg: add nm_l3cfg_property_emit_register() API
The NML3Cfg instance tracks and prepares the IP configuration.
However, that is also partly exposed on other objects, like
NMIP4Config's "route-data" property.
Add an API, so that NMIP4Config can register itself to be notified
when something relevant changes.
This is an alternative to standard GObject properties and signals. They
often seem more effort than worth. That is, because in this case,
NMIP4Config.route-data has no other task then to re-emit the signal.
So, to implement that with GObject properties/signals, we would have to
add a property/signal to NML3Cfg, subscribe to it from NMIP4Config,
and remit the signal. An alternative is to bind properties, but that
would still be quite some extra code, and unclear that it would be
simpler. Not to mention the overhead, as bindings are themself full
GObject instances, that register to and emit signals by name.
2020-07-21 12:52:42 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
static void _l3_acd_data_notify_acd_completed_all(NML3Cfg *self);
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
static gboolean _acd_has_valid_link(const NMPObject *obj,
|
|
|
|
|
const guint8 ** out_addr_bin,
|
|
|
|
|
gboolean * out_acd_not_supported);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
static void
|
|
|
|
|
_l3_acd_nacd_instance_reset(NML3Cfg *self, NMTernary start_timer, gboolean acd_data_notify);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
static void _l3_acd_data_prune(NML3Cfg *self, gboolean all);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
static void _l3_acd_data_state_change(NML3Cfg * self,
|
|
|
|
|
AcdData * acd_data,
|
|
|
|
|
AcdStateChangeMode mode,
|
|
|
|
|
NAcdEvent * event);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
static AcdData *_l3_acd_data_find(NML3Cfg *self, in_addr_t addr);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
l3cfg: add nm_l3cfg_property_emit_register() API
The NML3Cfg instance tracks and prepares the IP configuration.
However, that is also partly exposed on other objects, like
NMIP4Config's "route-data" property.
Add an API, so that NMIP4Config can register itself to be notified
when something relevant changes.
This is an alternative to standard GObject properties and signals. They
often seem more effort than worth. That is, because in this case,
NMIP4Config.route-data has no other task then to re-emit the signal.
So, to implement that with GObject properties/signals, we would have to
add a property/signal to NML3Cfg, subscribe to it from NMIP4Config,
and remit the signal. An alternative is to bind properties, but that
would still be quite some extra code, and unclear that it would be
simpler. Not to mention the overhead, as bindings are themself full
GObject instances, that register to and emit signals by name.
2020-07-21 12:52:42 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
static NM_UTILS_ENUM2STR_DEFINE(_l3_cfg_commit_type_to_string,
|
|
|
|
|
NML3CfgCommitType,
|
|
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_AUTO, "auto"),
|
|
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_NONE, "none"),
|
|
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_ASSUME, "assume"),
|
|
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_UPDATE, "update"),
|
|
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_REAPPLY, "reapply"), );
|
|
|
|
|
|
|
|
|
|
static NM_UTILS_ENUM2STR_DEFINE(
|
|
|
|
|
_l3_config_notify_type_to_string,
|
|
|
|
|
NML3ConfigNotifyType,
|
|
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_ACD_COMPLETED, "acd-complete"),
|
2020-09-23 18:55:08 +02:00
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE, "platform-change"),
|
2020-09-28 16:03:33 +02:00
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE, "platform-change-on-idle"),
|
|
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT, "post-commit"),
|
|
|
|
|
NM_UTILS_ENUM2STR(NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED,
|
|
|
|
|
"routes-temporary-not-available-expired"),
|
|
|
|
|
NM_UTILS_ENUM2STR_IGNORE(_NM_L3_CONFIG_NOTIFY_TYPE_NUM), );
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-07-29 08:39:12 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-19 12:20:45 +02:00
|
|
|
static const char *
|
2020-09-28 16:19:31 +02:00
|
|
|
_l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data,
|
|
|
|
|
char * sbuf,
|
|
|
|
|
gsize sbuf_size)
|
2020-09-28 16:03:33 +02:00
|
|
|
{
|
|
|
|
|
char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
|
|
|
|
|
char *s = sbuf;
|
|
|
|
|
gsize l = sbuf_size;
|
|
|
|
|
|
|
|
|
|
nm_assert(sbuf);
|
|
|
|
|
nm_assert(sbuf_size > 0);
|
|
|
|
|
|
2020-09-28 16:19:31 +02:00
|
|
|
_l3_config_notify_type_to_string(notify_data->notify_type, s, l);
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_utils_strbuf_seek_end(&s, &l);
|
|
|
|
|
|
2020-09-28 16:19:31 +02:00
|
|
|
switch (notify_data->notify_type) {
|
2020-09-28 16:03:33 +02:00
|
|
|
case NM_L3_CONFIG_NOTIFY_TYPE_ACD_COMPLETED:
|
|
|
|
|
nm_utils_strbuf_append(&s,
|
|
|
|
|
&l,
|
|
|
|
|
", addr=%s, probe-result=%d",
|
2020-09-28 16:19:31 +02:00
|
|
|
_nm_utils_inet4_ntop(notify_data->acd_completed.addr, sbuf_addr),
|
|
|
|
|
(int) notify_data->acd_completed.probe_result);
|
2020-09-28 16:03:33 +02:00
|
|
|
break;
|
2020-09-23 18:55:08 +02:00
|
|
|
case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE:
|
|
|
|
|
nm_utils_strbuf_append(
|
|
|
|
|
&s,
|
|
|
|
|
&l,
|
|
|
|
|
", obj-type=%s, change=%s, obj=",
|
2020-09-28 16:19:31 +02:00
|
|
|
NMP_OBJECT_GET_CLASS(notify_data->platform_change.obj)->obj_type_name,
|
|
|
|
|
nm_platform_signal_change_type_to_string(notify_data->platform_change.change_type));
|
|
|
|
|
nmp_object_to_string(notify_data->platform_change.obj, NMP_OBJECT_TO_STRING_PUBLIC, s, l);
|
2020-09-23 18:55:08 +02:00
|
|
|
break;
|
2020-09-28 16:03:33 +02:00
|
|
|
case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE:
|
|
|
|
|
nm_utils_strbuf_append(&s,
|
|
|
|
|
&l,
|
|
|
|
|
", obj-type-flags=0x%x",
|
2020-09-28 16:19:31 +02:00
|
|
|
notify_data->platform_change_on_idle.obj_type_flags);
|
2020-09-28 16:03:33 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sbuf;
|
2020-09-19 12:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-09-28 16:19:31 +02:00
|
|
|
_nm_l3cfg_emit_signal_notify(NML3Cfg *self, const NML3ConfigNotifyData *notify_data)
|
2020-09-19 12:20:45 +02:00
|
|
|
{
|
2020-09-23 18:55:08 +02:00
|
|
|
char sbuf[sizeof(_nm_utils_to_string_buffer)];
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:19:31 +02:00
|
|
|
nm_assert(notify_data);
|
|
|
|
|
nm_assert(_NM_INT_NOT_NEGATIVE(notify_data->notify_type));
|
|
|
|
|
nm_assert(notify_data->notify_type < _NM_L3_CONFIG_NOTIFY_TYPE_NUM);
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:19:31 +02:00
|
|
|
_LOGT("emit signal (%s)", _l3_config_notify_data_to_string(notify_data, sbuf, sizeof(sbuf)));
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:19:31 +02:00
|
|
|
g_signal_emit(self, signals[SIGNAL_NOTIFY], 0, notify_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_nm_l3cfg_emit_signal_notify_simple(NML3Cfg *self, NML3ConfigNotifyType notify_type)
|
|
|
|
|
{
|
|
|
|
|
NML3ConfigNotifyData notify_data;
|
|
|
|
|
|
|
|
|
|
notify_data.notify_type = notify_type;
|
|
|
|
|
_nm_l3cfg_emit_signal_notify(self, ¬ify_data);
|
2020-07-28 13:29:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-16 12:44:38 +02:00
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_changed_configs_set_dirty(NML3Cfg *self)
|
2020-09-16 12:44:38 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT("configuration changed");
|
|
|
|
|
self->priv.changed_configs = TRUE;
|
2020-09-16 12:44:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-08-03 17:33:31 +02:00
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_ipv4_addresses_on_link_update(NML3Cfg * self,
|
|
|
|
|
in_addr_t addr,
|
|
|
|
|
gboolean add /* or else remove */)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
AcdData *acd_data;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
acd_data = _l3_acd_data_find(self, addr);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (add) {
|
|
|
|
|
if (self->priv.p->acd_ipv4_addresses_on_link)
|
|
|
|
|
g_hash_table_add(self->priv.p->acd_ipv4_addresses_on_link, GUINT_TO_POINTER(addr));
|
|
|
|
|
else
|
|
|
|
|
self->priv.p->acd_ipv4_addresses_on_link_has = FALSE;
|
|
|
|
|
if (acd_data)
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_EXTERNAL_ADDED, NULL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* when we remove an IPv4 address from kernel, we cannot know whether the same address is still
|
2020-09-28 14:50:01 +02:00
|
|
|
* present (with a different prefix length or peer). So we cannot be sure whether we removed
|
|
|
|
|
* the only address, or whether more are still present. All we can do is forget about the
|
|
|
|
|
* cached addresses, and fetch them new the next time we need the information. */
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_clear_pointer(&self->priv.p->acd_ipv4_addresses_on_link, g_hash_table_unref);
|
|
|
|
|
self->priv.p->acd_ipv4_addresses_on_link_has = FALSE;
|
|
|
|
|
if (acd_data)
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_EXTERNAL_REMOVED, NULL);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_ipv4_addresses_on_link_contains(NML3Cfg *self, in_addr_t addr)
|
|
|
|
|
{
|
|
|
|
|
if (!self->priv.p->acd_ipv4_addresses_on_link) {
|
|
|
|
|
if (self->priv.p->acd_ipv4_addresses_on_link_has)
|
|
|
|
|
return FALSE;
|
|
|
|
|
self->priv.p->acd_ipv4_addresses_on_link_has = TRUE;
|
|
|
|
|
self->priv.p->acd_ipv4_addresses_on_link =
|
|
|
|
|
nm_platform_ip4_address_addr_to_hash(self->priv.platform, self->priv.ifindex);
|
|
|
|
|
if (!self->priv.p->acd_ipv4_addresses_on_link)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
return g_hash_table_contains(self->priv.p->acd_ipv4_addresses_on_link, GUINT_TO_POINTER(addr));
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static NAcdProbe *
|
2020-09-28 16:03:33 +02:00
|
|
|
_nm_n_acd_data_probe_new(NML3Cfg *self, in_addr_t addr, guint32 timeout_msec, gpointer user_data)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_auto(n_acd_probe_config_freep) NAcdProbeConfig *probe_config = NULL;
|
|
|
|
|
NAcdProbe * probe;
|
|
|
|
|
int r;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(self);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!self->priv.p->nacd)
|
|
|
|
|
return NULL;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (addr == 0)
|
|
|
|
|
return nm_assert_unreachable_val(NULL);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
r = n_acd_probe_config_new(&probe_config);
|
|
|
|
|
if (r)
|
|
|
|
|
return NULL;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
n_acd_probe_config_set_ip(probe_config, (struct in_addr){addr});
|
|
|
|
|
n_acd_probe_config_set_timeout(probe_config, timeout_msec);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
r = n_acd_probe(self->priv.p->nacd, &probe, probe_config);
|
|
|
|
|
if (r)
|
|
|
|
|
return NULL;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
n_acd_probe_set_userdata(probe, user_data);
|
|
|
|
|
return probe;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-07-29 08:39:12 +02:00
|
|
|
static guint *
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_externally_removed_objs_counter(NML3Cfg *self, NMPObjectType obj_type)
|
|
|
|
|
{
|
|
|
|
|
switch (obj_type) {
|
|
|
|
|
case NMP_OBJECT_TYPE_IP4_ADDRESS:
|
|
|
|
|
return &self->priv.p->externally_removed_objs_cnt_addresses_4;
|
|
|
|
|
case NMP_OBJECT_TYPE_IP6_ADDRESS:
|
|
|
|
|
return &self->priv.p->externally_removed_objs_cnt_addresses_6;
|
|
|
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
|
|
|
return &self->priv.p->externally_removed_objs_cnt_routes_4;
|
|
|
|
|
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
|
|
|
|
return &self->priv.p->externally_removed_objs_cnt_routes_6;
|
|
|
|
|
default:
|
|
|
|
|
return nm_assert_unreachable_val(NULL);
|
|
|
|
|
}
|
2020-07-29 08:39:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_externally_removed_objs_drop(NML3Cfg *self)
|
2020-07-29 08:39:12 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
self->priv.p->externally_removed_objs_cnt_addresses_4 = 0;
|
|
|
|
|
self->priv.p->externally_removed_objs_cnt_addresses_6 = 0;
|
|
|
|
|
self->priv.p->externally_removed_objs_cnt_routes_4 = 0;
|
|
|
|
|
self->priv.p->externally_removed_objs_cnt_routes_6 = 0;
|
|
|
|
|
if (nm_g_hash_table_size(self->priv.p->externally_removed_objs_hash) > 0)
|
|
|
|
|
_LOGD("externally-removed: untrack all");
|
|
|
|
|
nm_clear_pointer(&self->priv.p->externally_removed_objs_hash, g_hash_table_unref);
|
2020-07-29 08:39:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_externally_removed_objs_drop_unused(NML3Cfg *self)
|
2020-07-29 08:39:12 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
GHashTableIter h_iter;
|
|
|
|
|
const NMPObject *obj;
|
|
|
|
|
char sbuf[sizeof(_nm_utils_to_string_buffer)];
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!self->priv.p->externally_removed_objs_hash)
|
|
|
|
|
return;
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!self->priv.p->combined_l3cd_commited) {
|
|
|
|
|
_l3cfg_externally_removed_objs_drop(self);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
g_hash_table_iter_init(&h_iter, self->priv.p->externally_removed_objs_hash);
|
|
|
|
|
while (g_hash_table_iter_next(&h_iter, (gpointer *) &obj, NULL)) {
|
|
|
|
|
if (!nm_l3_config_data_lookup_route_obj(self->priv.p->combined_l3cd_commited, obj)) {
|
|
|
|
|
/* The object is no longer tracked in the configuration.
|
2020-09-28 14:50:01 +02:00
|
|
|
* The externally_removed_objs_hash is to prevent adding entires that were
|
|
|
|
|
* removed externally, so if we don't plan to add the entry, we no longer need to track
|
|
|
|
|
* it. */
|
2020-09-28 16:03:33 +02:00
|
|
|
(*(_l3cfg_externally_removed_objs_counter(self, NMP_OBJECT_GET_TYPE(obj))))--;
|
|
|
|
|
g_hash_table_iter_remove(&h_iter);
|
|
|
|
|
_LOGD("externally-removed: untrack %s",
|
|
|
|
|
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-29 08:39:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_externally_removed_objs_track(NML3Cfg *self, const NMPObject *obj, gboolean is_removed)
|
|
|
|
|
{
|
|
|
|
|
char sbuf[1000];
|
|
|
|
|
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
|
|
|
|
|
if (!self->priv.p->combined_l3cd_commited)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!is_removed) {
|
|
|
|
|
/* the object is still (or again) present. It no longer gets hidden. */
|
|
|
|
|
if (self->priv.p->externally_removed_objs_hash) {
|
|
|
|
|
if (g_hash_table_remove(self->priv.p->externally_removed_objs_hash, obj)) {
|
|
|
|
|
(*(_l3cfg_externally_removed_objs_counter(self, NMP_OBJECT_GET_TYPE(obj))))--;
|
|
|
|
|
_LOGD("externally-removed: untrack %s",
|
|
|
|
|
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!nm_l3_config_data_lookup_obj(self->priv.p->combined_l3cd_commited, obj)) {
|
|
|
|
|
/* we don't care about this object, so there is nothing to hide hide */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (G_UNLIKELY(!self->priv.p->externally_removed_objs_hash)) {
|
|
|
|
|
self->priv.p->externally_removed_objs_hash =
|
|
|
|
|
g_hash_table_new_full((GHashFunc) nmp_object_id_hash,
|
|
|
|
|
(GEqualFunc) nmp_object_id_equal,
|
|
|
|
|
(GDestroyNotify) nmp_object_unref,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g_hash_table_add(self->priv.p->externally_removed_objs_hash,
|
|
|
|
|
(gpointer) nmp_object_ref(obj))) {
|
|
|
|
|
(*(_l3cfg_externally_removed_objs_counter(self, NMP_OBJECT_GET_TYPE(obj))))++;
|
|
|
|
|
_LOGD("externally-removed: track %s",
|
|
|
|
|
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
|
|
|
|
}
|
2020-07-29 08:39:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_externally_removed_objs_pickup(NML3Cfg *self, int addr_family)
|
|
|
|
|
{
|
|
|
|
|
const gboolean IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
|
|
|
NMDedupMultiIter iter;
|
|
|
|
|
const NMPObject *obj;
|
|
|
|
|
|
|
|
|
|
if (!self->priv.p->combined_l3cd_commited)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
nm_l3_config_data_iter_obj_for_each(&iter,
|
|
|
|
|
self->priv.p->combined_l3cd_commited,
|
|
|
|
|
&obj,
|
|
|
|
|
NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4))
|
|
|
|
|
{
|
|
|
|
|
if (!nm_platform_lookup_entry(self->priv.platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj))
|
|
|
|
|
_l3cfg_externally_removed_objs_track(self, obj, TRUE);
|
|
|
|
|
}
|
|
|
|
|
nm_l3_config_data_iter_obj_for_each(&iter,
|
|
|
|
|
self->priv.p->combined_l3cd_commited,
|
|
|
|
|
&obj,
|
|
|
|
|
NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4))
|
|
|
|
|
{
|
|
|
|
|
if (!nm_platform_lookup_entry(self->priv.platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj))
|
|
|
|
|
_l3cfg_externally_removed_objs_track(self, obj, TRUE);
|
|
|
|
|
}
|
2020-07-29 08:39:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_externally_removed_objs_filter(/* const NMDedupMultiObj * */ gconstpointer o,
|
|
|
|
|
gpointer user_data)
|
2020-07-29 08:39:12 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
const NMPObject *obj = o;
|
|
|
|
|
GHashTable * externally_removed_objs_hash = user_data;
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
return !g_hash_table_contains(externally_removed_objs_hash, obj);
|
2020-07-29 08:39:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-07-21 11:57:31 +02:00
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_load_link(NML3Cfg *self, gboolean initial)
|
|
|
|
|
{
|
|
|
|
|
nm_auto_nmpobj const NMPObject *obj_old = NULL;
|
|
|
|
|
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;
|
|
|
|
|
const guint8 * nacd_new_addr;
|
|
|
|
|
gboolean nacd_link_now_up;
|
|
|
|
|
AcdData * acd_data;
|
|
|
|
|
|
2020-09-25 17:05:58 +02:00
|
|
|
if (initial) {
|
|
|
|
|
obj = nm_platform_link_get_obj(self->priv.platform, self->priv.ifindex, TRUE);
|
|
|
|
|
self->priv.plobj_next = nmp_object_ref(obj);
|
|
|
|
|
} else {
|
|
|
|
|
obj = self->priv.plobj_next;
|
|
|
|
|
nm_assert(obj == nm_platform_link_get_obj(self->priv.platform, self->priv.ifindex, TRUE));
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-09-25 17:05:58 +02:00
|
|
|
if (initial && obj == self->priv.plobj)
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
|
|
|
|
|
2020-09-25 17:05:58 +02:00
|
|
|
obj_old = g_steal_pointer(&self->priv.plobj);
|
|
|
|
|
self->priv.plobj = nmp_object_ref(obj);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
|
|
|
|
if (obj && NM_FLAGS_HAS(NMP_OBJECT_CAST_LINK(obj)->n_ifi_flags, IFF_UP)
|
|
|
|
|
&& (!obj_old || !NM_FLAGS_HAS(NMP_OBJECT_CAST_LINK(obj_old)->n_ifi_flags, IFF_UP)))
|
|
|
|
|
nacd_link_now_up = TRUE;
|
|
|
|
|
else
|
|
|
|
|
nacd_link_now_up = FALSE;
|
|
|
|
|
|
|
|
|
|
nacd_changed = FALSE;
|
|
|
|
|
nacd_old_valid = _acd_has_valid_link(obj_old, &nacd_old_addr, NULL);
|
|
|
|
|
nacd_new_valid = _acd_has_valid_link(obj, &nacd_new_addr, NULL);
|
|
|
|
|
if (self->priv.p->nacd_instance_ensure_retry) {
|
|
|
|
|
if (nacd_new_valid
|
|
|
|
|
&& (!nacd_old_valid
|
|
|
|
|
|| memcmp(nacd_new_addr, nacd_old_addr, ACD_SUPPORTED_ETH_ALEN) == 0))
|
|
|
|
|
nacd_changed = TRUE;
|
|
|
|
|
} else if (self->priv.p->nacd) {
|
|
|
|
|
if (!nacd_new_valid)
|
|
|
|
|
nacd_changed = TRUE;
|
|
|
|
|
else if (!nacd_old_valid)
|
|
|
|
|
nacd_changed = nm_assert_unreachable_val(TRUE);
|
|
|
|
|
else if (memcmp(nacd_old_addr, nacd_new_addr, ACD_SUPPORTED_ETH_ALEN) != 0)
|
|
|
|
|
nacd_changed = TRUE;
|
|
|
|
|
} else if (nacd_new_valid)
|
|
|
|
|
nacd_changed = TRUE;
|
|
|
|
|
ifname_old = nmp_object_link_get_ifname(obj_old);
|
2020-09-25 17:05:58 +02:00
|
|
|
ifname = nmp_object_link_get_ifname(self->priv.plobj);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
|
|
|
|
if (initial) {
|
|
|
|
|
_LOGT("link ifname changed: %s%s%s (initial)", NM_PRINT_FMT_QUOTE_STRING(ifname));
|
|
|
|
|
} else if (!nm_streq0(ifname, ifname_old)) {
|
|
|
|
|
_LOGT("link ifname changed: %s%s%s (was %s%s%s)",
|
|
|
|
|
NM_PRINT_FMT_QUOTE_STRING(ifname),
|
|
|
|
|
NM_PRINT_FMT_QUOTE_STRING(ifname_old));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nacd_changed) {
|
|
|
|
|
if (!c_list_is_empty(&self->priv.p->acd_lst_head))
|
|
|
|
|
_LOGT("acd: link change causes restart of ACD");
|
|
|
|
|
_l3_acd_nacd_instance_reset(self, NM_TERNARY_FALSE, TRUE);
|
|
|
|
|
} else if (nacd_link_now_up) {
|
|
|
|
|
if (!c_list_is_empty(&self->priv.p->acd_lst_head)) {
|
|
|
|
|
_LOGT("acd: link up requires are re-initialize of ACD probes");
|
|
|
|
|
c_list_for_each_entry (acd_data, &self->priv.p->acd_lst_head, acd_lst)
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_LINK_NOW_UP, NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-21 11:57:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-07-21 11:21:44 +02:00
|
|
|
void
|
2020-09-28 16:03:33 +02:00
|
|
|
_nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint32 obj_type_flags)
|
2020-07-21 11:21:44 +02:00
|
|
|
{
|
2020-09-28 16:19:31 +02:00
|
|
|
NML3ConfigNotifyData notify_data;
|
2020-09-16 16:49:46 +02:00
|
|
|
|
2020-09-25 17:05:58 +02:00
|
|
|
if (self->priv.plobj_next != self->priv.plobj)
|
2020-09-28 16:03:33 +02:00
|
|
|
_load_link(self, FALSE);
|
2020-09-16 16:49:46 +02:00
|
|
|
|
2020-09-28 16:19:31 +02:00
|
|
|
notify_data.notify_type = NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE;
|
|
|
|
|
notify_data.platform_change_on_idle = (typeof(notify_data.platform_change_on_idle)){
|
|
|
|
|
.obj_type_flags = obj_type_flags,
|
2020-09-28 16:03:33 +02:00
|
|
|
};
|
2020-09-28 16:19:31 +02:00
|
|
|
_nm_l3cfg_emit_signal_notify(self, ¬ify_data);
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_notify_acd_completed_all(self);
|
2020-09-16 16:49:46 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (NM_FLAGS_ANY(obj_type_flags, nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP4_ROUTE)))
|
|
|
|
|
_property_emit_notify(self, NM_L3CFG_PROPERTY_EMIT_TYPE_IP4_ROUTE);
|
|
|
|
|
if (NM_FLAGS_ANY(obj_type_flags, nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP6_ROUTE)))
|
|
|
|
|
_property_emit_notify(self, NM_L3CFG_PROPERTY_EMIT_TYPE_IP6_ROUTE);
|
l3cfg: add nm_l3cfg_property_emit_register() API
The NML3Cfg instance tracks and prepares the IP configuration.
However, that is also partly exposed on other objects, like
NMIP4Config's "route-data" property.
Add an API, so that NMIP4Config can register itself to be notified
when something relevant changes.
This is an alternative to standard GObject properties and signals. They
often seem more effort than worth. That is, because in this case,
NMIP4Config.route-data has no other task then to re-emit the signal.
So, to implement that with GObject properties/signals, we would have to
add a property/signal to NML3Cfg, subscribe to it from NMIP4Config,
and remit the signal. An alternative is to bind properties, but that
would still be quite some extra code, and unclear that it would be
simpler. Not to mention the overhead, as bindings are themself full
GObject instances, that register to and emit signals by name.
2020-07-21 12:52:42 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 08:39:12 +02:00
|
|
|
void
|
2020-09-28 16:03:33 +02:00
|
|
|
_nm_l3cfg_notify_platform_change(NML3Cfg * self,
|
|
|
|
|
NMPlatformSignalChangeType change_type,
|
|
|
|
|
const NMPObject * obj)
|
|
|
|
|
{
|
2020-09-28 16:19:31 +02:00
|
|
|
NML3ConfigNotifyData notify_data;
|
|
|
|
|
NMPObjectType obj_type;
|
2020-09-23 18:55:08 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NMP_OBJECT_IS_VALID(obj));
|
|
|
|
|
|
2020-09-23 18:55:08 +02:00
|
|
|
obj_type = NMP_OBJECT_GET_TYPE(obj);
|
|
|
|
|
|
|
|
|
|
switch (obj_type) {
|
2020-09-25 17:05:58 +02:00
|
|
|
case NMP_OBJECT_TYPE_LINK:
|
|
|
|
|
{
|
|
|
|
|
const NMPObject *plobj;
|
|
|
|
|
|
|
|
|
|
plobj = (change_type != NM_PLATFORM_SIGNAL_REMOVED) ? obj : NULL;
|
|
|
|
|
nm_assert(plobj == nm_platform_link_get_obj(self->priv.platform, self->priv.ifindex, TRUE));
|
|
|
|
|
nmp_object_ref_set(&self->priv.plobj_next, plobj);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
case NMP_OBJECT_TYPE_IP4_ADDRESS:
|
|
|
|
|
_l3_acd_ipv4_addresses_on_link_update(self,
|
|
|
|
|
NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address,
|
|
|
|
|
change_type != NM_PLATFORM_SIGNAL_REMOVED);
|
|
|
|
|
/* fall-through */
|
|
|
|
|
case NMP_OBJECT_TYPE_IP6_ADDRESS:
|
|
|
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
|
|
|
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
|
|
|
|
_l3cfg_externally_removed_objs_track(self, obj, change_type == NM_PLATFORM_SIGNAL_REMOVED);
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-09-23 18:55:08 +02:00
|
|
|
|
2020-09-28 16:19:31 +02:00
|
|
|
notify_data.notify_type = NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE;
|
|
|
|
|
notify_data.platform_change = (typeof(notify_data.platform_change)){
|
|
|
|
|
.obj = obj,
|
|
|
|
|
.change_type = change_type,
|
2020-09-23 18:55:08 +02:00
|
|
|
};
|
2020-09-28 16:19:31 +02:00
|
|
|
_nm_l3cfg_emit_signal_notify(self, ¬ify_data);
|
2020-09-23 18:55:08 +02:00
|
|
|
|
|
|
|
|
nm_assert(NMP_OBJECT_IS_VALID(obj));
|
2020-07-29 08:39:12 +02:00
|
|
|
}
|
|
|
|
|
|
l3cfg: add nm_l3cfg_property_emit_register() API
The NML3Cfg instance tracks and prepares the IP configuration.
However, that is also partly exposed on other objects, like
NMIP4Config's "route-data" property.
Add an API, so that NMIP4Config can register itself to be notified
when something relevant changes.
This is an alternative to standard GObject properties and signals. They
often seem more effort than worth. That is, because in this case,
NMIP4Config.route-data has no other task then to re-emit the signal.
So, to implement that with GObject properties/signals, we would have to
add a property/signal to NML3Cfg, subscribe to it from NMIP4Config,
and remit the signal. An alternative is to bind properties, but that
would still be quite some extra code, and unclear that it would be
simpler. Not to mention the overhead, as bindings are themself full
GObject instances, that register to and emit signals by name.
2020-07-21 12:52:42 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2020-09-28 16:03:33 +02:00
|
|
|
GObject * target_obj;
|
|
|
|
|
const GParamSpec * target_property;
|
|
|
|
|
NML3CfgPropertyEmitType emit_type;
|
l3cfg: add nm_l3cfg_property_emit_register() API
The NML3Cfg instance tracks and prepares the IP configuration.
However, that is also partly exposed on other objects, like
NMIP4Config's "route-data" property.
Add an API, so that NMIP4Config can register itself to be notified
when something relevant changes.
This is an alternative to standard GObject properties and signals. They
often seem more effort than worth. That is, because in this case,
NMIP4Config.route-data has no other task then to re-emit the signal.
So, to implement that with GObject properties/signals, we would have to
add a property/signal to NML3Cfg, subscribe to it from NMIP4Config,
and remit the signal. An alternative is to bind properties, but that
would still be quite some extra code, and unclear that it would be
simpler. Not to mention the overhead, as bindings are themself full
GObject instances, that register to and emit signals by name.
2020-07-21 12:52:42 +02:00
|
|
|
} PropertyEmitData;
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_property_emit_notify(NML3Cfg *self, NML3CfgPropertyEmitType emit_type)
|
|
|
|
|
{
|
|
|
|
|
gs_free PropertyEmitData *collected_heap = NULL;
|
|
|
|
|
PropertyEmitData * collected = NULL;
|
|
|
|
|
PropertyEmitData * emit_data;
|
|
|
|
|
guint num;
|
|
|
|
|
guint i;
|
|
|
|
|
guint j;
|
|
|
|
|
|
|
|
|
|
if (!self->priv.p->property_emit_list)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
num = 0;
|
|
|
|
|
emit_data = &g_array_index(self->priv.p->property_emit_list, PropertyEmitData, 0);
|
|
|
|
|
for (i = 0; i < self->priv.p->property_emit_list->len; i++, emit_data++) {
|
|
|
|
|
if (emit_data->emit_type == emit_type) {
|
|
|
|
|
collected = emit_data;
|
|
|
|
|
num++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (num == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (num == 1) {
|
|
|
|
|
g_object_notify_by_pspec(collected->target_obj, (GParamSpec *) collected->target_property);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (num < 300u / sizeof(*collected))
|
|
|
|
|
collected = g_alloca(sizeof(PropertyEmitData) * num);
|
|
|
|
|
else {
|
|
|
|
|
collected_heap = g_new(PropertyEmitData, num);
|
|
|
|
|
collected = collected_heap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit_data = &g_array_index(self->priv.p->property_emit_list, PropertyEmitData, 0);
|
|
|
|
|
for (i = 0, j = 0; i < self->priv.p->property_emit_list->len; i++, emit_data++) {
|
|
|
|
|
if (emit_data->emit_type == emit_type) {
|
|
|
|
|
collected[j++] = *emit_data;
|
|
|
|
|
g_object_ref(collected->target_obj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_assert(j == num);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++) {
|
|
|
|
|
g_object_notify_by_pspec(collected[i].target_obj,
|
|
|
|
|
(GParamSpec *) collected[i].target_property);
|
|
|
|
|
if (i > 0)
|
|
|
|
|
g_object_unref(collected[i].target_obj);
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_property_emit_register(NML3Cfg * self,
|
|
|
|
|
GObject * target_obj,
|
|
|
|
|
const GParamSpec * target_property,
|
|
|
|
|
NML3CfgPropertyEmitType emit_type)
|
|
|
|
|
{
|
|
|
|
|
PropertyEmitData *emit_data;
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(G_IS_OBJECT(target_obj));
|
|
|
|
|
nm_assert(target_property);
|
|
|
|
|
nm_assert(NM_IN_SET(emit_type,
|
|
|
|
|
NM_L3CFG_PROPERTY_EMIT_TYPE_IP4_ROUTE,
|
|
|
|
|
NM_L3CFG_PROPERTY_EMIT_TYPE_IP6_ROUTE));
|
|
|
|
|
nm_assert(target_property
|
|
|
|
|
== nm_g_object_class_find_property_from_gtype(G_OBJECT_TYPE(target_obj),
|
|
|
|
|
target_property->name));
|
|
|
|
|
|
|
|
|
|
if (!self->priv.p->property_emit_list)
|
|
|
|
|
self->priv.p->property_emit_list = g_array_new(FALSE, FALSE, sizeof(PropertyEmitData));
|
|
|
|
|
else {
|
|
|
|
|
emit_data = &g_array_index(self->priv.p->property_emit_list, PropertyEmitData, 0);
|
|
|
|
|
for (i = 0; i < self->priv.p->property_emit_list->len; i++, emit_data++) {
|
|
|
|
|
if (emit_data->target_obj != target_obj
|
|
|
|
|
|| emit_data->target_property != target_property)
|
|
|
|
|
continue;
|
|
|
|
|
nm_assert(emit_data->emit_type == emit_type);
|
|
|
|
|
emit_data->emit_type = emit_type;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit_data = nm_g_array_append_new(self->priv.p->property_emit_list, PropertyEmitData);
|
|
|
|
|
*emit_data = (PropertyEmitData){
|
|
|
|
|
.target_obj = target_obj,
|
|
|
|
|
.target_property = target_property,
|
|
|
|
|
.emit_type = emit_type,
|
|
|
|
|
};
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_property_emit_unregister(NML3Cfg * self,
|
|
|
|
|
GObject * target_obj,
|
|
|
|
|
const GParamSpec *target_property)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
PropertyEmitData *emit_data;
|
|
|
|
|
guint i;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(G_IS_OBJECT(target_obj));
|
|
|
|
|
nm_assert(!target_property
|
|
|
|
|
|| target_property
|
|
|
|
|
== nm_g_object_class_find_property_from_gtype(G_OBJECT_TYPE(target_obj),
|
|
|
|
|
target_property->name));
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!self->priv.p->property_emit_list)
|
|
|
|
|
return;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
for (i = self->priv.p->property_emit_list->len; i > 0; i--) {
|
|
|
|
|
emit_data = &g_array_index(self->priv.p->property_emit_list, PropertyEmitData, i);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (emit_data->target_obj != target_obj)
|
|
|
|
|
continue;
|
|
|
|
|
if (target_property && emit_data->target_property != target_property)
|
|
|
|
|
continue;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
g_array_remove_index_fast(self->priv.p->property_emit_list, i);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (target_property) {
|
|
|
|
|
/* if a target-property is given, we don't have another entry in
|
2020-09-28 14:50:01 +02:00
|
|
|
* the list. */
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_get_acd_is_pending(NML3Cfg *self)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
g_return_val_if_fail(NM_IS_L3CFG(self), FALSE);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
return self->priv.p->acd_is_pending;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_acd_track_data_is_not_dirty(const AcdTrackData *acd_track)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
return acd_track && !acd_track->acd_dirty;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_acd_track_data_free(AcdTrackData *acd_track)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
c_list_unlink_stale(&acd_track->acd_track_lst);
|
|
|
|
|
nm_l3_config_data_unref(acd_track->l3cd);
|
|
|
|
|
nmp_object_unref(acd_track->obj);
|
|
|
|
|
nm_g_slice_free(acd_track);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_acd_data_free(AcdData *acd_data)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(c_list_is_empty(&acd_data->acd_track_lst_head));
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
n_acd_probe_free(acd_data->nacd_probe);
|
|
|
|
|
nm_clear_g_source_inst(&acd_data->acd_timeout_source);
|
|
|
|
|
c_list_unlink_stale(&acd_data->acd_lst);
|
|
|
|
|
nm_g_slice_free(acd_data);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_acd_data_probe_result_is_good(const AcdData *acd_data)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(acd_data);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (acd_data->acd_state < ACD_STATE_PROBE_DONE) {
|
|
|
|
|
/* we are currently probing. Wait. */
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* Probing is already completed. Use the probe result. */
|
|
|
|
|
return acd_data->probe_result;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint
|
2020-09-28 16:03:33 +02:00
|
|
|
_acd_data_collect_tracks_data(const AcdData *acd_data,
|
|
|
|
|
NMTernary dirty_selector,
|
|
|
|
|
NMTernary acd_failed_notified_selector,
|
|
|
|
|
guint32 * out_best_acd_timeout_msec)
|
|
|
|
|
{
|
|
|
|
|
guint32 best_acd_timeout_msec = G_MAXUINT32;
|
|
|
|
|
AcdTrackData *acd_track;
|
|
|
|
|
guint n = 0;
|
|
|
|
|
|
|
|
|
|
c_list_for_each_entry (acd_track, &acd_data->acd_track_lst_head, acd_track_lst) {
|
|
|
|
|
if (dirty_selector != NM_TERNARY_DEFAULT) {
|
|
|
|
|
if ((!!dirty_selector) != (!!acd_track->acd_dirty))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (acd_failed_notified_selector != NM_TERNARY_DEFAULT) {
|
|
|
|
|
if ((!!acd_failed_notified_selector) != (!!acd_track->acd_failed_notified))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
n++;
|
|
|
|
|
if (best_acd_timeout_msec > acd_track->acd_timeout_msec)
|
|
|
|
|
best_acd_timeout_msec = acd_track->acd_timeout_msec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NM_SET_OUT(out_best_acd_timeout_msec, n > 0 ? best_acd_timeout_msec : 0u);
|
|
|
|
|
return n;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static AcdTrackData *
|
2020-09-28 16:03:33 +02:00
|
|
|
_acd_data_find_track(const AcdData * acd_data,
|
|
|
|
|
const NML3ConfigData *l3cd,
|
|
|
|
|
const NMPObject * obj,
|
|
|
|
|
gconstpointer tag)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
AcdTrackData *acd_track;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
c_list_for_each_entry (acd_track, &acd_data->acd_track_lst_head, acd_track_lst) {
|
|
|
|
|
if (acd_track->obj == obj && acd_track->l3cd == l3cd && acd_track->tag == tag)
|
|
|
|
|
return acd_track;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
return NULL;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_acd_has_valid_link(const NMPObject *obj,
|
|
|
|
|
const guint8 ** out_addr_bin,
|
|
|
|
|
gboolean * out_acd_not_supported)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
const NMPlatformLink *link;
|
|
|
|
|
const guint8 * addr_bin;
|
|
|
|
|
gsize addr_len;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!obj) {
|
|
|
|
|
NM_SET_OUT(out_acd_not_supported, FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
link = NMP_OBJECT_CAST_LINK(obj);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
addr_bin = nmp_link_address_get(&link->l_address, &addr_len);
|
|
|
|
|
if (!addr_bin || addr_len != ACD_SUPPORTED_ETH_ALEN) {
|
|
|
|
|
NM_SET_OUT(out_acd_not_supported, TRUE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
NM_SET_OUT(out_acd_not_supported, FALSE);
|
|
|
|
|
NM_SET_OUT(out_addr_bin, addr_bin);
|
|
|
|
|
return TRUE;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_nacd_event(int fd, GIOCondition condition, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
NML3Cfg *self = user_data;
|
|
|
|
|
gboolean success = FALSE;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(self->priv.p->nacd);
|
|
|
|
|
|
|
|
|
|
r = n_acd_dispatch(self->priv.p->nacd);
|
|
|
|
|
if (!NM_IN_SET(r, 0, N_ACD_E_PREEMPTED)) {
|
|
|
|
|
_LOGT("acd: dispatch failed with error %d", r);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
|
AcdData * acd_data;
|
|
|
|
|
NAcdEvent *event;
|
|
|
|
|
|
|
|
|
|
r = n_acd_pop_event(self->priv.p->nacd, &event);
|
|
|
|
|
if (r) {
|
|
|
|
|
_LOGT("acd: pop-event failed with error %d", r);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (!event) {
|
|
|
|
|
success = TRUE;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
|
|
|
|
#define _acd_event_payload used
|
2020-09-28 16:03:33 +02:00
|
|
|
G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NAcdEvent, _acd_event_payload)
|
|
|
|
|
== G_STRUCT_OFFSET(NAcdEvent, defended));
|
|
|
|
|
G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NAcdEvent, _acd_event_payload)
|
|
|
|
|
== G_STRUCT_OFFSET(NAcdEvent, conflict));
|
|
|
|
|
G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NAcdEvent, _acd_event_payload)
|
|
|
|
|
== G_STRUCT_OFFSET(NAcdEvent, used));
|
|
|
|
|
nm_assert(&event->_acd_event_payload == &event->defended);
|
|
|
|
|
nm_assert(&event->_acd_event_payload == &event->conflict);
|
|
|
|
|
nm_assert(&event->_acd_event_payload == &event->used);
|
|
|
|
|
|
|
|
|
|
switch (event->event) {
|
|
|
|
|
case N_ACD_EVENT_READY:
|
|
|
|
|
n_acd_probe_get_userdata(event->_acd_event_payload.probe, (void **) &acd_data);
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_NACD_READY, event);
|
|
|
|
|
break;
|
|
|
|
|
case N_ACD_EVENT_USED:
|
|
|
|
|
n_acd_probe_get_userdata(event->_acd_event_payload.probe, (void **) &acd_data);
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_NACD_USED, event);
|
|
|
|
|
break;
|
|
|
|
|
case N_ACD_EVENT_DEFENDED:
|
|
|
|
|
case N_ACD_EVENT_CONFLICT:
|
|
|
|
|
{
|
|
|
|
|
gs_free char *sender_str = NULL;
|
|
|
|
|
const char * addr_str = NULL;
|
|
|
|
|
char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
|
|
|
|
|
|
|
|
|
|
/* since we announce with N_ACD_DEFEND_ALWAYS, we don't actually expect any
|
2020-09-28 14:50:01 +02:00
|
|
|
* conflict reported and don't handle it. It would be complicated to de-configure
|
|
|
|
|
* the address. */
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(event->event == N_ACD_EVENT_DEFENDED);
|
|
|
|
|
|
|
|
|
|
n_acd_probe_get_userdata(event->_acd_event_payload.probe, (void **) &acd_data);
|
|
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"address %s %s from %s",
|
|
|
|
|
(addr_str = _nm_utils_inet4_ntop(acd_data->addr, sbuf_addr)),
|
|
|
|
|
event->event == N_ACD_EVENT_DEFENDED ? "defended" : "conflict detected",
|
|
|
|
|
(sender_str = nm_utils_bin2hexstr_full(event->_acd_event_payload.sender,
|
|
|
|
|
event->_acd_event_payload.n_sender,
|
|
|
|
|
':',
|
|
|
|
|
FALSE,
|
|
|
|
|
NULL)));
|
|
|
|
|
if (event->event == N_ACD_EVENT_CONFLICT) {
|
|
|
|
|
_LOGW("IPv4 address collision detection sees conflict on interface %i%s%s%s for "
|
|
|
|
|
"address %s from host %s",
|
|
|
|
|
self->priv.ifindex,
|
2020-09-25 17:05:58 +02:00
|
|
|
NM_PRINT_FMT_QUOTED(self->priv.plobj,
|
2020-09-28 16:03:33 +02:00
|
|
|
" (",
|
2020-09-25 17:05:58 +02:00
|
|
|
NMP_OBJECT_CAST_LINK(self->priv.plobj)->name,
|
2020-09-28 16:03:33 +02:00
|
|
|
")",
|
|
|
|
|
""),
|
|
|
|
|
addr_str ?: _nm_utils_inet4_ntop(acd_data->addr, sbuf_addr),
|
|
|
|
|
sender_str
|
|
|
|
|
?: (sender_str =
|
|
|
|
|
nm_utils_bin2hexstr_full(event->_acd_event_payload.sender,
|
|
|
|
|
event->_acd_event_payload.n_sender,
|
|
|
|
|
':',
|
|
|
|
|
FALSE,
|
|
|
|
|
NULL)));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case N_ACD_EVENT_DOWN:
|
|
|
|
|
_LOGT("acd: message possibly dropped due to device down.");
|
|
|
|
|
c_list_for_each_entry (acd_data, &self->priv.p->acd_lst_head, acd_lst)
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_NACD_DOWN, NULL);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
_LOGT("acd: unexpected event %u. Ignore", event->event);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_assert_not_reached();
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-19 12:20:45 +02:00
|
|
|
out:
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!success) {
|
|
|
|
|
/* Something is seriously wrong with our nacd instance. We handle that by resetting the
|
2020-09-28 14:50:01 +02:00
|
|
|
* ACD instance. */
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_nacd_instance_reset(self, NM_TERNARY_TRUE, TRUE);
|
|
|
|
|
}
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_notify_acd_completed_all(self);
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
return G_SOURCE_CONTINUE;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_nacd_instance_ensure_retry_cb(gpointer user_data)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
NML3Cfg *self = user_data;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_clear_g_source_inst(&self->priv.p->nacd_instance_ensure_retry);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_changed_configs_set_dirty(self);
|
2020-09-25 15:46:06 +02:00
|
|
|
nm_l3cfg_commit(self, NM_L3_CFG_COMMIT_TYPE_AUTO);
|
2020-09-28 16:03:33 +02:00
|
|
|
return G_SOURCE_REMOVE;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_nacd_instance_reset(NML3Cfg *self, NMTernary start_timer, gboolean acd_data_notify)
|
|
|
|
|
{
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
|
|
|
|
|
if (self->priv.p->nacd) {
|
|
|
|
|
_LOGT("acd: clear nacd instance");
|
|
|
|
|
self->priv.p->nacd = n_acd_unref(self->priv.p->nacd);
|
|
|
|
|
}
|
|
|
|
|
nm_clear_g_source_inst(&self->priv.p->nacd_source);
|
|
|
|
|
nm_clear_g_source_inst(&self->priv.p->nacd_instance_ensure_retry);
|
|
|
|
|
|
|
|
|
|
if (c_list_is_empty(&self->priv.p->acd_lst_head))
|
|
|
|
|
start_timer = NM_TERNARY_DEFAULT;
|
|
|
|
|
|
|
|
|
|
switch (start_timer) {
|
|
|
|
|
case NM_TERNARY_FALSE:
|
2020-09-25 15:46:06 +02:00
|
|
|
_l3_changed_configs_set_dirty(self);
|
|
|
|
|
nm_l3cfg_commit_on_idle_schedule(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
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,
|
|
|
|
|
_l3_acd_nacd_instance_ensure_retry_cb,
|
|
|
|
|
self,
|
|
|
|
|
NULL);
|
|
|
|
|
g_source_attach(self->priv.p->nacd_instance_ensure_retry, NULL);
|
|
|
|
|
break;
|
|
|
|
|
case NM_TERNARY_DEFAULT:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (acd_data_notify) {
|
|
|
|
|
AcdData *acd_data;
|
|
|
|
|
|
|
|
|
|
c_list_for_each_entry (acd_data, &self->priv.p->acd_lst_head, acd_lst)
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_INSTANCE_RESET, NULL);
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NAcd *
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_nacd_instance_ensure(NML3Cfg *self, gboolean *out_acd_not_supported)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_auto(n_acd_config_freep) NAcdConfig *config = NULL;
|
|
|
|
|
nm_auto(n_acd_unrefp) NAcd * nacd = NULL;
|
|
|
|
|
const guint8 * addr_bin;
|
|
|
|
|
gboolean acd_not_supported;
|
|
|
|
|
gboolean valid;
|
|
|
|
|
int fd;
|
|
|
|
|
int r;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(self->priv.ifindex > 0);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
|
|
|
|
again:
|
2020-09-28 16:03:33 +02:00
|
|
|
if (G_LIKELY(self->priv.p->nacd)) {
|
|
|
|
|
NM_SET_OUT(out_acd_not_supported, FALSE);
|
|
|
|
|
return self->priv.p->nacd;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (self->priv.p->nacd_instance_ensure_retry) {
|
|
|
|
|
/* we just tried to create an instance and failed. We are rate-limited,
|
2020-09-28 14:50:01 +02:00
|
|
|
* don't yet try again. */
|
2020-09-28 16:03:33 +02:00
|
|
|
NM_SET_OUT(out_acd_not_supported, self->priv.p->nacd_acd_not_supported);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-25 17:05:58 +02:00
|
|
|
valid = _acd_has_valid_link(self->priv.plobj, &addr_bin, &acd_not_supported);
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!valid)
|
|
|
|
|
goto failed_create_acd;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(!acd_not_supported);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
r = n_acd_config_new(&config);
|
|
|
|
|
if (r)
|
|
|
|
|
goto failed_create_acd;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
n_acd_config_set_ifindex(config, self->priv.ifindex);
|
|
|
|
|
n_acd_config_set_transport(config, N_ACD_TRANSPORT_ETHERNET);
|
|
|
|
|
n_acd_config_set_mac(config, addr_bin, ACD_SUPPORTED_ETH_ALEN);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
r = n_acd_new(&nacd, config);
|
|
|
|
|
if (r)
|
|
|
|
|
goto failed_create_acd;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
self->priv.p->nacd = g_steal_pointer(&nacd);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
n_acd_get_fd(self->priv.p->nacd, &fd);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
self->priv.p->nacd_source =
|
|
|
|
|
nm_g_unix_fd_source_new(fd, G_IO_IN, G_PRIORITY_DEFAULT, _l3_acd_nacd_event, self, NULL);
|
|
|
|
|
nm_g_source_attach(self->priv.p->nacd_source, NULL);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
NM_SET_OUT(out_acd_not_supported, FALSE);
|
|
|
|
|
return self->priv.p->nacd;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
|
|
|
|
failed_create_acd:
|
2020-09-28 16:03:33 +02:00
|
|
|
/* is-internal-error means that we failed to create the NAcd instance. Most likely due
|
2020-09-28 14:50:01 +02:00
|
|
|
* to being unable to create a file descriptor. Anyway, something is seriously wrong here.
|
|
|
|
|
*
|
|
|
|
|
* Otherwise, the MAC address might just not be suitable (ETH_ALEN) or we might have
|
|
|
|
|
* not NMPlatformLink. In that case, it means the interface is currently not ready to
|
|
|
|
|
* do acd. */
|
2020-09-28 16:03:33 +02:00
|
|
|
self->priv.p->nacd_acd_not_supported = acd_not_supported;
|
|
|
|
|
_l3_acd_nacd_instance_reset(self, NM_TERNARY_TRUE, FALSE);
|
|
|
|
|
goto again;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NAcdProbe *
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_nacd_instance_create_probe(NML3Cfg * self,
|
|
|
|
|
in_addr_t addr,
|
|
|
|
|
guint32 timeout_msec,
|
|
|
|
|
gpointer user_data,
|
|
|
|
|
gboolean * out_acd_not_supported,
|
|
|
|
|
const char **out_failure_reason)
|
|
|
|
|
{
|
|
|
|
|
gboolean acd_not_supported;
|
|
|
|
|
NAcdProbe *probe;
|
|
|
|
|
|
|
|
|
|
if (!_l3_acd_nacd_instance_ensure(self, &acd_not_supported)) {
|
|
|
|
|
NM_SET_OUT(out_acd_not_supported, acd_not_supported);
|
|
|
|
|
if (acd_not_supported)
|
|
|
|
|
NM_SET_OUT(out_failure_reason, "interface not suitable for ACD");
|
|
|
|
|
else
|
|
|
|
|
NM_SET_OUT(out_failure_reason, "failure to create nacd instance");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_assert(!acd_not_supported);
|
|
|
|
|
NM_SET_OUT(out_acd_not_supported, FALSE);
|
|
|
|
|
|
|
|
|
|
probe = _nm_n_acd_data_probe_new(self, addr, timeout_msec, user_data);
|
|
|
|
|
if (!probe) {
|
|
|
|
|
NM_SET_OUT(out_failure_reason, "failure to create nacd probe");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NM_SET_OUT(out_failure_reason, NULL);
|
|
|
|
|
return probe;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_free_trackers(NML3Cfg *self, AcdData *acd_data, gboolean all /* or only dirty */)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
AcdTrackData *acd_track;
|
|
|
|
|
AcdTrackData *acd_track_safe;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
c_list_for_each_entry_safe (acd_track,
|
|
|
|
|
acd_track_safe,
|
|
|
|
|
&acd_data->acd_track_lst_head,
|
|
|
|
|
acd_track_lst) {
|
|
|
|
|
/* If not "all" is requested, we only delete the dirty ones
|
2020-09-28 14:50:01 +02:00
|
|
|
* (and mark the survivors as dirty right away). */
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!all && !acd_track->acd_dirty) {
|
|
|
|
|
acd_track->acd_dirty = TRUE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT_acd(acd_data, "untrack " ACD_TRACK_FMT "", ACD_TRACK_PTR(acd_track));
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_acd_track_data_free(acd_track);
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!c_list_is_empty(&acd_data->acd_track_lst_head))
|
|
|
|
|
return;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!g_hash_table_remove(self->priv.p->acd_lst_hash, acd_data))
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
_acd_data_free(acd_data);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_prune(NML3Cfg *self, gboolean all /* or only dirty */)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
AcdData *acd_data_safe;
|
|
|
|
|
AcdData *acd_data;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
c_list_for_each_entry_safe (acd_data, acd_data_safe, &self->priv.p->acd_lst_head, acd_lst)
|
|
|
|
|
_l3_acd_data_free_trackers(self, acd_data, all);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static AcdData *
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_find(NML3Cfg *self, in_addr_t addr)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
return nm_g_hash_table_lookup(self->priv.p->acd_lst_hash, &addr);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_add(NML3Cfg * self,
|
|
|
|
|
const NML3ConfigData *l3cd,
|
|
|
|
|
const NMPObject * obj,
|
|
|
|
|
gconstpointer tag,
|
|
|
|
|
guint32 acd_timeout_msec)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
in_addr_t addr = NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address;
|
|
|
|
|
AcdTrackData *acd_track;
|
|
|
|
|
AcdData * acd_data;
|
|
|
|
|
const char * track_mode;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (ACD_ADDR_SKIP(addr))
|
|
|
|
|
return;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
acd_data = _l3_acd_data_find(self, addr);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (acd_timeout_msec > ACD_MAX_TIMEOUT_MSEC) {
|
|
|
|
|
/* we limit the maximum timeout. Otherwise we have to handle integer overflow
|
2020-09-28 14:50:01 +02:00
|
|
|
* when adding timeouts. */
|
2020-09-28 16:03:33 +02:00
|
|
|
acd_timeout_msec = ACD_MAX_TIMEOUT_MSEC;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!acd_data) {
|
|
|
|
|
if (G_UNLIKELY(!self->priv.p->acd_lst_hash)) {
|
|
|
|
|
G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(AcdData, addr) == 0);
|
|
|
|
|
self->priv.p->acd_lst_hash = g_hash_table_new(nm_puint32_hash, nm_puint32_equals);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
acd_data = g_slice_new(AcdData);
|
|
|
|
|
*acd_data = (AcdData){
|
|
|
|
|
.self = self,
|
|
|
|
|
.addr = addr,
|
|
|
|
|
.acd_track_lst_head = C_LIST_INIT(acd_data->acd_track_lst_head),
|
|
|
|
|
.acd_notify_complete_lst = C_LIST_INIT(acd_data->acd_notify_complete_lst),
|
|
|
|
|
.acd_state = ACD_STATE_INIT,
|
|
|
|
|
.probing_timestamp_msec = 0,
|
|
|
|
|
.probe_result = FALSE,
|
|
|
|
|
.initializing = TRUE,
|
|
|
|
|
};
|
|
|
|
|
c_list_link_tail(&self->priv.p->acd_lst_head, &acd_data->acd_lst);
|
|
|
|
|
if (!g_hash_table_add(self->priv.p->acd_lst_hash, acd_data))
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
acd_track = NULL;
|
|
|
|
|
} else {
|
|
|
|
|
acd_track = _acd_data_find_track(acd_data, l3cd, obj, tag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!acd_track) {
|
|
|
|
|
acd_track = g_slice_new(AcdTrackData);
|
|
|
|
|
*acd_track = (AcdTrackData){
|
|
|
|
|
.l3cd = nm_l3_config_data_ref(l3cd),
|
|
|
|
|
.obj = nmp_object_ref(obj),
|
|
|
|
|
.tag = tag,
|
|
|
|
|
.acd_dirty = FALSE,
|
|
|
|
|
.acd_timeout_msec = acd_timeout_msec,
|
|
|
|
|
};
|
|
|
|
|
c_list_link_tail(&acd_data->acd_track_lst_head, &acd_track->acd_track_lst);
|
|
|
|
|
track_mode = "new";
|
|
|
|
|
} else {
|
|
|
|
|
nm_assert(acd_track->acd_dirty);
|
|
|
|
|
acd_track->acd_dirty = FALSE;
|
|
|
|
|
if (acd_track->acd_timeout_msec != acd_timeout_msec) {
|
|
|
|
|
acd_track->acd_timeout_msec = acd_timeout_msec;
|
|
|
|
|
track_mode = "update";
|
|
|
|
|
} else
|
|
|
|
|
track_mode = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (track_mode) {
|
|
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"track " ACD_TRACK_FMT " with timeout %u msec (%s)",
|
|
|
|
|
ACD_TRACK_PTR(acd_track),
|
|
|
|
|
acd_timeout_msec,
|
|
|
|
|
track_mode);
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_add_all(NML3Cfg *self, const L3ConfigData *const *infos, guint infos_len)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
AcdData *acd_data;
|
|
|
|
|
guint i_info;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* First we add/track all the relevant addresses for ACD. */
|
|
|
|
|
for (i_info = 0; i_info < infos_len; i_info++) {
|
|
|
|
|
const L3ConfigData *info = infos[i_info];
|
|
|
|
|
NMDedupMultiIter iter;
|
|
|
|
|
const NMPObject * obj;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3_config_data_iter_obj_for_each(&iter, info->l3cd, &obj, NMP_OBJECT_TYPE_IP4_ADDRESS)
|
|
|
|
|
_l3_acd_data_add(self, info->l3cd, obj, info->tag, info->acd_timeout_msec);
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* Then we do a pre-flight check, whether some of the acd_data entries can already
|
2020-09-28 14:50:01 +02:00
|
|
|
* move forward to automatically pass ACD. That is the case if acd_timeout_msec
|
|
|
|
|
* is zero (to disable ACD) or if the address is already configured on the
|
|
|
|
|
* interface. */
|
2020-09-28 16:03:33 +02:00
|
|
|
c_list_for_each_entry (acd_data, &self->priv.p->acd_lst_head, acd_lst)
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_INIT, NULL);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_timeout_cb(gpointer user_data)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
AcdData *acd_data = user_data;
|
|
|
|
|
NML3Cfg *self = acd_data->self;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_clear_g_source_inst(&acd_data->acd_timeout_source);
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_TIMEOUT, NULL);
|
|
|
|
|
return G_SOURCE_REMOVE;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_timeout_schedule(AcdData *acd_data,
|
|
|
|
|
gint64 now_msec,
|
|
|
|
|
gint64 expiry_msec,
|
|
|
|
|
gboolean msec_granularity)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(expiry_msec > 0);
|
|
|
|
|
nm_assert(now_msec > 0);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (acd_data->acd_timeout_source && acd_data->acd_timeout_expiry_msec == expiry_msec)
|
|
|
|
|
return;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_clear_g_source_inst(&acd_data->acd_timeout_source);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
acd_data->acd_timeout_expiry_msec = expiry_msec;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (msec_granularity) {
|
|
|
|
|
acd_data->acd_timeout_source = nm_g_timeout_source_new(NM_MAX(0, expiry_msec - now_msec),
|
|
|
|
|
G_PRIORITY_DEFAULT,
|
|
|
|
|
_l3_acd_data_timeout_cb,
|
|
|
|
|
acd_data,
|
|
|
|
|
NULL);
|
|
|
|
|
} else {
|
|
|
|
|
acd_data->acd_timeout_source =
|
|
|
|
|
nm_g_timeout_source_new_seconds((NM_MAX(0, expiry_msec - now_msec) + 999) / 1000,
|
|
|
|
|
G_PRIORITY_DEFAULT,
|
|
|
|
|
_l3_acd_data_timeout_cb,
|
|
|
|
|
acd_data,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
g_source_attach(acd_data->acd_timeout_source, NULL);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_timeout_schedule_probing_restart(AcdData *acd_data, gint64 now_msec)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
gint64 expiry_msec;
|
|
|
|
|
gint64 timeout_msec;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(acd_data);
|
|
|
|
|
nm_assert(now_msec > 0);
|
|
|
|
|
nm_assert(acd_data->acd_state == ACD_STATE_PROBING);
|
|
|
|
|
nm_assert(!acd_data->nacd_probe);
|
|
|
|
|
nm_assert(acd_data->probing_timeout_msec > 0);
|
|
|
|
|
nm_assert(acd_data->probing_timestamp_msec > 0);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
expiry_msec = acd_data->probing_timestamp_msec + ACD_WAIT_PROBING_EXTRA_TIME_MSEC;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
timeout_msec = NM_MAX(0, expiry_msec - now_msec);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (timeout_msec > 1000) {
|
|
|
|
|
/* we poll at least once per second to re-check the state. */
|
|
|
|
|
timeout_msec = 1000;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_timeout_schedule(acd_data, now_msec, now_msec + timeout_msec, TRUE);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_timeout_schedule_probing_full_restart(AcdData *acd_data, gint64 now_msec)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(acd_data);
|
|
|
|
|
nm_assert(now_msec > 0);
|
|
|
|
|
nm_assert(acd_data->acd_state == ACD_STATE_PROBE_DONE);
|
|
|
|
|
nm_assert(!acd_data->probe_result);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_timeout_schedule(acd_data,
|
|
|
|
|
now_msec,
|
|
|
|
|
now_msec + ACD_WAIT_TIME_PROBING_FULL_RESTART_MSEC,
|
|
|
|
|
FALSE);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_timeout_schedule_announce_restart(AcdData *acd_data, gint64 now_msec)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(acd_data);
|
|
|
|
|
nm_assert(now_msec > 0);
|
|
|
|
|
nm_assert(acd_data->acd_state == ACD_STATE_PROBE_DONE);
|
|
|
|
|
nm_assert(acd_data->probe_result);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_timeout_schedule(acd_data,
|
|
|
|
|
now_msec,
|
|
|
|
|
now_msec + ACD_WAIT_TIME_ANNOUNCE_RESTART_MSEC,
|
|
|
|
|
FALSE);
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_notify_acd_completed(NML3Cfg *self, AcdData *acd_data, gboolean force_all)
|
|
|
|
|
{
|
|
|
|
|
gs_free NML3ConfigNotifyPayloadAcdFailedSource *sources_free = NULL;
|
|
|
|
|
NML3ConfigNotifyPayloadAcdFailedSource * sources = NULL;
|
2020-09-28 16:19:31 +02:00
|
|
|
NML3ConfigNotifyData notify_data;
|
2020-09-28 16:03:33 +02:00
|
|
|
AcdTrackData * acd_track;
|
|
|
|
|
guint i, n;
|
|
|
|
|
NMTernary acd_failed_notified_selector;
|
|
|
|
|
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(acd_data);
|
|
|
|
|
nm_assert(_acd_data_collect_tracks_data(acd_data, FALSE, NM_TERNARY_DEFAULT, NULL) == 0);
|
|
|
|
|
|
|
|
|
|
acd_failed_notified_selector = force_all ? NM_TERNARY_DEFAULT : FALSE;
|
|
|
|
|
|
|
|
|
|
n = _acd_data_collect_tracks_data(acd_data,
|
|
|
|
|
NM_TERNARY_DEFAULT,
|
|
|
|
|
acd_failed_notified_selector,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!force_all) {
|
|
|
|
|
_LOGT_acd(acd_data, "state: acd probe failed earlier. Emit notification for new trackers");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n * sizeof(sources[0]) > 300) {
|
|
|
|
|
sources_free = g_new(NML3ConfigNotifyPayloadAcdFailedSource, n);
|
|
|
|
|
sources = sources_free;
|
|
|
|
|
} else
|
|
|
|
|
sources = g_newa(NML3ConfigNotifyPayloadAcdFailedSource, n);
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
c_list_for_each_entry (acd_track, &acd_data->acd_track_lst_head, acd_track_lst) {
|
|
|
|
|
if (!force_all && acd_track->acd_failed_notified) {
|
|
|
|
|
/* already notified before. Skip. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
nm_assert(i < n);
|
|
|
|
|
acd_track->acd_failed_notified = TRUE;
|
|
|
|
|
sources[i++] = (NML3ConfigNotifyPayloadAcdFailedSource){
|
|
|
|
|
.obj = nmp_object_ref(acd_track->obj),
|
|
|
|
|
.l3cd = nm_l3_config_data_ref(acd_track->l3cd),
|
|
|
|
|
.tag = acd_track->tag,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
nm_assert(i == n);
|
|
|
|
|
|
2020-09-28 16:19:31 +02:00
|
|
|
notify_data.notify_type = NM_L3_CONFIG_NOTIFY_TYPE_ACD_COMPLETED;
|
|
|
|
|
notify_data.acd_completed = (typeof(notify_data.acd_completed)){
|
|
|
|
|
.addr = acd_data->addr,
|
|
|
|
|
.probe_result = acd_data->probe_result,
|
|
|
|
|
.sources_len = n,
|
|
|
|
|
.sources = sources,
|
2020-09-28 16:03:33 +02:00
|
|
|
};
|
2020-09-28 16:19:31 +02:00
|
|
|
_nm_l3cfg_emit_signal_notify(self, ¬ify_data);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
nmp_object_unref(sources[i].obj);
|
|
|
|
|
nm_l3_config_data_unref(sources[i].l3cd);
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
2020-09-19 12:20:45 +02:00
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_notify_acd_completed_queue(NML3Cfg *self, AcdData *acd_data)
|
2020-09-19 12:20:45 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!c_list_is_empty(&acd_data->acd_notify_complete_lst)) {
|
|
|
|
|
nm_assert(c_list_contains(&self->priv.p->acd_notify_complete_lst_head,
|
|
|
|
|
&acd_data->acd_notify_complete_lst));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
c_list_link_tail(&self->priv.p->acd_notify_complete_lst_head,
|
|
|
|
|
&acd_data->acd_notify_complete_lst);
|
2020-09-19 12:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_notify_acd_completed_all(NML3Cfg *self)
|
2020-09-19 12:20:45 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
gs_unref_object NML3Cfg *self_keep_alive = NULL;
|
|
|
|
|
AcdData * acd_data;
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
while ((acd_data = c_list_first_entry(&self->priv.p->acd_notify_complete_lst_head,
|
|
|
|
|
AcdData,
|
|
|
|
|
acd_notify_complete_lst))) {
|
|
|
|
|
if (!self_keep_alive)
|
|
|
|
|
self_keep_alive = g_object_ref(self);
|
|
|
|
|
c_list_unlink(&acd_data->acd_notify_complete_lst);
|
|
|
|
|
_l3_acd_data_notify_acd_completed(self, acd_data, TRUE);
|
|
|
|
|
}
|
2020-09-19 12:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-03 17:33:31 +02:00
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_state_change(NML3Cfg * self,
|
|
|
|
|
AcdData * acd_data,
|
|
|
|
|
AcdStateChangeMode state_change_mode,
|
|
|
|
|
NAcdEvent * event)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
guint32 acd_timeout_msec;
|
|
|
|
|
gint64 now_msec = 0;
|
|
|
|
|
const char *log_reason;
|
|
|
|
|
gboolean was_probing;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* Keeping track of ACD inevitably requires keeping (and mutating) state. Then a multitude of
|
2020-09-28 14:50:01 +02:00
|
|
|
* things can happen, and depending on the state, we need to do something.
|
|
|
|
|
*
|
|
|
|
|
* Here, all the state for one address that we probe/announce is tracked in AcdData/acd_data.
|
|
|
|
|
*
|
|
|
|
|
* The acd_data has a list of AcdTrackData/acd_track_lst_head, which are configuration items
|
|
|
|
|
* that are interested in configuring this address. The "owners" of the ACD check for a certain
|
|
|
|
|
* address.
|
|
|
|
|
*
|
|
|
|
|
* We try to do all the state changes in this _l3_acd_data_state_change() function, where --
|
|
|
|
|
* depending on the @state_change_mode -- we progress the state.
|
|
|
|
|
*
|
|
|
|
|
* It is complicated, but I think this is not really avoidable if you want to handle all
|
|
|
|
|
* the special things (state-changes) that can happen.
|
|
|
|
|
*/
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(acd_data);
|
|
|
|
|
nm_assert(!c_list_is_empty(&acd_data->acd_track_lst_head));
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
was_probing = acd_data->acd_state < ACD_STATE_PROBE_DONE;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
switch (state_change_mode) {
|
|
|
|
|
case ACD_STATE_CHANGE_MODE_INIT:
|
|
|
|
|
{
|
|
|
|
|
AcdTrackData *acd_track;
|
|
|
|
|
gboolean any_no_timeout;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* we are called from _l3_acd_data_add_all(), and we do a fast check whether
|
2020-09-28 14:50:01 +02:00
|
|
|
* newly tracked entries already passed ACD so that we can use the address
|
|
|
|
|
* right away. */
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (_l3_acd_ipv4_addresses_on_link_contains(self, acd_data->addr)) {
|
|
|
|
|
/* the address is already configured on the link. It is an automatic pass. */
|
|
|
|
|
if (_acd_data_collect_tracks_data(acd_data, FALSE, NM_TERNARY_DEFAULT, NULL) <= 0) {
|
|
|
|
|
/* The entry has no non-dirty trackers, that means, it's no longer referenced
|
2020-09-28 14:50:01 +02:00
|
|
|
* and will be removed during the next _l3_acd_data_prune(). We can ignore
|
|
|
|
|
* this entry. */
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
log_reason = "address initially already configured";
|
|
|
|
|
goto handle_probing_acd_good;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* we are called at the end of _l3_acd_data_add_all(). We updated the list of a
|
2020-09-28 14:50:01 +02:00
|
|
|
* all tracked IP addresses before we actually collect the addresses that are
|
|
|
|
|
* ready. We don't do regular handling of ACD states at this point, however,
|
|
|
|
|
* we check whether ACD for new elements is disabled entirely, so we can signal
|
|
|
|
|
* the address are ready right away (without going through another hop). */
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (acd_data->acd_state != ACD_STATE_INIT) {
|
|
|
|
|
/* this element is not new and we don't perform the quick-check. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
any_no_timeout = FALSE;
|
|
|
|
|
c_list_for_each_entry (acd_track, &acd_data->acd_track_lst_head, acd_track_lst) {
|
|
|
|
|
/* There should be no dirty trackers, because the element is in init-state. */
|
|
|
|
|
nm_assert(!acd_track->acd_dirty);
|
|
|
|
|
if (acd_track->acd_timeout_msec <= 0) {
|
|
|
|
|
/* ACD for this element is disabled. We can process is right away. */
|
|
|
|
|
any_no_timeout = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!any_no_timeout) {
|
|
|
|
|
/* there are elements that request the address, but they all specify
|
2020-09-28 14:50:01 +02:00
|
|
|
* an ACD timeout. We cannot progress the state. */
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* ACD is disabled, we can artificially moving the state further to
|
2020-09-28 14:50:01 +02:00
|
|
|
* ACD_STATE_PROBE_DONE and configure the address right away. This avoids
|
|
|
|
|
* that we go through another hop.
|
|
|
|
|
*/
|
2020-09-28 16:03:33 +02:00
|
|
|
log_reason = "ACD disabled by configuration from the start";
|
|
|
|
|
goto handle_probing_acd_good;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ACD_STATE_CHANGE_MODE_POST_COMMIT:
|
|
|
|
|
acd_data->initializing = FALSE;
|
|
|
|
|
goto handle_post_commit;
|
|
|
|
|
|
|
|
|
|
case ACD_STATE_CHANGE_MODE_TIMEOUT:
|
|
|
|
|
{
|
|
|
|
|
if (acd_data->acd_state == ACD_STATE_PROBING && !acd_data->nacd_probe) {
|
|
|
|
|
const char *failure_reason;
|
|
|
|
|
gboolean acd_not_supported;
|
|
|
|
|
|
|
|
|
|
nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
|
|
|
|
|
|
|
|
|
|
if (acd_data->probing_timestamp_msec + ACD_WAIT_PROBING_EXTRA_TIME_MSEC
|
|
|
|
|
+ ACD_WAIT_PROBING_EXTRA_TIME2_MSEC
|
|
|
|
|
>= now_msec) {
|
|
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: probe-good (waiting for creating probe timed out. Assume good)");
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBE_DONE;
|
|
|
|
|
acd_data->probe_result = TRUE;
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* try create a new probe. The timeout is always as originally requested. */
|
|
|
|
|
acd_data->nacd_probe =
|
|
|
|
|
_l3_acd_nacd_instance_create_probe(self,
|
|
|
|
|
acd_data->addr,
|
|
|
|
|
acd_data->probing_timeout_msec,
|
|
|
|
|
acd_data,
|
|
|
|
|
&acd_not_supported,
|
|
|
|
|
&failure_reason);
|
|
|
|
|
if (acd_not_supported) {
|
|
|
|
|
nm_assert(!acd_data->nacd_probe);
|
|
|
|
|
_LOGT_acd(
|
|
|
|
|
acd_data,
|
|
|
|
|
"state: probe-good (interface does not support ACD anymore after timeout)");
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBE_DONE;
|
|
|
|
|
acd_data->probe_result = TRUE;
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!acd_data->nacd_probe) {
|
|
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: probing not possible at this time (%s). Wait longer",
|
|
|
|
|
failure_reason);
|
|
|
|
|
_l3_acd_data_timeout_schedule_probing_restart(acd_data, now_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* probing started (with the original timeout. Note that acd_data->probing_time*_msec
|
2020-09-28 14:50:01 +02:00
|
|
|
* no longer corresponds to the actual timeout of the nacd_probe. This is not a problem
|
|
|
|
|
* because at this point we only trust the internal timer from nacd_probe to get
|
|
|
|
|
* it right. Instead, we keep acd_data->probing_time*_msec unchanged, to remember when
|
|
|
|
|
* we originally wanted to start. */
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: probing started (after retry, timeout %u msec)",
|
|
|
|
|
acd_data->probing_timeout_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (acd_data->acd_state == ACD_STATE_PROBE_DONE && !acd_data->probe_result) {
|
|
|
|
|
/* Probing is done, but previously we detected a conflict. After a restart, we retry to
|
2020-09-28 14:50:01 +02:00
|
|
|
* probe. */
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(!acd_data->nacd_probe);
|
|
|
|
|
nm_assert(!acd_data->announcing_failed_is_retrying);
|
|
|
|
|
|
|
|
|
|
_LOGT_acd(acd_data, "state: restart a new probe after previous conflict");
|
|
|
|
|
acd_data->acd_state = ACD_STATE_INIT;
|
|
|
|
|
goto handle_post_commit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (acd_data->acd_state == ACD_STATE_PROBE_DONE && acd_data->probe_result
|
|
|
|
|
&& !acd_data->nacd_probe && acd_data->announcing_failed_is_retrying) {
|
|
|
|
|
/* Probing is done, but previously we failed to start announcing. Retry now. */
|
|
|
|
|
nm_assert(!was_probing);
|
|
|
|
|
_LOGT_acd(acd_data, "state: retry announcing address");
|
|
|
|
|
acd_data->announcing_failed_is_retrying = FALSE;
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ACD_STATE_CHANGE_MODE_NACD_READY:
|
|
|
|
|
if (acd_data->acd_state == ACD_STATE_PROBING) {
|
|
|
|
|
log_reason = "acd indicates ready";
|
|
|
|
|
goto handle_probing_acd_good;
|
|
|
|
|
}
|
|
|
|
|
if (acd_data->acd_state == ACD_STATE_ANNOUNCING) {
|
|
|
|
|
_LOGT_acd(acd_data, "state: ready to start announcing");
|
|
|
|
|
if (n_acd_probe_announce(acd_data->nacd_probe, N_ACD_DEFEND_ALWAYS) != 0)
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* nacd really shouldn't call us in this state. There is a bug somewhere. */
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case ACD_STATE_CHANGE_MODE_NACD_USED:
|
|
|
|
|
{
|
|
|
|
|
gs_free char *str_to_free = NULL;
|
|
|
|
|
|
|
|
|
|
nm_assert(acd_data->acd_state == ACD_STATE_PROBING);
|
|
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: probe-done bad (address already in use by %s)",
|
|
|
|
|
nm_utils_bin2hexstr_a(event->_acd_event_payload.sender,
|
|
|
|
|
event->_acd_event_payload.n_sender,
|
|
|
|
|
':',
|
|
|
|
|
FALSE,
|
|
|
|
|
&str_to_free));
|
|
|
|
|
acd_data->nacd_probe = n_acd_probe_free(acd_data->nacd_probe);
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBE_DONE;
|
|
|
|
|
acd_data->probe_result = FALSE;
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ACD_STATE_CHANGE_MODE_EXTERNAL_ADDED:
|
|
|
|
|
/* the address is configured on the link. This means, ACD passed */
|
|
|
|
|
log_reason = "address configured on link";
|
|
|
|
|
goto handle_probing_acd_good;
|
|
|
|
|
|
|
|
|
|
case ACD_STATE_CHANGE_MODE_EXTERNAL_REMOVED:
|
|
|
|
|
/* The address got removed. Either we ourself removed it or it was removed externally.
|
2020-09-28 14:50:01 +02:00
|
|
|
* In either case, it's not clear what we should do about that, regardless in which
|
|
|
|
|
* ACD state we are, so ignore it. */
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT_acd(acd_data, "state: address was externally removed. Ignore");
|
|
|
|
|
return;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
case ACD_STATE_CHANGE_MODE_NACD_DOWN:
|
|
|
|
|
if (acd_data->acd_state < ACD_STATE_PROBE_DONE) {
|
|
|
|
|
/* we are probing, but the probe has a problem that the link went down. Maybe
|
2020-09-28 14:50:01 +02:00
|
|
|
* we need to restart. */
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(acd_data->acd_state == ACD_STATE_PROBING);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!acd_data->nacd_probe) {
|
|
|
|
|
/* we are in probing state, but currently not really probing. A timer is
|
2020-09-28 14:50:01 +02:00
|
|
|
* running, and we will handle this situation that way. */
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* We abort the probing, but we also schedule a timer to restart it. Let
|
2020-09-28 14:50:01 +02:00
|
|
|
* the regular re-start handling handle this. */
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: interface-down. Probing aborted but we keep waiting to retry");
|
|
|
|
|
acd_data->nacd_probe = n_acd_probe_free(acd_data->nacd_probe);
|
|
|
|
|
_l3_acd_data_timeout_schedule_probing_restart(acd_data, now_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We already completed a probe and acted accordingly (by either configuring the address
|
2020-09-28 14:50:01 +02:00
|
|
|
* already or by rejecting it). We cannot (easily) re-evaluate what to do now. Should
|
|
|
|
|
* we later restart probing? But what about the decisions we already made??
|
|
|
|
|
* Ignore the situation. */
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
case ACD_STATE_CHANGE_MODE_LINK_NOW_UP:
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* The interface just came up. */
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (acd_data->acd_state <= ACD_STATE_PROBING) {
|
|
|
|
|
nm_auto(n_acd_probe_freep) NAcdProbe *probe = NULL;
|
|
|
|
|
const char * failure_reason;
|
|
|
|
|
gboolean acd_not_supported;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* the interface was probing. We will restart the probe. */
|
|
|
|
|
nm_assert(acd_data->acd_state == ACD_STATE_PROBING);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!acd_data->nacd_probe) {
|
|
|
|
|
/* We currently are waiting to restart probing. We don't handle the link-up
|
2020-09-28 14:50:01 +02:00
|
|
|
* event here, we only trigger a timeout right away. */
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: ignore link up event while we are waiting to start probing");
|
|
|
|
|
_l3_acd_data_timeout_schedule(acd_data, now_msec, now_msec, TRUE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (acd_data->probing_timestamp_msec + ACD_WAIT_PROBING_RESTART_TIME_MSEC >= now_msec) {
|
|
|
|
|
/* This probe was already started quite a while ago. We ignore the link-up event
|
2020-09-28 14:50:01 +02:00
|
|
|
* and let it complete regularly. This is to avoid restarting to probing indefinitely. */
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT_acd(acd_data, "state: ignore link up event for a probe started long ago");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
probe = _l3_acd_nacd_instance_create_probe(self,
|
|
|
|
|
acd_data->addr,
|
|
|
|
|
acd_data->probing_timeout_msec,
|
|
|
|
|
acd_data,
|
|
|
|
|
&acd_not_supported,
|
|
|
|
|
&failure_reason);
|
|
|
|
|
if (!probe) {
|
|
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: link up event would cause to retry probing, but creating a probe "
|
|
|
|
|
"failed (%s). Ignore and keep previous probe",
|
|
|
|
|
failure_reason);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NM_SWAP(&probe, &acd_data->nacd_probe);
|
|
|
|
|
|
|
|
|
|
/* We just restarted probing. Note that we don't touch the original acd_data->probing_time*_msec
|
2020-09-28 14:50:01 +02:00
|
|
|
* times, otherwise a repeated link up/down cycle could extend the probing indefinitely.
|
|
|
|
|
*
|
|
|
|
|
* This is despite the new probe just started counting now. So, at this point, the
|
|
|
|
|
* timestamp/timeout of acd_data no longer corresponds to the internal timestamp of
|
|
|
|
|
* acd_data->nacd_probe. But since we don't run our own timer against the internal timer of
|
|
|
|
|
* acd_data->nacd_probe, that is not a problem.
|
|
|
|
|
*/
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: probing restarted (after link up, new timeout %u msec)",
|
|
|
|
|
acd_data->probing_timeout_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
l3cfg: add nm_l3cfg_property_emit_register() API
The NML3Cfg instance tracks and prepares the IP configuration.
However, that is also partly exposed on other objects, like
NMIP4Config's "route-data" property.
Add an API, so that NMIP4Config can register itself to be notified
when something relevant changes.
This is an alternative to standard GObject properties and signals. They
often seem more effort than worth. That is, because in this case,
NMIP4Config.route-data has no other task then to re-emit the signal.
So, to implement that with GObject properties/signals, we would have to
add a property/signal to NML3Cfg, subscribe to it from NMIP4Config,
and remit the signal. An alternative is to bind properties, but that
would still be quite some extra code, and unclear that it would be
simpler. Not to mention the overhead, as bindings are themself full
GObject instances, that register to and emit signals by name.
2020-07-21 12:52:42 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* we are already done with the ACD state. Bringing up an interface has
|
2020-09-28 14:50:01 +02:00
|
|
|
* no further consequence w.r.t. the ACD state. */
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case ACD_STATE_CHANGE_MODE_INSTANCE_RESET:
|
|
|
|
|
nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
|
|
|
|
|
if (acd_data->acd_state <= ACD_STATE_PROBING) {
|
|
|
|
|
nm_assert(acd_data->acd_state == ACD_STATE_PROBING);
|
|
|
|
|
|
|
|
|
|
_LOGT_acd(
|
|
|
|
|
acd_data,
|
|
|
|
|
"state: n-acd instance reset. Trigger a restart of the probing (was %sprobing)",
|
|
|
|
|
acd_data->nacd_probe ? "" : "not");
|
|
|
|
|
/* Just destroy the current probe (if any) and retrigger a restart right away. */
|
|
|
|
|
acd_data->nacd_probe = n_acd_probe_free(acd_data->nacd_probe);
|
|
|
|
|
_l3_acd_data_timeout_schedule(acd_data, now_msec, now_msec, TRUE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (acd_data->probe_result) {
|
|
|
|
|
_LOGT_acd(acd_data, "state: n-acd instance reset. Restart announcing");
|
|
|
|
|
} else {
|
|
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: n-acd instance reset. Reprobe the address that conflicted before");
|
|
|
|
|
}
|
|
|
|
|
acd_data->nacd_probe = n_acd_probe_free(acd_data->nacd_probe);
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBE_DONE;
|
|
|
|
|
_l3_acd_data_timeout_schedule(acd_data, now_msec, now_msec, TRUE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
return;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
|
|
|
|
handle_post_commit:
|
2020-09-28 16:03:33 +02:00
|
|
|
/* we just did a commit of the IP configuration and now visit all ACD states
|
2020-09-28 14:50:01 +02:00
|
|
|
* and kick off the necessary actions... */
|
2020-09-28 16:03:33 +02:00
|
|
|
if (_l3_acd_ipv4_addresses_on_link_contains(self, acd_data->addr)) {
|
|
|
|
|
log_reason = "address already configured";
|
|
|
|
|
goto handle_probing_acd_good;
|
|
|
|
|
}
|
|
|
|
|
if (_acd_data_collect_tracks_data(acd_data, TRUE, NM_TERNARY_DEFAULT, &acd_timeout_msec) <= 0)
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
if (acd_timeout_msec <= 0) {
|
|
|
|
|
log_reason = "ACD disabled by configuration";
|
|
|
|
|
goto handle_probing_acd_good;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (acd_data->acd_state) {
|
|
|
|
|
case ACD_STATE_INIT:
|
|
|
|
|
{
|
|
|
|
|
const char *failure_reason;
|
|
|
|
|
gboolean acd_not_supported;
|
|
|
|
|
NAcdProbe * probe;
|
|
|
|
|
|
|
|
|
|
nm_assert(!acd_data->nacd_probe);
|
|
|
|
|
|
|
|
|
|
probe = _l3_acd_nacd_instance_create_probe(self,
|
|
|
|
|
acd_data->addr,
|
|
|
|
|
acd_timeout_msec,
|
|
|
|
|
acd_data,
|
|
|
|
|
&acd_not_supported,
|
|
|
|
|
&failure_reason);
|
|
|
|
|
if (acd_not_supported) {
|
|
|
|
|
nm_assert(!probe);
|
|
|
|
|
_LOGT_acd(acd_data, "state: probe-good (interface does not support ACD)");
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBE_DONE;
|
|
|
|
|
acd_data->probe_result = TRUE;
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!probe) {
|
|
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: probing currently not possible (timeout %u msec; %s)",
|
|
|
|
|
acd_timeout_msec,
|
|
|
|
|
failure_reason);
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBING;
|
|
|
|
|
acd_data->probing_timeout_msec = acd_timeout_msec;
|
|
|
|
|
acd_data->probing_timestamp_msec =
|
|
|
|
|
nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
|
|
|
|
|
_l3_acd_data_timeout_schedule_probing_restart(acd_data, now_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_LOGT_acd(acd_data, "state: start probing (timeout %u msec)", acd_timeout_msec);
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBING;
|
|
|
|
|
acd_data->nacd_probe = probe;
|
|
|
|
|
acd_data->probing_timeout_msec = acd_timeout_msec;
|
|
|
|
|
acd_data->probing_timestamp_msec = nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ACD_STATE_PROBING:
|
|
|
|
|
{
|
|
|
|
|
nm_auto(n_acd_probe_freep) NAcdProbe *probe = NULL;
|
|
|
|
|
const char * failure_reason;
|
|
|
|
|
gboolean acd_not_supported;
|
|
|
|
|
gint64 old_expiry_msec;
|
|
|
|
|
gint64 new_expiry_msec;
|
|
|
|
|
|
|
|
|
|
nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
|
|
|
|
|
|
|
|
|
|
new_expiry_msec = now_msec + acd_timeout_msec;
|
|
|
|
|
old_expiry_msec = acd_data->probing_timestamp_msec + acd_data->probing_timeout_msec;
|
|
|
|
|
|
|
|
|
|
if (!acd_data->nacd_probe) {
|
|
|
|
|
/* we are currently waiting for restarting a probe. At this point, at most we have
|
2020-09-28 14:50:01 +02:00
|
|
|
* to adjust the timeout/timestamp and let the regular timeouts handle this. */
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (new_expiry_msec >= old_expiry_msec) {
|
|
|
|
|
/* the running timeout expires before the new timeout. We don't update the timestamp/timerout,
|
2020-09-28 14:50:01 +02:00
|
|
|
* because we don't want to prolong the overall probing time. */
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* update the timers after out timeout got reduced. Also, reschedule the timeout
|
2020-09-28 14:50:01 +02:00
|
|
|
* so that it expires immediately. */
|
2020-09-28 16:03:33 +02:00
|
|
|
acd_data->probing_timestamp_msec = now_msec;
|
|
|
|
|
acd_data->probing_timeout_msec = acd_timeout_msec;
|
|
|
|
|
_l3_acd_data_timeout_schedule(acd_data, now_msec, now_msec, TRUE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (new_expiry_msec >= old_expiry_msec) {
|
|
|
|
|
/* we already have ACD running with a timeout that expires before the requested one. There
|
2020-09-28 14:50:01 +02:00
|
|
|
* is nothing to do at this time. */
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* the timeout got reduced. We try to restart the probe. */
|
|
|
|
|
probe = _l3_acd_nacd_instance_create_probe(self,
|
|
|
|
|
acd_data->addr,
|
|
|
|
|
acd_timeout_msec,
|
|
|
|
|
acd_data,
|
|
|
|
|
&acd_not_supported,
|
|
|
|
|
&failure_reason);
|
|
|
|
|
NM_SWAP(&probe, &acd_data->nacd_probe);
|
|
|
|
|
|
|
|
|
|
if (acd_not_supported) {
|
|
|
|
|
nm_assert(!acd_data->nacd_probe);
|
|
|
|
|
_LOGT_acd(acd_data, "state: probe-good (interface does not support ACD anymore)");
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBE_DONE;
|
|
|
|
|
acd_data->probe_result = TRUE;
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!acd_data->nacd_probe) {
|
|
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: probing currently still not possible (timeout %u msec; %s)",
|
|
|
|
|
acd_timeout_msec,
|
|
|
|
|
failure_reason);
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBING;
|
|
|
|
|
acd_data->probing_timeout_msec = acd_timeout_msec;
|
|
|
|
|
acd_data->probing_timestamp_msec = now_msec;
|
|
|
|
|
_l3_acd_data_timeout_schedule_probing_restart(acd_data, now_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We update the timestamps (after also restarting the probe).
|
2020-09-28 14:50:01 +02:00
|
|
|
*
|
|
|
|
|
* Note that we only reduced the overall expiry. */
|
2020-09-28 16:03:33 +02:00
|
|
|
acd_data->probing_timestamp_msec = now_msec;
|
|
|
|
|
acd_data->probing_timeout_msec = acd_timeout_msec;
|
|
|
|
|
_LOGT_acd(acd_data, "state: restart probing (timeout %u msec)", acd_timeout_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ACD_STATE_PROBE_DONE:
|
|
|
|
|
case ACD_STATE_ANNOUNCING:
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
}
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
return;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
|
|
|
|
handle_probing_acd_good:
|
2020-09-28 16:03:33 +02:00
|
|
|
switch (acd_data->acd_state) {
|
|
|
|
|
case ACD_STATE_INIT:
|
|
|
|
|
_LOGT_acd(acd_data, "state: probe-done good (%s, initializing)", log_reason);
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBE_DONE;
|
|
|
|
|
acd_data->probe_result = TRUE;
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
case ACD_STATE_PROBING:
|
|
|
|
|
_LOGT_acd(acd_data, "state: probe-done good (%s, probing done)", log_reason);
|
|
|
|
|
if (state_change_mode != ACD_STATE_CHANGE_MODE_NACD_READY)
|
|
|
|
|
acd_data->nacd_probe = n_acd_probe_free(acd_data->nacd_probe);
|
|
|
|
|
acd_data->acd_state = ACD_STATE_PROBE_DONE;
|
|
|
|
|
acd_data->probe_result = TRUE;
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
case ACD_STATE_PROBE_DONE:
|
|
|
|
|
if (!acd_data->probe_result) {
|
|
|
|
|
nm_assert(!acd_data->nacd_probe);
|
|
|
|
|
_LOGT_acd(acd_data, "state: probe-done good (%s, after probe failed)", log_reason);
|
|
|
|
|
acd_data->probe_result = TRUE;
|
|
|
|
|
}
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
case ACD_STATE_ANNOUNCING:
|
|
|
|
|
nm_assert(acd_data->probe_result);
|
|
|
|
|
goto handle_probe_done;
|
|
|
|
|
}
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
return;
|
l3cfg: add nm_l3cfg_property_emit_register() API
The NML3Cfg instance tracks and prepares the IP configuration.
However, that is also partly exposed on other objects, like
NMIP4Config's "route-data" property.
Add an API, so that NMIP4Config can register itself to be notified
when something relevant changes.
This is an alternative to standard GObject properties and signals. They
often seem more effort than worth. That is, because in this case,
NMIP4Config.route-data has no other task then to re-emit the signal.
So, to implement that with GObject properties/signals, we would have to
add a property/signal to NML3Cfg, subscribe to it from NMIP4Config,
and remit the signal. An alternative is to bind properties, but that
would still be quite some extra code, and unclear that it would be
simpler. Not to mention the overhead, as bindings are themself full
GObject instances, that register to and emit signals by name.
2020-07-21 12:52:42 +02:00
|
|
|
|
2020-08-03 17:33:31 +02:00
|
|
|
handle_probe_done:
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IN_SET(acd_data->acd_state, ACD_STATE_PROBE_DONE, ACD_STATE_ANNOUNCING));
|
|
|
|
|
|
|
|
|
|
if (acd_data->initializing)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (acd_data->acd_state >= ACD_STATE_ANNOUNCING) {
|
|
|
|
|
nm_assert(acd_data->nacd_probe);
|
|
|
|
|
nm_assert(acd_data->probe_result);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!acd_data->probe_result) {
|
|
|
|
|
nm_assert(acd_data->acd_state == ACD_STATE_PROBE_DONE);
|
|
|
|
|
nm_assert(!acd_data->nacd_probe);
|
|
|
|
|
/* we just completed probing with negative result.
|
2020-09-28 14:50:01 +02:00
|
|
|
* Emit a signal, but also reschedule a timer to restart. */
|
2020-09-28 16:03:33 +02:00
|
|
|
if (was_probing) {
|
|
|
|
|
_LOGT_acd(acd_data, "state: acd probe failed; signal failure");
|
|
|
|
|
acd_data->probing_timestamp_msec =
|
|
|
|
|
nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
|
|
|
|
|
_l3_acd_data_timeout_schedule_probing_full_restart(acd_data, now_msec);
|
|
|
|
|
}
|
|
|
|
|
_l3_acd_data_notify_acd_completed_queue(self, acd_data);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (was_probing && acd_data->probe_result) {
|
|
|
|
|
/* probing just completed. Schedule handling the change. */
|
|
|
|
|
_LOGT_acd(acd_data, "state: acd probe succeed");
|
|
|
|
|
_l3_acd_data_notify_acd_completed_queue(self, acd_data);
|
2020-09-25 15:46:06 +02:00
|
|
|
if (state_change_mode != ACD_STATE_CHANGE_MODE_POST_COMMIT)
|
|
|
|
|
_l3_changed_configs_set_dirty(self);
|
|
|
|
|
nm_l3cfg_commit_on_idle_schedule(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!acd_data->nacd_probe) {
|
|
|
|
|
const char *failure_reason;
|
|
|
|
|
NAcdProbe * probe;
|
|
|
|
|
|
|
|
|
|
if (acd_data->announcing_failed_is_retrying) {
|
|
|
|
|
/* we already failed to create a probe. We are ratelimited to retry, but
|
2020-09-28 14:50:01 +02:00
|
|
|
* we have a timer pending... */
|
2020-09-28 16:03:33 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
probe = _l3_acd_nacd_instance_create_probe(self,
|
|
|
|
|
acd_data->addr,
|
|
|
|
|
0,
|
|
|
|
|
acd_data,
|
|
|
|
|
NULL,
|
|
|
|
|
&failure_reason);
|
|
|
|
|
if (!probe) {
|
|
|
|
|
/* we failed to create a probe for announcing the address. We log a (rate limited)
|
2020-09-28 14:50:01 +02:00
|
|
|
* warning and start a timer to retry. */
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT_acd(acd_data,
|
|
|
|
|
"state: start announcing failed to create probe (%s)",
|
|
|
|
|
failure_reason);
|
|
|
|
|
acd_data->announcing_failed_is_retrying = TRUE;
|
|
|
|
|
acd_data->probing_timestamp_msec =
|
|
|
|
|
nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
|
|
|
|
|
_l3_acd_data_timeout_schedule_announce_restart(acd_data, now_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_LOGT_acd(acd_data, "state: start announcing (with new probe)");
|
|
|
|
|
acd_data->nacd_probe = probe;
|
|
|
|
|
acd_data->acd_state = ACD_STATE_ANNOUNCING;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (acd_data->acd_state == ACD_STATE_PROBE_DONE) {
|
|
|
|
|
_LOGT_acd(acd_data, "state: start announcing (with existing probe)");
|
|
|
|
|
acd_data->acd_state = ACD_STATE_ANNOUNCING;
|
|
|
|
|
if (n_acd_probe_announce(acd_data->nacd_probe, N_ACD_DEFEND_ALWAYS) != 0)
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_process_changes(NML3Cfg *self)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
gboolean acd_is_announcing = FALSE;
|
|
|
|
|
gboolean acd_is_pending = FALSE;
|
|
|
|
|
AcdData *acd_data;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_prune(self, FALSE);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
c_list_for_each_entry (acd_data, &self->priv.p->acd_lst_head, acd_lst) {
|
|
|
|
|
_l3_acd_data_state_change(self, acd_data, ACD_STATE_CHANGE_MODE_POST_COMMIT, NULL);
|
|
|
|
|
if (acd_data->acd_state < ACD_STATE_PROBE_DONE)
|
|
|
|
|
acd_is_pending = TRUE;
|
|
|
|
|
else if (acd_data->acd_state >= ACD_STATE_ANNOUNCING
|
|
|
|
|
|| (acd_data->acd_state >= ACD_STATE_PROBE_DONE && acd_data->probe_result))
|
|
|
|
|
acd_is_announcing = TRUE;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
self->priv.p->acd_is_pending = acd_is_pending;
|
|
|
|
|
self->priv.p->acd_is_announcing = acd_is_announcing;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!acd_is_pending && !acd_is_announcing)
|
|
|
|
|
_l3_acd_nacd_instance_reset(self, NM_TERNARY_DEFAULT, FALSE);
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_notify_acd_completed_all(self);
|
2020-07-21 11:21:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
static gboolean
|
|
|
|
|
_l3_commit_on_idle_cb(gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
NML3Cfg *self = user_data;
|
|
|
|
|
|
|
|
|
|
nm_clear_g_source_inst(&self->priv.p->commit_on_idle_source);
|
|
|
|
|
|
|
|
|
|
_LOGT("platform commit on idle");
|
|
|
|
|
_l3_commit(self, NM_L3_CFG_COMMIT_TYPE_AUTO, TRUE);
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nm_l3cfg_commit_on_idle_schedule(NML3Cfg *self)
|
|
|
|
|
{
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
|
|
|
|
|
if (self->priv.p->commit_on_idle_source)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
self->priv.p->commit_on_idle_source =
|
|
|
|
|
nm_g_idle_source_new(G_PRIORITY_DEFAULT, _l3_commit_on_idle_cb, self, NULL);
|
|
|
|
|
g_source_attach(self->priv.p->commit_on_idle_source, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-07-23 16:15:26 +02:00
|
|
|
#define _l3_config_datas_at(l3_config_datas, idx) \
|
2020-09-28 16:03:33 +02:00
|
|
|
(&g_array_index((l3_config_datas), L3ConfigData, (idx)))
|
2020-07-23 16:15:26 +02:00
|
|
|
|
|
|
|
|
static gssize
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_config_datas_find_next(GArray * l3_config_datas,
|
|
|
|
|
guint start_idx,
|
|
|
|
|
gconstpointer needle_tag,
|
|
|
|
|
const NML3ConfigData *needle_l3cd)
|
2020-07-23 16:15:26 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
guint i;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(l3_config_datas);
|
|
|
|
|
nm_assert(start_idx <= l3_config_datas->len);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
for (i = start_idx; i < l3_config_datas->len; i++) {
|
|
|
|
|
const L3ConfigData *l3_config_data = _l3_config_datas_at(l3_config_datas, i);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (NM_IN_SET(needle_tag, NULL, l3_config_data->tag)
|
|
|
|
|
&& NM_IN_SET(needle_l3cd, NULL, l3_config_data->l3cd))
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
2020-07-23 16:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-03 17:33:31 +02:00
|
|
|
static int
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_config_datas_get_sorted_cmp(gconstpointer p_a, gconstpointer p_b, gpointer user_data)
|
2020-08-03 17:33:31 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
const L3ConfigData *a = *((L3ConfigData **) p_a);
|
|
|
|
|
const L3ConfigData *b = *((L3ConfigData **) p_b);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(a);
|
|
|
|
|
nm_assert(b);
|
|
|
|
|
nm_assert(nm_l3_config_data_get_ifindex(a->l3cd) == nm_l3_config_data_get_ifindex(b->l3cd));
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* we sort the entries with higher priority (more important, lower numerical value)
|
2020-09-28 14:50:01 +02:00
|
|
|
* first. */
|
2020-09-28 16:03:33 +02:00
|
|
|
NM_CMP_FIELD(a, b, priority);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* if the priority is not unique, we sort them in the order they were added,
|
2020-09-28 14:50:01 +02:00
|
|
|
* with the oldest first (lower numerical value). */
|
2020-09-28 16:03:33 +02:00
|
|
|
NM_CMP_FIELD(a, b, pseudo_timestamp);
|
|
|
|
|
|
|
|
|
|
return nm_assert_unreachable_val(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define _l3_config_datas_get_sorted_a(l3_config_datas, out_infos, out_infos_len, out_infos_free) \
|
|
|
|
|
G_STMT_START \
|
|
|
|
|
{ \
|
|
|
|
|
GArray *const _l3_config_datas = (l3_config_datas); \
|
|
|
|
|
const L3ConfigData *const **const _out_infos = (out_infos); \
|
|
|
|
|
guint *const _out_infos_len = (out_infos_len); \
|
|
|
|
|
const L3ConfigData ***const _out_infos_free = (out_infos_free); \
|
|
|
|
|
gs_free const L3ConfigData **_infos_free = NULL; \
|
|
|
|
|
const L3ConfigData ** _infos; \
|
|
|
|
|
guint _l3_config_datas_len; \
|
|
|
|
|
guint _i; \
|
|
|
|
|
\
|
|
|
|
|
_l3_config_datas_len = nm_g_array_len(_l3_config_datas); \
|
|
|
|
|
\
|
|
|
|
|
if (_l3_config_datas_len == 0) \
|
|
|
|
|
_infos = NULL; \
|
|
|
|
|
else if (_l3_config_datas_len < 300 / sizeof(_infos[0])) \
|
|
|
|
|
_infos = g_alloca(_l3_config_datas_len * sizeof(_infos[0])); \
|
|
|
|
|
else { \
|
|
|
|
|
_infos_free = g_new(const L3ConfigData *, _l3_config_datas_len); \
|
|
|
|
|
_infos = _infos_free; \
|
|
|
|
|
} \
|
|
|
|
|
for (_i = 0; _i < _l3_config_datas_len; _i++) \
|
|
|
|
|
_infos[_i] = _l3_config_datas_at(_l3_config_datas, _i); \
|
|
|
|
|
\
|
|
|
|
|
if (_l3_config_datas_len > 1) { \
|
|
|
|
|
g_qsort_with_data(_infos, \
|
|
|
|
|
_l3_config_datas_len, \
|
|
|
|
|
sizeof(_infos[0]), \
|
|
|
|
|
_l3_config_datas_get_sorted_cmp, \
|
|
|
|
|
NULL); \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
*_out_infos = _infos; \
|
|
|
|
|
*_out_infos_len = _l3_config_datas_len; \
|
|
|
|
|
*_out_infos_free = g_steal_pointer(&_infos_free); \
|
|
|
|
|
} \
|
|
|
|
|
G_STMT_END
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-07-23 16:15:26 +02:00
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_config_datas_remove_index_fast(GArray *arr, guint idx)
|
2020-07-23 16:15:26 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
L3ConfigData *l3_config_data;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(arr);
|
|
|
|
|
nm_assert(idx < arr->len);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
l3_config_data = _l3_config_datas_at(arr, idx);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3_config_data_unref(l3_config_data->l3cd);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
g_array_remove_index_fast(arr, idx);
|
2020-07-23 16:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_mark_config_dirty(NML3Cfg *self, gconstpointer tag, gboolean dirty)
|
2020-07-23 16:15:26 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
gssize idx;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(tag);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!self->priv.p->l3_config_datas)
|
|
|
|
|
return;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(self->priv.p->l3_config_datas->len > 0);
|
2020-09-22 18:25:25 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
idx = 0;
|
|
|
|
|
while (TRUE) {
|
|
|
|
|
idx = _l3_config_datas_find_next(self->priv.p->l3_config_datas, idx, tag, NULL);
|
|
|
|
|
if (idx < 0)
|
|
|
|
|
return;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_config_datas_at(self->priv.p->l3_config_datas, idx)->dirty = dirty;
|
|
|
|
|
idx++;
|
|
|
|
|
}
|
2020-07-23 16:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-28 17:20:53 +02:00
|
|
|
gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_add_config(NML3Cfg * self,
|
|
|
|
|
gconstpointer tag,
|
|
|
|
|
gboolean replace_same_tag,
|
|
|
|
|
const NML3ConfigData *l3cd,
|
|
|
|
|
int priority,
|
2020-09-23 15:55:28 +02:00
|
|
|
guint32 default_route_table_4,
|
|
|
|
|
guint32 default_route_table_6,
|
|
|
|
|
guint32 default_route_metric_4,
|
|
|
|
|
guint32 default_route_metric_6,
|
2020-09-28 16:03:33 +02:00
|
|
|
guint32 default_route_penalty_4,
|
|
|
|
|
guint32 default_route_penalty_6,
|
|
|
|
|
guint32 acd_timeout_msec,
|
|
|
|
|
NML3ConfigMergeFlags merge_flags)
|
|
|
|
|
{
|
|
|
|
|
L3ConfigData *l3_config_data;
|
|
|
|
|
gssize idx;
|
|
|
|
|
gboolean changed = FALSE;
|
|
|
|
|
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(tag);
|
|
|
|
|
nm_assert(l3cd);
|
|
|
|
|
nm_assert(nm_l3_config_data_get_ifindex(l3cd) == self->priv.ifindex);
|
|
|
|
|
|
2020-09-23 15:55:28 +02:00
|
|
|
nm_assert(default_route_metric_6 != 0u); /* IPv6 default route metric cannot be zero. */
|
|
|
|
|
|
|
|
|
|
if (default_route_table_4 == 0u)
|
|
|
|
|
default_route_table_4 = RT_TABLE_MAIN;
|
|
|
|
|
if (default_route_table_6 == 0u)
|
|
|
|
|
default_route_table_6 = RT_TABLE_MAIN;
|
|
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!self->priv.p->l3_config_datas) {
|
|
|
|
|
self->priv.p->l3_config_datas = g_array_new(FALSE, FALSE, sizeof(L3ConfigData));
|
|
|
|
|
g_object_ref(self);
|
|
|
|
|
} else
|
|
|
|
|
nm_assert(self->priv.p->l3_config_datas->len > 0);
|
|
|
|
|
|
|
|
|
|
idx = _l3_config_datas_find_next(self->priv.p->l3_config_datas,
|
|
|
|
|
0,
|
|
|
|
|
tag,
|
|
|
|
|
replace_same_tag ? NULL : l3cd);
|
|
|
|
|
|
|
|
|
|
if (replace_same_tag && idx >= 0) {
|
|
|
|
|
gssize idx2;
|
|
|
|
|
|
|
|
|
|
idx2 = idx;
|
|
|
|
|
idx = -1;
|
|
|
|
|
while (TRUE) {
|
|
|
|
|
l3_config_data = _l3_config_datas_at(self->priv.p->l3_config_datas, idx2);
|
|
|
|
|
|
|
|
|
|
if (l3_config_data->l3cd == l3cd) {
|
|
|
|
|
nm_assert(idx == -1);
|
|
|
|
|
idx = idx2;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
_l3_config_datas_remove_index_fast(self->priv.p->l3_config_datas, idx2);
|
|
|
|
|
|
|
|
|
|
idx2 = _l3_config_datas_find_next(self->priv.p->l3_config_datas, idx2, tag, NULL);
|
|
|
|
|
if (idx2 < 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idx < 0) {
|
|
|
|
|
l3_config_data = nm_g_array_append_new(self->priv.p->l3_config_datas, L3ConfigData);
|
|
|
|
|
*l3_config_data = (L3ConfigData){
|
|
|
|
|
.tag = tag,
|
|
|
|
|
.l3cd = nm_l3_config_data_ref_and_seal(l3cd),
|
|
|
|
|
.merge_flags = merge_flags,
|
2020-09-23 15:55:28 +02:00
|
|
|
.default_route_table_4 = default_route_table_4,
|
|
|
|
|
.default_route_table_6 = default_route_table_6,
|
|
|
|
|
.default_route_metric_4 = default_route_metric_4,
|
|
|
|
|
.default_route_metric_6 = default_route_metric_6,
|
2020-09-28 16:03:33 +02:00
|
|
|
.default_route_penalty_4 = default_route_penalty_4,
|
|
|
|
|
.default_route_penalty_6 = default_route_penalty_6,
|
|
|
|
|
.acd_timeout_msec = acd_timeout_msec,
|
|
|
|
|
.priority = priority,
|
|
|
|
|
.pseudo_timestamp = ++self->priv.p->pseudo_timestamp_counter,
|
|
|
|
|
.dirty = FALSE,
|
|
|
|
|
};
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
} else {
|
|
|
|
|
l3_config_data = _l3_config_datas_at(self->priv.p->l3_config_datas, idx);
|
|
|
|
|
l3_config_data->dirty = FALSE;
|
|
|
|
|
nm_assert(l3_config_data->tag == tag);
|
|
|
|
|
nm_assert(l3_config_data->l3cd == l3cd);
|
|
|
|
|
if (l3_config_data->priority != priority) {
|
|
|
|
|
l3_config_data->priority = priority;
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
if (l3_config_data->merge_flags != merge_flags) {
|
|
|
|
|
l3_config_data->merge_flags = merge_flags;
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
2020-09-23 15:55:28 +02:00
|
|
|
if (l3_config_data->default_route_table_4 != default_route_table_4) {
|
|
|
|
|
l3_config_data->default_route_table_4 = default_route_table_4;
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
if (l3_config_data->default_route_table_6 != default_route_table_6) {
|
|
|
|
|
l3_config_data->default_route_table_6 = default_route_table_6;
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
if (l3_config_data->default_route_metric_4 != default_route_metric_4) {
|
|
|
|
|
l3_config_data->default_route_metric_4 = default_route_metric_4;
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
if (l3_config_data->default_route_metric_6 != default_route_metric_6) {
|
|
|
|
|
l3_config_data->default_route_metric_6 = default_route_metric_6;
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
if (l3_config_data->default_route_penalty_4 != default_route_penalty_4) {
|
|
|
|
|
l3_config_data->default_route_penalty_4 = default_route_penalty_4;
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
if (l3_config_data->default_route_penalty_6 != default_route_penalty_6) {
|
|
|
|
|
l3_config_data->default_route_penalty_6 = default_route_penalty_6;
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
if (l3_config_data->acd_timeout_msec != acd_timeout_msec) {
|
|
|
|
|
l3_config_data->acd_timeout_msec = acd_timeout_msec;
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (changed)
|
|
|
|
|
_l3_changed_configs_set_dirty(self);
|
|
|
|
|
|
|
|
|
|
return changed;
|
2020-07-23 16:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-28 17:20:53 +02:00
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_remove_config(NML3Cfg * self,
|
|
|
|
|
gconstpointer tag,
|
|
|
|
|
gboolean only_dirty,
|
|
|
|
|
const NML3ConfigData *l3cd)
|
|
|
|
|
{
|
|
|
|
|
gboolean changed;
|
|
|
|
|
gssize idx;
|
|
|
|
|
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(tag);
|
|
|
|
|
|
|
|
|
|
if (!self->priv.p->l3_config_datas)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
nm_assert(self->priv.p->l3_config_datas->len > 0);
|
|
|
|
|
|
|
|
|
|
idx = 0;
|
|
|
|
|
changed = FALSE;
|
|
|
|
|
while (TRUE) {
|
|
|
|
|
idx = _l3_config_datas_find_next(self->priv.p->l3_config_datas, idx, tag, l3cd);
|
|
|
|
|
if (idx < 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (only_dirty && !_l3_config_datas_at(self->priv.p->l3_config_datas, idx)->dirty) {
|
|
|
|
|
idx++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_l3_changed_configs_set_dirty(self);
|
|
|
|
|
_l3_config_datas_remove_index_fast(self->priv.p->l3_config_datas, idx);
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
if (l3cd) {
|
|
|
|
|
/* only one was requested to be removed. We are done. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self->priv.p->l3_config_datas->len == 0) {
|
|
|
|
|
nm_assert(changed);
|
|
|
|
|
nm_clear_pointer(&self->priv.p->l3_config_datas, g_array_unref);
|
|
|
|
|
g_object_unref(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return changed;
|
2020-07-23 16:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-28 17:20:53 +02:00
|
|
|
gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_remove_config(NML3Cfg *self, gconstpointer tag, const NML3ConfigData *ifcfg)
|
2020-07-23 16:15:26 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(ifcfg);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
return _l3cfg_remove_config(self, tag, FALSE, ifcfg);
|
2020-07-23 16:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-28 17:20:53 +02:00
|
|
|
gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_remove_config_all(NML3Cfg *self, gconstpointer tag, gboolean only_dirty)
|
2020-07-23 16:15:26 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
return _l3cfg_remove_config(self, tag, only_dirty, NULL);
|
2020-07-23 16:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-08-03 17:33:31 +02:00
|
|
|
typedef struct {
|
2020-09-28 16:03:33 +02:00
|
|
|
NML3Cfg * self;
|
|
|
|
|
gconstpointer tag;
|
2020-08-03 17:33:31 +02:00
|
|
|
} L3ConfigMergeHookAddObjData;
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_hook_add_addr_cb(const NML3ConfigData *l3cd, const NMPObject *obj, gpointer user_data)
|
2020-07-23 16:15:26 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
const L3ConfigMergeHookAddObjData *hook_data = user_data;
|
|
|
|
|
NML3Cfg * self = hook_data->self;
|
|
|
|
|
AcdData * acd_data;
|
|
|
|
|
in_addr_t addr;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (NMP_OBJECT_GET_TYPE(obj) != NMP_OBJECT_TYPE_IP4_ADDRESS)
|
|
|
|
|
return TRUE;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
addr = NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (ACD_ADDR_SKIP(addr))
|
|
|
|
|
return TRUE;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
acd_data = _l3_acd_data_find(self, addr);
|
|
|
|
|
nm_assert(acd_data);
|
|
|
|
|
nm_assert(
|
|
|
|
|
_acd_track_data_is_not_dirty(_acd_data_find_track(acd_data, l3cd, obj, hook_data->tag)));
|
|
|
|
|
return _acd_data_probe_result_is_good(acd_data);
|
2020-07-23 16:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-03 17:33:31 +02:00
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_update_combined_config(NML3Cfg * self,
|
|
|
|
|
gboolean to_commit,
|
|
|
|
|
const NML3ConfigData **out_old /* transfer reference */,
|
|
|
|
|
gboolean * out_changed_configs,
|
|
|
|
|
gboolean * out_changed_combined_l3cd)
|
2020-07-23 16:15:26 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
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 *const * l3_config_datas_sorted;
|
|
|
|
|
guint l3_config_datas_len;
|
|
|
|
|
guint i;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(!out_old || !*out_old);
|
|
|
|
|
NM_SET_OUT(out_changed_configs, self->priv.changed_configs);
|
|
|
|
|
NM_SET_OUT(out_changed_combined_l3cd, FALSE);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!self->priv.changed_configs)
|
|
|
|
|
goto out;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
self->priv.changed_configs = FALSE;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_config_datas_get_sorted_a(self->priv.p->l3_config_datas,
|
|
|
|
|
&l3_config_datas_sorted,
|
|
|
|
|
&l3_config_datas_len,
|
|
|
|
|
&l3_config_datas_free);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_add_all(self, l3_config_datas_sorted, l3_config_datas_len);
|
2020-08-03 17:19:27 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (l3_config_datas_len > 0) {
|
|
|
|
|
L3ConfigMergeHookAddObjData hook_data = {
|
|
|
|
|
.self = self,
|
|
|
|
|
};
|
2020-08-03 17:19:27 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
l3cd = nm_l3_config_data_new(nm_platform_get_multi_idx(self->priv.platform),
|
|
|
|
|
self->priv.ifindex);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
for (i = 0; i < l3_config_datas_len; i++) {
|
2020-09-23 16:38:46 +02:00
|
|
|
const L3ConfigData *l3cd_data = l3_config_datas_sorted[i];
|
|
|
|
|
|
|
|
|
|
if (NM_FLAGS_HAS(l3cd_data->merge_flags, NM_L3_CONFIG_MERGE_FLAGS_ONLY_FOR_ACD))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
hook_data.tag = l3cd_data->tag;
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3_config_data_merge(l3cd,
|
2020-09-23 16:38:46 +02:00
|
|
|
l3cd_data->l3cd,
|
|
|
|
|
l3cd_data->merge_flags,
|
|
|
|
|
l3cd_data->default_route_table_x,
|
|
|
|
|
l3cd_data->default_route_metric_x,
|
|
|
|
|
l3cd_data->default_route_penalty_x,
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_hook_add_addr_cb,
|
|
|
|
|
&hook_data);
|
|
|
|
|
}
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(l3cd);
|
|
|
|
|
nm_assert(nm_l3_config_data_get_ifindex(l3cd) == self->priv.ifindex);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3_config_data_seal(l3cd);
|
|
|
|
|
}
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (nm_l3_config_data_equal(l3cd, self->priv.p->combined_l3cd_merged))
|
|
|
|
|
goto out;
|
2020-09-16 12:44:38 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
l3cd_old = g_steal_pointer(&self->priv.p->combined_l3cd_merged);
|
|
|
|
|
self->priv.p->combined_l3cd_merged = nm_l3_config_data_seal(g_steal_pointer(&l3cd));
|
2020-09-16 12:44:38 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!to_commit) {
|
|
|
|
|
NM_SET_OUT(out_old, g_steal_pointer(&l3cd_old));
|
|
|
|
|
NM_SET_OUT(out_changed_combined_l3cd, TRUE);
|
|
|
|
|
_LOGT("desired IP configuration changed");
|
|
|
|
|
}
|
2020-09-16 12:44:38 +02:00
|
|
|
|
|
|
|
|
out:
|
2020-09-28 16:03:33 +02:00
|
|
|
if (to_commit && self->priv.p->combined_l3cd_commited != self->priv.p->combined_l3cd_merged) {
|
|
|
|
|
nm_auto_unref_l3cd const NML3ConfigData *l3cd_commited_old = NULL;
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
l3cd_commited_old = g_steal_pointer(&self->priv.p->combined_l3cd_commited);
|
|
|
|
|
self->priv.p->combined_l3cd_commited =
|
|
|
|
|
nm_l3_config_data_ref(self->priv.p->combined_l3cd_merged);
|
2020-07-28 10:43:33 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
NM_SET_OUT(out_old, g_steal_pointer(&l3cd_commited_old));
|
|
|
|
|
NM_SET_OUT(out_changed_combined_l3cd, TRUE);
|
|
|
|
|
_LOGT("desired IP configuration changed for commit");
|
|
|
|
|
}
|
2020-07-23 16:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-07-28 13:29:36 +02:00
|
|
|
typedef struct {
|
2020-09-28 16:03:33 +02:00
|
|
|
const NMPObject *obj;
|
|
|
|
|
gint64 timestamp_msec;
|
|
|
|
|
bool dirty;
|
2020-07-28 13:29:36 +02:00
|
|
|
} RoutesTemporaryNotAvailableData;
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
_routes_temporary_not_available_data_free(gpointer user_data)
|
2020-07-28 13:29:36 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
RoutesTemporaryNotAvailableData *data = user_data;
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nmp_object_unref(data->obj);
|
|
|
|
|
nm_g_slice_free(data);
|
2020-07-28 13:29:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC ((gint64) 20000)
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_routes_temporary_not_available_timeout(gpointer user_data)
|
2020-07-28 13:29:36 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
RoutesTemporaryNotAvailableData *data;
|
|
|
|
|
NML3Cfg * self = NM_L3CFG(user_data);
|
|
|
|
|
GHashTableIter iter;
|
|
|
|
|
gint64 expiry_threshold_msec;
|
|
|
|
|
gboolean any_expired = FALSE;
|
|
|
|
|
gint64 now_msec;
|
|
|
|
|
gint64 oldest_msec;
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
self->priv.p->routes_temporary_not_available_id = 0;
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!self->priv.p->routes_temporary_not_available_hash)
|
|
|
|
|
return G_SOURCE_REMOVE;
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* we check the timeouts again. That is, because we allow to remove
|
2020-09-28 14:50:01 +02:00
|
|
|
* entries from routes_temporary_not_available_hash, without rescheduling
|
|
|
|
|
* out timeouts. */
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
now_msec = nm_utils_get_monotonic_timestamp_msec();
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
expiry_threshold_msec = now_msec - ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC;
|
|
|
|
|
oldest_msec = G_MAXINT64;
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
g_hash_table_iter_init(&iter, self->priv.p->routes_temporary_not_available_hash);
|
|
|
|
|
while (g_hash_table_iter_next(&iter, (gpointer *) &data, NULL)) {
|
|
|
|
|
if (data->timestamp_msec >= expiry_threshold_msec) {
|
|
|
|
|
any_expired = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (data->timestamp_msec < oldest_msec)
|
|
|
|
|
oldest_msec = data->timestamp_msec;
|
|
|
|
|
}
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (any_expired) {
|
|
|
|
|
/* a route expired. We emit a signal, but we don't schedule it again. That will
|
2020-09-25 15:46:06 +02:00
|
|
|
* only happen if the user calls nm_l3cfg_commit() again. */
|
2020-09-28 16:19:31 +02:00
|
|
|
_nm_l3cfg_emit_signal_notify_simple(
|
2020-09-28 16:03:33 +02:00
|
|
|
self,
|
2020-09-28 16:19:31 +02:00
|
|
|
NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED);
|
2020-09-28 16:03:33 +02:00
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (oldest_msec != G_MAXINT64) {
|
|
|
|
|
/* we have a timeout still. Reschedule. */
|
|
|
|
|
self->priv.p->routes_temporary_not_available_id =
|
|
|
|
|
g_timeout_add(oldest_msec + ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC - now_msec,
|
|
|
|
|
_routes_temporary_not_available_timeout,
|
|
|
|
|
self);
|
|
|
|
|
}
|
|
|
|
|
return G_SOURCE_REMOVE;
|
2020-07-28 13:29:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
_routes_temporary_not_available_update(NML3Cfg * self,
|
|
|
|
|
int addr_family,
|
|
|
|
|
GPtrArray *routes_temporary_not_available_arr)
|
|
|
|
|
{
|
|
|
|
|
RoutesTemporaryNotAvailableData *data;
|
|
|
|
|
GHashTableIter iter;
|
|
|
|
|
gint64 oldest_msec;
|
|
|
|
|
gint64 now_msec;
|
|
|
|
|
gboolean prune_all = FALSE;
|
|
|
|
|
gboolean success = TRUE;
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
now_msec = nm_utils_get_monotonic_timestamp_msec();
|
|
|
|
|
|
|
|
|
|
if (nm_g_ptr_array_len(routes_temporary_not_available_arr) <= 0) {
|
|
|
|
|
prune_all = TRUE;
|
|
|
|
|
goto out_prune;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self->priv.p->routes_temporary_not_available_hash) {
|
|
|
|
|
g_hash_table_iter_init(&iter, self->priv.p->routes_temporary_not_available_hash);
|
|
|
|
|
while (g_hash_table_iter_next(&iter, (gpointer *) &data, NULL)) {
|
|
|
|
|
if (NMP_OBJECT_GET_ADDR_FAMILY(data->obj) == addr_family)
|
|
|
|
|
data->dirty = TRUE;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
self->priv.p->routes_temporary_not_available_hash =
|
|
|
|
|
g_hash_table_new_full(nmp_object_indirect_id_hash,
|
|
|
|
|
nmp_object_indirect_id_equal,
|
|
|
|
|
_routes_temporary_not_available_data_free,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < routes_temporary_not_available_arr->len; i++) {
|
|
|
|
|
const NMPObject *o = routes_temporary_not_available_arr->pdata[i];
|
|
|
|
|
char sbuf[1024];
|
|
|
|
|
|
|
|
|
|
nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)));
|
|
|
|
|
|
|
|
|
|
data = g_hash_table_lookup(self->priv.p->routes_temporary_not_available_hash, &o);
|
|
|
|
|
|
|
|
|
|
if (data) {
|
|
|
|
|
if (!data->dirty)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
nm_assert(data->timestamp_msec > 0 && data->timestamp_msec <= now_msec);
|
|
|
|
|
|
|
|
|
|
if (now_msec > data->timestamp_msec + ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC) {
|
|
|
|
|
/* timeout. Could not add this address. */
|
|
|
|
|
_LOGW("failure to add IPv%c route: %s",
|
|
|
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
|
|
|
nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
|
|
|
|
success = FALSE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data->dirty = FALSE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_LOGT("(temporarily) unable to add IPv%c route: %s",
|
|
|
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
|
|
|
nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
|
|
|
|
|
|
|
|
|
data = g_slice_new(RoutesTemporaryNotAvailableData);
|
|
|
|
|
*data = (RoutesTemporaryNotAvailableData){
|
|
|
|
|
.obj = nmp_object_ref(o),
|
|
|
|
|
.timestamp_msec = now_msec,
|
|
|
|
|
.dirty = FALSE,
|
|
|
|
|
};
|
|
|
|
|
g_hash_table_add(self->priv.p->routes_temporary_not_available_hash, data);
|
|
|
|
|
}
|
2020-07-28 13:29:36 +02:00
|
|
|
|
|
|
|
|
out_prune:
|
2020-09-28 16:03:33 +02:00
|
|
|
oldest_msec = G_MAXINT64;
|
|
|
|
|
|
|
|
|
|
if (self->priv.p->routes_temporary_not_available_hash) {
|
|
|
|
|
g_hash_table_iter_init(&iter, self->priv.p->routes_temporary_not_available_hash);
|
|
|
|
|
while (g_hash_table_iter_next(&iter, (gpointer *) &data, NULL)) {
|
|
|
|
|
nm_assert(NMP_OBJECT_GET_ADDR_FAMILY(data->obj) == addr_family || !data->dirty);
|
|
|
|
|
if (!prune_all && !data->dirty) {
|
|
|
|
|
if (data->timestamp_msec < oldest_msec)
|
|
|
|
|
oldest_msec = data->timestamp_msec;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
g_hash_table_iter_remove(&iter);
|
|
|
|
|
}
|
|
|
|
|
if (oldest_msec != G_MAXINT64)
|
|
|
|
|
nm_clear_pointer(&self->priv.p->routes_temporary_not_available_hash,
|
|
|
|
|
g_hash_table_unref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_clear_g_source(&self->priv.p->routes_temporary_not_available_id);
|
|
|
|
|
if (oldest_msec != G_MAXINT64) {
|
|
|
|
|
nm_assert(oldest_msec + ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC < now_msec);
|
|
|
|
|
self->priv.p->routes_temporary_not_available_id =
|
|
|
|
|
g_timeout_add(oldest_msec + ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC - now_msec,
|
|
|
|
|
_routes_temporary_not_available_timeout,
|
|
|
|
|
self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success;
|
2020-07-28 13:29:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-08-03 17:33:31 +02:00
|
|
|
static gboolean
|
2020-09-25 15:46:06 +02:00
|
|
|
_l3_commit_one(NML3Cfg *self, int addr_family, NML3CfgCommitType commit_type)
|
2020-09-28 16:03:33 +02:00
|
|
|
{
|
|
|
|
|
const gboolean IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
|
|
|
nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL;
|
|
|
|
|
gs_unref_ptrarray GPtrArray *addresses = NULL;
|
|
|
|
|
gs_unref_ptrarray GPtrArray *routes = 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;
|
|
|
|
|
NMIPRouteTableSyncMode route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE;
|
|
|
|
|
gboolean final_failure_for_temporary_not_available = FALSE;
|
|
|
|
|
gboolean changed_combined_l3cd;
|
|
|
|
|
gboolean changed_configs;
|
|
|
|
|
char sbuf_commit_type[50];
|
|
|
|
|
gboolean success = TRUE;
|
|
|
|
|
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(NM_IN_SET(commit_type,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_NONE,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_REAPPLY,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_UPDATE,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_ASSUME));
|
|
|
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
|
|
|
|
|
|
_LOGT("committing IPv%c configuration (%s)",
|
|
|
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
|
|
|
_l3_cfg_commit_type_to_string(commit_type, sbuf_commit_type, sizeof(sbuf_commit_type)));
|
|
|
|
|
|
|
|
|
|
_l3cfg_update_combined_config(self, TRUE, &l3cd_old, &changed_configs, &changed_combined_l3cd);
|
|
|
|
|
|
|
|
|
|
if (changed_combined_l3cd) {
|
|
|
|
|
/* our combined configuration changed. We may track entries in externally_removed_objs_hash,
|
2020-09-28 14:50:01 +02:00
|
|
|
* which are not longer to be considered by our configuration. We need to forget about them. */
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_externally_removed_objs_drop_unused(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME) {
|
|
|
|
|
/* we need to artificially pre-populate the externally remove hash. */
|
|
|
|
|
_l3cfg_externally_removed_objs_pickup(self, addr_family);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self->priv.p->combined_l3cd_commited) {
|
|
|
|
|
NMDedupMultiFcnSelectPredicate predicate;
|
|
|
|
|
|
|
|
|
|
if (commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY
|
|
|
|
|
&& self->priv.p->externally_removed_objs_cnt_addresses_x[IS_IPv4] > 0)
|
|
|
|
|
predicate = _l3cfg_externally_removed_objs_filter;
|
|
|
|
|
else
|
|
|
|
|
predicate = NULL;
|
|
|
|
|
addresses = nm_dedup_multi_objs_to_ptr_array_head(
|
|
|
|
|
nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited,
|
|
|
|
|
NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)),
|
|
|
|
|
predicate,
|
|
|
|
|
self->priv.p->externally_removed_objs_hash);
|
|
|
|
|
|
|
|
|
|
if (commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY
|
|
|
|
|
&& self->priv.p->externally_removed_objs_cnt_routes_x[IS_IPv4] > 0)
|
|
|
|
|
predicate = _l3cfg_externally_removed_objs_filter;
|
|
|
|
|
else
|
|
|
|
|
predicate = NULL;
|
|
|
|
|
routes = nm_dedup_multi_objs_to_ptr_array_head(
|
|
|
|
|
nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited,
|
|
|
|
|
NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)),
|
|
|
|
|
predicate,
|
|
|
|
|
self->priv.p->externally_removed_objs_hash);
|
|
|
|
|
|
|
|
|
|
route_table_sync =
|
|
|
|
|
nm_l3_config_data_get_route_table_sync(self->priv.p->combined_l3cd_commited,
|
|
|
|
|
addr_family);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE)
|
|
|
|
|
route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_ALL;
|
|
|
|
|
|
|
|
|
|
if (commit_type == NM_L3_CFG_COMMIT_TYPE_REAPPLY) {
|
|
|
|
|
addresses_prune = nm_platform_ip_address_get_prune_list(self->priv.platform,
|
|
|
|
|
addr_family,
|
|
|
|
|
self->priv.ifindex,
|
|
|
|
|
TRUE);
|
|
|
|
|
routes_prune = nm_platform_ip_route_get_prune_list(self->priv.platform,
|
|
|
|
|
addr_family,
|
|
|
|
|
self->priv.ifindex,
|
|
|
|
|
route_table_sync);
|
|
|
|
|
} else if (commit_type == NM_L3_CFG_COMMIT_TYPE_UPDATE) {
|
|
|
|
|
/* during update, we do a cross with the previous configuration.
|
2020-09-28 14:50:01 +02:00
|
|
|
*
|
|
|
|
|
* Of course, if an entry is both to be pruned and to be added, then
|
|
|
|
|
* the latter wins. So, this works just nicely. */
|
2020-09-28 16:03:33 +02:00
|
|
|
if (l3cd_old) {
|
|
|
|
|
const NMDedupMultiHeadEntry *head_entry;
|
|
|
|
|
|
|
|
|
|
head_entry =
|
|
|
|
|
nm_l3_config_data_lookup_objs(l3cd_old, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4));
|
|
|
|
|
addresses_prune = nm_dedup_multi_objs_to_ptr_array_head(head_entry, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
head_entry = nm_l3_config_data_lookup_objs(l3cd_old, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4));
|
|
|
|
|
addresses_prune = nm_dedup_multi_objs_to_ptr_array_head(head_entry, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_ip6_privacy(). */
|
|
|
|
|
/* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_ndisc_*(). */
|
|
|
|
|
/* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_ip6_mtu(). */
|
|
|
|
|
/* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_mtu(). */
|
|
|
|
|
|
|
|
|
|
nm_platform_ip_address_sync(self->priv.platform,
|
|
|
|
|
addr_family,
|
|
|
|
|
self->priv.ifindex,
|
|
|
|
|
addresses,
|
|
|
|
|
addresses_prune);
|
|
|
|
|
|
|
|
|
|
if (!nm_platform_ip_route_sync(self->priv.platform,
|
|
|
|
|
addr_family,
|
|
|
|
|
self->priv.ifindex,
|
|
|
|
|
routes,
|
|
|
|
|
routes_prune,
|
|
|
|
|
&routes_temporary_not_available_arr))
|
|
|
|
|
success = FALSE;
|
|
|
|
|
|
|
|
|
|
final_failure_for_temporary_not_available = FALSE;
|
|
|
|
|
if (!_routes_temporary_not_available_update(self,
|
|
|
|
|
addr_family,
|
|
|
|
|
routes_temporary_not_available_arr))
|
|
|
|
|
final_failure_for_temporary_not_available = TRUE;
|
|
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
/* FIXME(l3cfg) */
|
|
|
|
|
(void) final_failure_for_temporary_not_available;
|
|
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
return success;
|
2020-08-03 17:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
static void
|
|
|
|
|
_l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle)
|
2020-09-28 16:03:33 +02:00
|
|
|
{
|
|
|
|
|
gboolean commit_type_detected = FALSE;
|
|
|
|
|
char sbuf_ct[30];
|
|
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
g_return_if_fail(NM_IS_L3CFG(self));
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IN_SET(commit_type,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_NONE,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_AUTO,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_ASSUME,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_UPDATE,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_REAPPLY));
|
|
|
|
|
|
|
|
|
|
switch (commit_type) {
|
|
|
|
|
case NM_L3_CFG_COMMIT_TYPE_AUTO:
|
|
|
|
|
/* if in "AUTO" mode we currently have commit-type "UPDATE", that
|
2020-09-28 14:50:01 +02:00
|
|
|
* causes also the following update to still be "UPDATE". Either
|
|
|
|
|
* the same commit */
|
2020-09-28 16:03:33 +02:00
|
|
|
commit_type_detected = TRUE;
|
|
|
|
|
commit_type = nm_l3cfg_commit_type_get(self);
|
|
|
|
|
if (commit_type == NM_L3_CFG_COMMIT_TYPE_UPDATE)
|
|
|
|
|
self->priv.p->commit_type_update_sticky = TRUE;
|
|
|
|
|
else if (self->priv.p->commit_type_update_sticky) {
|
|
|
|
|
self->priv.p->commit_type_update_sticky = FALSE;
|
|
|
|
|
commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case NM_L3_CFG_COMMIT_TYPE_ASSUME:
|
|
|
|
|
break;
|
|
|
|
|
case NM_L3_CFG_COMMIT_TYPE_REAPPLY:
|
|
|
|
|
case NM_L3_CFG_COMMIT_TYPE_UPDATE:
|
|
|
|
|
self->priv.p->commit_type_update_sticky = FALSE;
|
|
|
|
|
break;
|
|
|
|
|
case NM_L3_CFG_COMMIT_TYPE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
_LOGT("platform-commit %s%s%s",
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_cfg_commit_type_to_string(commit_type, sbuf_ct, sizeof(sbuf_ct)),
|
2020-09-25 15:46:06 +02:00
|
|
|
commit_type_detected ? " (auto)" : "",
|
|
|
|
|
is_idle ? " (idle handler)" : "");
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (commit_type == NM_L3_CFG_COMMIT_TYPE_NONE)
|
2020-09-25 15:46:06 +02:00
|
|
|
return;
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
nm_clear_g_source_inst(&self->priv.p->commit_on_idle_source);
|
2020-09-03 12:24:08 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (commit_type == NM_L3_CFG_COMMIT_TYPE_REAPPLY)
|
|
|
|
|
_l3cfg_externally_removed_objs_drop(self);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* FIXME(l3cfg): handle items currently not configurable in kernel. */
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
_l3_commit_one(self, AF_INET, commit_type);
|
|
|
|
|
_l3_commit_one(self, AF_INET6, commit_type);
|
2020-09-19 12:20:45 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_process_changes(self);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:19:31 +02:00
|
|
|
_nm_l3cfg_emit_signal_notify_simple(self, NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT);
|
2020-09-25 15:46:06 +02:00
|
|
|
}
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
void
|
|
|
|
|
nm_l3cfg_commit(NML3Cfg *self, NML3CfgCommitType commit_type)
|
|
|
|
|
{
|
|
|
|
|
_l3_commit(self, commit_type, FALSE);
|
2020-07-28 10:43:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-03 12:24:08 +02:00
|
|
|
NML3CfgCommitType
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_commit_type_get(NML3Cfg *self)
|
2020-09-03 12:24:08 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
NML3CfgCommitTypeHandle *handle;
|
2020-09-03 12:24:08 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
2020-09-03 12:24:08 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
handle = c_list_first_entry(&self->priv.p->commit_type_lst_head,
|
|
|
|
|
NML3CfgCommitTypeHandle,
|
|
|
|
|
commit_type_lst);
|
|
|
|
|
return handle ? handle->commit_type : NM_L3_CFG_COMMIT_TYPE_NONE;
|
2020-09-03 12:24:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_l3cfg_commit_type_register:
|
|
|
|
|
* @self: the #NML3Cfg
|
|
|
|
|
* @commit_type: the commit type to register
|
|
|
|
|
* @existing_handle: instead of being a new registration, update an existing handle.
|
|
|
|
|
* This may be %NULL, which is like having no previous registration.
|
|
|
|
|
*
|
|
|
|
|
* NML3Cfg needs to know whether it is in charge of an interface (and how "much").
|
|
|
|
|
* By default, it is not in charge, but various users can register themself with
|
|
|
|
|
* a certain @commit_type. The "higher" commit type is the used one when calling
|
2020-09-25 15:46:06 +02:00
|
|
|
* nm_l3cfg_commit() with %NM_L3_CFG_COMMIT_TYPE_AUTO.
|
2020-09-03 12:24:08 +02:00
|
|
|
*
|
|
|
|
|
* Returns: a handle tracking the registration, or %NULL of @commit_type
|
|
|
|
|
* is %NM_L3_CFG_COMMIT_TYPE_NONE.
|
|
|
|
|
*/
|
|
|
|
|
NML3CfgCommitTypeHandle *
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_commit_type_register(NML3Cfg * self,
|
|
|
|
|
NML3CfgCommitType commit_type,
|
|
|
|
|
NML3CfgCommitTypeHandle *existing_handle)
|
|
|
|
|
{
|
|
|
|
|
NML3CfgCommitTypeHandle *handle;
|
|
|
|
|
NML3CfgCommitTypeHandle *h;
|
|
|
|
|
gboolean linked;
|
|
|
|
|
|
|
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
|
|
|
|
nm_assert(NM_IN_SET(commit_type,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_NONE,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_ASSUME,
|
|
|
|
|
NM_L3_CFG_COMMIT_TYPE_UPDATE));
|
|
|
|
|
nm_assert(
|
|
|
|
|
!existing_handle
|
|
|
|
|
|| c_list_contains(&self->priv.p->commit_type_lst_head, &existing_handle->commit_type_lst));
|
|
|
|
|
|
|
|
|
|
if (existing_handle) {
|
|
|
|
|
if (commit_type == NM_L3_CFG_COMMIT_TYPE_NONE) {
|
|
|
|
|
nm_l3cfg_commit_type_unregister(self, existing_handle);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if (existing_handle->commit_type == commit_type)
|
|
|
|
|
return existing_handle;
|
|
|
|
|
c_list_unlink_stale(&existing_handle->commit_type_lst);
|
|
|
|
|
handle = existing_handle;
|
|
|
|
|
} else {
|
|
|
|
|
if (commit_type == NM_L3_CFG_COMMIT_TYPE_NONE)
|
|
|
|
|
return NULL;
|
|
|
|
|
handle = g_slice_new(NML3CfgCommitTypeHandle);
|
|
|
|
|
handle->commit_type = commit_type;
|
|
|
|
|
if (c_list_is_empty(&self->priv.p->commit_type_lst_head))
|
|
|
|
|
g_object_ref(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
linked = FALSE;
|
|
|
|
|
c_list_for_each_entry (h, &self->priv.p->commit_type_lst_head, commit_type_lst) {
|
|
|
|
|
if (handle->commit_type >= h->commit_type) {
|
|
|
|
|
c_list_link_before(&self->priv.p->commit_type_lst_head, &handle->commit_type_lst);
|
|
|
|
|
linked = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!linked)
|
|
|
|
|
c_list_link_tail(&self->priv.p->commit_type_lst_head, &handle->commit_type_lst);
|
|
|
|
|
|
|
|
|
|
return handle;
|
2020-09-03 12:24:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_commit_type_unregister(NML3Cfg *self, NML3CfgCommitTypeHandle *handle)
|
2020-09-03 12:24:08 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
2020-09-03 12:24:08 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (!handle)
|
|
|
|
|
return;
|
2020-09-03 12:24:08 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(c_list_contains(&self->priv.p->commit_type_lst_head, &handle->commit_type_lst));
|
2020-09-03 12:24:08 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
c_list_unlink_stale(&handle->commit_type_lst);
|
|
|
|
|
if (c_list_is_empty(&self->priv.p->commit_type_lst_head))
|
|
|
|
|
g_object_unref(self);
|
|
|
|
|
nm_g_slice_free(handle);
|
2020-09-03 12:24:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-08 18:25:41 +02:00
|
|
|
const NML3ConfigData *
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_get_combined_l3cd(NML3Cfg *self, gboolean get_commited)
|
2020-09-08 18:25:41 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
2020-09-08 18:25:41 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (get_commited)
|
|
|
|
|
return self->priv.p->combined_l3cd_commited;
|
2020-09-16 12:44:38 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3cfg_update_combined_config(self, FALSE, NULL, NULL, NULL);
|
|
|
|
|
return self->priv.p->combined_l3cd_merged;
|
2020-09-08 18:25:41 +02:00
|
|
|
}
|
|
|
|
|
|
2020-09-08 17:05:34 +02:00
|
|
|
const NMPObject *
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_get_best_default_route(NML3Cfg *self, int addr_family, gboolean get_commited)
|
2020-09-08 17:05:34 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
const NML3ConfigData *l3cd;
|
2020-09-08 17:05:34 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
l3cd = nm_l3cfg_get_combined_l3cd(self, get_commited);
|
|
|
|
|
if (!l3cd)
|
|
|
|
|
return NULL;
|
2020-09-08 17:05:34 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
return nm_l3_config_data_get_best_default_route(l3cd, addr_family);
|
2020-09-08 17:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-16 13:18:54 +02:00
|
|
|
gboolean
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_has_commited_ip6_addresses_pending_dad(NML3Cfg *self)
|
2020-09-16 13:18:54 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
const NML3ConfigData *l3cd;
|
|
|
|
|
const NMPObject * plat_obj;
|
|
|
|
|
NMPLookup plat_lookup;
|
|
|
|
|
NMDedupMultiIter iter;
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_L3CFG(self));
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
l3cd = nm_l3cfg_get_combined_l3cd(self, TRUE);
|
|
|
|
|
if (!l3cd)
|
|
|
|
|
return FALSE;
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
/* we iterate over all addresses in platform, and check whether the tentative
|
2020-09-28 14:50:01 +02:00
|
|
|
* addresses are tracked by our l3cd. Not the other way around, because we assume
|
|
|
|
|
* that there are few addresses in platform that are still tentative, so
|
|
|
|
|
* we only need to lookup few platform addresses in l3cd.
|
|
|
|
|
*
|
|
|
|
|
* Of course, all lookups are O(1) anyway, so in any case the operation is
|
|
|
|
|
* O(n) (once "n" being the addresses in platform, and once in l3cd). */
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nmp_lookup_init_object(&plat_lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, self->priv.ifindex);
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
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;
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
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))
|
|
|
|
|
continue;
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
l3cd_entry = nm_l3_config_data_lookup_obj(l3cd, plat_obj);
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NMP_OBJECT_CAST_IP6_ADDRESS(nm_dedup_multi_entry_get_obj(l3cd_entry))
|
|
|
|
|
== nm_l3_config_data_lookup_address_6(l3cd, &plat_addr->address));
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
if (l3cd_entry)
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2020-09-16 13:18:54 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
return FALSE;
|
2020-09-16 13:18:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-07-18 19:01:04 +02:00
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
|
|
|
|
|
{
|
|
|
|
|
NML3Cfg *self = NM_L3CFG(object);
|
|
|
|
|
|
|
|
|
|
switch (prop_id) {
|
|
|
|
|
case PROP_NETNS:
|
|
|
|
|
/* construct-only */
|
|
|
|
|
self->priv.netns = g_object_ref(g_value_get_pointer(value));
|
|
|
|
|
nm_assert(NM_IS_NETNS(self->priv.netns));
|
|
|
|
|
break;
|
|
|
|
|
case PROP_IFINDEX:
|
|
|
|
|
/* construct-only */
|
|
|
|
|
self->priv.ifindex = g_value_get_int(value);
|
|
|
|
|
nm_assert(self->priv.ifindex > 0);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-07-18 19:01:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_init(NML3Cfg *self)
|
2020-07-18 19:01:04 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
self->priv.p = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_L3CFG, NML3CfgPrivate);
|
2020-07-21 12:39:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
c_list_init(&self->priv.p->acd_lst_head);
|
|
|
|
|
c_list_init(&self->priv.p->acd_notify_complete_lst_head);
|
|
|
|
|
c_list_init(&self->priv.p->commit_type_lst_head);
|
2020-07-18 19:01:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
constructed(GObject *object)
|
2020-07-18 19:01:04 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
NML3Cfg *self = NM_L3CFG(object);
|
2020-07-18 19:01:04 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_NETNS(self->priv.netns));
|
|
|
|
|
nm_assert(self->priv.ifindex > 0);
|
2020-07-18 19:01:04 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
self->priv.platform = g_object_ref(nm_netns_get_platform(self->priv.netns));
|
|
|
|
|
nm_assert(NM_IS_PLATFORM(self->priv.platform));
|
2020-07-18 19:01:04 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT("created (netns=" NM_HASH_OBFUSCATE_PTR_FMT ")", NM_HASH_OBFUSCATE_PTR(self->priv.netns));
|
2020-07-18 19:01:04 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
G_OBJECT_CLASS(nm_l3cfg_parent_class)->constructed(object);
|
2020-07-21 11:57:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_load_link(self, TRUE);
|
2020-07-18 19:01:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NML3Cfg *
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_new(NMNetns *netns, int ifindex)
|
2020-07-18 19:01:04 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(NM_IS_NETNS(netns));
|
|
|
|
|
nm_assert(ifindex > 0);
|
2020-07-18 19:01:04 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
return g_object_new(NM_TYPE_L3CFG, NM_L3CFG_NETNS, netns, NM_L3CFG_IFINDEX, ifindex, NULL);
|
2020-07-18 19:01:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
finalize(GObject *object)
|
2020-07-18 19:01:04 +02:00
|
|
|
{
|
2020-09-28 16:03:33 +02:00
|
|
|
NML3Cfg *self = NM_L3CFG(object);
|
2020-07-18 19:01:04 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(!self->priv.p->l3_config_datas);
|
2020-09-22 18:25:25 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(c_list_is_empty(&self->priv.p->commit_type_lst_head));
|
2020-09-03 12:24:08 +02:00
|
|
|
|
2020-09-25 15:46:06 +02:00
|
|
|
nm_clear_g_source_inst(&self->priv.p->commit_on_idle_source);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(nm_g_array_len(self->priv.p->property_emit_list) == 0u);
|
l3cfg: add nm_l3cfg_property_emit_register() API
The NML3Cfg instance tracks and prepares the IP configuration.
However, that is also partly exposed on other objects, like
NMIP4Config's "route-data" property.
Add an API, so that NMIP4Config can register itself to be notified
when something relevant changes.
This is an alternative to standard GObject properties and signals. They
often seem more effort than worth. That is, because in this case,
NMIP4Config.route-data has no other task then to re-emit the signal.
So, to implement that with GObject properties/signals, we would have to
add a property/signal to NML3Cfg, subscribe to it from NMIP4Config,
and remit the signal. An alternative is to bind properties, but that
would still be quite some extra code, and unclear that it would be
simpler. Not to mention the overhead, as bindings are themself full
GObject instances, that register to and emit signals by name.
2020-07-21 12:52:42 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_l3_acd_data_prune(self, TRUE);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_assert(c_list_is_empty(&self->priv.p->acd_lst_head));
|
|
|
|
|
nm_assert(c_list_is_empty(&self->priv.p->acd_notify_complete_lst_head));
|
|
|
|
|
nm_assert(nm_g_hash_table_size(self->priv.p->acd_lst_hash) == 0);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_clear_pointer(&self->priv.p->acd_lst_hash, g_hash_table_unref);
|
|
|
|
|
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);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_clear_g_source(&self->priv.p->routes_temporary_not_available_id);
|
|
|
|
|
nm_clear_pointer(&self->priv.p->routes_temporary_not_available_hash, g_hash_table_unref);
|
2020-07-28 13:29:36 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_clear_pointer(&self->priv.p->externally_removed_objs_hash, g_hash_table_unref);
|
2020-07-29 08:39:12 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
g_clear_object(&self->priv.netns);
|
|
|
|
|
g_clear_object(&self->priv.platform);
|
2020-07-18 19:01:04 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_clear_l3cd(&self->priv.p->combined_l3cd_merged);
|
|
|
|
|
nm_clear_l3cd(&self->priv.p->combined_l3cd_commited);
|
2020-07-23 16:15:26 +02:00
|
|
|
|
2020-09-25 17:05:58 +02:00
|
|
|
nm_clear_pointer(&self->priv.plobj, nmp_object_unref);
|
|
|
|
|
nm_clear_pointer(&self->priv.plobj_next, nmp_object_unref);
|
2020-07-21 11:57:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_clear_pointer(&self->priv.p->acd_ipv4_addresses_on_link, g_hash_table_unref);
|
2020-08-03 17:33:31 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
_LOGT("finalized");
|
2020-07-18 19:01:04 +02:00
|
|
|
|
2020-09-28 16:03:33 +02:00
|
|
|
G_OBJECT_CLASS(nm_l3cfg_parent_class)->finalize(object);
|
2020-07-18 19:01:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-09-28 16:03:33 +02:00
|
|
|
nm_l3cfg_class_init(NML3CfgClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
|
|
|
|
|
|
|
|
g_type_class_add_private(klass, sizeof(NML3CfgPrivate));
|
|
|
|
|
|
|
|
|
|
object_class->set_property = set_property;
|
|
|
|
|
object_class->constructed = constructed;
|
|
|
|
|
object_class->finalize = finalize;
|
|
|
|
|
|
|
|
|
|
obj_properties[PROP_NETNS] =
|
|
|
|
|
g_param_spec_pointer(NM_L3CFG_NETNS,
|
|
|
|
|
"",
|
|
|
|
|
"",
|
|
|
|
|
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
|
|
|
|
|
|
obj_properties[PROP_IFINDEX] =
|
|
|
|
|
g_param_spec_int(NM_L3CFG_IFINDEX,
|
|
|
|
|
"",
|
|
|
|
|
"",
|
|
|
|
|
0,
|
|
|
|
|
G_MAXINT,
|
|
|
|
|
0,
|
|
|
|
|
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
|
|
|
|
|
|
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
|
|
|
|
|
|
|
|
|
|
signals[SIGNAL_NOTIFY] = g_signal_new(NM_L3CFG_SIGNAL_NOTIFY,
|
|
|
|
|
G_OBJECT_CLASS_TYPE(object_class),
|
|
|
|
|
G_SIGNAL_RUN_FIRST,
|
|
|
|
|
0,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
2020-09-28 16:19:31 +02:00
|
|
|
g_cclosure_marshal_VOID__POINTER,
|
2020-09-28 16:03:33 +02:00
|
|
|
G_TYPE_NONE,
|
2020-09-28 16:19:31 +02:00
|
|
|
1,
|
|
|
|
|
G_TYPE_POINTER /* (const NML3ConfigNotifyData *) */);
|
2020-07-18 19:01:04 +02:00
|
|
|
}
|