mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-10 01:10:22 +01:00
and rename to nm_utils_ip_route_attribute_to_platform(). The function is independent from NMIP4Config. We also will use it outside of NMIP4Config. Also, "NetworkManagerUtils.c" already has similar functions that parse libnm structures to internal structures.
3449 lines
107 KiB
C
3449 lines
107 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2005 - 2017 Red Hat, Inc.
|
|
* Copyright (C) 2006 - 2008 Novell, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "nm-ip4-config.h"
|
|
|
|
#include <arpa/inet.h>
|
|
#include <resolv.h>
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include "nm-glib-aux/nm-dedup-multi.h"
|
|
|
|
#include "nm-utils.h"
|
|
#include "platform/nmp-object.h"
|
|
#include "platform/nm-platform.h"
|
|
#include "platform/nm-platform-utils.h"
|
|
#include "NetworkManagerUtils.h"
|
|
#include "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;
|
|
}
|
|
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;
|
|
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 {
|
|
NMDBusObject parent;
|
|
NMIP4ConfigPrivate _priv;
|
|
};
|
|
|
|
struct _NMIP4ConfigClass {
|
|
NMDBusObjectClass parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_DBUS_OBJECT)
|
|
|
|
#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;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
_ipv4_is_zeronet (in_addr_t network)
|
|
{
|
|
/* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */
|
|
return (network & htonl(0xff000000)) == htonl(0x00000000);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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)
|
|
{
|
|
g_return_if_fail (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)
|
|
{
|
|
g_return_if_fail (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_defaut_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_defaut_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);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static int
|
|
_addresses_sort_cmp_get_prio (in_addr_t addr)
|
|
{
|
|
if (nm_utils_ip4_address_is_link_local (addr))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
|
|
{
|
|
int p1, p2;
|
|
const NMPlatformIP4Address *a1 = NMP_OBJECT_CAST_IP4_ADDRESS (*((const NMPObject **) a));
|
|
const NMPlatformIP4Address *a2 = NMP_OBJECT_CAST_IP4_ADDRESS (*((const NMPObject **) b));
|
|
guint32 n1, n2;
|
|
|
|
nm_assert (a1);
|
|
nm_assert (a2);
|
|
|
|
/* Sort by address type. For example link local will
|
|
* be sorted *after* a global address. */
|
|
p1 = _addresses_sort_cmp_get_prio (a1->address);
|
|
p2 = _addresses_sort_cmp_get_prio (a2->address);
|
|
if (p1 != p2)
|
|
return p1 > p2 ? -1 : 1;
|
|
|
|
/* Sort the addresses based on their source. */
|
|
if (a1->addr_source != a2->addr_source)
|
|
return a1->addr_source > a2->addr_source ? -1 : 1;
|
|
|
|
if ((a1->label[0] == '\0') != (a2->label[0] == '\0'))
|
|
return (a1->label[0] == '\0') ? -1 : 1;
|
|
|
|
/* Finally, sort addresses lexically. We compare only the
|
|
* network part so that the order of addresses in the same
|
|
* subnet (and thus also the primary/secondary role) is
|
|
* preserved.
|
|
*/
|
|
n1 = a1->address & _nm_utils_ip4_prefix_to_netmask (a1->plen);
|
|
n2 = a2->address & _nm_utils_ip4_prefix_to_netmask (a2->plen);
|
|
|
|
return memcmp (&n1, &n2, sizeof (guint32));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static int
|
|
sort_captured_addresses (const CList *lst_a, const CList *lst_b, gconstpointer user_data)
|
|
{
|
|
const NMPlatformIP4Address *addr_a = NMP_OBJECT_CAST_IP4_ADDRESS (c_list_entry (lst_a, NMDedupMultiEntry, lst_entries)->obj);
|
|
const NMPlatformIP4Address *addr_b = NMP_OBJECT_CAST_IP4_ADDRESS (c_list_entry (lst_b, NMDedupMultiEntry, lst_entries)->obj);
|
|
|
|
nm_assert (addr_a);
|
|
nm_assert (addr_b);
|
|
|
|
/* Primary addresses first */
|
|
return NM_FLAGS_HAS (addr_a->n_ifa_flags, IFA_F_SECONDARY) -
|
|
NM_FLAGS_HAS (addr_b->n_ifa_flags, IFA_F_SECONDARY);
|
|
}
|
|
|
|
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 ();
|
|
}
|
|
head_entry = nm_ip4_config_lookup_addresses (self);
|
|
nm_assert (head_entry);
|
|
nm_dedup_multi_head_entry_sort (head_entry,
|
|
sort_captured_addresses,
|
|
NULL);
|
|
_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));
|
|
}
|
|
|
|
static void
|
|
_add_local_route_from_addr4 (NMIP4Config * self,
|
|
const NMPlatformIP4Address *addr,
|
|
int ifindex,
|
|
guint32 route_table,
|
|
gboolean is_vrf)
|
|
{
|
|
nm_auto_nmpobj NMPObject *r = NULL;
|
|
NMPlatformIP4Route *route;
|
|
|
|
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 = addr->address;
|
|
route->plen = 32;
|
|
route->pref_src = addr->address;
|
|
route->type_coerced = nm_platform_route_type_coerce (RTN_LOCAL);
|
|
route->scope_inv = nm_platform_route_scope_inv (RT_SCOPE_HOST);
|
|
route->table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL);
|
|
|
|
_add_route (self, r, NULL, NULL);
|
|
}
|
|
|
|
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;
|
|
|
|
_add_local_route_from_addr4 (self, my_addr, ifindex, route_table, is_vrf);
|
|
|
|
if (_ipv4_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);
|
|
|
|
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));
|
|
|
|
/* 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_ip_config_iter_ip4_address_next (&ipconf_iter_src, &r_src);
|
|
if (has != nm_ip_config_iter_ip4_address_next (&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_ip_config_iter_ip4_route_next (&ipconf_iter_src, &r_src);
|
|
if (has != nm_ip_config_iter_ip4_route_next (&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;
|
|
}
|
|
|
|
#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));
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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_utils_strv_find_first ((char **) array->pdata, array->len, 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;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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);
|
|
const NMDedupMultiHeadEntry *head_entry;
|
|
NMDedupMultiIter ipconf_iter;
|
|
const NMPlatformIP4Route *route;
|
|
GVariantBuilder builder_data, builder_legacy;
|
|
guint i;
|
|
char addr_str[NM_UTILS_INET_ADDRSTRLEN];
|
|
|
|
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)
|
|
goto out_addresses_cached;
|
|
|
|
g_variant_builder_init (&builder_data, G_VARIANT_TYPE ("aa{sv}"));
|
|
g_variant_builder_init (&builder_legacy, G_VARIANT_TYPE ("aau"));
|
|
|
|
head_entry = nm_ip4_config_lookup_addresses (self);
|
|
if (head_entry) {
|
|
gs_free const NMPObject **addresses = NULL;
|
|
guint naddr;
|
|
|
|
addresses = (const NMPObject **) nm_dedup_multi_objs_to_array_head (head_entry, NULL, NULL, &naddr);
|
|
nm_assert (addresses && naddr);
|
|
|
|
g_qsort_with_data (addresses,
|
|
naddr,
|
|
sizeof (addresses[0]),
|
|
_addresses_sort_cmp,
|
|
NULL);
|
|
|
|
/* Build address data variant */
|
|
for (i = 0; i < naddr; i++) {
|
|
GVariantBuilder addr_builder;
|
|
const NMPlatformIP4Address *address = NMP_OBJECT_CAST_IP4_ADDRESS (addresses[i]);
|
|
|
|
g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}"));
|
|
g_variant_builder_add (&addr_builder, "{sv}",
|
|
"address",
|
|
g_variant_new_string (_nm_utils_inet4_ntop (address->address, addr_str)));
|
|
g_variant_builder_add (&addr_builder, "{sv}",
|
|
"prefix",
|
|
g_variant_new_uint32 (address->plen));
|
|
if (address->peer_address != address->address) {
|
|
g_variant_builder_add (&addr_builder, "{sv}",
|
|
"peer",
|
|
g_variant_new_string (_nm_utils_inet4_ntop (address->peer_address, addr_str)));
|
|
}
|
|
|
|
if (*address->label) {
|
|
g_variant_builder_add (&addr_builder, "{sv}",
|
|
NM_IP_ADDRESS_ATTRIBUTE_LABEL,
|
|
g_variant_new_string (address->label));
|
|
}
|
|
|
|
g_variant_builder_add (&builder_data, "a{sv}", &addr_builder);
|
|
|
|
{
|
|
const guint32 dbus_addr[3] = {
|
|
address->address,
|
|
address->plen,
|
|
( i == 0
|
|
&& priv->best_default_route)
|
|
? NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway
|
|
: (guint32) 0,
|
|
};
|
|
|
|
g_variant_builder_add (&builder_legacy, "@au",
|
|
g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
|
|
dbus_addr, 3, sizeof (guint32)));
|
|
}
|
|
}
|
|
}
|
|
|
|
priv->address_data_variant = g_variant_ref_sink (g_variant_builder_end (&builder_data));
|
|
priv->addresses_variant = g_variant_ref_sink (g_variant_builder_end (&builder_legacy));
|
|
|
|
out_addresses_cached:
|
|
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)
|
|
goto out_routes_cached;
|
|
|
|
g_variant_builder_init (&builder_data, G_VARIANT_TYPE ("aa{sv}"));
|
|
g_variant_builder_init (&builder_legacy, G_VARIANT_TYPE ("aau"));
|
|
|
|
nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) {
|
|
GVariantBuilder route_builder;
|
|
|
|
nm_assert (_route_valid (route));
|
|
|
|
if (route->type_coerced != nm_platform_route_type_coerce (RTN_UNICAST))
|
|
continue;
|
|
|
|
g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}"));
|
|
g_variant_builder_add (&route_builder, "{sv}",
|
|
"dest",
|
|
g_variant_new_string (_nm_utils_inet4_ntop (route->network, addr_str)));
|
|
g_variant_builder_add (&route_builder, "{sv}",
|
|
"prefix",
|
|
g_variant_new_uint32 (route->plen));
|
|
if (route->gateway) {
|
|
g_variant_builder_add (&route_builder, "{sv}",
|
|
"next-hop",
|
|
g_variant_new_string (_nm_utils_inet4_ntop (route->gateway, addr_str)));
|
|
}
|
|
g_variant_builder_add (&route_builder, "{sv}",
|
|
"metric",
|
|
g_variant_new_uint32 (route->metric));
|
|
|
|
if (!nm_platform_route_table_is_main (route->table_coerced)) {
|
|
g_variant_builder_add (&route_builder, "{sv}",
|
|
"table",
|
|
g_variant_new_uint32 (nm_platform_route_table_uncoerce (route->table_coerced, TRUE)));
|
|
}
|
|
|
|
g_variant_builder_add (&builder_data, "a{sv}", &route_builder);
|
|
|
|
/* legacy versions of nm_ip4_route_set_prefix() in libnm-util assert that the
|
|
* plen is positive. Skip the default routes not to break older clients. */
|
|
if ( nm_platform_route_table_is_main (route->table_coerced)
|
|
&& !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) {
|
|
const guint32 dbus_route[4] = {
|
|
route->network,
|
|
route->plen,
|
|
route->gateway,
|
|
route->metric,
|
|
};
|
|
|
|
g_variant_builder_add (&builder_legacy, "@au",
|
|
g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
|
|
dbus_route, 4, sizeof (guint32)));
|
|
}
|
|
}
|
|
|
|
priv->route_data_variant = g_variant_ref_sink (g_variant_builder_end (&builder_data));
|
|
priv->routes_variant = g_variant_ref_sink (g_variant_builder_end (&builder_legacy));
|
|
|
|
out_routes_cached:
|
|
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,
|
|
g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
|
|
priv->nameservers->data,
|
|
priv->nameservers->len,
|
|
sizeof (guint32)));
|
|
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,
|
|
g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
|
|
priv->wins->data,
|
|
priv->wins->len,
|
|
sizeof (guint32)));
|
|
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 (NMIP4Config *) 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,
|
|
.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
|
|
&nm_signal_info_property_changed_legacy,
|
|
),
|
|
.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Addresses", "aau", NM_IP4_CONFIG_ADDRESSES),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AddressData", "aa{sv}", NM_IP4_CONFIG_ADDRESS_DATA),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Gateway", "s", NM_IP4_CONFIG_GATEWAY),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Routes", "aau", NM_IP4_CONFIG_ROUTES),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("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_L ("Nameservers", "au", NM_IP4_CONFIG_NAMESERVERS),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Domains", "as", NM_IP4_CONFIG_DOMAINS),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Searches", "as", NM_IP4_CONFIG_SEARCHES),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DnsOptions", "as", NM_IP4_CONFIG_DNS_OPTIONS),
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("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_L ("WinsServers", "au", NM_IP4_CONFIG_WINS_SERVERS),
|
|
),
|
|
),
|
|
.legacy_property_changed = TRUE,
|
|
};
|
|
|
|
static void
|
|
nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (config_class);
|
|
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (config_class);
|
|
|
|
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);
|
|
}
|