summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2020-07-23 16:15:26 +0200
committerThomas Haller <thaller@redhat.com>2020-07-31 08:53:06 +0200
commit81cf493b96fb2138e046b00fdfbebe95294b459b (patch)
treed7501f3a1579ca07de6bf823058a89feccb684ef /src
parent297eb4d1692d00bd755caad398e604baf4cddf83 (diff)
downloadNetworkManager-81cf493b96fb2138e046b00fdfbebe95294b459b.tar.gz
l3cfg: support tracking NML3ConfigData in NML3Cfg
NML3Cfg is supposed to manage an interface (by ifindex). As such, it later will itself implement DHCP and similar addressing methods. However, in various cases we get additional IP configuration from external (e.g. from a VPN connection). To support that, let NML3Cfg track any number of NML3ConfigData instances.
Diffstat (limited to 'src')
-rw-r--r--src/nm-l3-config-data.c252
-rw-r--r--src/nm-l3-config-data.h35
-rw-r--r--src/nm-l3cfg.c340
-rw-r--r--src/nm-l3cfg.h29
4 files changed, 625 insertions, 31 deletions
diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c
index 78a0e332fd..1dec11ed89 100644
--- a/src/nm-l3-config-data.c
+++ b/src/nm-l3-config-data.c
@@ -143,6 +143,44 @@ _garray_inaddr_clone (const GArray *src,
return dst;
}
+static void
+_garray_inaddr_merge (GArray **p_dst,
+ const GArray *src,
+ int addr_family)
+{
+ guint dst_initial_len;
+ const char *p_dst_arr;
+ const char *p_src;
+ gsize elt_size;
+ guint i;
+ guint j;
+
+ if (nm_g_array_len (src) == 0)
+ return;
+
+ if (!*p_dst) {
+ *p_dst = _garray_inaddr_clone (src, addr_family);
+ return;
+ }
+
+ elt_size = nm_utils_addr_family_to_size (addr_family);
+
+ dst_initial_len = (*p_dst)->len;
+ p_dst_arr = (*p_dst)->data;
+ p_src = src->data;
+
+ for (i = 0; i < src->len; i++, p_src += elt_size) {
+ for (j = 0; j < dst_initial_len; j++) {
+ if (memcmp (&p_dst_arr[j * elt_size], p_src, elt_size) == 0)
+ goto next;
+ }
+ g_array_append_vals (*p_dst, p_src, 1);
+ p_dst_arr = (*p_dst)->data;
+next:
+ ;
+ }
+}
+
static gssize
_garray_inaddr_find (GArray *arr,
int addr_family,
@@ -204,6 +242,36 @@ _garray_inaddr_cmp (const GArray *a, const GArray *b, int addr_family)
return 0;
}
+static void
+_strv_ptrarray_merge (GPtrArray **p_dst, const GPtrArray *src)
+{
+ guint dst_initial_len;
+ guint i;
+
+ if (nm_g_ptr_array_len (src) == 0)
+ return;
+
+ if (!*p_dst) {
+ /* we trust src to contain unique strings. Just clone it. */
+ *p_dst = nm_strv_ptrarray_clone (src, TRUE);
+ return;
+ }
+
+ nm_strv_ptrarray_ensure (p_dst);
+
+ dst_initial_len = (*p_dst)->len;
+
+ for (i = 0; i < src->len; i++) {
+ const char *s = src->pdata[i];
+
+ if ( dst_initial_len > 0
+ && nm_utils_strv_find_first ((char **) ((*p_dst)->pdata), dst_initial_len, s) >= 0)
+ continue;
+
+ g_ptr_array_add (*p_dst, g_strdup (s));
+ }
+}
+
/*****************************************************************************/
static gboolean
@@ -1316,13 +1384,143 @@ nm_l3_config_data_new_from_platform (NMDedupMultiIndex *multi_idx,
/*****************************************************************************/
+static void
+_init_merge (NML3ConfigData *self,
+ const NML3ConfigData *src,
+ NML3ConfigMergeFlags merge_flags,
+ const guint32 *default_route_penalty_x /* length 2, for IS_IPv4 */)
+{
+ NMDedupMultiIter iter;
+ const NMPObject *obj;
+ int IS_IPv4;
+
+ nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE));
+ nm_assert (_NM_IS_L3_CONFIG_DATA (src, TRUE));
+
+ for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
+ const int addr_family = IS_IPv4 ? AF_INET : AF_INET6;
+ const NML3ConfigDatFlags has_dns_priority_flag = NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY (IS_IPv4);
+
+ nm_l3_config_data_iter_obj_for_each (iter,
+ src,
+ obj,
+ NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4)) {
+ if ( NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_EXTERNAL)
+ && !NMP_OBJECT_CAST_IP_ADDRESS (obj)->external) {
+ NMPlatformIPXAddress a;
+
+ if (IS_IPv4)
+ a.a4 = *NMP_OBJECT_CAST_IP4_ADDRESS (obj);
+ else
+ a.a6 = *NMP_OBJECT_CAST_IP6_ADDRESS (obj);
+ a.ax.ifindex = self->ifindex;
+ a.ax.external = TRUE;
+ nm_l3_config_data_add_address_full (self,
+ addr_family,
+ NULL,
+ &a.ax,
+ NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE,
+ NULL);
+ continue;
+ }
+
+ nm_l3_config_data_add_address_full (self,
+ addr_family,
+ obj,
+ NULL,
+ NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE,
+ NULL);
+ }
+
+ if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES)) {
+ nm_l3_config_data_iter_obj_for_each (iter,
+ src,
+ obj,
+ NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)) {
+ if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (NMP_OBJECT_CAST_IP_ROUTE (obj))) {
+ if ( NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES)
+ && !NM_FLAGS_HAS (src->flags, NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES))
+ continue;
+
+ if ( default_route_penalty_x
+ && default_route_penalty_x[IS_IPv4] > 0) {
+ NMPlatformIPXRoute r;
+
+ if (IS_IPv4)
+ r.r4 = *NMP_OBJECT_CAST_IP4_ROUTE (obj);
+ else
+ r.r6 = *NMP_OBJECT_CAST_IP6_ROUTE (obj);
+ r.rx.ifindex = self->ifindex;
+ r.rx.metric = nm_utils_ip_route_metric_penalize (r.rx.metric, default_route_penalty_x[IS_IPv4]);
+ nm_l3_config_data_add_route_full (self,
+ addr_family,
+ NULL,
+ &r.rx,
+ NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE,
+ NULL,
+ NULL);
+ continue;
+ }
+ }
+ nm_l3_config_data_add_route_full (self,
+ addr_family,
+ obj,
+ NULL,
+ NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE,
+ NULL,
+ NULL);
+ }
+ }
+
+ if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS))
+ _garray_inaddr_merge (&self->nameservers_x[IS_IPv4], src->nameservers_x[IS_IPv4], addr_family);
+
+ if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS))
+ _strv_ptrarray_merge (&self->domains_x[IS_IPv4], src->domains_x[IS_IPv4]);
+
+ if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS))
+ _strv_ptrarray_merge (&self->searches_x[IS_IPv4], src->searches_x[IS_IPv4]);
+
+ if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS))
+ _strv_ptrarray_merge (&self->dns_options_x[IS_IPv4], src->dns_options_x[IS_IPv4]);
+
+ if ( !NM_FLAGS_ANY (self->flags, has_dns_priority_flag)
+ && NM_FLAGS_ANY (src->flags, has_dns_priority_flag)) {
+ self->dns_priority_x[IS_IPv4] = src->dns_priority_x[IS_IPv4];
+ self->flags |= has_dns_priority_flag;
+ }
+ }
+
+ if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) {
+ _garray_inaddr_merge (&self->wins, src->wins, AF_INET);
+ _garray_inaddr_merge (&self->nis_servers, src->nis_servers, AF_INET);
+
+ if ( !self->nis_domain
+ && src->nis_domain)
+ self->nis_domain = g_strdup (src->nis_domain);
+ }
+
+ if (self->mdns == NM_SETTING_CONNECTION_MDNS_DEFAULT)
+ self->mdns = src->mdns;
+
+ if (self->llmnr == NM_SETTING_CONNECTION_LLMNR_DEFAULT)
+ self->llmnr = src->llmnr;
+
+ if (self->metered == NM_TERNARY_DEFAULT)
+ self->metered = src->metered;
+
+ if ( !NM_FLAGS_HAS (self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_MTU)
+ && NM_FLAGS_HAS (src->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_MTU)) {
+ self->mtu = src->mtu;
+ self->flags |= NM_L3_CONFIG_DAT_FLAGS_HAS_MTU;
+ }
+}
+
NML3ConfigData *
nm_l3_config_data_new_clone (const NML3ConfigData *src,
int ifindex)
{
NML3ConfigData *self;
- NMDedupMultiIter iter;
- const NMPObject *obj;
nm_assert (_NM_IS_L3_CONFIG_DATA (src, TRUE));
@@ -1333,34 +1531,30 @@ nm_l3_config_data_new_clone (const NML3ConfigData *src,
ifindex = src->ifindex;
self = nm_l3_config_data_new (src->multi_idx, ifindex);
+ _init_merge (self, src, NM_L3_CONFIG_MERGE_FLAGS_NONE, NULL);
+ return self;
+}
+
+NML3ConfigData *
+nm_l3_config_data_new_combined (NMDedupMultiIndex *multi_idx,
+ int ifindex,
+ const NML3ConfigDatMergeInfo *const*merge_infos,
+ guint merge_infos_len)
+{
+ NML3ConfigData *self;
+ guint i;
+
+ nm_assert (multi_idx);
+ nm_assert (ifindex > 0);
- nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP4_ADDRESS)
- nm_l3_config_data_add_address (self, AF_INET, obj, NULL);
- nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP6_ADDRESS)
- nm_l3_config_data_add_address (self, AF_INET6, obj, NULL);
- nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP4_ROUTE)
- nm_l3_config_data_add_route (self, AF_INET, obj, NULL);
- nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP6_ROUTE)
- nm_l3_config_data_add_route (self, AF_INET6, obj, NULL);
-
- nm_assert (self->best_default_route_4 == src->best_default_route_4);
- nm_assert (self->best_default_route_6 == src->best_default_route_6);
-
- self->wins = _garray_inaddr_clone (src->wins, AF_INET);
- self->nameservers_4 = _garray_inaddr_clone (src->nameservers_4, AF_INET);
- self->nameservers_6 = _garray_inaddr_clone (src->nameservers_6, AF_INET6);
- self->domains_4 = nm_strv_ptrarray_clone (src->domains_4, TRUE);
- self->domains_6 = nm_strv_ptrarray_clone (src->domains_6, TRUE);
- self->searches_4 = nm_strv_ptrarray_clone (src->searches_4, TRUE);
- self->searches_6 = nm_strv_ptrarray_clone (src->searches_6, TRUE);
- self->dns_options_4 = nm_strv_ptrarray_clone (src->dns_options_4, TRUE);
- self->dns_options_6 = nm_strv_ptrarray_clone (src->dns_options_6, TRUE);
- self->dns_priority_4 = src->dns_priority_4;
- self->dns_priority_6 = src->dns_priority_6;
- self->mdns = src->mdns;
- self->llmnr = src->llmnr;
-
- /* TODO: some fields are not cloned. Will be done next. */
+ self = nm_l3_config_data_new (multi_idx, ifindex);
+
+ for (i = 0; i < merge_infos_len; i++) {
+ _init_merge (self,
+ merge_infos[i]->l3cfg,
+ merge_infos[i]->merge_flags,
+ merge_infos[i]->default_route_penalty_x);
+ }
return self;
}
diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h
index e8649e83e1..09b46a2ce4 100644
--- a/src/nm-l3-config-data.h
+++ b/src/nm-l3-config-data.h
@@ -43,10 +43,40 @@ typedef enum {
NM_L3_CONFIG_ADD_FLAGS_APPEND_FORCE = (1ull << 2),
} NML3ConfigAddFlags;
+/**
+ * NML3ConfigMergeFlags:
+ * @NM_L3_CONFIG_MERGE_FLAGS_NONE: no flags set
+ * @NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES: don't merge routes
+ * @NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES: don't merge default routes.
+ * Note that if the respective NML3ConfigData has NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES
+ * set, this flag gets ignored during merge.
+ * @NM_L3_CONFIG_MERGE_FLAGS_NO_DNS: don't merge DNS information
+ * @NM_L3_CONFIG_MERGE_FLAGS_EXTERNAL: mark new addresses as external
+ */
+typedef enum {
+ NM_L3_CONFIG_MERGE_FLAGS_NONE = 0,
+ NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES = (1LL << 0),
+ NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES = (1LL << 1),
+ NM_L3_CONFIG_MERGE_FLAGS_NO_DNS = (1LL << 2),
+ NM_L3_CONFIG_MERGE_FLAGS_EXTERNAL = (1LL << 3),
+} NML3ConfigMergeFlags;
+
/*****************************************************************************/
typedef struct _NML3ConfigData NML3ConfigData;
+typedef struct {
+ const NML3ConfigData *l3cfg;
+ NML3ConfigMergeFlags merge_flags;
+ union {
+ struct {
+ guint32 default_route_penalty_6;
+ guint32 default_route_penalty_4;
+ };
+ guint32 default_route_penalty_x[2];
+ };
+} NML3ConfigDatMergeInfo;
+
NML3ConfigData *nm_l3_config_data_new (NMDedupMultiIndex *multi_idx,
int ifindex);
const NML3ConfigData *nm_l3_config_data_ref (const NML3ConfigData *self);
@@ -76,6 +106,11 @@ NML3ConfigData *nm_l3_config_data_new_from_platform (NMDedupMultiIndex *multi_id
NMPlatform *platform,
NMSettingIP6ConfigPrivacy ipv6_privacy_rfc4941);
+NML3ConfigData *nm_l3_config_data_new_combined (NMDedupMultiIndex *multi_idx,
+ int ifindex,
+ const NML3ConfigDatMergeInfo *const*merge_infos,
+ guint merge_infos_len);
+
/*****************************************************************************/
int nm_l3_config_data_get_ifindex (const NML3ConfigData *self);
diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c
index aefaef1cd8..3709ff7dbc 100644
--- a/src/nm-l3cfg.c
+++ b/src/nm-l3cfg.c
@@ -10,6 +10,16 @@
/*****************************************************************************/
+typedef struct {
+ NML3ConfigDatMergeInfo merge_info;
+ gconstpointer tag;
+ guint64 pseudo_timestamp;
+ int priority;
+ bool dirty:1;
+} L3ConfigData;
+
+/*****************************************************************************/
+
NM_GOBJECT_PROPERTIES_DEFINE (NML3Cfg,
PROP_NETNS,
PROP_IFINDEX,
@@ -17,6 +27,9 @@ NM_GOBJECT_PROPERTIES_DEFINE (NML3Cfg,
typedef struct _NML3CfgPrivate {
GArray *property_emit_list;
+ GArray *l3_config_datas;
+ const NML3ConfigData *combined_l3cfg;
+ guint64 pseudo_timestamp_counter;
} NML3CfgPrivate;
struct _NML3CfgClass {
@@ -226,6 +239,331 @@ nm_l3cfg_property_emit_unregister (NML3Cfg *self,
/*****************************************************************************/
+static GArray *
+_l3_config_datas_ensure (GArray **p_arr)
+{
+ if (!*p_arr)
+ *p_arr = g_array_new (FALSE, FALSE, sizeof (L3ConfigData));
+ return *p_arr;
+}
+
+#define _l3_config_datas_at(l3_config_datas, idx) \
+ (&g_array_index ((l3_config_datas), L3ConfigData, (idx)))
+
+static gssize
+_l3_config_datas_find_next (GArray *l3_config_datas,
+ guint start_idx,
+ gconstpointer needle_tag,
+ const NML3ConfigData *needle_l3cfg)
+{
+ guint i;
+
+ nm_assert (l3_config_datas);
+ nm_assert (start_idx <= l3_config_datas->len);
+
+ for (i = start_idx; i < l3_config_datas->len; i++) {
+ const L3ConfigData *l3_config_data = _l3_config_datas_at (l3_config_datas, i);
+
+ if ( NM_IN_SET (needle_tag, NULL, l3_config_data->tag)
+ && NM_IN_SET (needle_l3cfg, NULL, l3_config_data->merge_info.l3cfg))
+ return i;
+ }
+ return -1;
+}
+
+static void
+_l3_config_datas_remove_index_fast (GArray *arr,
+ guint idx)
+{
+ L3ConfigData *l3_config_data;
+
+ nm_assert (arr);
+ nm_assert (idx < arr->len);
+
+ l3_config_data = _l3_config_datas_at (arr, idx);
+
+ nm_l3_config_data_unref (l3_config_data->merge_info.l3cfg);
+
+ g_array_remove_index_fast (arr, idx);
+}
+
+void
+nm_l3cfg_mark_config_dirty (NML3Cfg *self,
+ gconstpointer tag,
+ gboolean dirty)
+{
+ GArray *l3_config_datas;
+ gssize idx;
+
+ nm_assert (NM_IS_L3CFG (self));
+ nm_assert (tag);
+
+ l3_config_datas = self->priv.p->l3_config_datas;
+ if (!l3_config_datas)
+ return;
+
+ idx = 0;
+ while (TRUE) {
+ idx = _l3_config_datas_find_next (l3_config_datas,
+ idx,
+ tag,
+ NULL);
+ if (idx < 0)
+ return;
+
+ _l3_config_datas_at (l3_config_datas, idx)->dirty = dirty;
+ idx++;
+ }
+}
+
+void
+nm_l3cfg_add_config (NML3Cfg *self,
+ gconstpointer tag,
+ gboolean replace_same_tag,
+ const NML3ConfigData *l3cfg,
+ int priority,
+ guint32 default_route_penalty_4,
+ guint32 default_route_penalty_6,
+ NML3ConfigMergeFlags merge_flags)
+{
+ GArray *l3_config_datas;
+ L3ConfigData *l3_config_data;
+ gssize idx;
+ gboolean changed = FALSE;
+
+ nm_assert (NM_IS_L3CFG (self));
+ nm_assert (tag);
+ nm_assert (l3cfg);
+ nm_assert (nm_l3_config_data_get_ifindex (l3cfg) == self->priv.ifindex);
+
+ l3_config_datas = _l3_config_datas_ensure (&self->priv.p->l3_config_datas);
+
+ idx = _l3_config_datas_find_next (l3_config_datas,
+ 0,
+ tag,
+ replace_same_tag ? NULL : l3cfg);
+
+ if (replace_same_tag) {
+ gssize idx2;
+
+ idx2 = idx;
+ idx = -1;
+ while (TRUE) {
+ l3_config_data = _l3_config_datas_at (l3_config_datas, idx2);
+
+ if (l3_config_data->merge_info.l3cfg == l3cfg) {
+ nm_assert (idx == -1);
+ idx = idx2;
+ continue;
+ }
+
+ changed = TRUE;
+ _l3_config_datas_remove_index_fast (l3_config_datas, idx2);
+
+ idx2 = _l3_config_datas_find_next (l3_config_datas, idx2, tag, NULL);
+ if (idx2 < 0)
+ break;
+ }
+ }
+
+ if (idx < 0) {
+ l3_config_data = nm_g_array_append_new (l3_config_datas, L3ConfigData);
+ *l3_config_data = (L3ConfigData) {
+ .tag = tag,
+ .merge_info.l3cfg = nm_l3_config_data_ref_and_seal (l3cfg),
+ .merge_info.merge_flags = merge_flags,
+ .merge_info.default_route_penalty_4 = default_route_penalty_4,
+ .merge_info.default_route_penalty_6 = default_route_penalty_6,
+ .priority = priority,
+ .pseudo_timestamp = ++self->priv.p->pseudo_timestamp_counter,
+ .dirty = FALSE,
+ };
+ changed = TRUE;
+ } else {
+ l3_config_data = _l3_config_datas_at (l3_config_datas, idx);
+ l3_config_data->dirty = FALSE;
+ nm_assert (l3_config_data->tag == tag);
+ nm_assert (l3_config_data->merge_info.l3cfg == l3cfg);
+ if (l3_config_data->priority != priority) {
+ l3_config_data->priority = priority;
+ changed = TRUE;
+ }
+ if (l3_config_data->merge_info.merge_flags != merge_flags) {
+ l3_config_data->merge_info.merge_flags = merge_flags;
+ changed = TRUE;
+ }
+ if (l3_config_data->merge_info.default_route_penalty_4 != default_route_penalty_4) {
+ l3_config_data->merge_info.default_route_penalty_4 = default_route_penalty_4;
+ changed = TRUE;
+ }
+ if (l3_config_data->merge_info.default_route_penalty_6 != default_route_penalty_6) {
+ l3_config_data->merge_info.default_route_penalty_6 = default_route_penalty_6;
+ changed = TRUE;
+ }
+ }
+
+ if (changed)
+ self->priv.changed_configs = TRUE;
+}
+
+static void
+_l3cfg_remove_config (NML3Cfg *self,
+ gconstpointer tag,
+ gboolean only_dirty,
+ const NML3ConfigData *l3cfg)
+{
+ GArray *l3_config_datas;
+ gssize idx;
+
+ nm_assert (NM_IS_L3CFG (self));
+ nm_assert (tag);
+
+ l3_config_datas = self->priv.p->l3_config_datas;
+ if (!l3_config_datas)
+ return;
+
+ idx = 0;
+ while (TRUE) {
+ idx = _l3_config_datas_find_next (l3_config_datas,
+ idx,
+ tag,
+ l3cfg);
+ if (idx < 0)
+ return;
+
+ if ( only_dirty
+ && !_l3_config_datas_at (l3_config_datas, idx)->dirty) {
+ idx++;
+ continue;
+ }
+
+ self->priv.changed_configs = TRUE;
+ _l3_config_datas_remove_index_fast (l3_config_datas, idx);
+ if (!l3cfg)
+ return;
+ }
+}
+
+void
+nm_l3cfg_remove_config (NML3Cfg *self,
+ gconstpointer tag,
+ const NML3ConfigData *ifcfg)
+{
+ nm_assert (ifcfg);
+
+ _l3cfg_remove_config (self, tag, FALSE, ifcfg);
+}
+
+void
+nm_l3cfg_remove_config_all (NML3Cfg *self,
+ gconstpointer tag,
+ gboolean only_dirty)
+{
+ _l3cfg_remove_config (self, tag, only_dirty, NULL);
+}
+
+/*****************************************************************************/
+
+static int
+_l3_config_combine_sort_fcn (gconstpointer p_a,
+ gconstpointer p_b,
+ gpointer user_data)
+{
+ const L3ConfigData *a = *((L3ConfigData **) p_a);
+ const L3ConfigData *b = *((L3ConfigData **) p_b);
+
+ nm_assert (a);
+ nm_assert (b);
+ nm_assert (nm_l3_config_data_get_ifindex (a->merge_info.l3cfg) == nm_l3_config_data_get_ifindex (b->merge_info.l3cfg));
+
+ /* we sort the entries with higher priority (more important, lower numerical value)
+ * first. */
+ NM_CMP_FIELD (a, b, priority);
+
+ /* if the priority is not unique, we sort them in the order they were added,
+ * with the oldest first (lower numerical value). */
+ NM_CMP_FIELD (a, b, pseudo_timestamp);
+
+ return nm_assert_unreachable_val (0);
+}
+
+static const NML3ConfigData *
+_l3cfg_combine_config (GArray *l3_config_datas,
+ NMDedupMultiIndex *multi_idx,
+ int ifindex)
+{
+ gs_free L3ConfigData **infos_heap = NULL;
+ NML3ConfigData *l3cfg;
+ L3ConfigData **infos;
+ guint i;
+
+ if ( !l3_config_datas
+ || l3_config_datas->len == 0)
+ return NULL;
+
+ if (l3_config_datas->len == 1)
+ return nm_l3_config_data_ref (_l3_config_datas_at (l3_config_datas, 0)->merge_info.l3cfg);
+
+ if (l3_config_datas->len < 300 / sizeof (infos[0]))
+ infos = g_alloca (l3_config_datas->len * sizeof (infos[0]));
+ else {
+ infos_heap = g_new (L3ConfigData *, l3_config_datas->len);
+ infos = infos_heap;
+ }
+
+ for (i = 0; i < l3_config_datas->len; i++)
+ infos[i] = _l3_config_datas_at (l3_config_datas, i);
+
+ g_qsort_with_data (infos,
+ l3_config_datas->len,
+ sizeof (infos[0]),
+ _l3_config_combine_sort_fcn,
+ NULL);
+
+ nm_assert (&infos[0]->merge_info == (NML3ConfigDatMergeInfo *) infos[0]);
+
+ l3cfg = nm_l3_config_data_new_combined (multi_idx,
+ ifindex,
+ (const NML3ConfigDatMergeInfo *const*) infos,
+ l3_config_datas->len);
+
+ nm_assert (l3cfg);
+ nm_assert (nm_l3_config_data_get_ifindex (l3cfg) == ifindex);
+
+ return nm_l3_config_data_seal (l3cfg);
+}
+
+_nm_unused
+static gboolean
+_l3cfg_update_combined_config (NML3Cfg *self,
+ const NML3ConfigData **out_old /* transfer reference */)
+{
+ nm_auto_unref_l3cfg const NML3ConfigData *l3cfg_old = NULL;
+ nm_auto_unref_l3cfg const NML3ConfigData *l3cfg = NULL;
+
+ nm_assert (NM_IS_L3CFG (self));
+ nm_assert (!out_old || !*out_old);
+
+ if (!self->priv.changed_configs)
+ return FALSE;
+
+ self->priv.changed_configs = FALSE;
+
+ l3cfg = _l3cfg_combine_config (self->priv.p->l3_config_datas,
+ nm_platform_get_multi_idx (self->priv.platform),
+ self->priv.ifindex);
+
+ if (nm_l3_config_data_equal (l3cfg, self->priv.p->combined_l3cfg))
+ return FALSE;
+
+ l3cfg_old = g_steal_pointer (&self->priv.p->combined_l3cfg);
+ self->priv.p->combined_l3cfg = nm_l3_config_data_seal (g_steal_pointer (&l3cfg));
+ NM_SET_OUT (out_old, nm_l3_config_data_ref (self->priv.p->combined_l3cfg));
+ return TRUE;
+}
+
+/*****************************************************************************/
+
static void
set_property (GObject *object,
guint prop_id,
@@ -304,6 +642,8 @@ finalize (GObject *object)
g_clear_object (&self->priv.netns);
g_clear_object (&self->priv.platform);
+ nm_clear_pointer (&self->priv.p->combined_l3cfg, nm_l3_config_data_unref);
+
nm_clear_pointer (&self->priv.pllink, nmp_object_unref);
_LOGT ("finalized");
diff --git a/src/nm-l3cfg.h b/src/nm-l3cfg.h
index 7fc4b4b7d2..0c2b9c814a 100644
--- a/src/nm-l3cfg.h
+++ b/src/nm-l3cfg.h
@@ -4,6 +4,7 @@
#define __NM_L3CFG_H__
#include "platform/nmp-object.h"
+#include "nm-l3-config-data.h"
#define NM_TYPE_L3CFG (nm_l3cfg_get_type ())
#define NM_L3CFG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_L3CFG, NML3Cfg))
@@ -20,11 +21,12 @@ struct _NML3CfgPrivate;
struct _NML3Cfg {
GObject parent;
struct {
+ struct _NML3CfgPrivate *p;
NMNetns *netns;
NMPlatform *platform;
- int ifindex;
const NMPObject *pllink;
- struct _NML3CfgPrivate *p;
+ int ifindex;
+ bool changed_configs:1;
} priv;
};
@@ -89,4 +91,27 @@ void nm_l3cfg_property_emit_unregister (NML3Cfg *self,
GObject *target_obj,
const GParamSpec *target_property);
+/*****************************************************************************/
+
+void nm_l3cfg_mark_config_dirty (NML3Cfg *self,
+ gconstpointer tag,
+ gboolean dirty);
+
+void nm_l3cfg_add_config (NML3Cfg *self,
+ gconstpointer tag,
+ gboolean replace_same_tag,
+ const NML3ConfigData *l3cfg,
+ int priority,
+ guint32 default_route_penalty_4,
+ guint32 default_route_penalty_6,
+ NML3ConfigMergeFlags merge_flags);
+
+void nm_l3cfg_remove_config (NML3Cfg *self,
+ gconstpointer tag,
+ const NML3ConfigData *ifcfg);
+
+void nm_l3cfg_remove_config_all (NML3Cfg *self,
+ gconstpointer tag,
+ gboolean only_dirty);
+
#endif /* __NM_L3CFG_H__ */