mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-03 19:50:14 +01:00
3303 lines
113 KiB
C
3303 lines
113 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright (C) 2005 - 2017 Red Hat, Inc.
|
|
* Copyright (C) 2006 - 2008 Novell, Inc.
|
|
*/
|
|
|
|
#include "src/core/nm-default-daemon.h"
|
|
|
|
#include "nm-ip4-config.h"
|
|
|
|
#include <arpa/inet.h>
|
|
#include <resolv.h>
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include "libnm-glib-aux/nm-dedup-multi.h"
|
|
|
|
#include "nm-utils.h"
|
|
#include "libnm-platform/nmp-object.h"
|
|
#include "libnm-platform/nm-platform.h"
|
|
#include "libnm-platform/nm-platform-utils.h"
|
|
#include "NetworkManagerUtils.h"
|
|
#include "libnm-core-intern/nm-core-internal.h"
|
|
#include "nm-dbus-object.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */
|
|
G_STATIC_ASSERT(sizeof(uint) >= sizeof(guint32));
|
|
G_STATIC_ASSERT(G_MAXUINT >= 0xFFFFFFFF);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
_route_valid(const NMPlatformIP4Route *r)
|
|
{
|
|
return r && r->plen <= 32
|
|
&& r->network == nm_utils_ip4_address_clear_host_address(r->network, r->plen);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_idx_obj_id_hash_update(const NMDedupMultiIdxType *idx_type,
|
|
const NMDedupMultiObj * obj,
|
|
NMHashState * h)
|
|
{
|
|
nmp_object_id_hash_update((NMPObject *) obj, h);
|
|
}
|
|
|
|
static gboolean
|
|
_idx_obj_id_equal(const NMDedupMultiIdxType *idx_type,
|
|
const NMDedupMultiObj * obj_a,
|
|
const NMDedupMultiObj * obj_b)
|
|
{
|
|
return nmp_object_id_equal((NMPObject *) obj_a, (NMPObject *) obj_b);
|
|
}
|
|
|
|
void
|
|
nm_ip_config_dedup_multi_idx_type_init(NMIPConfigDedupMultiIdxType *idx_type,
|
|
NMPObjectType obj_type)
|
|
{
|
|
static const NMDedupMultiIdxTypeClass idx_type_class = {
|
|
.idx_obj_id_hash_update = _idx_obj_id_hash_update,
|
|
.idx_obj_id_equal = _idx_obj_id_equal,
|
|
};
|
|
|
|
nm_dedup_multi_idx_type_init((NMDedupMultiIdxType *) idx_type, &idx_type_class);
|
|
idx_type->obj_type = obj_type;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
_nm_ip_config_add_obj(NMDedupMultiIndex * multi_idx,
|
|
NMIPConfigDedupMultiIdxType *idx_type,
|
|
int ifindex,
|
|
const NMPObject * obj_new,
|
|
const NMPlatformObject * pl_new,
|
|
gboolean merge,
|
|
gboolean append_force,
|
|
const NMPObject ** out_obj_old /* returns a reference! */,
|
|
const NMPObject ** out_obj_new /* does not return a reference */)
|
|
{
|
|
NMPObject obj_new_stackinit;
|
|
const NMDedupMultiEntry *entry_old;
|
|
const NMDedupMultiEntry *entry_new;
|
|
|
|
nm_assert(multi_idx);
|
|
nm_assert(idx_type);
|
|
nm_assert(NM_IN_SET(idx_type->obj_type,
|
|
NMP_OBJECT_TYPE_IP4_ADDRESS,
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE));
|
|
nm_assert(ifindex > 0);
|
|
|
|
/* we go through extra lengths to accept a full obj_new object. That one,
|
|
* can be reused by increasing the ref-count. */
|
|
if (!obj_new) {
|
|
nm_assert(pl_new);
|
|
obj_new = nmp_object_stackinit(&obj_new_stackinit, idx_type->obj_type, pl_new);
|
|
NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(&obj_new_stackinit)->ifindex = ifindex;
|
|
} else {
|
|
nm_assert(!pl_new);
|
|
nm_assert(NMP_OBJECT_GET_TYPE(obj_new) == idx_type->obj_type);
|
|
if (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_new)->ifindex != ifindex) {
|
|
obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new);
|
|
NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(&obj_new_stackinit)->ifindex = ifindex;
|
|
}
|
|
}
|
|
nm_assert(NMP_OBJECT_GET_TYPE(obj_new) == idx_type->obj_type);
|
|
nm_assert(nmp_object_is_alive(obj_new));
|
|
|
|
entry_old = nm_dedup_multi_index_lookup_obj(multi_idx, &idx_type->parent, obj_new);
|
|
|
|
if (entry_old) {
|
|
gboolean modified = FALSE;
|
|
const NMPObject *obj_old = entry_old->obj;
|
|
|
|
if (nmp_object_equal(obj_new, obj_old)) {
|
|
nm_dedup_multi_entry_set_dirty(entry_old, FALSE);
|
|
goto append_force_and_out;
|
|
}
|
|
|
|
/* if @merge, we merge the new object with the existing one.
|
|
* Otherwise, we replace it entirely. */
|
|
if (merge) {
|
|
switch (idx_type->obj_type) {
|
|
case NMP_OBJECT_TYPE_IP4_ADDRESS:
|
|
case NMP_OBJECT_TYPE_IP6_ADDRESS:
|
|
/* for addresses that we read from the kernel, we keep the timestamps as defined
|
|
* by the previous source (item_old). The reason is, that the other source configured the lifetimes
|
|
* with "what should be" and the kernel values are "what turned out after configuring it".
|
|
*
|
|
* For other sources, the longer lifetime wins. */
|
|
if ((obj_new->ip_address.addr_source == NM_IP_CONFIG_SOURCE_KERNEL
|
|
&& obj_old->ip_address.addr_source != NM_IP_CONFIG_SOURCE_KERNEL)
|
|
|| nm_platform_ip_address_cmp_expiry(NMP_OBJECT_CAST_IP_ADDRESS(obj_old),
|
|
NMP_OBJECT_CAST_IP_ADDRESS(obj_new))
|
|
> 0) {
|
|
obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new);
|
|
obj_new_stackinit.ip_address.timestamp =
|
|
NMP_OBJECT_CAST_IP_ADDRESS(obj_old)->timestamp;
|
|
obj_new_stackinit.ip_address.lifetime =
|
|
NMP_OBJECT_CAST_IP_ADDRESS(obj_old)->lifetime;
|
|
obj_new_stackinit.ip_address.preferred =
|
|
NMP_OBJECT_CAST_IP_ADDRESS(obj_old)->preferred;
|
|
modified = TRUE;
|
|
}
|
|
|
|
/* keep the maximum addr_source. */
|
|
if (obj_new->ip_address.addr_source < obj_old->ip_address.addr_source) {
|
|
obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new);
|
|
obj_new_stackinit.ip_address.addr_source = obj_old->ip_address.addr_source;
|
|
modified = TRUE;
|
|
}
|
|
break;
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
|
/* keep the maximum rt_source. */
|
|
if (obj_new->ip_route.rt_source < obj_old->ip_route.rt_source) {
|
|
obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new);
|
|
obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source;
|
|
modified = TRUE;
|
|
}
|
|
if (!obj_new->ip_route.is_external && obj_old->ip_route.is_external) {
|
|
obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new);
|
|
obj_new_stackinit.ip_route.is_external = FALSE;
|
|
modified = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
nm_assert_not_reached();
|
|
break;
|
|
}
|
|
|
|
if (modified && nmp_object_equal(obj_new, obj_old)) {
|
|
nm_dedup_multi_entry_set_dirty(entry_old, FALSE);
|
|
goto append_force_and_out;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!nm_dedup_multi_index_add_full(multi_idx,
|
|
&idx_type->parent,
|
|
obj_new,
|
|
append_force ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE
|
|
: NM_DEDUP_MULTI_IDX_MODE_APPEND,
|
|
NULL,
|
|
entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING,
|
|
NULL,
|
|
&entry_new,
|
|
out_obj_old)) {
|
|
nm_assert_not_reached();
|
|
NM_SET_OUT(out_obj_new, NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
NM_SET_OUT(out_obj_new, entry_new->obj);
|
|
return TRUE;
|
|
|
|
append_force_and_out:
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(entry_old->obj));
|
|
NM_SET_OUT(out_obj_new, entry_old->obj);
|
|
if (append_force) {
|
|
if (nm_dedup_multi_entry_reorder(entry_old, NULL, TRUE))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* _nm_ip_config_lookup_ip_route:
|
|
* @multi_idx:
|
|
* @idx_type:
|
|
* @needle:
|
|
* @cmp_type: after lookup, filter the result by comparing with @cmp_type. Only
|
|
* return the result, if it compares equal to @needle according to this @cmp_type.
|
|
* Note that the index uses %NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST type, so passing
|
|
* that compare-type means not to filter any further.
|
|
*
|
|
* Returns: the found entry or %NULL.
|
|
*/
|
|
const NMDedupMultiEntry *
|
|
_nm_ip_config_lookup_ip_route(const NMDedupMultiIndex * multi_idx,
|
|
const NMIPConfigDedupMultiIdxType *idx_type,
|
|
const NMPObject * needle,
|
|
NMPlatformIPRouteCmpType cmp_type)
|
|
{
|
|
const NMDedupMultiEntry *entry;
|
|
|
|
nm_assert(multi_idx);
|
|
nm_assert(idx_type);
|
|
nm_assert(NM_IN_SET(idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
|
|
nm_assert(NMP_OBJECT_GET_TYPE(needle) == idx_type->obj_type);
|
|
|
|
entry = nm_dedup_multi_index_lookup_obj(multi_idx, &idx_type->parent, needle);
|
|
if (!entry)
|
|
return NULL;
|
|
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
|
|
nm_assert((NMP_OBJECT_GET_TYPE(needle) == NMP_OBJECT_TYPE_IP4_ROUTE
|
|
&& nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(entry->obj),
|
|
NMP_OBJECT_CAST_IP4_ROUTE(needle),
|
|
cmp_type)
|
|
== 0)
|
|
|| (NMP_OBJECT_GET_TYPE(needle) == NMP_OBJECT_TYPE_IP6_ROUTE
|
|
&& nm_platform_ip6_route_cmp(NMP_OBJECT_CAST_IP6_ROUTE(entry->obj),
|
|
NMP_OBJECT_CAST_IP6_ROUTE(needle),
|
|
cmp_type)
|
|
== 0));
|
|
} else {
|
|
if (NMP_OBJECT_GET_TYPE(needle) == NMP_OBJECT_TYPE_IP4_ROUTE) {
|
|
if (nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(entry->obj),
|
|
NMP_OBJECT_CAST_IP4_ROUTE(needle),
|
|
cmp_type)
|
|
!= 0)
|
|
return NULL;
|
|
} else {
|
|
if (nm_platform_ip6_route_cmp(NMP_OBJECT_CAST_IP6_ROUTE(entry->obj),
|
|
NMP_OBJECT_CAST_IP6_ROUTE(needle),
|
|
cmp_type)
|
|
!= 0)
|
|
return NULL;
|
|
}
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NM_GOBJECT_PROPERTIES_DEFINE(NMIP4Config,
|
|
PROP_MULTI_IDX,
|
|
PROP_IFINDEX,
|
|
PROP_ADDRESS_DATA,
|
|
PROP_ADDRESSES,
|
|
PROP_ROUTE_DATA,
|
|
PROP_ROUTES,
|
|
PROP_GATEWAY,
|
|
PROP_NAMESERVER_DATA,
|
|
PROP_NAMESERVERS,
|
|
PROP_DOMAINS,
|
|
PROP_SEARCHES,
|
|
PROP_DNS_OPTIONS,
|
|
PROP_WINS_SERVER_DATA,
|
|
PROP_WINS_SERVERS,
|
|
PROP_DNS_PRIORITY, );
|
|
|
|
typedef struct {
|
|
bool metered : 1;
|
|
bool never_default : 1;
|
|
guint32 mtu;
|
|
int ifindex;
|
|
NMIPConfigSource mtu_source;
|
|
int dns_priority;
|
|
NMSettingConnectionMdns mdns;
|
|
NMSettingConnectionLlmnr llmnr;
|
|
GArray * nameservers;
|
|
GPtrArray * domains;
|
|
GPtrArray * searches;
|
|
GPtrArray * dns_options;
|
|
GArray * nis;
|
|
char * nis_domain;
|
|
GArray * wins;
|
|
GVariant * address_data_variant;
|
|
GVariant * addresses_variant;
|
|
GVariant * route_data_variant;
|
|
GVariant * routes_variant;
|
|
NMDedupMultiIndex * multi_idx;
|
|
const NMPObject * best_default_route;
|
|
union {
|
|
NMIPConfigDedupMultiIdxType idx_ip4_addresses_;
|
|
NMDedupMultiIdxType idx_ip4_addresses;
|
|
};
|
|
union {
|
|
NMIPConfigDedupMultiIdxType idx_ip4_routes_;
|
|
NMDedupMultiIdxType idx_ip4_routes;
|
|
};
|
|
NMIPConfigFlags config_flags;
|
|
} NMIP4ConfigPrivate;
|
|
|
|
struct _NMIP4Config {
|
|
NMIPConfig parent;
|
|
NMIP4ConfigPrivate _priv;
|
|
};
|
|
|
|
struct _NMIP4ConfigClass {
|
|
NMIPConfigClass parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE(NMIP4Config, nm_ip4_config, NM_TYPE_IP_CONFIG)
|
|
|
|
#define NM_IP4_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP4Config, NM_IS_IP4_CONFIG)
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_add_address(NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new);
|
|
static void _add_route(NMIP4Config * self,
|
|
const NMPObject *obj_new,
|
|
const NMPlatformIP4Route *new,
|
|
const NMPObject **out_obj_new);
|
|
static const NMDedupMultiEntry *
|
|
_lookup_route(const NMIP4Config *self, const NMPObject *needle, NMPlatformIPRouteCmpType cmp_type);
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
nm_ip4_config_get_ifindex(const NMIP4Config *self)
|
|
{
|
|
return NM_IP4_CONFIG_GET_PRIVATE(self)->ifindex;
|
|
}
|
|
|
|
NMDedupMultiIndex *
|
|
nm_ip4_config_get_multi_idx(const NMIP4Config *self)
|
|
{
|
|
return NM_IP4_CONFIG_GET_PRIVATE(self)->multi_idx;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const NMDedupMultiHeadEntry *
|
|
nm_ip4_config_lookup_addresses(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return nm_dedup_multi_index_lookup_head(priv->multi_idx, &priv->idx_ip4_addresses, NULL);
|
|
}
|
|
|
|
void
|
|
nm_ip_config_iter_ip4_address_init(NMDedupMultiIter *ipconf_iter, const NMIP4Config *self)
|
|
{
|
|
nm_assert(NM_IS_IP4_CONFIG(self));
|
|
|
|
nm_dedup_multi_iter_init(ipconf_iter, nm_ip4_config_lookup_addresses(self));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const NMDedupMultiHeadEntry *
|
|
nm_ip4_config_lookup_routes(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return nm_dedup_multi_index_lookup_head(priv->multi_idx, &priv->idx_ip4_routes, NULL);
|
|
}
|
|
|
|
void
|
|
nm_ip_config_iter_ip4_route_init(NMDedupMultiIter *ipconf_iter, const NMIP4Config *self)
|
|
{
|
|
nm_assert(NM_IS_IP4_CONFIG(self));
|
|
|
|
nm_dedup_multi_iter_init(ipconf_iter, nm_ip4_config_lookup_routes(self));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const NMPObject *
|
|
_nm_ip_config_best_default_route_find_better(const NMPObject *obj_cur, const NMPObject *obj_cmp)
|
|
{
|
|
nm_assert(!obj_cur
|
|
|| NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_cur),
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE));
|
|
nm_assert(!obj_cmp
|
|
|| (!obj_cur
|
|
&& NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_cmp),
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE))
|
|
|| NMP_OBJECT_GET_TYPE(obj_cur) == NMP_OBJECT_GET_TYPE(obj_cmp));
|
|
nm_assert(!obj_cur || nmp_object_ip_route_is_best_default_route(obj_cur));
|
|
|
|
/* assumes that @obj_cur is already the best default route (or NULL). It checks whether
|
|
* @obj_cmp is also a default route and returns the best of both. */
|
|
if (obj_cmp && nmp_object_ip_route_is_best_default_route(obj_cmp)) {
|
|
guint32 metric_cur, metric_cmp;
|
|
|
|
if (!obj_cur)
|
|
return obj_cmp;
|
|
|
|
metric_cur = NMP_OBJECT_CAST_IP_ROUTE(obj_cur)->metric;
|
|
metric_cmp = NMP_OBJECT_CAST_IP_ROUTE(obj_cmp)->metric;
|
|
|
|
if (metric_cmp < metric_cur)
|
|
return obj_cmp;
|
|
|
|
if (metric_cmp == metric_cur) {
|
|
int c;
|
|
|
|
/* Routes have the same metric. We still want to deterministically
|
|
* prefer one or the other. It's important to consistently choose one
|
|
* or the other, so that the order doesn't matter how routes are added
|
|
* (and merged). */
|
|
c = nmp_object_cmp(obj_cur, obj_cmp);
|
|
if (c != 0)
|
|
return c < 0 ? obj_cur : obj_cmp;
|
|
|
|
/* as last resort, compare pointers. */
|
|
if (obj_cmp < obj_cur)
|
|
return obj_cmp;
|
|
}
|
|
}
|
|
return obj_cur;
|
|
}
|
|
|
|
gboolean
|
|
_nm_ip_config_best_default_route_merge(const NMPObject **best_default_route,
|
|
const NMPObject * new_candidate)
|
|
{
|
|
new_candidate =
|
|
_nm_ip_config_best_default_route_find_better(*best_default_route, new_candidate);
|
|
return nmp_object_ref_set(best_default_route, new_candidate);
|
|
}
|
|
|
|
const NMPObject *
|
|
nm_ip4_config_best_default_route_get(const NMIP4Config *self)
|
|
{
|
|
g_return_val_if_fail(NM_IS_IP4_CONFIG(self), NULL);
|
|
|
|
return NM_IP4_CONFIG_GET_PRIVATE(self)->best_default_route;
|
|
}
|
|
|
|
const NMPObject *
|
|
_nm_ip4_config_best_default_route_find(const NMIP4Config *self)
|
|
{
|
|
NMDedupMultiIter ipconf_iter;
|
|
const NMPObject *new_best_default_route = NULL;
|
|
|
|
nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, NULL) {
|
|
new_best_default_route =
|
|
_nm_ip_config_best_default_route_find_better(new_best_default_route,
|
|
ipconf_iter.current->obj);
|
|
}
|
|
return new_best_default_route;
|
|
}
|
|
|
|
in_addr_t
|
|
nmtst_ip4_config_get_gateway(NMIP4Config *config)
|
|
{
|
|
const NMPObject *rt;
|
|
|
|
g_assert(NM_IS_IP4_CONFIG(config));
|
|
|
|
rt = nm_ip4_config_best_default_route_get(config);
|
|
if (!rt)
|
|
return 0;
|
|
return NMP_OBJECT_CAST_IP4_ROUTE(rt)->gateway;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_notify_addresses(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
nm_clear_g_variant(&priv->address_data_variant);
|
|
nm_clear_g_variant(&priv->addresses_variant);
|
|
nm_gobject_notify_together(self, PROP_ADDRESS_DATA, PROP_ADDRESSES);
|
|
}
|
|
|
|
static void
|
|
_notify_routes(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
nm_assert(priv->best_default_route == _nm_ip4_config_best_default_route_find(self));
|
|
nm_clear_g_variant(&priv->route_data_variant);
|
|
nm_clear_g_variant(&priv->routes_variant);
|
|
nm_gobject_notify_together(self, PROP_ROUTE_DATA, PROP_ROUTES);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMIP4Config *
|
|
nm_ip4_config_clone(const NMIP4Config *self)
|
|
{
|
|
NMIP4Config *copy;
|
|
|
|
copy = nm_ip4_config_new(nm_ip4_config_get_multi_idx(self), -1);
|
|
nm_ip4_config_replace(copy, self, NULL);
|
|
|
|
return copy;
|
|
}
|
|
|
|
NMIP4Config *
|
|
nm_ip4_config_capture(NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex)
|
|
{
|
|
NMIP4Config * self;
|
|
NMIP4ConfigPrivate * priv;
|
|
const NMDedupMultiHeadEntry *head_entry;
|
|
NMDedupMultiIter iter;
|
|
const NMPObject * plobj = NULL;
|
|
|
|
nm_assert(ifindex > 0);
|
|
|
|
/* Slaves have no IP configuration */
|
|
if (nm_platform_link_get_master(platform, ifindex) > 0)
|
|
return NULL;
|
|
|
|
self = nm_ip4_config_new(multi_idx, ifindex);
|
|
priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
head_entry = nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex);
|
|
if (head_entry) {
|
|
nmp_cache_iter_for_each (&iter, head_entry, &plobj) {
|
|
if (!_nm_ip_config_add_obj(priv->multi_idx,
|
|
&priv->idx_ip4_addresses_,
|
|
ifindex,
|
|
plobj,
|
|
NULL,
|
|
FALSE,
|
|
TRUE,
|
|
NULL,
|
|
NULL))
|
|
nm_assert_not_reached();
|
|
}
|
|
_notify_addresses(self);
|
|
}
|
|
|
|
head_entry = nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex);
|
|
|
|
/* Extract gateway from default route */
|
|
nmp_cache_iter_for_each (&iter, head_entry, &plobj)
|
|
_add_route(self, plobj, NULL, NULL);
|
|
|
|
return self;
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_update_routes_metric(NMIP4Config *self, gint64 metric)
|
|
{
|
|
gs_free NMPlatformIP4Route *routes = NULL;
|
|
gboolean need_update = FALSE;
|
|
const NMPlatformIP4Route * r;
|
|
NMDedupMultiIter iter;
|
|
guint num = 0, i = 0;
|
|
|
|
nm_ip_config_iter_ip4_route_for_each (&iter, self, &r) {
|
|
if (r->metric != metric)
|
|
need_update = TRUE;
|
|
num++;
|
|
}
|
|
if (!need_update)
|
|
return;
|
|
|
|
routes = g_new(NMPlatformIP4Route, num);
|
|
nm_ip_config_iter_ip4_route_for_each (&iter, self, &r) {
|
|
routes[i] = *r;
|
|
routes[i].metric = metric;
|
|
i++;
|
|
}
|
|
|
|
g_object_freeze_notify(G_OBJECT(self));
|
|
nm_ip4_config_reset_routes(self);
|
|
for (i = 0; i < num; i++)
|
|
nm_ip4_config_add_route(self, &routes[i], NULL);
|
|
g_object_thaw_notify(G_OBJECT(self));
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_add_dependent_routes(NMIP4Config *self,
|
|
guint32 route_table,
|
|
guint32 route_metric,
|
|
gboolean is_vrf,
|
|
GPtrArray ** out_ip4_dev_route_blacklist)
|
|
{
|
|
GPtrArray * ip4_dev_route_blacklist = NULL;
|
|
const NMPlatformIP4Address *my_addr;
|
|
const NMPlatformIP4Route * my_route;
|
|
int ifindex;
|
|
NMDedupMultiIter iter;
|
|
|
|
g_return_if_fail(NM_IS_IP4_CONFIG(self));
|
|
|
|
ifindex = nm_ip4_config_get_ifindex(self);
|
|
g_return_if_fail(ifindex > 0);
|
|
|
|
/* For IPv6 slaac, we explicitly add the device-routes (onlink) to NMIP6Config.
|
|
* As we don't do that for IPv4 (and manual IPv6 addresses), add them explicitly. */
|
|
|
|
nm_ip_config_iter_ip4_address_for_each (&iter, self, &my_addr) {
|
|
nm_auto_nmpobj NMPObject *r = NULL;
|
|
NMPlatformIP4Route * route;
|
|
in_addr_t network;
|
|
|
|
if (my_addr->plen == 0)
|
|
continue;
|
|
|
|
nm_assert(my_addr->plen <= 32);
|
|
|
|
/* The destination network depends on the peer-address. */
|
|
network = nm_utils_ip4_address_clear_host_address(my_addr->peer_address, my_addr->plen);
|
|
|
|
if (my_addr->external)
|
|
continue;
|
|
|
|
if (nm_utils_ip4_address_is_zeronet(network)) {
|
|
/* Kernel doesn't add device-routes for destinations that
|
|
* start with 0.x.y.z. Skip them. */
|
|
continue;
|
|
}
|
|
|
|
if (my_addr->plen == 32 && my_addr->address == my_addr->peer_address) {
|
|
/* Kernel doesn't add device-routes for /32 addresses unless
|
|
* they have a peer. */
|
|
continue;
|
|
}
|
|
|
|
r = nmp_object_new(NMP_OBJECT_TYPE_IP4_ROUTE, NULL);
|
|
route = NMP_OBJECT_CAST_IP4_ROUTE(r);
|
|
|
|
route->ifindex = ifindex;
|
|
route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL;
|
|
route->network = network;
|
|
route->plen = my_addr->plen;
|
|
route->pref_src = my_addr->address;
|
|
route->table_coerced = nm_platform_route_table_coerce(route_table);
|
|
route->metric = route_metric;
|
|
route->scope_inv = nm_platform_route_scope_inv(NM_RT_SCOPE_LINK);
|
|
|
|
nm_platform_ip_route_normalize(AF_INET, (NMPlatformIPRoute *) route);
|
|
|
|
if (_lookup_route(self, r, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) {
|
|
/* we already track this route. Don't add it again. */
|
|
} else
|
|
_add_route(self, r, NULL, NULL);
|
|
|
|
if (out_ip4_dev_route_blacklist
|
|
&& (route_table != RT_TABLE_MAIN
|
|
|| route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE)) {
|
|
nm_auto_nmpobj NMPObject *r_dev = NULL;
|
|
|
|
r_dev = nmp_object_clone(r, FALSE);
|
|
route = NMP_OBJECT_CAST_IP4_ROUTE(r_dev);
|
|
route->table_coerced = nm_platform_route_table_coerce(RT_TABLE_MAIN);
|
|
route->metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE;
|
|
|
|
nm_platform_ip_route_normalize(AF_INET, (NMPlatformIPRoute *) route);
|
|
|
|
if (_lookup_route(self, r_dev, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) {
|
|
/* we track such a route explicitly. Don't blacklist it. */
|
|
} else {
|
|
if (!ip4_dev_route_blacklist)
|
|
ip4_dev_route_blacklist =
|
|
g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
|
|
|
|
g_ptr_array_add(ip4_dev_route_blacklist, g_steal_pointer(&r_dev));
|
|
}
|
|
}
|
|
}
|
|
|
|
again:
|
|
nm_ip_config_iter_ip4_route_for_each (&iter, self, &my_route) {
|
|
NMPlatformIP4Route rt;
|
|
|
|
if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(my_route) || my_route->gateway == 0
|
|
|| NM_IS_IP_CONFIG_SOURCE_RTPROT(my_route->rt_source)
|
|
|| nm_ip4_config_get_direct_route_for_host(
|
|
self,
|
|
my_route->gateway,
|
|
nm_platform_route_table_uncoerce(my_route->table_coerced, TRUE)))
|
|
continue;
|
|
|
|
rt = *my_route;
|
|
rt.network = my_route->gateway;
|
|
rt.plen = 32;
|
|
rt.gateway = 0;
|
|
_add_route(self, NULL, &rt, NULL);
|
|
/* adding the route might have invalidated the iteration. Start again. */
|
|
goto again;
|
|
}
|
|
|
|
NM_SET_OUT(out_ip4_dev_route_blacklist, ip4_dev_route_blacklist);
|
|
}
|
|
|
|
gboolean
|
|
nm_ip4_config_commit(const NMIP4Config * self,
|
|
NMPlatform * platform,
|
|
NMIPRouteTableSyncMode route_table_sync)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *addresses = NULL;
|
|
gs_unref_ptrarray GPtrArray *routes = NULL;
|
|
gs_unref_ptrarray GPtrArray *routes_prune = NULL;
|
|
int ifindex;
|
|
gboolean success = TRUE;
|
|
|
|
g_return_val_if_fail(NM_IS_IP4_CONFIG(self), FALSE);
|
|
|
|
ifindex = nm_ip4_config_get_ifindex(self);
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
|
|
|
addresses =
|
|
nm_dedup_multi_objs_to_ptr_array_head(nm_ip4_config_lookup_addresses(self), NULL, NULL);
|
|
|
|
routes = nm_dedup_multi_objs_to_ptr_array_head(nm_ip4_config_lookup_routes(self), NULL, NULL);
|
|
|
|
routes_prune =
|
|
nm_platform_ip_route_get_prune_list(platform, AF_INET, ifindex, route_table_sync);
|
|
|
|
nm_platform_ip4_address_sync(platform, ifindex, addresses);
|
|
|
|
if (!nm_platform_ip_route_sync(platform, AF_INET, ifindex, routes, routes_prune, NULL))
|
|
success = FALSE;
|
|
|
|
return success;
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_merge_setting(NMIP4Config * self,
|
|
NMSettingIPConfig * setting,
|
|
NMSettingConnectionMdns mdns,
|
|
NMSettingConnectionLlmnr llmnr,
|
|
guint32 route_table,
|
|
guint32 route_metric)
|
|
{
|
|
guint naddresses, nroutes, nnameservers, nsearches;
|
|
int i, priority;
|
|
const char *gateway_str;
|
|
guint32 gateway_bin;
|
|
|
|
if (!setting)
|
|
return;
|
|
|
|
g_return_if_fail(NM_IS_SETTING_IP4_CONFIG(setting));
|
|
|
|
g_object_freeze_notify(G_OBJECT(self));
|
|
|
|
naddresses = nm_setting_ip_config_get_num_addresses(setting);
|
|
nroutes = nm_setting_ip_config_get_num_routes(setting);
|
|
nnameservers = nm_setting_ip_config_get_num_dns(setting);
|
|
nsearches = nm_setting_ip_config_get_num_dns_searches(setting);
|
|
|
|
/* Gateway */
|
|
if (!nm_setting_ip_config_get_never_default(setting)
|
|
&& (gateway_str = nm_setting_ip_config_get_gateway(setting))
|
|
&& inet_pton(AF_INET, gateway_str, &gateway_bin) == 1 && gateway_bin) {
|
|
const NMPlatformIP4Route r = {
|
|
.rt_source = NM_IP_CONFIG_SOURCE_USER,
|
|
.gateway = gateway_bin,
|
|
.table_coerced = nm_platform_route_table_coerce(route_table),
|
|
.metric = route_metric,
|
|
};
|
|
|
|
_add_route(self, NULL, &r, NULL);
|
|
}
|
|
|
|
/* Addresses */
|
|
for (i = 0; i < naddresses; i++) {
|
|
NMIPAddress * s_addr = nm_setting_ip_config_get_address(setting, i);
|
|
GVariant * label;
|
|
NMPlatformIP4Address address;
|
|
|
|
memset(&address, 0, sizeof(address));
|
|
nm_ip_address_get_address_binary(s_addr, &address.address);
|
|
address.peer_address = address.address;
|
|
address.plen = nm_ip_address_get_prefix(s_addr);
|
|
nm_assert(address.plen <= 32);
|
|
address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
|
|
address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
|
|
address.addr_source = NM_IP_CONFIG_SOURCE_USER;
|
|
|
|
label = nm_ip_address_get_attribute(s_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
|
|
if (label)
|
|
g_strlcpy(address.label, g_variant_get_string(label, NULL), sizeof(address.label));
|
|
|
|
_add_address(self, NULL, &address);
|
|
}
|
|
|
|
/* Routes */
|
|
for (i = 0; i < nroutes; i++) {
|
|
NMIPRoute * s_route = nm_setting_ip_config_get_route(setting, i);
|
|
NMPlatformIP4Route route;
|
|
gint64 m;
|
|
|
|
if (nm_ip_route_get_family(s_route) != AF_INET) {
|
|
nm_assert_not_reached();
|
|
continue;
|
|
}
|
|
|
|
memset(&route, 0, sizeof(route));
|
|
nm_ip_route_get_dest_binary(s_route, &route.network);
|
|
|
|
route.plen = nm_ip_route_get_prefix(s_route);
|
|
nm_assert(route.plen <= 32);
|
|
|
|
nm_ip_route_get_next_hop_binary(s_route, &route.gateway);
|
|
m = nm_ip_route_get_metric(s_route);
|
|
if (m < 0)
|
|
route.metric = route_metric;
|
|
else
|
|
route.metric = m;
|
|
route.rt_source = NM_IP_CONFIG_SOURCE_USER;
|
|
|
|
route.network = nm_utils_ip4_address_clear_host_address(route.network, route.plen);
|
|
|
|
nm_utils_ip_route_attribute_to_platform(AF_INET,
|
|
s_route,
|
|
NM_PLATFORM_IP_ROUTE_CAST(&route),
|
|
route_table);
|
|
_add_route(self, NULL, &route, NULL);
|
|
}
|
|
|
|
/* DNS */
|
|
if (nm_setting_ip_config_get_ignore_auto_dns(setting)) {
|
|
nm_ip4_config_reset_nameservers(self);
|
|
nm_ip4_config_reset_domains(self);
|
|
nm_ip4_config_reset_searches(self);
|
|
}
|
|
for (i = 0; i < nnameservers; i++) {
|
|
guint32 ip;
|
|
|
|
if (inet_pton(AF_INET, nm_setting_ip_config_get_dns(setting, i), &ip) == 1)
|
|
nm_ip4_config_add_nameserver(self, ip);
|
|
}
|
|
for (i = 0; i < nsearches; i++)
|
|
nm_ip4_config_add_search(self, nm_setting_ip_config_get_dns_search(setting, i));
|
|
|
|
i = 0;
|
|
while ((i = nm_setting_ip_config_next_valid_dns_option(setting, i)) >= 0) {
|
|
nm_ip4_config_add_dns_option(self, nm_setting_ip_config_get_dns_option(setting, i));
|
|
i++;
|
|
}
|
|
|
|
priority = nm_setting_ip_config_get_dns_priority(setting);
|
|
if (priority)
|
|
nm_ip4_config_set_dns_priority(self, priority);
|
|
|
|
nm_ip4_config_mdns_set(self, mdns);
|
|
nm_ip4_config_llmnr_set(self, llmnr);
|
|
|
|
nm_ip4_config_set_never_default(self, nm_setting_ip_config_get_never_default(setting));
|
|
|
|
g_object_thaw_notify(G_OBJECT(self));
|
|
}
|
|
|
|
NMSetting *
|
|
nm_ip4_config_create_setting(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate * priv;
|
|
NMSettingIPConfig * s_ip4;
|
|
guint nnameservers, nsearches, noptions;
|
|
const char * method = NULL;
|
|
int i;
|
|
NMDedupMultiIter ipconf_iter;
|
|
const NMPlatformIP4Address *address;
|
|
const NMPlatformIP4Route * route;
|
|
char sbuf[NM_UTILS_INET_ADDRSTRLEN];
|
|
|
|
s_ip4 = NM_SETTING_IP_CONFIG(nm_setting_ip4_config_new());
|
|
|
|
if (!self) {
|
|
g_object_set(s_ip4,
|
|
NM_SETTING_IP_CONFIG_METHOD,
|
|
NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
|
|
NULL);
|
|
return NM_SETTING(s_ip4);
|
|
}
|
|
|
|
priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
nnameservers = nm_ip4_config_get_num_nameservers(self);
|
|
nsearches = nm_ip4_config_get_num_searches(self);
|
|
noptions = nm_ip4_config_get_num_dns_options(self);
|
|
|
|
/* Addresses */
|
|
nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) {
|
|
NMIPAddress *s_addr;
|
|
|
|
/* Detect dynamic address */
|
|
if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
|
|
method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
|
|
continue;
|
|
}
|
|
|
|
/* Static address found. */
|
|
if (!method)
|
|
method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
|
|
|
|
s_addr = nm_ip_address_new_binary(AF_INET, &address->address, address->plen, NULL);
|
|
if (*address->label)
|
|
nm_ip_address_set_attribute(s_addr,
|
|
NM_IP_ADDRESS_ATTRIBUTE_LABEL,
|
|
g_variant_new_string(address->label));
|
|
|
|
nm_setting_ip_config_add_address(s_ip4, s_addr);
|
|
nm_ip_address_unref(s_addr);
|
|
}
|
|
|
|
/* Gateway */
|
|
if (priv->best_default_route && nm_setting_ip_config_get_num_addresses(s_ip4) > 0) {
|
|
g_object_set(
|
|
s_ip4,
|
|
NM_SETTING_IP_CONFIG_GATEWAY,
|
|
_nm_utils_inet4_ntop(NMP_OBJECT_CAST_IP4_ROUTE(priv->best_default_route)->gateway,
|
|
sbuf),
|
|
NULL);
|
|
}
|
|
|
|
/* Use 'disabled' if the method wasn't previously set */
|
|
if (!method)
|
|
method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
|
|
|
|
g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, NULL);
|
|
|
|
/* Routes */
|
|
nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) {
|
|
NMIPRoute *s_route;
|
|
|
|
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route))
|
|
continue;
|
|
|
|
/* Ignore routes provided by external sources */
|
|
if (route->rt_source
|
|
!= nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER))
|
|
continue;
|
|
|
|
s_route = nm_ip_route_new_binary(AF_INET,
|
|
&route->network,
|
|
route->plen,
|
|
&route->gateway,
|
|
route->metric,
|
|
NULL);
|
|
nm_setting_ip_config_add_route(s_ip4, s_route);
|
|
nm_ip_route_unref(s_route);
|
|
}
|
|
|
|
/* DNS */
|
|
for (i = 0; i < nnameservers; i++) {
|
|
guint32 nameserver = nm_ip4_config_get_nameserver(self, i);
|
|
|
|
nm_setting_ip_config_add_dns(s_ip4, _nm_utils_inet4_ntop(nameserver, sbuf));
|
|
}
|
|
for (i = 0; i < nsearches; i++) {
|
|
const char *search = nm_ip4_config_get_search(self, i);
|
|
|
|
nm_setting_ip_config_add_dns_search(s_ip4, search);
|
|
}
|
|
|
|
for (i = 0; i < noptions; i++) {
|
|
const char *option = nm_ip4_config_get_dns_option(self, i);
|
|
|
|
nm_setting_ip_config_add_dns_option(s_ip4, option);
|
|
}
|
|
|
|
g_object_set(s_ip4,
|
|
NM_SETTING_IP_CONFIG_DNS_PRIORITY,
|
|
nm_ip4_config_get_dns_priority(self),
|
|
NULL);
|
|
|
|
return NM_SETTING(s_ip4);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_merge(NMIP4Config * dst,
|
|
const NMIP4Config * src,
|
|
NMIPConfigMergeFlags merge_flags,
|
|
guint32 default_route_metric_penalty)
|
|
{
|
|
NMIP4ConfigPrivate * dst_priv;
|
|
const NMIP4ConfigPrivate * src_priv;
|
|
guint32 i;
|
|
NMDedupMultiIter ipconf_iter;
|
|
const NMPlatformIP4Address *address = NULL;
|
|
|
|
g_return_if_fail(src != NULL);
|
|
g_return_if_fail(dst != NULL);
|
|
|
|
dst_priv = NM_IP4_CONFIG_GET_PRIVATE(dst);
|
|
src_priv = NM_IP4_CONFIG_GET_PRIVATE(src);
|
|
|
|
g_object_freeze_notify(G_OBJECT(dst));
|
|
|
|
/* addresses */
|
|
nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &address) {
|
|
if (NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_EXTERNAL) && !address->external) {
|
|
NMPlatformIP4Address a;
|
|
|
|
a = *address;
|
|
a.external = TRUE;
|
|
_add_address(dst, NULL, &a);
|
|
} else
|
|
_add_address(dst, NMP_OBJECT_UP_CAST(address), NULL);
|
|
}
|
|
|
|
/* nameservers */
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
|
|
for (i = 0; i < nm_ip4_config_get_num_nameservers(src); i++)
|
|
nm_ip4_config_add_nameserver(dst, nm_ip4_config_get_nameserver(src, i));
|
|
}
|
|
|
|
/* routes */
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) {
|
|
const NMPlatformIP4Route *r_src;
|
|
|
|
nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r_src) {
|
|
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r_src)) {
|
|
if (NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES)
|
|
&& !NM_FLAGS_HAS(src_priv->config_flags,
|
|
NM_IP_CONFIG_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES))
|
|
continue;
|
|
if (default_route_metric_penalty) {
|
|
NMPlatformIP4Route r = *r_src;
|
|
|
|
r.metric =
|
|
nm_utils_ip_route_metric_penalize(r.metric, default_route_metric_penalty);
|
|
_add_route(dst, NULL, &r, NULL);
|
|
continue;
|
|
}
|
|
}
|
|
_add_route(dst, ipconf_iter.current->obj, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
/* domains */
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
|
|
for (i = 0; i < nm_ip4_config_get_num_domains(src); i++)
|
|
nm_ip4_config_add_domain(dst, nm_ip4_config_get_domain(src, i));
|
|
}
|
|
|
|
/* dns searches */
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
|
|
for (i = 0; i < nm_ip4_config_get_num_searches(src); i++)
|
|
nm_ip4_config_add_search(dst, nm_ip4_config_get_search(src, i));
|
|
}
|
|
|
|
/* dns options */
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
|
|
for (i = 0; i < nm_ip4_config_get_num_dns_options(src); i++)
|
|
nm_ip4_config_add_dns_option(dst, nm_ip4_config_get_dns_option(src, i));
|
|
}
|
|
|
|
/* MTU */
|
|
if (src_priv->mtu_source > dst_priv->mtu_source
|
|
|| (src_priv->mtu_source == dst_priv->mtu_source
|
|
&& ((!dst_priv->mtu && src_priv->mtu)
|
|
|| (dst_priv->mtu && src_priv->mtu < dst_priv->mtu))))
|
|
nm_ip4_config_set_mtu(dst, src_priv->mtu, src_priv->mtu_source);
|
|
|
|
/* NIS */
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
|
|
for (i = 0; i < nm_ip4_config_get_num_nis_servers(src); i++)
|
|
nm_ip4_config_add_nis_server(dst, nm_ip4_config_get_nis_server(src, i));
|
|
|
|
if (nm_ip4_config_get_nis_domain(src))
|
|
nm_ip4_config_set_nis_domain(dst, nm_ip4_config_get_nis_domain(src));
|
|
}
|
|
|
|
/* WINS */
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
|
|
for (i = 0; i < nm_ip4_config_get_num_wins(src); i++)
|
|
nm_ip4_config_add_wins(dst, nm_ip4_config_get_wins(src, i));
|
|
}
|
|
|
|
/* metered flag */
|
|
nm_ip4_config_set_metered(dst,
|
|
nm_ip4_config_get_metered(dst) || nm_ip4_config_get_metered(src));
|
|
|
|
/* never default */
|
|
nm_ip4_config_set_never_default(dst,
|
|
nm_ip4_config_get_never_default(dst)
|
|
|| nm_ip4_config_get_never_default(src));
|
|
|
|
/* DNS priority */
|
|
if (nm_ip4_config_get_dns_priority(src))
|
|
nm_ip4_config_set_dns_priority(dst, nm_ip4_config_get_dns_priority(src));
|
|
|
|
/* mdns */
|
|
nm_ip4_config_mdns_set(dst, NM_MAX(nm_ip4_config_mdns_get(src), nm_ip4_config_mdns_get(dst)));
|
|
/* LLMNR */
|
|
nm_ip4_config_llmnr_set(dst,
|
|
NM_MAX(nm_ip4_config_llmnr_get(src), nm_ip4_config_llmnr_get(dst)));
|
|
|
|
g_object_thaw_notify(G_OBJECT(dst));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static int
|
|
_nameservers_get_index(const NMIP4Config *self, guint32 ns)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->nameservers->len; i++) {
|
|
guint32 n = g_array_index(priv->nameservers, guint32, i);
|
|
|
|
if (ns == n)
|
|
return (int) i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
_domains_get_index(const NMIP4Config *self, const char *domain)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->domains->len; i++) {
|
|
const char *d = g_ptr_array_index(priv->domains, i);
|
|
|
|
if (g_strcmp0(domain, d) == 0)
|
|
return (int) i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
_searches_get_index(const NMIP4Config *self, const char *search)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->searches->len; i++) {
|
|
const char *s = g_ptr_array_index(priv->searches, i);
|
|
|
|
if (g_strcmp0(search, s) == 0)
|
|
return (int) i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
_dns_options_get_index(const NMIP4Config *self, const char *option)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->dns_options->len; i++) {
|
|
const char *s = g_ptr_array_index(priv->dns_options, i);
|
|
|
|
if (g_strcmp0(option, s) == 0)
|
|
return (int) i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
_nis_servers_get_index(const NMIP4Config *self, guint32 nis_server)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->nis->len; i++) {
|
|
guint32 n = g_array_index(priv->nis, guint32, i);
|
|
|
|
if (n == nis_server)
|
|
return (int) i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
_wins_get_index(const NMIP4Config *self, guint32 wins_server)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->wins->len; i++) {
|
|
guint32 n = g_array_index(priv->wins, guint32, i);
|
|
|
|
if (n == wins_server)
|
|
return (int) i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_ip4_config_subtract:
|
|
* @dst: config from which to remove everything in @src
|
|
* @src: config to remove from @dst
|
|
* @default_route_metric_penalty: pretend that on source we applied
|
|
* a route penalty on the default-route. It means, for default routes
|
|
* we don't remove routes that match exactly, but those with a lower
|
|
* metric (with the penalty removed).
|
|
*
|
|
* Removes everything in @src from @dst.
|
|
*/
|
|
void
|
|
nm_ip4_config_subtract(NMIP4Config * dst,
|
|
const NMIP4Config *src,
|
|
guint32 default_route_metric_penalty)
|
|
{
|
|
NMIP4ConfigPrivate * dst_priv;
|
|
guint i;
|
|
int idx;
|
|
const NMPlatformIP4Address *a;
|
|
const NMPlatformIP4Route * r;
|
|
NMDedupMultiIter ipconf_iter;
|
|
gboolean changed;
|
|
gboolean changed_default_route;
|
|
|
|
g_return_if_fail(src != NULL);
|
|
g_return_if_fail(dst != NULL);
|
|
|
|
dst_priv = NM_IP4_CONFIG_GET_PRIVATE(dst);
|
|
|
|
g_object_freeze_notify(G_OBJECT(dst));
|
|
|
|
/* addresses */
|
|
changed = FALSE;
|
|
nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &a) {
|
|
if (nm_dedup_multi_index_remove_obj(dst_priv->multi_idx,
|
|
&dst_priv->idx_ip4_addresses,
|
|
NMP_OBJECT_UP_CAST(a),
|
|
NULL))
|
|
changed = TRUE;
|
|
}
|
|
if (changed)
|
|
_notify_addresses(dst);
|
|
|
|
/* nameservers */
|
|
for (i = 0; i < nm_ip4_config_get_num_nameservers(src); i++) {
|
|
idx = _nameservers_get_index(dst, nm_ip4_config_get_nameserver(src, i));
|
|
if (idx >= 0)
|
|
nm_ip4_config_del_nameserver(dst, idx);
|
|
}
|
|
|
|
/* routes */
|
|
changed = FALSE;
|
|
changed_default_route = FALSE;
|
|
nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r) {
|
|
const NMPObject * o_src = NMP_OBJECT_UP_CAST(r);
|
|
NMPObject o_lookup_copy;
|
|
const NMPObject * o_lookup;
|
|
nm_auto_nmpobj const NMPObject *obj_old = NULL;
|
|
|
|
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r) && default_route_metric_penalty) {
|
|
NMPlatformIP4Route *rr;
|
|
|
|
/* the default route was penalized when merging it to the combined ip-config.
|
|
* When subtracting the routes, we must re-do that process when comparing
|
|
* the routes. */
|
|
o_lookup = nmp_object_stackinit_obj(&o_lookup_copy, o_src);
|
|
rr = NMP_OBJECT_CAST_IP4_ROUTE(&o_lookup_copy);
|
|
rr->metric =
|
|
nm_utils_ip_route_metric_penalize(rr->metric, default_route_metric_penalty);
|
|
} else
|
|
o_lookup = o_src;
|
|
|
|
if (nm_dedup_multi_index_remove_obj(dst_priv->multi_idx,
|
|
&dst_priv->idx_ip4_routes,
|
|
o_lookup,
|
|
(gconstpointer *) &obj_old)) {
|
|
if (dst_priv->best_default_route == obj_old) {
|
|
nm_clear_nmp_object(&dst_priv->best_default_route);
|
|
changed_default_route = TRUE;
|
|
}
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
if (changed_default_route) {
|
|
nmp_object_ref_set(&dst_priv->best_default_route,
|
|
_nm_ip4_config_best_default_route_find(dst));
|
|
_notify(dst, PROP_GATEWAY);
|
|
}
|
|
if (changed)
|
|
_notify_routes(dst);
|
|
|
|
/* domains */
|
|
for (i = 0; i < nm_ip4_config_get_num_domains(src); i++) {
|
|
idx = _domains_get_index(dst, nm_ip4_config_get_domain(src, i));
|
|
if (idx >= 0)
|
|
nm_ip4_config_del_domain(dst, idx);
|
|
}
|
|
|
|
/* dns searches */
|
|
for (i = 0; i < nm_ip4_config_get_num_searches(src); i++) {
|
|
idx = _searches_get_index(dst, nm_ip4_config_get_search(src, i));
|
|
if (idx >= 0)
|
|
nm_ip4_config_del_search(dst, idx);
|
|
}
|
|
|
|
/* dns options */
|
|
for (i = 0; i < nm_ip4_config_get_num_dns_options(src); i++) {
|
|
idx = _dns_options_get_index(dst, nm_ip4_config_get_dns_option(src, i));
|
|
if (idx >= 0)
|
|
nm_ip4_config_del_dns_option(dst, idx);
|
|
}
|
|
|
|
/* MTU */
|
|
if (nm_ip4_config_get_mtu(src) == nm_ip4_config_get_mtu(dst)
|
|
&& nm_ip4_config_get_mtu_source(src) == nm_ip4_config_get_mtu_source(dst))
|
|
nm_ip4_config_set_mtu(dst, 0, NM_IP_CONFIG_SOURCE_UNKNOWN);
|
|
|
|
/* NIS */
|
|
for (i = 0; i < nm_ip4_config_get_num_nis_servers(src); i++) {
|
|
idx = _nis_servers_get_index(dst, nm_ip4_config_get_nis_server(src, i));
|
|
if (idx >= 0)
|
|
nm_ip4_config_del_nis_server(dst, idx);
|
|
}
|
|
|
|
if (g_strcmp0(nm_ip4_config_get_nis_domain(src), nm_ip4_config_get_nis_domain(dst)) == 0)
|
|
nm_ip4_config_set_nis_domain(dst, NULL);
|
|
|
|
/* WINS */
|
|
for (i = 0; i < nm_ip4_config_get_num_wins(src); i++) {
|
|
idx = _wins_get_index(dst, nm_ip4_config_get_wins(src, i));
|
|
if (idx >= 0)
|
|
nm_ip4_config_del_wins(dst, idx);
|
|
}
|
|
|
|
/* DNS priority */
|
|
if (nm_ip4_config_get_dns_priority(src) == nm_ip4_config_get_dns_priority(dst))
|
|
nm_ip4_config_set_dns_priority(dst, 0);
|
|
|
|
/* mdns */
|
|
if (nm_ip4_config_mdns_get(src) == nm_ip4_config_mdns_get(dst))
|
|
nm_ip4_config_mdns_set(dst, NM_SETTING_CONNECTION_MDNS_DEFAULT);
|
|
|
|
/* LLMNR */
|
|
if (nm_ip4_config_llmnr_get(src) == nm_ip4_config_llmnr_get(dst))
|
|
nm_ip4_config_llmnr_set(dst, NM_SETTING_CONNECTION_LLMNR_DEFAULT);
|
|
|
|
g_object_thaw_notify(G_OBJECT(dst));
|
|
}
|
|
|
|
static gboolean
|
|
_nm_ip4_config_intersect_helper(NMIP4Config * dst,
|
|
const NMIP4Config *src,
|
|
gboolean intersect_addresses,
|
|
gboolean intersect_routes,
|
|
guint32 default_route_metric_penalty,
|
|
gboolean update_dst)
|
|
{
|
|
NMIP4ConfigPrivate * dst_priv;
|
|
const NMIP4ConfigPrivate * src_priv;
|
|
NMDedupMultiIter ipconf_iter;
|
|
const NMPlatformIP4Address *a;
|
|
const NMPlatformIP4Route * r;
|
|
const NMPObject * new_best_default_route;
|
|
gboolean changed, result = FALSE;
|
|
|
|
g_return_val_if_fail(src, FALSE);
|
|
g_return_val_if_fail(dst, FALSE);
|
|
|
|
dst_priv = NM_IP4_CONFIG_GET_PRIVATE(dst);
|
|
src_priv = NM_IP4_CONFIG_GET_PRIVATE(src);
|
|
|
|
if (update_dst)
|
|
g_object_freeze_notify(G_OBJECT(dst));
|
|
|
|
/* addresses */
|
|
if (intersect_addresses) {
|
|
changed = FALSE;
|
|
nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, dst, &a) {
|
|
if (nm_dedup_multi_index_lookup_obj(src_priv->multi_idx,
|
|
&src_priv->idx_ip4_addresses,
|
|
NMP_OBJECT_UP_CAST(a)))
|
|
continue;
|
|
|
|
if (!update_dst)
|
|
return TRUE;
|
|
|
|
if (nm_dedup_multi_index_remove_entry(dst_priv->multi_idx, ipconf_iter.current) != 1)
|
|
nm_assert_not_reached();
|
|
changed = TRUE;
|
|
}
|
|
if (changed) {
|
|
_notify_addresses(dst);
|
|
result = TRUE;
|
|
}
|
|
}
|
|
|
|
/* ignore nameservers */
|
|
|
|
/* routes */
|
|
if (!intersect_routes)
|
|
goto skip_routes;
|
|
|
|
changed = FALSE;
|
|
new_best_default_route = NULL;
|
|
nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, dst, &r) {
|
|
const NMPObject *o_dst = NMP_OBJECT_UP_CAST(r);
|
|
const NMPObject *o_lookup;
|
|
NMPObject o_lookup_copy;
|
|
|
|
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r) && default_route_metric_penalty) {
|
|
NMPlatformIP4Route *rr;
|
|
|
|
/* the default route was penalized when merging it to the combined ip-config.
|
|
* When intersecting the routes, we must re-do that process when comparing
|
|
* the routes. */
|
|
o_lookup = nmp_object_stackinit_obj(&o_lookup_copy, o_dst);
|
|
rr = NMP_OBJECT_CAST_IP4_ROUTE(&o_lookup_copy);
|
|
rr->metric =
|
|
nm_utils_ip_route_metric_penalize(rr->metric, default_route_metric_penalty);
|
|
} else
|
|
o_lookup = o_dst;
|
|
|
|
if (nm_dedup_multi_index_lookup_obj(src_priv->multi_idx,
|
|
&src_priv->idx_ip4_routes,
|
|
o_lookup)) {
|
|
new_best_default_route =
|
|
_nm_ip_config_best_default_route_find_better(new_best_default_route, o_dst);
|
|
continue;
|
|
}
|
|
|
|
if (!update_dst)
|
|
return TRUE;
|
|
|
|
if (nm_dedup_multi_index_remove_entry(dst_priv->multi_idx, ipconf_iter.current) != 1)
|
|
nm_assert_not_reached();
|
|
changed = TRUE;
|
|
}
|
|
if (nmp_object_ref_set(&dst_priv->best_default_route, new_best_default_route)) {
|
|
nm_assert(changed);
|
|
_notify(dst, PROP_GATEWAY);
|
|
}
|
|
|
|
if (changed) {
|
|
_notify_routes(dst);
|
|
result = TRUE;
|
|
}
|
|
|
|
skip_routes:
|
|
/* ignore domains */
|
|
/* ignore dns searches */
|
|
/* ignore dns options */
|
|
/* ignore NIS */
|
|
/* ignore WINS */
|
|
/* ignore mdns */
|
|
/* ignore LLMNR */
|
|
|
|
if (update_dst)
|
|
g_object_thaw_notify(G_OBJECT(dst));
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* nm_ip4_config_intersect:
|
|
* @dst: a configuration to be updated
|
|
* @src: another configuration
|
|
* @intersect_addresses: whether addresses should be intersected
|
|
* @intersect_routes: whether routes should be intersected
|
|
* @default_route_metric_penalty: the default route metric penalty
|
|
*
|
|
* Computes the intersection between @src and @dst and updates @dst in place
|
|
* with the result.
|
|
*/
|
|
void
|
|
nm_ip4_config_intersect(NMIP4Config * dst,
|
|
const NMIP4Config *src,
|
|
gboolean intersect_addresses,
|
|
gboolean intersect_routes,
|
|
guint32 default_route_metric_penalty)
|
|
{
|
|
_nm_ip4_config_intersect_helper(dst,
|
|
src,
|
|
intersect_addresses,
|
|
intersect_routes,
|
|
default_route_metric_penalty,
|
|
TRUE);
|
|
}
|
|
|
|
/**
|
|
* nm_ip4_config_intersect_alloc:
|
|
* @a: a configuration
|
|
* @b: another configuration
|
|
* @intersect_addresses: whether addresses should be intersected
|
|
* @intersect_routes: whether routes should be intersected
|
|
* @default_route_metric_penalty: the default route metric penalty
|
|
*
|
|
* Computes the intersection between @a and @b and returns the result in a newly
|
|
* allocated configuration. As a special case, if @a and @b are identical (with
|
|
* respect to the only properties considered - addresses and routes) the
|
|
* functions returns NULL so that one of existing configuration can be reused
|
|
* without allocation.
|
|
*
|
|
* Returns: the intersection between @a and @b, or %NULL if the result is equal
|
|
* to @a and @b.
|
|
*/
|
|
NMIP4Config *
|
|
nm_ip4_config_intersect_alloc(const NMIP4Config *a,
|
|
const NMIP4Config *b,
|
|
gboolean intersect_addresses,
|
|
gboolean intersect_routes,
|
|
guint32 default_route_metric_penalty)
|
|
{
|
|
NMIP4Config *a_copy;
|
|
|
|
if (_nm_ip4_config_intersect_helper((NMIP4Config *) a,
|
|
b,
|
|
intersect_addresses,
|
|
intersect_routes,
|
|
default_route_metric_penalty,
|
|
FALSE)) {
|
|
a_copy = nm_ip4_config_clone(a);
|
|
_nm_ip4_config_intersect_helper(a_copy,
|
|
b,
|
|
intersect_addresses,
|
|
intersect_routes,
|
|
default_route_metric_penalty,
|
|
TRUE);
|
|
return a_copy;
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* nm_ip4_config_replace:
|
|
* @dst: config to replace with @src content
|
|
* @src: source config to copy
|
|
* @relevant_changes: return whether there are changes to the
|
|
* destination object that are relevant. This is equal to
|
|
* nm_ip4_config_equal() showing any difference.
|
|
*
|
|
* Replaces everything in @dst with @src so that the two configurations
|
|
* contain the same content -- with the exception of the dbus path.
|
|
*
|
|
* Returns: whether the @dst instance changed in any way (including minor changes,
|
|
* that are not signaled by the output parameter @relevant_changes).
|
|
*/
|
|
gboolean
|
|
nm_ip4_config_replace(NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes)
|
|
{
|
|
#if NM_MORE_ASSERTS
|
|
gboolean config_equal;
|
|
#endif
|
|
gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal;
|
|
guint i, num;
|
|
NMIP4ConfigPrivate * dst_priv;
|
|
const NMIP4ConfigPrivate * src_priv;
|
|
NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst;
|
|
const NMDedupMultiHeadEntry *head_entry_src;
|
|
const NMPObject * new_best_default_route;
|
|
|
|
g_return_val_if_fail(src != NULL, FALSE);
|
|
g_return_val_if_fail(dst != NULL, FALSE);
|
|
g_return_val_if_fail(src != dst, FALSE);
|
|
|
|
#if NM_MORE_ASSERTS
|
|
config_equal = nm_ip4_config_equal(dst, src);
|
|
#endif
|
|
|
|
dst_priv = NM_IP4_CONFIG_GET_PRIVATE(dst);
|
|
src_priv = NM_IP4_CONFIG_GET_PRIVATE(src);
|
|
|
|
g_object_freeze_notify(G_OBJECT(dst));
|
|
|
|
/* ifindex */
|
|
if (src_priv->ifindex != dst_priv->ifindex) {
|
|
dst_priv->ifindex = src_priv->ifindex;
|
|
has_minor_changes = TRUE;
|
|
}
|
|
|
|
/* addresses */
|
|
head_entry_src = nm_ip4_config_lookup_addresses(src);
|
|
nm_dedup_multi_iter_init(&ipconf_iter_src, head_entry_src);
|
|
nm_ip_config_iter_ip4_address_init(&ipconf_iter_dst, dst);
|
|
are_equal = TRUE;
|
|
while (TRUE) {
|
|
gboolean has;
|
|
const NMPlatformIP4Address *r_src = NULL;
|
|
const NMPlatformIP4Address *r_dst = NULL;
|
|
|
|
has = nm_platform_dedup_multi_iter_next_ip4_address(&ipconf_iter_src, &r_src);
|
|
if (has != nm_platform_dedup_multi_iter_next_ip4_address(&ipconf_iter_dst, &r_dst)) {
|
|
are_equal = FALSE;
|
|
has_relevant_changes = TRUE;
|
|
break;
|
|
}
|
|
if (!has)
|
|
break;
|
|
|
|
if (nm_platform_ip4_address_cmp(r_src, r_dst) != 0) {
|
|
are_equal = FALSE;
|
|
if (r_src->address != r_dst->address || r_src->plen != r_dst->plen
|
|
|| r_src->peer_address != r_dst->peer_address) {
|
|
has_relevant_changes = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!are_equal) {
|
|
has_minor_changes = TRUE;
|
|
nm_dedup_multi_index_dirty_set_idx(dst_priv->multi_idx, &dst_priv->idx_ip4_addresses);
|
|
nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) {
|
|
_nm_ip_config_add_obj(dst_priv->multi_idx,
|
|
&dst_priv->idx_ip4_addresses_,
|
|
dst_priv->ifindex,
|
|
ipconf_iter_src.current->obj,
|
|
NULL,
|
|
FALSE,
|
|
TRUE,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
nm_dedup_multi_index_dirty_remove_idx(dst_priv->multi_idx,
|
|
&dst_priv->idx_ip4_addresses,
|
|
FALSE);
|
|
_notify_addresses(dst);
|
|
}
|
|
|
|
/* routes */
|
|
head_entry_src = nm_ip4_config_lookup_routes(src);
|
|
nm_dedup_multi_iter_init(&ipconf_iter_src, head_entry_src);
|
|
nm_ip_config_iter_ip4_route_init(&ipconf_iter_dst, dst);
|
|
are_equal = TRUE;
|
|
while (TRUE) {
|
|
gboolean has;
|
|
const NMPlatformIP4Route *r_src = NULL;
|
|
const NMPlatformIP4Route *r_dst = NULL;
|
|
|
|
has = nm_platform_dedup_multi_iter_next_ip4_route(&ipconf_iter_src, &r_src);
|
|
if (has != nm_platform_dedup_multi_iter_next_ip4_route(&ipconf_iter_dst, &r_dst)) {
|
|
are_equal = FALSE;
|
|
has_relevant_changes = TRUE;
|
|
break;
|
|
}
|
|
if (!has)
|
|
break;
|
|
|
|
if (nm_platform_ip4_route_cmp_full(r_src, r_dst) != 0) {
|
|
are_equal = FALSE;
|
|
if (r_src->plen != r_dst->plen
|
|
|| !nm_utils_ip4_address_same_prefix(r_src->network, r_dst->network, r_src->plen)
|
|
|| r_src->gateway != r_dst->gateway || r_src->metric != r_dst->metric) {
|
|
has_relevant_changes = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!are_equal) {
|
|
has_minor_changes = TRUE;
|
|
new_best_default_route = NULL;
|
|
nm_dedup_multi_index_dirty_set_idx(dst_priv->multi_idx, &dst_priv->idx_ip4_routes);
|
|
nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) {
|
|
const NMPObject *o = ipconf_iter_src.current->obj;
|
|
const NMPObject *obj_new;
|
|
|
|
_nm_ip_config_add_obj(dst_priv->multi_idx,
|
|
&dst_priv->idx_ip4_routes_,
|
|
dst_priv->ifindex,
|
|
o,
|
|
NULL,
|
|
FALSE,
|
|
TRUE,
|
|
NULL,
|
|
&obj_new);
|
|
new_best_default_route =
|
|
_nm_ip_config_best_default_route_find_better(new_best_default_route, obj_new);
|
|
}
|
|
nm_dedup_multi_index_dirty_remove_idx(dst_priv->multi_idx,
|
|
&dst_priv->idx_ip4_routes,
|
|
FALSE);
|
|
if (nmp_object_ref_set(&dst_priv->best_default_route, new_best_default_route))
|
|
_notify(dst, PROP_GATEWAY);
|
|
_notify_routes(dst);
|
|
}
|
|
|
|
/* nameservers */
|
|
num = nm_ip4_config_get_num_nameservers(src);
|
|
are_equal = num == nm_ip4_config_get_num_nameservers(dst);
|
|
if (are_equal) {
|
|
for (i = 0; i < num; i++) {
|
|
if (nm_ip4_config_get_nameserver(src, i) != nm_ip4_config_get_nameserver(dst, i)) {
|
|
are_equal = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!are_equal) {
|
|
nm_ip4_config_reset_nameservers(dst);
|
|
for (i = 0; i < num; i++)
|
|
nm_ip4_config_add_nameserver(dst, nm_ip4_config_get_nameserver(src, i));
|
|
has_relevant_changes = TRUE;
|
|
}
|
|
|
|
/* domains */
|
|
num = nm_ip4_config_get_num_domains(src);
|
|
are_equal = num == nm_ip4_config_get_num_domains(dst);
|
|
if (are_equal) {
|
|
for (i = 0; i < num; i++) {
|
|
if (g_strcmp0(nm_ip4_config_get_domain(src, i), nm_ip4_config_get_domain(dst, i))) {
|
|
are_equal = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!are_equal) {
|
|
nm_ip4_config_reset_domains(dst);
|
|
for (i = 0; i < num; i++)
|
|
nm_ip4_config_add_domain(dst, nm_ip4_config_get_domain(src, i));
|
|
has_relevant_changes = TRUE;
|
|
}
|
|
|
|
/* dns searches */
|
|
num = nm_ip4_config_get_num_searches(src);
|
|
are_equal = num == nm_ip4_config_get_num_searches(dst);
|
|
if (are_equal) {
|
|
for (i = 0; i < num; i++) {
|
|
if (g_strcmp0(nm_ip4_config_get_search(src, i), nm_ip4_config_get_search(dst, i))) {
|
|
are_equal = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!are_equal) {
|
|
nm_ip4_config_reset_searches(dst);
|
|
for (i = 0; i < num; i++)
|
|
nm_ip4_config_add_search(dst, nm_ip4_config_get_search(src, i));
|
|
has_relevant_changes = TRUE;
|
|
}
|
|
|
|
/* dns options */
|
|
num = nm_ip4_config_get_num_dns_options(src);
|
|
are_equal = num == nm_ip4_config_get_num_dns_options(dst);
|
|
if (are_equal) {
|
|
for (i = 0; i < num; i++) {
|
|
if (g_strcmp0(nm_ip4_config_get_dns_option(src, i),
|
|
nm_ip4_config_get_dns_option(dst, i))) {
|
|
are_equal = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!are_equal) {
|
|
nm_ip4_config_reset_dns_options(dst);
|
|
for (i = 0; i < num; i++)
|
|
nm_ip4_config_add_dns_option(dst, nm_ip4_config_get_dns_option(src, i));
|
|
has_relevant_changes = TRUE;
|
|
}
|
|
|
|
if (src_priv->mdns != dst_priv->mdns) {
|
|
dst_priv->mdns = src_priv->mdns;
|
|
has_relevant_changes = TRUE;
|
|
}
|
|
|
|
if (src_priv->llmnr != dst_priv->llmnr) {
|
|
dst_priv->llmnr = src_priv->llmnr;
|
|
has_relevant_changes = TRUE;
|
|
}
|
|
|
|
/* DNS priority */
|
|
if (src_priv->dns_priority != dst_priv->dns_priority) {
|
|
nm_ip4_config_set_dns_priority(dst, src_priv->dns_priority);
|
|
has_minor_changes = TRUE;
|
|
}
|
|
|
|
/* nis */
|
|
num = nm_ip4_config_get_num_nis_servers(src);
|
|
are_equal = num == nm_ip4_config_get_num_nis_servers(dst);
|
|
if (are_equal) {
|
|
for (i = 0; i < num; i++) {
|
|
if (nm_ip4_config_get_nis_server(src, i) != nm_ip4_config_get_nis_server(dst, i)) {
|
|
are_equal = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!are_equal) {
|
|
nm_ip4_config_reset_nis_servers(dst);
|
|
for (i = 0; i < num; i++)
|
|
nm_ip4_config_add_nis_server(dst, nm_ip4_config_get_nis_server(src, i));
|
|
has_relevant_changes = TRUE;
|
|
}
|
|
|
|
/* nis_domain */
|
|
if (g_strcmp0(src_priv->nis_domain, dst_priv->nis_domain)) {
|
|
nm_ip4_config_set_nis_domain(dst, src_priv->nis_domain);
|
|
has_relevant_changes = TRUE;
|
|
}
|
|
|
|
/* wins */
|
|
num = nm_ip4_config_get_num_wins(src);
|
|
are_equal = num == nm_ip4_config_get_num_wins(dst);
|
|
if (are_equal) {
|
|
for (i = 0; i < num; i++) {
|
|
if (nm_ip4_config_get_wins(src, i) != nm_ip4_config_get_wins(dst, i)) {
|
|
are_equal = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!are_equal) {
|
|
nm_ip4_config_reset_wins(dst);
|
|
for (i = 0; i < num; i++)
|
|
nm_ip4_config_add_wins(dst, nm_ip4_config_get_wins(src, i));
|
|
has_relevant_changes = TRUE;
|
|
}
|
|
|
|
/* mtu */
|
|
if (src_priv->mtu != dst_priv->mtu || src_priv->mtu_source != dst_priv->mtu_source) {
|
|
nm_ip4_config_set_mtu(dst, src_priv->mtu, src_priv->mtu_source);
|
|
has_minor_changes = TRUE;
|
|
}
|
|
|
|
/* metered */
|
|
if (src_priv->metered != dst_priv->metered) {
|
|
dst_priv->metered = src_priv->metered;
|
|
has_minor_changes = TRUE;
|
|
}
|
|
|
|
/* never default */
|
|
if (src_priv->never_default != dst_priv->never_default) {
|
|
dst_priv->never_default = src_priv->never_default;
|
|
has_minor_changes = TRUE;
|
|
}
|
|
|
|
#if NM_MORE_ASSERTS
|
|
/* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes
|
|
* regardless of config_equal. But config_equal must correspond to has_relevant_changes. */
|
|
nm_assert(config_equal == !has_relevant_changes);
|
|
#endif
|
|
|
|
g_object_thaw_notify(G_OBJECT(dst));
|
|
|
|
if (relevant_changes)
|
|
*relevant_changes = has_relevant_changes;
|
|
|
|
return has_relevant_changes || has_minor_changes;
|
|
}
|
|
|
|
void
|
|
nm_ip_config_dump(const NMIPConfig *self, const char *detail, NMLogLevel level, NMLogDomain domain)
|
|
{
|
|
NMDedupMultiIter ipconf_iter;
|
|
const NMPlatformIP4Address *addr4;
|
|
const NMPlatformIP6Address *addr6;
|
|
const NMPlatformIP4Route * route4;
|
|
const NMPlatformIP6Route * route6;
|
|
const NMIP4Config * ip4;
|
|
const NMIP6Config * ip6;
|
|
int addr_family = AF_UNSPEC;
|
|
char addr_family_char = '?';
|
|
const char * path;
|
|
gconstpointer ptr;
|
|
guint i;
|
|
|
|
if (self) {
|
|
addr_family = nm_ip_config_get_addr_family(self);
|
|
addr_family_char = nm_utils_addr_family_to_char(addr_family);
|
|
}
|
|
|
|
nm_log(level, domain, NULL, NULL, "---- NMIP%cConfig %p (%s)", addr_family_char, self, detail);
|
|
|
|
if (!self)
|
|
return;
|
|
|
|
path = nm_dbus_object_get_path(NM_DBUS_OBJECT(self));
|
|
if (path)
|
|
nm_log(level, domain, NULL, NULL, " path : %s", path);
|
|
|
|
if (addr_family == AF_INET) {
|
|
ip4 = NM_IP4_CONFIG(self);
|
|
nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &addr4) {
|
|
nm_log(level,
|
|
domain,
|
|
NULL,
|
|
NULL,
|
|
" address : %s",
|
|
nm_platform_ip4_address_to_string(addr4, NULL, 0));
|
|
}
|
|
nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route4) {
|
|
nm_log(level,
|
|
domain,
|
|
NULL,
|
|
NULL,
|
|
" route : %s",
|
|
nm_platform_ip4_route_to_string(route4, NULL, 0));
|
|
}
|
|
} else {
|
|
ip6 = NM_IP6_CONFIG(self);
|
|
nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &addr6) {
|
|
nm_log(level,
|
|
domain,
|
|
NULL,
|
|
NULL,
|
|
" address : %s",
|
|
nm_platform_ip6_address_to_string(addr6, NULL, 0));
|
|
}
|
|
nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route6) {
|
|
nm_log(level,
|
|
domain,
|
|
NULL,
|
|
NULL,
|
|
" route : %s",
|
|
nm_platform_ip6_route_to_string(route6, NULL, 0));
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < nm_ip_config_get_num_nameservers(self); i++) {
|
|
char buf[NM_UTILS_INET_ADDRSTRLEN];
|
|
|
|
ptr = nm_ip_config_get_nameserver(self, i);
|
|
nm_log(level,
|
|
domain,
|
|
NULL,
|
|
NULL,
|
|
" dns : %s",
|
|
nm_utils_inet_ntop(addr_family, ptr, buf));
|
|
}
|
|
|
|
for (i = 0; i < nm_ip_config_get_num_domains(self); i++)
|
|
nm_log(level, domain, NULL, NULL, " domain : %s", nm_ip_config_get_domain(self, i));
|
|
|
|
for (i = 0; i < nm_ip_config_get_num_searches(self); i++)
|
|
nm_log(level, domain, NULL, NULL, " search : %s", nm_ip_config_get_search(self, i));
|
|
|
|
for (i = 0; i < nm_ip_config_get_num_dns_options(self); i++)
|
|
nm_log(level, domain, NULL, NULL, "dns-option: %s", nm_ip_config_get_dns_option(self, i));
|
|
|
|
nm_log(level, domain, NULL, NULL, " dns-prio : %d", nm_ip_config_get_dns_priority(self));
|
|
|
|
if (addr_family == AF_INET) {
|
|
ip4 = NM_IP4_CONFIG(self);
|
|
nm_log(level,
|
|
domain,
|
|
NULL,
|
|
NULL,
|
|
" mtu : %" G_GUINT32_FORMAT " (source: %d)",
|
|
nm_ip4_config_get_mtu(ip4),
|
|
(int) nm_ip4_config_get_mtu_source(ip4));
|
|
nm_log(level, domain, NULL, NULL, " metered : %d", (int) nm_ip4_config_get_metered(ip4));
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gconstpointer
|
|
nm_ip_config_find_first_address(const NMIPConfig *self, NMPlatformMatchFlags match_flag)
|
|
{
|
|
NMDedupMultiIter iter;
|
|
const NMPlatformIPAddress *address;
|
|
|
|
g_return_val_if_fail(NM_IS_IP_CONFIG(self), NULL);
|
|
|
|
nm_assert(!NM_FLAGS_ANY(
|
|
match_flag,
|
|
~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)));
|
|
|
|
nm_ip_config_iter_ip_address_for_each (&iter, self, &address) {
|
|
if (nm_platform_ip_address_match(nm_ip_config_get_addr_family(self), address, match_flag))
|
|
return address;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_reset_addresses(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip4_addresses) > 0)
|
|
_notify_addresses(self);
|
|
}
|
|
|
|
static void
|
|
_add_address(NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (_nm_ip_config_add_obj(priv->multi_idx,
|
|
&priv->idx_ip4_addresses_,
|
|
priv->ifindex,
|
|
obj_new,
|
|
(const NMPlatformObject *) new,
|
|
TRUE,
|
|
FALSE,
|
|
NULL,
|
|
NULL))
|
|
_notify_addresses(self);
|
|
}
|
|
|
|
/**
|
|
* nm_ip4_config_add_address:
|
|
* @self: the #NMIP4Config
|
|
* @new: the new address to add to @self
|
|
*
|
|
* Adds the new address to @self. If an address with the same basic properties
|
|
* (address, prefix) already exists in @self, it is overwritten with the
|
|
* lifetime and preferred of @new. The source is also overwritten by the source
|
|
* from @new if that source is higher priority.
|
|
*/
|
|
void
|
|
nm_ip4_config_add_address(NMIP4Config *self, const NMPlatformIP4Address *new)
|
|
{
|
|
g_return_if_fail(self);
|
|
g_return_if_fail(new);
|
|
g_return_if_fail(new->plen <= 32);
|
|
g_return_if_fail(NM_IP4_CONFIG_GET_PRIVATE(self)->ifindex > 0);
|
|
|
|
_add_address(self, NULL, new);
|
|
}
|
|
|
|
void
|
|
_nmtst_ip4_config_del_address(NMIP4Config *self, guint i)
|
|
{
|
|
const NMPlatformIP4Address *a;
|
|
|
|
a = _nmtst_ip4_config_get_address(self, i);
|
|
if (!nm_ip4_config_nmpobj_remove(self, NMP_OBJECT_UP_CAST(a)))
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
guint
|
|
nm_ip4_config_get_num_addresses(const NMIP4Config *self)
|
|
{
|
|
const NMDedupMultiHeadEntry *head_entry;
|
|
|
|
head_entry = nm_ip4_config_lookup_addresses(self);
|
|
return head_entry ? head_entry->len : 0;
|
|
}
|
|
|
|
const NMPlatformIP4Address *
|
|
nm_ip4_config_get_first_address(const NMIP4Config *self)
|
|
{
|
|
NMDedupMultiIter iter;
|
|
const NMPlatformIP4Address *a = NULL;
|
|
|
|
nm_ip_config_iter_ip4_address_for_each (&iter, self, &a)
|
|
return a;
|
|
return NULL;
|
|
}
|
|
|
|
const NMPlatformIP4Address *
|
|
_nmtst_ip4_config_get_address(const NMIP4Config *self, guint i)
|
|
{
|
|
NMDedupMultiIter iter = {};
|
|
const NMPlatformIP4Address *a = NULL;
|
|
guint j;
|
|
|
|
j = 0;
|
|
nm_ip_config_iter_ip4_address_for_each (&iter, self, &a) {
|
|
if (i == j)
|
|
return a;
|
|
j++;
|
|
}
|
|
g_return_val_if_reached(NULL);
|
|
}
|
|
|
|
gboolean
|
|
nm_ip4_config_address_exists(const NMIP4Config *self, const NMPlatformIP4Address *needle)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
NMPObject obj_stack;
|
|
|
|
nmp_object_stackinit_id_ip4_address(&obj_stack,
|
|
priv->ifindex,
|
|
needle->address,
|
|
needle->plen,
|
|
needle->peer_address);
|
|
return !!nm_dedup_multi_index_lookup_obj(priv->multi_idx, &priv->idx_ip4_addresses, &obj_stack);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const NMDedupMultiEntry *
|
|
_lookup_route(const NMIP4Config *self, const NMPObject *needle, NMPlatformIPRouteCmpType cmp_type)
|
|
{
|
|
const NMIP4ConfigPrivate *priv;
|
|
|
|
nm_assert(NM_IS_IP4_CONFIG(self));
|
|
nm_assert(NMP_OBJECT_GET_TYPE(needle) == NMP_OBJECT_TYPE_IP4_ROUTE);
|
|
|
|
priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return _nm_ip_config_lookup_ip_route(priv->multi_idx, &priv->idx_ip4_routes_, needle, cmp_type);
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_reset_routes(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip4_routes) > 0) {
|
|
if (nm_clear_nmp_object(&priv->best_default_route))
|
|
_notify(self, PROP_GATEWAY);
|
|
_notify_routes(self);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_add_route(NMIP4Config * self,
|
|
const NMPObject *obj_new,
|
|
const NMPlatformIP4Route *new,
|
|
const NMPObject **out_obj_new)
|
|
{
|
|
NMIP4ConfigPrivate * priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
nm_auto_nmpobj const NMPObject *obj_old = NULL;
|
|
const NMPObject * obj_new_2;
|
|
|
|
nm_assert((!new) != (!obj_new));
|
|
nm_assert(!new || _route_valid(new));
|
|
nm_assert(!obj_new || _route_valid(NMP_OBJECT_CAST_IP4_ROUTE(obj_new)));
|
|
|
|
if (_nm_ip_config_add_obj(priv->multi_idx,
|
|
&priv->idx_ip4_routes_,
|
|
priv->ifindex,
|
|
obj_new,
|
|
(const NMPlatformObject *) new,
|
|
TRUE,
|
|
FALSE,
|
|
&obj_old,
|
|
&obj_new_2)) {
|
|
gboolean changed_default_route = FALSE;
|
|
|
|
if (priv->best_default_route == obj_old && obj_old != obj_new_2) {
|
|
changed_default_route = TRUE;
|
|
nm_clear_nmp_object(&priv->best_default_route);
|
|
}
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(obj_new_2));
|
|
if (_nm_ip_config_best_default_route_merge(&priv->best_default_route, obj_new_2))
|
|
changed_default_route = TRUE;
|
|
|
|
if (changed_default_route)
|
|
_notify(self, PROP_GATEWAY);
|
|
_notify_routes(self);
|
|
} else
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(obj_new_2));
|
|
}
|
|
|
|
/**
|
|
* nm_ip4_config_add_route:
|
|
* @self: the #NMIP4Config
|
|
* @new: the new route to add to @self
|
|
* @out_obj_new: (allow-none) (out): the added route object. Must be unrefed
|
|
* by caller.
|
|
*
|
|
* Adds the new route to @self. If a route with the same basic properties
|
|
* (network, prefix) already exists in @self, it is overwritten including the
|
|
* gateway and metric of @new. The source is also overwritten by the source
|
|
* from @new if that source is higher priority.
|
|
*/
|
|
void
|
|
nm_ip4_config_add_route(NMIP4Config *self,
|
|
const NMPlatformIP4Route *new,
|
|
const NMPObject **out_obj_new)
|
|
{
|
|
g_return_if_fail(self);
|
|
g_return_if_fail(new);
|
|
g_return_if_fail(new->plen <= 32);
|
|
g_return_if_fail(NM_IP4_CONFIG_GET_PRIVATE(self)->ifindex > 0);
|
|
|
|
_add_route(self, NULL, new, out_obj_new);
|
|
}
|
|
|
|
void
|
|
_nmtst_ip4_config_del_route(NMIP4Config *self, guint i)
|
|
{
|
|
const NMPlatformIP4Route *r;
|
|
|
|
r = _nmtst_ip4_config_get_route(self, i);
|
|
if (!nm_ip4_config_nmpobj_remove(self, NMP_OBJECT_UP_CAST(r)))
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
guint
|
|
nm_ip4_config_get_num_routes(const NMIP4Config *self)
|
|
{
|
|
const NMDedupMultiHeadEntry *head_entry;
|
|
|
|
head_entry = nm_ip4_config_lookup_routes(self);
|
|
nm_assert(!head_entry || head_entry->len == c_list_length(&head_entry->lst_entries_head));
|
|
return head_entry ? head_entry->len : 0;
|
|
}
|
|
|
|
const NMPlatformIP4Route *
|
|
_nmtst_ip4_config_get_route(const NMIP4Config *self, guint i)
|
|
{
|
|
NMDedupMultiIter iter;
|
|
const NMPlatformIP4Route *r = NULL;
|
|
guint j;
|
|
|
|
j = 0;
|
|
nm_ip_config_iter_ip4_route_for_each (&iter, self, &r) {
|
|
if (i == j)
|
|
return r;
|
|
j++;
|
|
}
|
|
g_return_val_if_reached(NULL);
|
|
}
|
|
|
|
const NMPlatformIP4Route *
|
|
nm_ip4_config_get_direct_route_for_host(const NMIP4Config *self,
|
|
in_addr_t host,
|
|
guint32 route_table)
|
|
{
|
|
const NMPlatformIP4Route *best_route = NULL;
|
|
const NMPlatformIP4Route *item;
|
|
NMDedupMultiIter ipconf_iter;
|
|
|
|
g_return_val_if_fail(host, NULL);
|
|
|
|
nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &item) {
|
|
if (item->gateway != 0)
|
|
continue;
|
|
|
|
if (best_route && best_route->plen > item->plen)
|
|
continue;
|
|
|
|
if (nm_platform_route_table_uncoerce(item->table_coerced, TRUE) != route_table)
|
|
continue;
|
|
|
|
if (nm_utils_ip4_address_clear_host_address(host, item->plen)
|
|
!= nm_utils_ip4_address_clear_host_address(item->network, item->plen))
|
|
continue;
|
|
|
|
if (best_route && best_route->metric <= item->metric)
|
|
continue;
|
|
|
|
best_route = item;
|
|
}
|
|
return best_route;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_reset_nameservers(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (priv->nameservers->len != 0) {
|
|
g_array_set_size(priv->nameservers, 0);
|
|
nm_gobject_notify_together(self, PROP_NAMESERVER_DATA, PROP_NAMESERVERS);
|
|
}
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_add_nameserver(NMIP4Config *self, guint32 new)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
int i;
|
|
|
|
g_return_if_fail(new != 0);
|
|
|
|
for (i = 0; i < priv->nameservers->len; i++)
|
|
if (new == g_array_index(priv->nameservers, guint32, i))
|
|
return;
|
|
|
|
g_array_append_val(priv->nameservers, new);
|
|
nm_gobject_notify_together(self, PROP_NAMESERVER_DATA, PROP_NAMESERVERS);
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_del_nameserver(NMIP4Config *self, guint i)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
g_return_if_fail(i < priv->nameservers->len);
|
|
|
|
g_array_remove_index(priv->nameservers, i);
|
|
nm_gobject_notify_together(self, PROP_NAMESERVER_DATA, PROP_NAMESERVERS);
|
|
}
|
|
|
|
guint
|
|
nm_ip4_config_get_num_nameservers(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->nameservers->len;
|
|
}
|
|
|
|
guint32
|
|
nm_ip4_config_get_nameserver(const NMIP4Config *self, guint i)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return g_array_index(priv->nameservers, guint32, i);
|
|
}
|
|
|
|
const in_addr_t *
|
|
_nm_ip4_config_get_nameserver(const NMIP4Config *self, guint i)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return &g_array_index(priv->nameservers, guint32, i);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
_nm_ip_config_check_and_add_domain(GPtrArray *array, const char *domain)
|
|
{
|
|
char * copy = NULL;
|
|
size_t len;
|
|
|
|
g_return_val_if_fail(domain, FALSE);
|
|
g_return_val_if_fail(domain[0] != '\0', FALSE);
|
|
|
|
if (domain[0] == '.' || strstr(domain, ".."))
|
|
return FALSE;
|
|
|
|
len = strlen(domain);
|
|
if (domain[len - 1] == '.')
|
|
domain = copy = g_strndup(domain, len - 1);
|
|
|
|
if (nm_strv_ptrarray_find_first(array, domain) >= 0) {
|
|
g_free(copy);
|
|
return FALSE;
|
|
}
|
|
|
|
g_ptr_array_add(array, copy ?: g_strdup(domain));
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_reset_domains(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (priv->domains->len != 0) {
|
|
g_ptr_array_set_size(priv->domains, 0);
|
|
_notify(self, PROP_DOMAINS);
|
|
}
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_add_domain(NMIP4Config *self, const char *domain)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (_nm_ip_config_check_and_add_domain(priv->domains, domain))
|
|
_notify(self, PROP_DOMAINS);
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_del_domain(NMIP4Config *self, guint i)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
g_return_if_fail(i < priv->domains->len);
|
|
|
|
g_ptr_array_remove_index(priv->domains, i);
|
|
_notify(self, PROP_DOMAINS);
|
|
}
|
|
|
|
guint
|
|
nm_ip4_config_get_num_domains(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->domains->len;
|
|
}
|
|
|
|
const char *
|
|
nm_ip4_config_get_domain(const NMIP4Config *self, guint i)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return g_ptr_array_index(priv->domains, i);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_reset_searches(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (priv->searches->len != 0) {
|
|
g_ptr_array_set_size(priv->searches, 0);
|
|
_notify(self, PROP_SEARCHES);
|
|
}
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_add_search(NMIP4Config *self, const char *search)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (_nm_ip_config_check_and_add_domain(priv->searches, search))
|
|
_notify(self, PROP_SEARCHES);
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_del_search(NMIP4Config *self, guint i)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
g_return_if_fail(i < priv->searches->len);
|
|
|
|
g_ptr_array_remove_index(priv->searches, i);
|
|
_notify(self, PROP_SEARCHES);
|
|
}
|
|
|
|
guint
|
|
nm_ip4_config_get_num_searches(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->searches->len;
|
|
}
|
|
|
|
const char *
|
|
nm_ip4_config_get_search(const NMIP4Config *self, guint i)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return g_ptr_array_index(priv->searches, i);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_reset_dns_options(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (priv->dns_options->len != 0) {
|
|
g_ptr_array_set_size(priv->dns_options, 0);
|
|
_notify(self, PROP_DNS_OPTIONS);
|
|
}
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_add_dns_option(NMIP4Config *self, const char *new)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
int i;
|
|
|
|
g_return_if_fail(new != NULL);
|
|
g_return_if_fail(new[0] != '\0');
|
|
|
|
for (i = 0; i < priv->dns_options->len; i++)
|
|
if (!g_strcmp0(g_ptr_array_index(priv->dns_options, i), new))
|
|
return;
|
|
|
|
g_ptr_array_add(priv->dns_options, g_strdup(new));
|
|
_notify(self, PROP_DNS_OPTIONS);
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_del_dns_option(NMIP4Config *self, guint i)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
g_return_if_fail(i < priv->dns_options->len);
|
|
|
|
g_ptr_array_remove_index(priv->dns_options, i);
|
|
_notify(self, PROP_DNS_OPTIONS);
|
|
}
|
|
|
|
guint
|
|
nm_ip4_config_get_num_dns_options(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->dns_options->len;
|
|
}
|
|
|
|
const char *
|
|
nm_ip4_config_get_dns_option(const NMIP4Config *self, guint i)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return g_ptr_array_index(priv->dns_options, i);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMSettingConnectionMdns
|
|
nm_ip4_config_mdns_get(const NMIP4Config *self)
|
|
{
|
|
return NM_IP4_CONFIG_GET_PRIVATE(self)->mdns;
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_mdns_set(NMIP4Config *self, NMSettingConnectionMdns mdns)
|
|
{
|
|
NM_IP4_CONFIG_GET_PRIVATE(self)->mdns = mdns;
|
|
}
|
|
|
|
NMSettingConnectionLlmnr
|
|
nm_ip4_config_llmnr_get(const NMIP4Config *self)
|
|
{
|
|
return NM_IP4_CONFIG_GET_PRIVATE(self)->llmnr;
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_llmnr_set(NMIP4Config *self, NMSettingConnectionLlmnr llmnr)
|
|
{
|
|
NM_IP4_CONFIG_GET_PRIVATE(self)->llmnr = llmnr;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMIPConfigFlags
|
|
nm_ip4_config_get_config_flags(const NMIP4Config *self)
|
|
{
|
|
return NM_IP4_CONFIG_GET_PRIVATE(self)->config_flags;
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_set_config_flags(NMIP4Config *self, NMIPConfigFlags flags, NMIPConfigFlags mask)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (mask == 0) {
|
|
/* for convenience, accept 0 mask to set any flags. */
|
|
mask = flags;
|
|
}
|
|
|
|
nm_assert(!NM_FLAGS_ANY(flags, ~mask));
|
|
priv->config_flags = (flags & mask) | (priv->config_flags & ~mask);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_set_dns_priority(NMIP4Config *self, int priority)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (priority != priv->dns_priority) {
|
|
priv->dns_priority = priority;
|
|
_notify(self, PROP_DNS_PRIORITY);
|
|
}
|
|
}
|
|
|
|
int
|
|
nm_ip4_config_get_dns_priority(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->dns_priority;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_reset_nis_servers(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
g_array_set_size(priv->nis, 0);
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_add_nis_server(NMIP4Config *self, guint32 nis)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
int i;
|
|
|
|
for (i = 0; i < priv->nis->len; i++)
|
|
if (nis == g_array_index(priv->nis, guint32, i))
|
|
return;
|
|
|
|
g_array_append_val(priv->nis, nis);
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_del_nis_server(NMIP4Config *self, guint i)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
g_return_if_fail(i < priv->nis->len);
|
|
|
|
g_array_remove_index(priv->nis, i);
|
|
}
|
|
|
|
guint
|
|
nm_ip4_config_get_num_nis_servers(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->nis->len;
|
|
}
|
|
|
|
guint32
|
|
nm_ip4_config_get_nis_server(const NMIP4Config *self, guint i)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return g_array_index(priv->nis, guint32, i);
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_set_nis_domain(NMIP4Config *self, const char *domain)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
g_free(priv->nis_domain);
|
|
priv->nis_domain = g_strdup(domain);
|
|
}
|
|
|
|
const char *
|
|
nm_ip4_config_get_nis_domain(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->nis_domain;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_reset_wins(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (priv->wins->len != 0) {
|
|
g_array_set_size(priv->wins, 0);
|
|
nm_gobject_notify_together(self, PROP_WINS_SERVER_DATA, PROP_WINS_SERVERS);
|
|
}
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_add_wins(NMIP4Config *self, guint32 wins)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
int i;
|
|
|
|
g_return_if_fail(wins != 0);
|
|
|
|
for (i = 0; i < priv->wins->len; i++)
|
|
if (wins == g_array_index(priv->wins, guint32, i))
|
|
return;
|
|
|
|
g_array_append_val(priv->wins, wins);
|
|
nm_gobject_notify_together(self, PROP_WINS_SERVER_DATA, PROP_WINS_SERVERS);
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_del_wins(NMIP4Config *self, guint i)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
g_return_if_fail(i < priv->wins->len);
|
|
|
|
g_array_remove_index(priv->wins, i);
|
|
nm_gobject_notify_together(self, PROP_WINS_SERVER_DATA, PROP_WINS_SERVERS);
|
|
}
|
|
|
|
guint
|
|
nm_ip4_config_get_num_wins(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->wins->len;
|
|
}
|
|
|
|
guint32
|
|
nm_ip4_config_get_wins(const NMIP4Config *self, guint i)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return g_array_index(priv->wins, guint32, i);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_set_mtu(NMIP4Config *self, guint32 mtu, NMIPConfigSource source)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
if (!mtu)
|
|
source = NM_IP_CONFIG_SOURCE_UNKNOWN;
|
|
|
|
priv->mtu = mtu;
|
|
priv->mtu_source = source;
|
|
}
|
|
|
|
guint32
|
|
nm_ip4_config_get_mtu(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->mtu;
|
|
}
|
|
|
|
NMIPConfigSource
|
|
nm_ip4_config_get_mtu_source(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->mtu_source;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_set_metered(NMIP4Config *self, gboolean metered)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
priv->metered = metered;
|
|
}
|
|
|
|
gboolean
|
|
nm_ip4_config_get_metered(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->metered;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_ip4_config_set_never_default(NMIP4Config *self, gboolean never_default)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
priv->never_default = never_default;
|
|
}
|
|
|
|
gboolean
|
|
nm_ip4_config_get_never_default(const NMIP4Config *self)
|
|
{
|
|
const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
return priv->never_default;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const NMPObject *
|
|
nm_ip4_config_nmpobj_lookup(const NMIP4Config *self, const NMPObject *needle)
|
|
{
|
|
const NMIP4ConfigPrivate * priv;
|
|
const NMDedupMultiIdxType *idx_type;
|
|
|
|
g_return_val_if_fail(NM_IS_IP4_CONFIG(self), NULL);
|
|
|
|
priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
switch (NMP_OBJECT_GET_TYPE(needle)) {
|
|
case NMP_OBJECT_TYPE_IP4_ADDRESS:
|
|
idx_type = &priv->idx_ip4_addresses;
|
|
break;
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
idx_type = &priv->idx_ip4_routes;
|
|
break;
|
|
default:
|
|
g_return_val_if_reached(NULL);
|
|
}
|
|
|
|
return nm_dedup_multi_entry_get_obj(
|
|
nm_dedup_multi_index_lookup_obj(priv->multi_idx, idx_type, needle));
|
|
}
|
|
|
|
gboolean
|
|
nm_ip4_config_nmpobj_remove(NMIP4Config *self, const NMPObject *needle)
|
|
{
|
|
NMIP4ConfigPrivate * priv;
|
|
NMDedupMultiIdxType *idx_type;
|
|
nm_auto_nmpobj const NMPObject *obj_old = NULL;
|
|
guint n;
|
|
|
|
g_return_val_if_fail(NM_IS_IP4_CONFIG(self), FALSE);
|
|
|
|
priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
switch (NMP_OBJECT_GET_TYPE(needle)) {
|
|
case NMP_OBJECT_TYPE_IP4_ADDRESS:
|
|
idx_type = &priv->idx_ip4_addresses;
|
|
break;
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
idx_type = &priv->idx_ip4_routes;
|
|
break;
|
|
default:
|
|
g_return_val_if_reached(FALSE);
|
|
}
|
|
|
|
n = nm_dedup_multi_index_remove_obj(priv->multi_idx,
|
|
idx_type,
|
|
needle,
|
|
(gconstpointer *) &obj_old);
|
|
if (n != 1) {
|
|
nm_assert(n == 0);
|
|
return FALSE;
|
|
}
|
|
|
|
nm_assert(NMP_OBJECT_GET_TYPE(obj_old) == NMP_OBJECT_GET_TYPE(needle));
|
|
|
|
switch (NMP_OBJECT_GET_TYPE(obj_old)) {
|
|
case NMP_OBJECT_TYPE_IP4_ADDRESS:
|
|
_notify_addresses(self);
|
|
break;
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
if (priv->best_default_route == obj_old) {
|
|
if (nmp_object_ref_set(&priv->best_default_route,
|
|
_nm_ip4_config_best_default_route_find(self)))
|
|
_notify(self, PROP_GATEWAY);
|
|
}
|
|
_notify_routes(self);
|
|
break;
|
|
default:
|
|
nm_assert_not_reached();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
hash_u32(GChecksum *sum, guint32 n)
|
|
{
|
|
g_checksum_update(sum, (const guint8 *) &n, sizeof(n));
|
|
}
|
|
|
|
void
|
|
nm_ip4_config_hash(const NMIP4Config *self, GChecksum *sum, gboolean dns_only)
|
|
{
|
|
guint i;
|
|
const char * s;
|
|
NMDedupMultiIter ipconf_iter;
|
|
const NMPlatformIP4Address *address;
|
|
const NMPlatformIP4Route * route;
|
|
int val;
|
|
|
|
g_return_if_fail(self);
|
|
g_return_if_fail(sum);
|
|
|
|
if (!dns_only) {
|
|
nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) {
|
|
hash_u32(sum, address->address);
|
|
hash_u32(sum, address->plen);
|
|
hash_u32(sum, address->peer_address & _nm_utils_ip4_prefix_to_netmask(address->plen));
|
|
}
|
|
|
|
nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) {
|
|
hash_u32(sum, route->network);
|
|
hash_u32(sum, route->plen);
|
|
hash_u32(sum, route->gateway);
|
|
hash_u32(sum, route->metric);
|
|
}
|
|
|
|
for (i = 0; i < nm_ip4_config_get_num_nis_servers(self); i++)
|
|
hash_u32(sum, nm_ip4_config_get_nis_server(self, i));
|
|
|
|
s = nm_ip4_config_get_nis_domain(self);
|
|
if (s)
|
|
g_checksum_update(sum, (const guint8 *) s, strlen(s));
|
|
}
|
|
|
|
for (i = 0; i < nm_ip4_config_get_num_nameservers(self); i++)
|
|
hash_u32(sum, nm_ip4_config_get_nameserver(self, i));
|
|
|
|
for (i = 0; i < nm_ip4_config_get_num_wins(self); i++)
|
|
hash_u32(sum, nm_ip4_config_get_wins(self, i));
|
|
|
|
for (i = 0; i < nm_ip4_config_get_num_domains(self); i++) {
|
|
s = nm_ip4_config_get_domain(self, i);
|
|
g_checksum_update(sum, (const guint8 *) s, strlen(s));
|
|
}
|
|
|
|
for (i = 0; i < nm_ip4_config_get_num_searches(self); i++) {
|
|
s = nm_ip4_config_get_search(self, i);
|
|
g_checksum_update(sum, (const guint8 *) s, strlen(s));
|
|
}
|
|
|
|
for (i = 0; i < nm_ip4_config_get_num_dns_options(self); i++) {
|
|
s = nm_ip4_config_get_dns_option(self, i);
|
|
g_checksum_update(sum, (const guint8 *) s, strlen(s));
|
|
}
|
|
|
|
val = nm_ip4_config_mdns_get(self);
|
|
if (val != NM_SETTING_CONNECTION_MDNS_DEFAULT)
|
|
g_checksum_update(sum, (const guint8 *) &val, sizeof(val));
|
|
|
|
val = nm_ip4_config_llmnr_get(self);
|
|
if (val != NM_SETTING_CONNECTION_LLMNR_DEFAULT)
|
|
g_checksum_update(sum, (const guint8 *) &val, sizeof(val));
|
|
|
|
/* FIXME(ip-config-checksum): the DNS priority should be considered relevant
|
|
* and added into the checksum as well, but this can't be done right now
|
|
* because in the DNS manager we rely on the fact that an empty
|
|
* configuration (i.e. just created) has a zero checksum. This is needed to
|
|
* avoid rewriting resolv.conf when there is no change.
|
|
*
|
|
* The DNS priority initial value depends on the connection type (VPN or
|
|
* not), so it's a bit difficult to add it to checksum maintaining the
|
|
* assumption of checksum(empty)=0
|
|
*/
|
|
}
|
|
|
|
/**
|
|
* nm_ip4_config_equal:
|
|
* @a: first config to compare
|
|
* @b: second config to compare
|
|
*
|
|
* Compares two #NMIP4Configs for basic equality. This means that all
|
|
* attributes must exist in the same order in both configs (addresses, routes,
|
|
* domains, DNS servers, etc) but some attributes (address lifetimes, and address
|
|
* and route sources) are ignored.
|
|
*
|
|
* Returns: %TRUE if the configurations are basically equal to each other,
|
|
* %FALSE if not
|
|
*/
|
|
gboolean
|
|
nm_ip4_config_equal(const NMIP4Config *a, const NMIP4Config *b)
|
|
{
|
|
nm_auto_free_checksum GChecksum *a_checksum = g_checksum_new(G_CHECKSUM_SHA1);
|
|
nm_auto_free_checksum GChecksum *b_checksum = g_checksum_new(G_CHECKSUM_SHA1);
|
|
guint8 a_data[NM_UTILS_CHECKSUM_LENGTH_SHA1];
|
|
guint8 b_data[NM_UTILS_CHECKSUM_LENGTH_SHA1];
|
|
|
|
if (a)
|
|
nm_ip4_config_hash(a, a_checksum, FALSE);
|
|
if (b)
|
|
nm_ip4_config_hash(b, b_checksum, FALSE);
|
|
|
|
nm_utils_checksum_get_digest(a_checksum, a_data);
|
|
nm_utils_checksum_get_digest(b_checksum, b_data);
|
|
return !memcmp(a_data, b_data, sizeof(a_data));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMIP4Config * self = NM_IP4_CONFIG(object);
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
char addr_str[NM_UTILS_INET_ADDRSTRLEN];
|
|
GVariantBuilder builder_data;
|
|
guint i;
|
|
|
|
switch (prop_id) {
|
|
case PROP_IFINDEX:
|
|
g_value_set_int(value, priv->ifindex);
|
|
break;
|
|
case PROP_ADDRESS_DATA:
|
|
case PROP_ADDRESSES:
|
|
nm_assert(!!priv->address_data_variant == !!priv->addresses_variant);
|
|
|
|
if (!priv->address_data_variant) {
|
|
nm_utils_ip_addresses_to_dbus(AF_INET,
|
|
nm_ip4_config_lookup_addresses(self),
|
|
priv->best_default_route,
|
|
NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
|
|
&priv->address_data_variant,
|
|
&priv->addresses_variant);
|
|
g_variant_ref_sink(priv->address_data_variant);
|
|
g_variant_ref_sink(priv->addresses_variant);
|
|
}
|
|
|
|
g_value_set_variant(value,
|
|
prop_id == PROP_ADDRESS_DATA ? priv->address_data_variant
|
|
: priv->addresses_variant);
|
|
break;
|
|
case PROP_ROUTE_DATA:
|
|
case PROP_ROUTES:
|
|
nm_assert(!!priv->route_data_variant == !!priv->routes_variant);
|
|
|
|
if (!priv->route_data_variant) {
|
|
nm_utils_ip_routes_to_dbus(AF_INET,
|
|
nm_ip4_config_lookup_routes(self),
|
|
&priv->route_data_variant,
|
|
&priv->routes_variant);
|
|
g_variant_ref_sink(priv->route_data_variant);
|
|
g_variant_ref_sink(priv->routes_variant);
|
|
}
|
|
|
|
g_value_set_variant(value,
|
|
prop_id == PROP_ROUTE_DATA ? priv->route_data_variant
|
|
: priv->routes_variant);
|
|
break;
|
|
case PROP_GATEWAY:
|
|
if (priv->best_default_route) {
|
|
g_value_take_string(value,
|
|
nm_utils_inet4_ntop_dup(
|
|
NMP_OBJECT_CAST_IP4_ROUTE(priv->best_default_route)->gateway));
|
|
} else
|
|
g_value_set_string(value, NULL);
|
|
break;
|
|
case PROP_NAMESERVER_DATA:
|
|
g_variant_builder_init(&builder_data, G_VARIANT_TYPE("aa{sv}"));
|
|
|
|
for (i = 0; i < priv->nameservers->len; i++) {
|
|
GVariantBuilder nested_builder;
|
|
|
|
_nm_utils_inet4_ntop(g_array_index(priv->nameservers, in_addr_t, i), addr_str);
|
|
|
|
g_variant_builder_init(&nested_builder, G_VARIANT_TYPE("a{sv}"));
|
|
g_variant_builder_add(&nested_builder,
|
|
"{sv}",
|
|
"address",
|
|
g_variant_new_string(addr_str));
|
|
g_variant_builder_add(&builder_data, "a{sv}", &nested_builder);
|
|
}
|
|
|
|
g_value_take_variant(value, g_variant_builder_end(&builder_data));
|
|
break;
|
|
case PROP_NAMESERVERS:
|
|
g_value_take_variant(
|
|
value,
|
|
nm_g_variant_new_au((const guint32 *) priv->nameservers->data, priv->nameservers->len));
|
|
break;
|
|
case PROP_DOMAINS:
|
|
nm_utils_g_value_set_strv(value, priv->domains);
|
|
break;
|
|
case PROP_SEARCHES:
|
|
nm_utils_g_value_set_strv(value, priv->searches);
|
|
break;
|
|
case PROP_DNS_OPTIONS:
|
|
nm_utils_g_value_set_strv(value, priv->dns_options);
|
|
break;
|
|
case PROP_DNS_PRIORITY:
|
|
g_value_set_int(value, priv->dns_priority);
|
|
break;
|
|
case PROP_WINS_SERVER_DATA:
|
|
g_variant_builder_init(&builder_data, G_VARIANT_TYPE("as"));
|
|
for (i = 0; i < priv->wins->len; i++) {
|
|
g_variant_builder_add(
|
|
&builder_data,
|
|
"s",
|
|
_nm_utils_inet4_ntop(g_array_index(priv->wins, in_addr_t, i), addr_str));
|
|
}
|
|
g_value_take_variant(value, g_variant_builder_end(&builder_data));
|
|
break;
|
|
case PROP_WINS_SERVERS:
|
|
g_value_take_variant(
|
|
value,
|
|
nm_g_variant_new_au((const guint32 *) priv->wins->data, priv->wins->len));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMIP4Config * self = NM_IP4_CONFIG(object);
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
switch (prop_id) {
|
|
case PROP_MULTI_IDX:
|
|
/* construct-only */
|
|
priv->multi_idx = g_value_get_pointer(value);
|
|
if (!priv->multi_idx)
|
|
g_return_if_reached();
|
|
nm_dedup_multi_index_ref(priv->multi_idx);
|
|
break;
|
|
case PROP_IFINDEX:
|
|
/* construct-only */
|
|
priv->ifindex = g_value_get_int(value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
nm_ip4_config_init(NMIP4Config *self)
|
|
{
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
nm_ip_config_dedup_multi_idx_type_init((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_addresses,
|
|
NMP_OBJECT_TYPE_IP4_ADDRESS);
|
|
nm_ip_config_dedup_multi_idx_type_init((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_routes,
|
|
NMP_OBJECT_TYPE_IP4_ROUTE);
|
|
|
|
priv->mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
|
|
priv->llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
|
|
priv->nameservers = g_array_new(FALSE, FALSE, sizeof(guint32));
|
|
priv->domains = g_ptr_array_new_with_free_func(g_free);
|
|
priv->searches = g_ptr_array_new_with_free_func(g_free);
|
|
priv->dns_options = g_ptr_array_new_with_free_func(g_free);
|
|
priv->nis = g_array_new(FALSE, TRUE, sizeof(guint32));
|
|
priv->wins = g_array_new(FALSE, TRUE, sizeof(guint32));
|
|
}
|
|
|
|
NMIP4Config *
|
|
nm_ip4_config_new(NMDedupMultiIndex *multi_idx, int ifindex)
|
|
{
|
|
g_return_val_if_fail(ifindex >= -1, NULL);
|
|
return g_object_new(NM_TYPE_IP4_CONFIG,
|
|
NM_IP4_CONFIG_MULTI_IDX,
|
|
multi_idx,
|
|
NM_IP4_CONFIG_IFINDEX,
|
|
ifindex,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
finalize(GObject *object)
|
|
{
|
|
NMIP4Config * self = NM_IP4_CONFIG(object);
|
|
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self);
|
|
|
|
nm_clear_nmp_object(&priv->best_default_route);
|
|
|
|
nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip4_addresses);
|
|
nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip4_routes);
|
|
|
|
nm_clear_g_variant(&priv->address_data_variant);
|
|
nm_clear_g_variant(&priv->addresses_variant);
|
|
nm_clear_g_variant(&priv->route_data_variant);
|
|
nm_clear_g_variant(&priv->routes_variant);
|
|
|
|
g_array_unref(priv->nameservers);
|
|
g_ptr_array_unref(priv->domains);
|
|
g_ptr_array_unref(priv->searches);
|
|
g_ptr_array_unref(priv->dns_options);
|
|
g_array_unref(priv->nis);
|
|
g_free(priv->nis_domain);
|
|
g_array_unref(priv->wins);
|
|
|
|
G_OBJECT_CLASS(nm_ip4_config_parent_class)->finalize(object);
|
|
|
|
nm_dedup_multi_index_unref(priv->multi_idx);
|
|
}
|
|
|
|
static const NMDBusInterfaceInfoExtended interface_info_ip4_config = {
|
|
.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
|
|
NM_DBUS_INTERFACE_IP4_CONFIG,
|
|
.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Addresses",
|
|
"aau",
|
|
NM_IP4_CONFIG_ADDRESSES),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData",
|
|
"aa{sv}",
|
|
NM_IP4_CONFIG_ADDRESS_DATA),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP4_CONFIG_GATEWAY),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Routes", "aau", NM_IP4_CONFIG_ROUTES),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RouteData",
|
|
"aa{sv}",
|
|
NM_IP4_CONFIG_ROUTE_DATA),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("NameserverData",
|
|
"aa{sv}",
|
|
NM_IP4_CONFIG_NAMESERVER_DATA),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Nameservers",
|
|
"au",
|
|
NM_IP4_CONFIG_NAMESERVERS),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Domains", "as", NM_IP4_CONFIG_DOMAINS),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Searches",
|
|
"as",
|
|
NM_IP4_CONFIG_SEARCHES),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsOptions",
|
|
"as",
|
|
NM_IP4_CONFIG_DNS_OPTIONS),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsPriority",
|
|
"i",
|
|
NM_IP4_CONFIG_DNS_PRIORITY),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WinsServerData",
|
|
"as",
|
|
NM_IP4_CONFIG_WINS_SERVER_DATA),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WinsServers",
|
|
"au",
|
|
NM_IP4_CONFIG_WINS_SERVERS), ), ),
|
|
};
|
|
|
|
static void
|
|
nm_ip4_config_class_init(NMIP4ConfigClass *klass)
|
|
{
|
|
GObjectClass * object_class = G_OBJECT_CLASS(klass);
|
|
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
|
|
NMIPConfigClass * ip_config_class = NM_IP_CONFIG_CLASS(klass);
|
|
|
|
ip_config_class->is_ipv4 = TRUE;
|
|
ip_config_class->addr_family = AF_INET;
|
|
|
|
dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED(NM_DBUS_PATH "/IP4Config");
|
|
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_ip4_config);
|
|
|
|
object_class->get_property = get_property;
|
|
object_class->set_property = set_property;
|
|
object_class->finalize = finalize;
|
|
|
|
obj_properties[PROP_MULTI_IDX] =
|
|
g_param_spec_pointer(NM_IP4_CONFIG_MULTI_IDX,
|
|
"",
|
|
"",
|
|
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_IFINDEX] =
|
|
g_param_spec_int(NM_IP4_CONFIG_IFINDEX,
|
|
"",
|
|
"",
|
|
-1,
|
|
G_MAXINT,
|
|
-1,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_ADDRESS_DATA] =
|
|
g_param_spec_variant(NM_IP4_CONFIG_ADDRESS_DATA,
|
|
"",
|
|
"",
|
|
G_VARIANT_TYPE("aa{sv}"),
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_ADDRESSES] =
|
|
g_param_spec_variant(NM_IP4_CONFIG_ADDRESSES,
|
|
"",
|
|
"",
|
|
G_VARIANT_TYPE("aau"),
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_ROUTE_DATA] =
|
|
g_param_spec_variant(NM_IP4_CONFIG_ROUTE_DATA,
|
|
"",
|
|
"",
|
|
G_VARIANT_TYPE("aa{sv}"),
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_ROUTES] = g_param_spec_variant(NM_IP4_CONFIG_ROUTES,
|
|
"",
|
|
"",
|
|
G_VARIANT_TYPE("aau"),
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_GATEWAY] = g_param_spec_string(NM_IP4_CONFIG_GATEWAY,
|
|
"",
|
|
"",
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_NAMESERVER_DATA] =
|
|
g_param_spec_variant(NM_IP4_CONFIG_NAMESERVER_DATA,
|
|
"",
|
|
"",
|
|
G_VARIANT_TYPE("aa{sv}"),
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_NAMESERVERS] =
|
|
g_param_spec_variant(NM_IP4_CONFIG_NAMESERVERS,
|
|
"",
|
|
"",
|
|
G_VARIANT_TYPE("au"),
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_DOMAINS] = g_param_spec_boxed(NM_IP4_CONFIG_DOMAINS,
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_SEARCHES] = g_param_spec_boxed(NM_IP4_CONFIG_SEARCHES,
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_DNS_OPTIONS] =
|
|
g_param_spec_boxed(NM_IP4_CONFIG_DNS_OPTIONS,
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_DNS_PRIORITY] = g_param_spec_int(NM_IP4_CONFIG_DNS_PRIORITY,
|
|
"",
|
|
"",
|
|
G_MININT32,
|
|
G_MAXINT32,
|
|
0,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_WINS_SERVER_DATA] =
|
|
g_param_spec_variant(NM_IP4_CONFIG_WINS_SERVER_DATA,
|
|
"",
|
|
"",
|
|
G_VARIANT_TYPE("as"),
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
obj_properties[PROP_WINS_SERVERS] =
|
|
g_param_spec_variant(NM_IP4_CONFIG_WINS_SERVERS,
|
|
"",
|
|
"",
|
|
G_VARIANT_TYPE("au"),
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
|
|
}
|