mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-29 09:10:10 +01:00
The entire point of NML3ConfigData is to be immutable and merging them. "Merging" means to combine existing settings, hence NMRefString can be used to share the same string instance.
2950 lines
101 KiB
C
2950 lines
101 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include "src/core/nm-default-daemon.h"
|
|
|
|
#include "nm-l3-config-data.h"
|
|
|
|
#include <linux/if.h>
|
|
#include <linux/if_addr.h>
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include "libnm-core-intern/nm-core-internal.h"
|
|
#include "libnm-glib-aux/nm-enum-utils.h"
|
|
#include "libnm-glib-aux/nm-ref-string.h"
|
|
#include "libnm-platform/nm-platform-utils.h"
|
|
#include "libnm-platform/nm-platform.h"
|
|
#include "libnm-platform/nmp-object.h"
|
|
#include "NetworkManagerUtils.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
NMDedupMultiIdxType parent;
|
|
NMPObjectType obj_type;
|
|
} DedupMultiIdxType;
|
|
|
|
struct _NML3ConfigData {
|
|
NMDedupMultiIndex *multi_idx;
|
|
|
|
union {
|
|
struct {
|
|
DedupMultiIdxType idx_addresses_6;
|
|
DedupMultiIdxType idx_addresses_4;
|
|
};
|
|
DedupMultiIdxType idx_addresses_x[2];
|
|
};
|
|
|
|
union {
|
|
struct {
|
|
DedupMultiIdxType idx_routes_6;
|
|
DedupMultiIdxType idx_routes_4;
|
|
};
|
|
DedupMultiIdxType idx_routes_x[2];
|
|
};
|
|
|
|
union {
|
|
struct {
|
|
const NMPObject *best_default_route_6;
|
|
const NMPObject *best_default_route_4;
|
|
};
|
|
const NMPObject *best_default_route_x[2];
|
|
};
|
|
|
|
GArray *wins;
|
|
GArray *nis_servers;
|
|
|
|
NMRefString *nis_domain;
|
|
NMRefString *proxy_pac_url;
|
|
NMRefString *proxy_pac_script;
|
|
|
|
union {
|
|
struct {
|
|
NMDhcpLease *dhcp_lease_6;
|
|
NMDhcpLease *dhcp_lease_4;
|
|
};
|
|
NMDhcpLease *dhcp_lease_x[2];
|
|
};
|
|
|
|
union {
|
|
struct {
|
|
GArray *nameservers_6;
|
|
GArray *nameservers_4;
|
|
};
|
|
GArray *nameservers_x[2];
|
|
};
|
|
|
|
union {
|
|
struct {
|
|
GPtrArray *domains_6;
|
|
GPtrArray *domains_4;
|
|
};
|
|
GPtrArray *domains_x[2];
|
|
};
|
|
|
|
union {
|
|
struct {
|
|
GPtrArray *searches_6;
|
|
GPtrArray *searches_4;
|
|
};
|
|
GPtrArray *searches_x[2];
|
|
};
|
|
|
|
union {
|
|
struct {
|
|
GPtrArray *dns_options_6;
|
|
GPtrArray *dns_options_4;
|
|
};
|
|
GPtrArray *dns_options_x[2];
|
|
};
|
|
|
|
int ifindex;
|
|
|
|
int ref_count;
|
|
|
|
union {
|
|
struct {
|
|
int dns_priority_6;
|
|
int dns_priority_4;
|
|
};
|
|
int dns_priority_x[2];
|
|
};
|
|
|
|
union {
|
|
struct {
|
|
NMIPRouteTableSyncMode route_table_sync_6;
|
|
NMIPRouteTableSyncMode route_table_sync_4;
|
|
};
|
|
NMIPRouteTableSyncMode route_table_sync_x[2];
|
|
};
|
|
|
|
NMSettingConnectionMdns mdns;
|
|
NMSettingConnectionLlmnr llmnr;
|
|
|
|
NML3ConfigDatFlags flags;
|
|
|
|
NMIPConfigSource source;
|
|
|
|
int ndisc_hop_limit_val;
|
|
|
|
guint32 mtu;
|
|
guint32 ip6_mtu;
|
|
guint32 ndisc_reachable_time_msec_val;
|
|
guint32 ndisc_retrans_timer_msec_val;
|
|
|
|
NMTernary metered : 3;
|
|
|
|
NMTernary proxy_browser_only : 3;
|
|
|
|
NMProxyConfigMethod proxy_method : 4;
|
|
|
|
NMSettingIP6ConfigPrivacy ip6_privacy : 4;
|
|
|
|
bool is_sealed : 1;
|
|
|
|
bool has_routes_with_type_local_4_set : 1;
|
|
bool has_routes_with_type_local_6_set : 1;
|
|
bool has_routes_with_type_local_4_val : 1;
|
|
bool has_routes_with_type_local_6_val : 1;
|
|
|
|
bool ndisc_hop_limit_set : 1;
|
|
bool ndisc_reachable_time_msec_set : 1;
|
|
bool ndisc_retrans_timer_msec_set : 1;
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
static GArray *
|
|
_garray_inaddr_ensure(GArray **p_arr, int addr_family)
|
|
{
|
|
nm_assert(p_arr);
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
if (G_UNLIKELY(!*p_arr)) {
|
|
*p_arr = g_array_new(FALSE, FALSE, nm_utils_addr_family_to_size(addr_family));
|
|
}
|
|
return *p_arr;
|
|
}
|
|
|
|
static GArray *
|
|
_garray_inaddr_clone(const GArray *src, int addr_family)
|
|
{
|
|
const gsize elt_size = nm_utils_addr_family_to_size(addr_family);
|
|
GArray * dst;
|
|
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
if (!src || src->len == 0)
|
|
return NULL;
|
|
|
|
dst = g_array_sized_new(FALSE, FALSE, elt_size, src->len);
|
|
g_array_set_size(dst, src->len);
|
|
memcpy(dst->data, src->data, src->len * elt_size);
|
|
return dst;
|
|
}
|
|
|
|
static void
|
|
_garray_inaddr_merge(GArray **p_dst, const GArray *src, int addr_family)
|
|
{
|
|
guint dst_initial_len;
|
|
const char *p_dst_arr;
|
|
const char *p_src;
|
|
gsize elt_size;
|
|
guint i;
|
|
guint j;
|
|
|
|
if (nm_g_array_len(src) == 0)
|
|
return;
|
|
|
|
if (!*p_dst) {
|
|
*p_dst = _garray_inaddr_clone(src, addr_family);
|
|
return;
|
|
}
|
|
|
|
elt_size = nm_utils_addr_family_to_size(addr_family);
|
|
|
|
dst_initial_len = (*p_dst)->len;
|
|
p_dst_arr = (*p_dst)->data;
|
|
p_src = src->data;
|
|
|
|
for (i = 0; i < src->len; i++, p_src += elt_size) {
|
|
for (j = 0; j < dst_initial_len; j++) {
|
|
if (memcmp(&p_dst_arr[j * elt_size], p_src, elt_size) == 0)
|
|
goto next;
|
|
}
|
|
g_array_append_vals(*p_dst, p_src, 1);
|
|
p_dst_arr = (*p_dst)->data;
|
|
next:;
|
|
}
|
|
}
|
|
|
|
static gssize
|
|
_garray_inaddr_find(GArray * arr,
|
|
int addr_family,
|
|
gconstpointer needle,
|
|
/* (const NMIPAddr **) */ gpointer out_addr)
|
|
{
|
|
guint i;
|
|
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(needle);
|
|
|
|
if (arr) {
|
|
const gsize elt_size = nm_utils_addr_family_to_size(addr_family);
|
|
const char *p;
|
|
|
|
p = arr->data;
|
|
for (i = 0; i < arr->len; i++, p += elt_size) {
|
|
if (memcmp(p, needle, elt_size) == 0) {
|
|
NM_SET_OUT((gconstpointer *) out_addr, p);
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
NM_SET_OUT((gconstpointer *) out_addr, NULL);
|
|
return -1;
|
|
}
|
|
|
|
static gconstpointer
|
|
_garray_inaddr_get(GArray *arr, guint *out_len)
|
|
{
|
|
nm_assert(out_len);
|
|
|
|
if (!arr) {
|
|
*out_len = 0;
|
|
return NULL;
|
|
}
|
|
*out_len = arr->len;
|
|
return arr->data;
|
|
}
|
|
|
|
static gconstpointer
|
|
_garray_inaddr_at(GArray *arr, gboolean IS_IPv4, guint idx)
|
|
{
|
|
nm_assert(arr);
|
|
nm_assert(idx < arr->len);
|
|
|
|
if (IS_IPv4)
|
|
return &g_array_index(arr, in_addr_t, idx);
|
|
return &g_array_index(arr, struct in6_addr, idx);
|
|
}
|
|
|
|
static gboolean
|
|
_garray_inaddr_add(GArray **p_arr, int addr_family, gconstpointer addr)
|
|
{
|
|
nm_assert(p_arr);
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(addr);
|
|
|
|
if (!*p_arr)
|
|
_garray_inaddr_ensure(p_arr, addr_family);
|
|
else {
|
|
if (_garray_inaddr_find(*p_arr, addr_family, addr, NULL) >= 0)
|
|
return FALSE;
|
|
}
|
|
|
|
g_array_append_vals(*p_arr, addr, 1);
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
_garray_inaddr_cmp(const GArray *a, const GArray *b, int addr_family)
|
|
{
|
|
guint l;
|
|
|
|
l = nm_g_array_len(a);
|
|
NM_CMP_DIRECT(l, nm_g_array_len(b));
|
|
|
|
if (l > 0)
|
|
NM_CMP_DIRECT_MEMCMP(a->data, b->data, l * nm_utils_addr_family_to_size(addr_family));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_strv_ptrarray_merge(GPtrArray **p_dst, const GPtrArray *src)
|
|
{
|
|
guint dst_initial_len;
|
|
guint i;
|
|
|
|
if (nm_g_ptr_array_len(src) == 0)
|
|
return;
|
|
|
|
if (!*p_dst) {
|
|
/* we trust src to contain unique strings. Just clone it. */
|
|
*p_dst = nm_strv_ptrarray_clone(src, TRUE);
|
|
return;
|
|
}
|
|
|
|
nm_strv_ptrarray_ensure(p_dst);
|
|
|
|
dst_initial_len = (*p_dst)->len;
|
|
|
|
for (i = 0; i < src->len; i++) {
|
|
const char *s = src->pdata[i];
|
|
|
|
if (dst_initial_len > 0
|
|
&& nm_utils_strv_find_first((char **) ((*p_dst)->pdata), dst_initial_len, s) >= 0)
|
|
continue;
|
|
|
|
g_ptr_array_add(*p_dst, g_strdup(s));
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_l3_config_data_log(const NML3ConfigData *self,
|
|
const char * title,
|
|
const char * prefix,
|
|
NMLogLevel log_level,
|
|
NMLogDomain log_domain)
|
|
{
|
|
char sbuf[sizeof(_nm_utils_to_string_buffer)];
|
|
char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
|
|
int IS_IPv4;
|
|
guint i;
|
|
|
|
if (!nm_logging_enabled(log_level, log_domain))
|
|
return;
|
|
|
|
#define _L(...) \
|
|
_nm_log(log_level, \
|
|
log_domain, \
|
|
0, \
|
|
NULL, \
|
|
NULL, \
|
|
"%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
|
|
prefix _NM_UTILS_MACRO_REST(__VA_ARGS__))
|
|
|
|
if (!prefix)
|
|
prefix = "";
|
|
|
|
if (!self) {
|
|
_L("l3cd %s%s%s(NULL)", NM_PRINT_FMT_QUOTED(title, "\"", title, "\" ", ""));
|
|
return;
|
|
}
|
|
|
|
nm_assert(!NM_FLAGS_ANY(self->flags,
|
|
~(NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES
|
|
| NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_4
|
|
| NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_6)));
|
|
|
|
_L("l3cd %s%s%s(" NM_HASH_OBFUSCATE_PTR_FMT ", ifindex=%d%s%s%s%s)",
|
|
NM_PRINT_FMT_QUOTED(title, "\"", title, "\" ", ""),
|
|
NM_HASH_OBFUSCATE_PTR(self),
|
|
self->ifindex,
|
|
NM_PRINT_FMT_QUOTED2(self->source != NM_IP_CONFIG_SOURCE_UNKNOWN,
|
|
", source=",
|
|
nmp_utils_ip_config_source_to_string(self->source, sbuf, sizeof(sbuf)),
|
|
""),
|
|
NM_FLAGS_HAS(self->flags, NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES)
|
|
? ", merge-no-default-routes"
|
|
: "",
|
|
!self->is_sealed ? ", not-sealed" : "");
|
|
|
|
if (self->mtu != 0 || self->ip6_mtu != 0) {
|
|
_L("mtu: %u, ip6-mtu: %u", self->mtu, self->ip6_mtu);
|
|
}
|
|
|
|
for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
|
|
const int addr_family = IS_IPv4 ? AF_INET : AF_INET6;
|
|
NMDedupMultiIter iter;
|
|
const NMPObject *obj;
|
|
|
|
i = 0;
|
|
nm_l3_config_data_iter_obj_for_each (&iter,
|
|
self,
|
|
&obj,
|
|
NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) {
|
|
_L("address%c[%u]: %s",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
i,
|
|
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
|
i++;
|
|
}
|
|
|
|
if (!IS_IPv4) {
|
|
if (self->ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) {
|
|
gs_free char *s = NULL;
|
|
|
|
_L("ip6-privacy: %s",
|
|
(s = _nm_utils_enum_to_str_full(nm_setting_ip6_config_privacy_get_type(),
|
|
self->ip6_privacy,
|
|
" ",
|
|
NULL)));
|
|
}
|
|
}
|
|
|
|
i = 0;
|
|
nm_l3_config_data_iter_obj_for_each (&iter, self, &obj, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) {
|
|
_L("route%c[%u]: %s%s",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
i,
|
|
self->best_default_route_x[IS_IPv4] == obj ? "[DEFAULT] " : "",
|
|
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
|
i++;
|
|
}
|
|
|
|
if (self->route_table_sync_x[IS_IPv4] != NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) {
|
|
_L("route-table-sync-mode%c: %d",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
(int) self->route_table_sync_x[IS_IPv4]);
|
|
}
|
|
|
|
if (!IS_IPv4) {
|
|
if (self->ndisc_hop_limit_set || self->ndisc_reachable_time_msec_set
|
|
|| self->ndisc_retrans_timer_msec_set) {
|
|
gsize l = sizeof(sbuf);
|
|
char * p = sbuf;
|
|
const char *s_prefix = "ndisc: ";
|
|
|
|
if (self->ndisc_hop_limit_set) {
|
|
nm_utils_strbuf_append(&p,
|
|
&l,
|
|
"%shop-limit=%d",
|
|
s_prefix,
|
|
self->ndisc_hop_limit_val);
|
|
s_prefix = ", ";
|
|
}
|
|
if (self->ndisc_reachable_time_msec_set) {
|
|
nm_utils_strbuf_append(&p,
|
|
&l,
|
|
"%sreachable-time-msec=%u",
|
|
s_prefix,
|
|
self->ndisc_reachable_time_msec_val);
|
|
s_prefix = ", ";
|
|
}
|
|
if (self->ndisc_retrans_timer_msec_set) {
|
|
nm_utils_strbuf_append(&p,
|
|
&l,
|
|
"%sretrans-timer-msec=%u",
|
|
s_prefix,
|
|
self->ndisc_retrans_timer_msec_val);
|
|
s_prefix = ", ";
|
|
}
|
|
_L("%s", sbuf);
|
|
}
|
|
}
|
|
|
|
if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY(IS_IPv4))) {
|
|
_L("dns-priority%c: %d",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
self->dns_priority_x[IS_IPv4]);
|
|
}
|
|
|
|
for (i = 0; i < nm_g_array_len(self->nameservers_x[IS_IPv4]); i++) {
|
|
_L("nameserver%c[%u]: %s",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
i,
|
|
nm_utils_inet_ntop(addr_family,
|
|
_garray_inaddr_at(self->nameservers_x[IS_IPv4], IS_IPv4, i),
|
|
sbuf_addr));
|
|
}
|
|
|
|
for (i = 0; i < nm_g_ptr_array_len(self->domains_x[IS_IPv4]); i++) {
|
|
_L("domain%c[%u]: %s",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
i,
|
|
(const char *) self->domains_x[IS_IPv4]->pdata[i]);
|
|
}
|
|
|
|
for (i = 0; i < nm_g_ptr_array_len(self->searches_x[IS_IPv4]); i++) {
|
|
_L("search%c[%u]: %s",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
i,
|
|
(const char *) self->searches_x[IS_IPv4]->pdata[i]);
|
|
}
|
|
|
|
for (i = 0; i < nm_g_ptr_array_len(self->dns_options_x[IS_IPv4]); i++) {
|
|
_L("dns_option%c[%u]: %s",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
i,
|
|
(const char *) self->dns_options_x[IS_IPv4]->pdata[i]);
|
|
}
|
|
|
|
if (IS_IPv4) {
|
|
for (i = 0; i < nm_g_array_len(self->wins); i++) {
|
|
_L("wins[%u]: %s",
|
|
i,
|
|
_nm_utils_inet4_ntop(g_array_index(self->wins, in_addr_t, i), sbuf_addr));
|
|
}
|
|
for (i = 0; i < nm_g_array_len(self->nis_servers); i++) {
|
|
_L("nis-server[%u]: %s",
|
|
i,
|
|
_nm_utils_inet4_ntop(g_array_index(self->nis_servers, in_addr_t, i), sbuf_addr));
|
|
}
|
|
if (self->nis_domain)
|
|
_L("nis-domain: %s", self->nis_domain->str);
|
|
}
|
|
|
|
if (self->dhcp_lease_x[IS_IPv4]) {
|
|
gs_free NMUtilsNamedValue *options_free = NULL;
|
|
NMUtilsNamedValue options_buffer[30];
|
|
NMUtilsNamedValue * options;
|
|
guint options_len;
|
|
|
|
options = nm_utils_named_values_from_strdict(
|
|
nm_dhcp_lease_get_options(self->dhcp_lease_x[IS_IPv4]),
|
|
&options_len,
|
|
options_buffer,
|
|
&options_free);
|
|
if (options_len == 0) {
|
|
_L("dhcp-lease%c (%u options)",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
options_len);
|
|
}
|
|
for (i = 0; i < options_len; i++) {
|
|
_L("dhcp-lease%c[%u]: \"%s\" => \"%s\"",
|
|
nm_utils_addr_family_to_char(addr_family),
|
|
i,
|
|
options[i].name,
|
|
options[i].value_str);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (self->mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) {
|
|
gs_free char *s = NULL;
|
|
|
|
_L("mdns: %s",
|
|
(s = _nm_utils_enum_to_str_full(nm_setting_connection_mdns_get_type(),
|
|
self->mdns,
|
|
" ",
|
|
NULL)));
|
|
}
|
|
|
|
if (self->llmnr != NM_SETTING_CONNECTION_LLMNR_DEFAULT) {
|
|
gs_free char *s = NULL;
|
|
|
|
_L("llmnr: %s",
|
|
(s = _nm_utils_enum_to_str_full(nm_setting_connection_llmnr_get_type(),
|
|
self->llmnr,
|
|
" ",
|
|
NULL)));
|
|
}
|
|
|
|
if (self->metered != NM_TERNARY_DEFAULT)
|
|
_L("metered: %s", self->metered ? "yes" : "no");
|
|
|
|
if (self->proxy_browser_only != NM_TERNARY_DEFAULT)
|
|
_L("proxy-browser-only: %s", self->proxy_browser_only ? "yes" : "no");
|
|
|
|
if (self->proxy_method != NM_PROXY_CONFIG_METHOD_UNKNOWN)
|
|
_L("proxy-method: %s", self->proxy_method == NM_PROXY_CONFIG_METHOD_AUTO ? "auto" : "none");
|
|
|
|
if (self->proxy_pac_url)
|
|
_L("proxy-pac-url: %s", self->proxy_pac_url->str);
|
|
|
|
if (self->proxy_pac_script)
|
|
_L("proxy-pac-script: %s", self->proxy_pac_script->str);
|
|
#undef _L
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
_route_valid_4(const NMPlatformIP4Route *r)
|
|
{
|
|
return r && r->plen <= 32
|
|
&& r->network == nm_utils_ip4_address_clear_host_address(r->network, r->plen);
|
|
}
|
|
|
|
static gboolean
|
|
_route_valid_6(const NMPlatformIP6Route *r)
|
|
{
|
|
struct in6_addr n;
|
|
|
|
return r && r->plen <= 128
|
|
&& (memcmp(&r->network,
|
|
nm_utils_ip6_address_clear_host_address(&n, &r->network, r->plen),
|
|
sizeof(n))
|
|
== 0);
|
|
}
|
|
|
|
static gboolean
|
|
_route_valid(int addr_family, gconstpointer r)
|
|
{
|
|
return NM_IS_IPv4(addr_family) ? _route_valid_4(r) : _route_valid_6(r);
|
|
}
|
|
|
|
static gboolean
|
|
_NM_IS_L3_CONFIG_DATA(const NML3ConfigData *self, gboolean allow_sealed)
|
|
{
|
|
nm_assert(!self || (self->ifindex > 0 && self->multi_idx && self->ref_count > 0));
|
|
return self && self->ref_count > 0 && (allow_sealed || !self->is_sealed);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
_idx_type_init(DedupMultiIdxType *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(&idx_type->parent, &idx_type_class);
|
|
idx_type->obj_type = obj_type;
|
|
}
|
|
|
|
NML3ConfigData *
|
|
nm_l3_config_data_new(NMDedupMultiIndex *multi_idx, int ifindex)
|
|
{
|
|
NML3ConfigData *self;
|
|
|
|
nm_assert(multi_idx);
|
|
nm_assert(ifindex > 0);
|
|
|
|
self = g_slice_new(NML3ConfigData);
|
|
*self = (NML3ConfigData){
|
|
.ref_count = 1,
|
|
.ifindex = ifindex,
|
|
.multi_idx = nm_dedup_multi_index_ref(multi_idx),
|
|
.mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT,
|
|
.llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT,
|
|
.flags = NM_L3_CONFIG_DAT_FLAGS_NONE,
|
|
.metered = NM_TERNARY_DEFAULT,
|
|
.proxy_browser_only = NM_TERNARY_DEFAULT,
|
|
.proxy_method = NM_PROXY_CONFIG_METHOD_UNKNOWN,
|
|
.route_table_sync_4 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE,
|
|
.route_table_sync_6 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE,
|
|
.source = NM_IP_CONFIG_SOURCE_UNKNOWN,
|
|
.ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
|
|
.ndisc_hop_limit_set = FALSE,
|
|
.ndisc_reachable_time_msec_set = FALSE,
|
|
.ndisc_retrans_timer_msec_set = FALSE,
|
|
};
|
|
|
|
_idx_type_init(&self->idx_addresses_4, NMP_OBJECT_TYPE_IP4_ADDRESS);
|
|
_idx_type_init(&self->idx_addresses_6, NMP_OBJECT_TYPE_IP6_ADDRESS);
|
|
_idx_type_init(&self->idx_routes_4, NMP_OBJECT_TYPE_IP4_ROUTE);
|
|
_idx_type_init(&self->idx_routes_6, NMP_OBJECT_TYPE_IP6_ROUTE);
|
|
|
|
return self;
|
|
}
|
|
|
|
const NML3ConfigData *
|
|
nm_l3_config_data_ref(const NML3ConfigData *self)
|
|
{
|
|
if (self) {
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
((NML3ConfigData *) self)->ref_count++;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
const NML3ConfigData *
|
|
nm_l3_config_data_ref_and_seal(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
((NML3ConfigData *) self)->is_sealed = TRUE;
|
|
((NML3ConfigData *) self)->ref_count++;
|
|
return self;
|
|
}
|
|
|
|
const NML3ConfigData *
|
|
nm_l3_config_data_seal(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
((NML3ConfigData *) self)->is_sealed = TRUE;
|
|
return self;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_is_sealed(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
return self->is_sealed;
|
|
}
|
|
|
|
void
|
|
nm_l3_config_data_unref(const NML3ConfigData *self)
|
|
{
|
|
NML3ConfigData *mutable;
|
|
|
|
if (!self)
|
|
return;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
/* NML3ConfigData aims to be an immutable, ref-counted type. The mode of operation
|
|
* is to create/initialize the instance once, then seal it and pass around the reference.
|
|
*
|
|
* That means, also ref/unref operate on const pointers (otherwise, you'd have to cast all
|
|
* the time). Hence, we cast away the constness during ref/unref/seal operations. */
|
|
|
|
mutable = (NML3ConfigData *) self;
|
|
|
|
if (--mutable->ref_count > 0)
|
|
return;
|
|
|
|
nm_dedup_multi_index_remove_idx(mutable->multi_idx, &mutable->idx_addresses_4.parent);
|
|
nm_dedup_multi_index_remove_idx(mutable->multi_idx, &mutable->idx_addresses_6.parent);
|
|
nm_dedup_multi_index_remove_idx(mutable->multi_idx, &mutable->idx_routes_4.parent);
|
|
nm_dedup_multi_index_remove_idx(mutable->multi_idx, &mutable->idx_routes_6.parent);
|
|
|
|
nmp_object_unref(mutable->best_default_route_4);
|
|
nmp_object_unref(mutable->best_default_route_6);
|
|
|
|
nm_clear_pointer(&mutable->wins, g_array_unref);
|
|
nm_clear_pointer(&mutable->nis_servers, g_array_unref);
|
|
|
|
nm_clear_pointer(&mutable->dhcp_lease_4, nm_dhcp_lease_unref);
|
|
nm_clear_pointer(&mutable->dhcp_lease_6, nm_dhcp_lease_unref);
|
|
|
|
nm_clear_pointer(&mutable->nameservers_4, g_array_unref);
|
|
nm_clear_pointer(&mutable->nameservers_6, g_array_unref);
|
|
|
|
nm_clear_pointer(&mutable->domains_4, g_ptr_array_unref);
|
|
nm_clear_pointer(&mutable->domains_6, g_ptr_array_unref);
|
|
|
|
nm_clear_pointer(&mutable->searches_4, g_ptr_array_unref);
|
|
nm_clear_pointer(&mutable->searches_6, g_ptr_array_unref);
|
|
|
|
nm_clear_pointer(&mutable->dns_options_4, g_ptr_array_unref);
|
|
nm_clear_pointer(&mutable->dns_options_6, g_ptr_array_unref);
|
|
|
|
nm_dedup_multi_index_unref(mutable->multi_idx);
|
|
|
|
nm_ref_string_unref(mutable->nis_domain);
|
|
nm_ref_string_unref(mutable->proxy_pac_url);
|
|
nm_ref_string_unref(mutable->proxy_pac_script);
|
|
|
|
nm_g_slice_free(mutable);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const NMDedupMultiEntry *
|
|
_lookup_route(const NMDedupMultiIndex *multi_idx,
|
|
const DedupMultiIdxType *idx_type,
|
|
const NMPObject * needle)
|
|
{
|
|
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);
|
|
nm_assert(!entry
|
|
|| (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),
|
|
NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)
|
|
== 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),
|
|
NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)
|
|
== 0));
|
|
|
|
return entry;
|
|
}
|
|
|
|
const NMDedupMultiEntry *
|
|
nm_l3_config_data_lookup_route_obj(const NML3ConfigData *self, const NMPObject *needle)
|
|
{
|
|
gboolean IS_IPv4;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(needle),
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE));
|
|
|
|
IS_IPv4 = NM_IS_IPv4(NMP_OBJECT_GET_ADDR_FAMILY(needle));
|
|
return _lookup_route(self->multi_idx, &self->idx_routes_x[IS_IPv4], needle);
|
|
}
|
|
|
|
const NMDedupMultiEntry *
|
|
nm_l3_config_data_lookup_route(const NML3ConfigData * self,
|
|
int addr_family,
|
|
const NMPlatformIPRoute *needle)
|
|
{
|
|
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
NMPObject obj_stack;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(needle);
|
|
|
|
return _lookup_route(
|
|
self->multi_idx,
|
|
&self->idx_routes_x[IS_IPv4],
|
|
nmp_object_stackinit(&obj_stack, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), needle));
|
|
}
|
|
|
|
const NMDedupMultiIdxType *
|
|
nm_l3_config_data_lookup_index(const NML3ConfigData *self, NMPObjectType obj_type)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
switch (obj_type) {
|
|
case NMP_OBJECT_TYPE_IP4_ADDRESS:
|
|
return &self->idx_addresses_4.parent;
|
|
case NMP_OBJECT_TYPE_IP6_ADDRESS:
|
|
return &self->idx_addresses_6.parent;
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
return &self->idx_routes_4.parent;
|
|
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
|
return &self->idx_routes_6.parent;
|
|
default:
|
|
return nm_assert_unreachable_val(NULL);
|
|
}
|
|
}
|
|
|
|
const NMDedupMultiHeadEntry *
|
|
nm_l3_config_data_lookup_objs(const NML3ConfigData *self, NMPObjectType obj_type)
|
|
{
|
|
if (!self)
|
|
return NULL;
|
|
return nm_dedup_multi_index_lookup_head(self->multi_idx,
|
|
nm_l3_config_data_lookup_index(self, obj_type),
|
|
NULL);
|
|
}
|
|
|
|
const NMDedupMultiEntry *
|
|
nm_l3_config_data_lookup_obj(const NML3ConfigData *self, const NMPObject *obj)
|
|
{
|
|
const NMDedupMultiIdxType *idx;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
idx = nm_l3_config_data_lookup_index(self, NMP_OBJECT_GET_TYPE(obj));
|
|
|
|
return nm_dedup_multi_index_lookup_obj(self->multi_idx, idx, obj);
|
|
}
|
|
|
|
const NMPlatformIP6Address *
|
|
nm_l3_config_data_lookup_address_6(const NML3ConfigData *self, const struct in6_addr *addr)
|
|
{
|
|
const NMDedupMultiEntry *head;
|
|
NMPObject obj_stack;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
/* this works only, because the primary key for a Ipv6 address is the
|
|
* ifindex and the "struct in6_addr". */
|
|
nmp_object_stackinit_id_ip6_address(&obj_stack, self->ifindex, addr);
|
|
|
|
head = nm_l3_config_data_lookup_obj(self, &obj_stack);
|
|
if (!head)
|
|
return NULL;
|
|
|
|
return NMP_OBJECT_CAST_IP6_ADDRESS(head->obj);
|
|
}
|
|
|
|
const NMPObject *
|
|
nmtst_l3_config_data_get_obj_at(const NML3ConfigData *self, NMPObjectType obj_type, guint i)
|
|
{
|
|
NMDedupMultiIter iter;
|
|
guint j;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
j = 0;
|
|
nm_dedup_multi_iter_init(&iter, nm_l3_config_data_lookup_objs(self, obj_type));
|
|
while (nm_dedup_multi_iter_next(&iter)) {
|
|
nm_assert(iter.current);
|
|
nm_assert(NMP_OBJECT_GET_TYPE(iter.current->obj) == obj_type);
|
|
if (i == j)
|
|
return iter.current->obj;
|
|
j++;
|
|
}
|
|
|
|
g_return_val_if_reached(NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nm_l3_config_data_has_routes_with_type_local(const NML3ConfigData *self, int addr_family)
|
|
{
|
|
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
NML3ConfigData * self_mutable;
|
|
NMDedupMultiIter iter;
|
|
const NMPObject *obj;
|
|
gboolean val;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
if (IS_IPv4) {
|
|
if (G_LIKELY(self->has_routes_with_type_local_4_set))
|
|
return self->has_routes_with_type_local_4_val;
|
|
} else {
|
|
if (G_LIKELY(self->has_routes_with_type_local_6_set))
|
|
return self->has_routes_with_type_local_6_val;
|
|
}
|
|
|
|
val = FALSE;
|
|
nm_l3_config_data_iter_obj_for_each (&iter, self, &obj, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) {
|
|
if (NMP_OBJECT_CAST_IP_ROUTE(obj)->type_coerced
|
|
== nm_platform_route_type_coerce(RTN_LOCAL)) {
|
|
val = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* the value gets accumulated and cached. Doing that is also permissible to a
|
|
* const/sealed instance. Hence, we cast the const-ness away. */
|
|
self_mutable = (NML3ConfigData *) self;
|
|
if (IS_IPv4) {
|
|
self_mutable->has_routes_with_type_local_4_set = TRUE;
|
|
self_mutable->has_routes_with_type_local_4_val = val;
|
|
} else {
|
|
self_mutable->has_routes_with_type_local_6_set = TRUE;
|
|
self_mutable->has_routes_with_type_local_6_val = val;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMDedupMultiIndex *
|
|
nm_l3_config_data_get_multi_idx(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->multi_idx;
|
|
}
|
|
|
|
int
|
|
nm_l3_config_data_get_ifindex(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->ifindex;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NML3ConfigDatFlags
|
|
nm_l3_config_data_get_flags(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->flags;
|
|
}
|
|
|
|
void
|
|
nm_l3_config_data_set_flags_full(NML3ConfigData * self,
|
|
NML3ConfigDatFlags flags,
|
|
NML3ConfigDatFlags mask)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert(!NM_FLAGS_ANY(flags, ~mask));
|
|
nm_assert(!NM_FLAGS_ANY(mask, ~NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES));
|
|
|
|
self->flags = (self->flags & ~mask) | (flags & mask);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const NMPObject *
|
|
nm_l3_config_data_get_first_obj(const NML3ConfigData *self,
|
|
NMPObjectType obj_type,
|
|
gboolean (*predicate)(const NMPObject *obj))
|
|
{
|
|
NMDedupMultiIter iter;
|
|
const NMPObject *obj;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
nm_l3_config_data_iter_obj_for_each (&iter, self, &obj, obj_type) {
|
|
if (!predicate || predicate(obj))
|
|
return obj;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
_l3_config_data_add_obj(NMDedupMultiIndex * multi_idx,
|
|
DedupMultiIdxType * idx_type,
|
|
int ifindex,
|
|
const NMPObject * obj_new,
|
|
const NMPlatformObject *pl_new,
|
|
NML3ConfigAddFlags add_flags,
|
|
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(nm_utils_is_power_of_two_or_zero(
|
|
add_flags & (NM_L3_CONFIG_ADD_FLAGS_MERGE | NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE)));
|
|
nm_assert(multi_idx);
|
|
nm_assert(idx_type);
|
|
nm_assert(ifindex > 0);
|
|
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));
|
|
/* 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 (NM_FLAGS_HAS(add_flags, NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE)) {
|
|
nm_dedup_multi_entry_set_dirty(entry_old, FALSE);
|
|
goto append_force_and_out;
|
|
}
|
|
|
|
if (nmp_object_equal(obj_new, obj_old)) {
|
|
nm_dedup_multi_entry_set_dirty(entry_old, FALSE);
|
|
goto append_force_and_out;
|
|
}
|
|
|
|
if (NM_FLAGS_HAS(add_flags, NM_L3_CONFIG_ADD_FLAGS_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,
|
|
NM_FLAGS_HAS(add_flags, NM_L3_CONFIG_ADD_FLAGS_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 (NM_FLAGS_HAS(add_flags, NM_L3_CONFIG_ADD_FLAGS_APPEND_FORCE)) {
|
|
if (nm_dedup_multi_entry_reorder(entry_old, NULL, TRUE))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static const NMPObject *
|
|
_l3_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;
|
|
|
|
if (obj_cur == obj_cmp)
|
|
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 (((uintptr_t) ((void *) (obj_cmp))) < ((uintptr_t) ((void *) (obj_cur))))
|
|
return obj_cmp;
|
|
}
|
|
}
|
|
return obj_cur;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_add_address_full(NML3ConfigData * self,
|
|
int addr_family,
|
|
const NMPObject * obj_new,
|
|
const NMPlatformIPAddress *pl_new,
|
|
NML3ConfigAddFlags add_flags,
|
|
const NMPObject ** out_obj_new)
|
|
{
|
|
const NMPObject *new;
|
|
gboolean changed;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert((!pl_new) != (!obj_new));
|
|
nm_assert(!obj_new || NMP_OBJECT_GET_ADDR_FAMILY(obj_new) == addr_family);
|
|
|
|
changed = _l3_config_data_add_obj(self->multi_idx,
|
|
&self->idx_addresses_x[NM_IS_IPv4(addr_family)],
|
|
self->ifindex,
|
|
obj_new,
|
|
(const NMPlatformObject *) pl_new,
|
|
add_flags,
|
|
NULL,
|
|
&new);
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(new));
|
|
return changed;
|
|
}
|
|
|
|
static gboolean
|
|
_l3_config_best_default_route_merge(const NMPObject **best_default_route,
|
|
const NMPObject * new_candidate)
|
|
{
|
|
new_candidate = _l3_config_best_default_route_find_better(*best_default_route, new_candidate);
|
|
return nmp_object_ref_set(best_default_route, new_candidate);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_add_route_full(NML3ConfigData * self,
|
|
int addr_family,
|
|
const NMPObject * obj_new,
|
|
const NMPlatformIPRoute *pl_new,
|
|
NML3ConfigAddFlags add_flags,
|
|
const NMPObject ** out_obj_new,
|
|
gboolean * out_changed_best_default_route)
|
|
{
|
|
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
nm_auto_nmpobj const NMPObject *obj_old = NULL;
|
|
const NMPObject * obj_new_2;
|
|
gboolean changed = FALSE;
|
|
gboolean changed_best_default_route = FALSE;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert((!pl_new) != (!obj_new));
|
|
nm_assert(!pl_new || _route_valid(addr_family, pl_new));
|
|
nm_assert(!obj_new
|
|
|| (NMP_OBJECT_GET_ADDR_FAMILY(obj_new) == addr_family
|
|
&& _route_valid(addr_family, NMP_OBJECT_CAST_IP_ROUTE(obj_new))));
|
|
|
|
if (IS_IPv4)
|
|
self->has_routes_with_type_local_4_set = FALSE;
|
|
else
|
|
self->has_routes_with_type_local_6_set = FALSE;
|
|
if (_l3_config_data_add_obj(self->multi_idx,
|
|
&self->idx_routes_x[NM_IS_IPv4(addr_family)],
|
|
self->ifindex,
|
|
obj_new,
|
|
(const NMPlatformObject *) pl_new,
|
|
add_flags,
|
|
&obj_old,
|
|
&obj_new_2)) {
|
|
if (self->best_default_route_x[IS_IPv4] == obj_old && obj_old != obj_new_2) {
|
|
changed_best_default_route = TRUE;
|
|
nm_clear_nmp_object(&self->best_default_route_x[IS_IPv4]);
|
|
}
|
|
|
|
if (_l3_config_best_default_route_merge(&self->best_default_route_x[IS_IPv4], obj_new_2))
|
|
changed_best_default_route = TRUE;
|
|
|
|
changed = TRUE;
|
|
}
|
|
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(obj_new_2));
|
|
NM_SET_OUT(out_changed_best_default_route, changed_best_default_route);
|
|
return changed;
|
|
}
|
|
|
|
const NMPObject *
|
|
nm_l3_config_data_get_best_default_route(const NML3ConfigData *self, int addr_family)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
return self->best_default_route_x[NM_IS_IPv4(addr_family)];
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
_check_and_add_domain(GPtrArray **p_arr, const char *domain)
|
|
{
|
|
gs_free char *copy = NULL;
|
|
gsize len;
|
|
|
|
nm_assert(p_arr);
|
|
g_return_val_if_fail(domain, FALSE);
|
|
|
|
if (domain[0] == '\0')
|
|
return FALSE;
|
|
|
|
if (domain[0] == '.' || strstr(domain, ".."))
|
|
return FALSE;
|
|
|
|
len = strlen(domain);
|
|
if (domain[len - 1] == '.') {
|
|
copy = g_strndup(domain, len - 1);
|
|
domain = copy;
|
|
}
|
|
|
|
if (nm_strv_ptrarray_contains(*p_arr, domain))
|
|
return FALSE;
|
|
|
|
nm_strv_ptrarray_add_string_take(nm_strv_ptrarray_ensure(p_arr),
|
|
g_steal_pointer(©) ?: g_strdup(domain));
|
|
return TRUE;
|
|
}
|
|
|
|
gconstpointer
|
|
nm_l3_config_data_get_nameservers(const NML3ConfigData *self, int addr_family, guint *out_len)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(out_len);
|
|
|
|
return _garray_inaddr_get(self->nameservers_x[NM_IS_IPv4(addr_family)], out_len);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_add_nameserver(NML3ConfigData * self,
|
|
int addr_family,
|
|
gconstpointer /* (const NMIPAddr *) */ nameserver)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(nameserver);
|
|
|
|
return _garray_inaddr_add(&self->nameservers_x[NM_IS_IPv4(addr_family)],
|
|
addr_family,
|
|
nameserver);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_clear_nameservers(NML3ConfigData *self, int addr_family)
|
|
{
|
|
gs_unref_array GArray *old = NULL;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
old = g_steal_pointer(&self->nameservers_x[NM_IS_IPv4(addr_family)]);
|
|
return (nm_g_array_len(old) > 0);
|
|
}
|
|
|
|
const in_addr_t *
|
|
nm_l3_config_data_get_wins(const NML3ConfigData *self, guint *out_len)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return _garray_inaddr_get(self->wins, out_len);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_add_wins(NML3ConfigData *self, in_addr_t wins)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
return _garray_inaddr_add(&self->wins, AF_INET, &wins);
|
|
}
|
|
|
|
const in_addr_t *
|
|
nm_l3_config_data_get_nis_servers(const NML3ConfigData *self, guint *out_len)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return _garray_inaddr_get(self->nis_servers, out_len);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_add_nis_server(NML3ConfigData *self, in_addr_t nis_server)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
return _garray_inaddr_add(&self->nis_servers, AF_INET, &nis_server);
|
|
}
|
|
|
|
const char *
|
|
nm_l3_config_data_get_nis_domain(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return nm_ref_string_get_str(self->nis_domain);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_nis_domain(NML3ConfigData *self, const char *nis_domain)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
return nm_ref_string_reset_str(&self->nis_domain, nis_domain);
|
|
}
|
|
|
|
const char *const *
|
|
nm_l3_config_data_get_domains(const NML3ConfigData *self, int addr_family, guint *out_len)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(out_len);
|
|
|
|
return nm_strv_ptrarray_get_unsafe(self->domains_x[NM_IS_IPv4(addr_family)], out_len);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_add_domain(NML3ConfigData *self, int addr_family, const char *domain)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
return _check_and_add_domain(&self->domains_x[NM_IS_IPv4(addr_family)], domain);
|
|
}
|
|
|
|
const char *const *
|
|
nm_l3_config_data_get_searches(const NML3ConfigData *self, int addr_family, guint *out_len)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(out_len);
|
|
|
|
return nm_strv_ptrarray_get_unsafe(self->searches_x[NM_IS_IPv4(addr_family)], out_len);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_add_search(NML3ConfigData *self, int addr_family, const char *search)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
return _check_and_add_domain(&self->searches_x[NM_IS_IPv4(addr_family)], search);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_clear_searches(NML3ConfigData *self, int addr_family)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *old = NULL;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
old = g_steal_pointer(&self->searches_x[NM_IS_IPv4(addr_family)]);
|
|
return (nm_g_ptr_array_len(old) > 0);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_add_dns_option(NML3ConfigData *self, int addr_family, const char *dns_option)
|
|
{
|
|
GPtrArray **p_arr;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
g_return_val_if_fail(dns_option, FALSE);
|
|
|
|
if (dns_option[0] == '\0')
|
|
return FALSE;
|
|
|
|
p_arr = &self->dns_options_x[NM_IS_IPv4(addr_family)];
|
|
|
|
if (nm_strv_ptrarray_contains(*p_arr, dns_option))
|
|
return FALSE;
|
|
|
|
nm_strv_ptrarray_add_string_dup(nm_strv_ptrarray_ensure(p_arr), dns_option);
|
|
return TRUE;
|
|
}
|
|
|
|
const char *const *
|
|
nm_l3_config_data_get_dns_options(const NML3ConfigData *self, int addr_family, guint *out_len)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(out_len);
|
|
|
|
return nm_strv_ptrarray_get_unsafe(self->dns_options_x[NM_IS_IPv4(addr_family)], out_len);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_get_dns_priority(const NML3ConfigData *self, int addr_family, int *out_prio)
|
|
{
|
|
switch (addr_family) {
|
|
case AF_UNSPEC:
|
|
if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_4)) {
|
|
if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_6)) {
|
|
NM_SET_OUT(out_prio, MIN(self->dns_priority_4, self->dns_priority_6));
|
|
return TRUE;
|
|
}
|
|
NM_SET_OUT(out_prio, self->dns_priority_4);
|
|
return TRUE;
|
|
}
|
|
if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_6)) {
|
|
NM_SET_OUT(out_prio, self->dns_priority_6);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case AF_INET:
|
|
if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_4)) {
|
|
NM_SET_OUT(out_prio, self->dns_priority_4);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case AF_INET6:
|
|
if (NM_FLAGS_ANY(self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_6)) {
|
|
NM_SET_OUT(out_prio, self->dns_priority_6);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
nm_assert_not_reached();
|
|
}
|
|
NM_SET_OUT(out_prio, 0);
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_dns_priority(NML3ConfigData *self, int addr_family, int dns_priority)
|
|
{
|
|
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
const NML3ConfigDatFlags has_dns_priority_flag =
|
|
NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY(IS_IPv4);
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
if (self->dns_priority_x[IS_IPv4] == dns_priority
|
|
&& NM_FLAGS_ANY(self->flags, has_dns_priority_flag))
|
|
return FALSE;
|
|
|
|
self->flags |= has_dns_priority_flag;
|
|
self->dns_priority_x[IS_IPv4] = dns_priority;
|
|
return TRUE;
|
|
}
|
|
|
|
NMSettingConnectionMdns
|
|
nm_l3_config_data_get_mdns(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->mdns;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_mdns(NML3ConfigData *self, NMSettingConnectionMdns mdns)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
if (self->mdns == mdns)
|
|
return FALSE;
|
|
|
|
self->mdns = mdns;
|
|
return TRUE;
|
|
}
|
|
|
|
NMSettingConnectionLlmnr
|
|
nm_l3_config_data_get_llmnr(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->llmnr;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_llmnr(NML3ConfigData *self, NMSettingConnectionLlmnr llmnr)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
if (self->llmnr == llmnr)
|
|
return FALSE;
|
|
|
|
self->llmnr = llmnr;
|
|
return TRUE;
|
|
}
|
|
|
|
NMIPRouteTableSyncMode
|
|
nm_l3_config_data_get_route_table_sync(const NML3ConfigData *self, int addr_family)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
return self->route_table_sync_x[NM_IS_IPv4(addr_family)];
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_route_table_sync(NML3ConfigData * self,
|
|
int addr_family,
|
|
NMIPRouteTableSyncMode route_table_sync)
|
|
{
|
|
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
if (self->route_table_sync_x[IS_IPv4] == route_table_sync)
|
|
return FALSE;
|
|
|
|
self->route_table_sync_x[IS_IPv4] = route_table_sync;
|
|
return TRUE;
|
|
}
|
|
|
|
NMTernary
|
|
nm_l3_config_data_get_metered(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->metered;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_metered(NML3ConfigData *self, NMTernary metered)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_is_ternary(metered);
|
|
|
|
if (self->metered == metered)
|
|
return FALSE;
|
|
|
|
self->metered = metered;
|
|
return TRUE;
|
|
}
|
|
|
|
guint32
|
|
nm_l3_config_data_get_mtu(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->mtu;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_mtu(NML3ConfigData *self, guint32 mtu)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
if (self->mtu == mtu)
|
|
return FALSE;
|
|
|
|
self->mtu = mtu;
|
|
return TRUE;
|
|
}
|
|
|
|
guint32
|
|
nm_l3_config_data_get_ip6_mtu(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->ip6_mtu;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_ip6_mtu(NML3ConfigData *self, guint32 ip6_mtu)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
if (self->ip6_mtu == ip6_mtu)
|
|
return FALSE;
|
|
|
|
self->ip6_mtu = ip6_mtu;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_source(NML3ConfigData *self, NMIPConfigSource source)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
if (self->source == source)
|
|
return FALSE;
|
|
|
|
self->source = source;
|
|
return TRUE;
|
|
}
|
|
|
|
NMSettingIP6ConfigPrivacy
|
|
nm_l3_config_data_get_ip6_privacy(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
return self->ip6_privacy;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_ip6_privacy(NML3ConfigData *self, NMSettingIP6ConfigPrivacy ip6_privacy)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert(NM_IN_SET(ip6_privacy,
|
|
NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
|
|
NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED,
|
|
NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR,
|
|
NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR));
|
|
|
|
if (self->ip6_privacy == ip6_privacy)
|
|
return FALSE;
|
|
self->ip6_privacy = ip6_privacy;
|
|
nm_assert(self->ip6_privacy == ip6_privacy);
|
|
return TRUE;
|
|
}
|
|
|
|
NMProxyConfigMethod
|
|
nm_l3_config_data_get_proxy_method(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->proxy_method;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_proxy_method(NML3ConfigData *self, NMProxyConfigMethod value)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert(NM_IN_SET(value,
|
|
NM_PROXY_CONFIG_METHOD_UNKNOWN,
|
|
NM_PROXY_CONFIG_METHOD_NONE,
|
|
NM_PROXY_CONFIG_METHOD_AUTO));
|
|
|
|
if (self->proxy_method == value)
|
|
return FALSE;
|
|
|
|
self->proxy_method = value;
|
|
return TRUE;
|
|
}
|
|
|
|
NMTernary
|
|
nm_l3_config_data_get_proxy_browser_only(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->proxy_browser_only;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_proxy_browser_only(NML3ConfigData *self, NMTernary value)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_is_ternary(value);
|
|
|
|
if (value == self->proxy_browser_only)
|
|
return FALSE;
|
|
|
|
self->proxy_browser_only = value;
|
|
return TRUE;
|
|
}
|
|
|
|
const char *
|
|
nm_l3_config_data_get_proxy_pac_url(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return nm_ref_string_get_str(self->proxy_pac_url);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_proxy_pac_url(NML3ConfigData *self, const char *value)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
return nm_ref_string_reset_str(&self->proxy_pac_url, value);
|
|
}
|
|
|
|
const char *
|
|
nm_l3_config_data_get_proxy_pac_script(const NML3ConfigData *self)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return nm_ref_string_get_str(self->proxy_pac_script);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_proxy_pac_script(NML3ConfigData *self, const char *value)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
return nm_ref_string_reset_str(&self->proxy_pac_script, value);
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_get_ndisc_hop_limit(const NML3ConfigData *self, int *out_val)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
if (!self->ndisc_hop_limit_set) {
|
|
NM_SET_OUT(out_val, 0);
|
|
return FALSE;
|
|
}
|
|
NM_SET_OUT(out_val, self->ndisc_hop_limit_val);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_ndisc_hop_limit(NML3ConfigData *self, int val)
|
|
{
|
|
if (self->ndisc_hop_limit_set && self->ndisc_hop_limit_val == val)
|
|
return FALSE;
|
|
self->ndisc_hop_limit_set = TRUE;
|
|
self->ndisc_hop_limit_val = val;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_get_ndisc_reachable_time_msec(const NML3ConfigData *self, guint32 *out_val)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
if (!self->ndisc_reachable_time_msec_set) {
|
|
NM_SET_OUT(out_val, 0);
|
|
return FALSE;
|
|
}
|
|
NM_SET_OUT(out_val, self->ndisc_reachable_time_msec_val);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_ndisc_reachable_time_msec(NML3ConfigData *self, guint32 val)
|
|
{
|
|
if (self->ndisc_reachable_time_msec_set && self->ndisc_reachable_time_msec_val == val)
|
|
return FALSE;
|
|
self->ndisc_reachable_time_msec_set = TRUE;
|
|
self->ndisc_reachable_time_msec_val = val;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_get_ndisc_retrans_timer_msec(const NML3ConfigData *self, guint32 *out_val)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
if (!self->ndisc_retrans_timer_msec_set) {
|
|
NM_SET_OUT(out_val, 0);
|
|
return FALSE;
|
|
}
|
|
NM_SET_OUT(out_val, self->ndisc_retrans_timer_msec_val);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_ndisc_retrans_timer_msec(NML3ConfigData *self, guint32 val)
|
|
{
|
|
if (self->ndisc_retrans_timer_msec_set && self->ndisc_retrans_timer_msec_val == val)
|
|
return FALSE;
|
|
self->ndisc_retrans_timer_msec_set = TRUE;
|
|
self->ndisc_retrans_timer_msec_val = val;
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMDhcpLease *
|
|
nm_l3_config_data_get_dhcp_lease(const NML3ConfigData *self, int addr_family)
|
|
{
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
|
|
return self->dhcp_lease_x[NM_IS_IPv4(addr_family)];
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_dhcp_lease(NML3ConfigData *self, int addr_family, NMDhcpLease *lease)
|
|
{
|
|
nm_auto_unref_dhcplease NMDhcpLease *lease_old = NULL;
|
|
NMDhcpLease ** p_lease;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
p_lease = &self->dhcp_lease_x[NM_IS_IPv4(addr_family)];
|
|
|
|
if (*p_lease == lease)
|
|
return FALSE;
|
|
|
|
if (lease)
|
|
nm_dhcp_lease_ref(lease);
|
|
lease_old = *p_lease;
|
|
*p_lease = lease;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_l3_config_data_set_dhcp_lease_from_options(NML3ConfigData *self,
|
|
int addr_family,
|
|
GHashTable * options_take)
|
|
{
|
|
nm_auto_unref_dhcplease NMDhcpLease *lease = NULL;
|
|
nm_auto_unref_dhcplease NMDhcpLease *lease_old = NULL;
|
|
NMDhcpLease ** p_lease;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
if (options_take)
|
|
lease = nm_dhcp_lease_new_from_options(g_steal_pointer(&options_take));
|
|
|
|
p_lease = &self->dhcp_lease_x[NM_IS_IPv4(addr_family)];
|
|
|
|
if (*p_lease == lease)
|
|
return FALSE;
|
|
|
|
lease_old = *p_lease;
|
|
*p_lease = g_steal_pointer(&lease);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static int
|
|
_dedup_multi_index_cmp(const NML3ConfigData *a, const NML3ConfigData *b, NMPObjectType obj_type)
|
|
{
|
|
const NMDedupMultiHeadEntry *h_a = nm_l3_config_data_lookup_objs(a, obj_type);
|
|
const NMDedupMultiHeadEntry *h_b = nm_l3_config_data_lookup_objs(b, obj_type);
|
|
NMDedupMultiIter iter_a;
|
|
NMDedupMultiIter iter_b;
|
|
|
|
NM_CMP_SELF(h_a, h_b);
|
|
NM_CMP_DIRECT(h_a->len, h_b->len);
|
|
|
|
nm_assert(h_a->len > 0);
|
|
|
|
nm_dedup_multi_iter_init(&iter_a, h_a);
|
|
nm_dedup_multi_iter_init(&iter_b, h_b);
|
|
|
|
while (TRUE) {
|
|
const NMPObject *obj_a;
|
|
const NMPObject *obj_b;
|
|
gboolean have_a;
|
|
gboolean have_b;
|
|
|
|
have_a = nm_platform_dedup_multi_iter_next_obj(&iter_a, &obj_a, obj_type);
|
|
if (!have_a) {
|
|
nm_assert(!nm_platform_dedup_multi_iter_next_obj(&iter_b, &obj_b, obj_type));
|
|
return 0;
|
|
}
|
|
|
|
have_b = nm_platform_dedup_multi_iter_next_obj(&iter_b, &obj_b, obj_type);
|
|
nm_assert(have_b);
|
|
|
|
NM_CMP_RETURN(nmp_object_cmp(obj_a, obj_b));
|
|
}
|
|
}
|
|
|
|
int
|
|
nm_l3_config_data_cmp_full(const NML3ConfigData *a,
|
|
const NML3ConfigData *b,
|
|
NML3ConfigCmpFlags cmp_flags)
|
|
{
|
|
int IS_IPv4;
|
|
|
|
NM_CMP_SELF(a, b);
|
|
|
|
if (!NM_FLAGS_HAS(cmp_flags, NM_L3_CONFIG_CMP_FLAGS_IGNORE_IFINDEX))
|
|
NM_CMP_DIRECT(a->ifindex, b->ifindex);
|
|
|
|
NM_CMP_DIRECT(a->flags, b->flags);
|
|
|
|
NM_CMP_RETURN(_dedup_multi_index_cmp(a, b, NMP_OBJECT_TYPE_IP4_ADDRESS));
|
|
NM_CMP_RETURN(_dedup_multi_index_cmp(a, b, NMP_OBJECT_TYPE_IP6_ADDRESS));
|
|
NM_CMP_RETURN(_dedup_multi_index_cmp(a, b, NMP_OBJECT_TYPE_IP4_ROUTE));
|
|
NM_CMP_RETURN(_dedup_multi_index_cmp(a, b, NMP_OBJECT_TYPE_IP6_ROUTE));
|
|
|
|
for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
|
|
const int addr_family = IS_IPv4 ? AF_INET : AF_INET6;
|
|
|
|
NM_CMP_RETURN(
|
|
nmp_object_cmp(a->best_default_route_x[IS_IPv4], b->best_default_route_x[IS_IPv4]));
|
|
|
|
NM_CMP_RETURN(
|
|
_garray_inaddr_cmp(a->nameservers_x[IS_IPv4], b->nameservers_x[IS_IPv4], addr_family));
|
|
|
|
NM_CMP_RETURN(nm_utils_hashtable_cmp(nm_dhcp_lease_get_options(a->dhcp_lease_x[IS_IPv4]),
|
|
nm_dhcp_lease_get_options(b->dhcp_lease_x[IS_IPv4]),
|
|
TRUE,
|
|
nm_strcmp_with_data,
|
|
nm_strcmp_with_data,
|
|
NULL));
|
|
|
|
NM_CMP_RETURN(nm_strv_ptrarray_cmp(a->domains_x[IS_IPv4], b->domains_x[IS_IPv4]));
|
|
NM_CMP_RETURN(nm_strv_ptrarray_cmp(a->searches_x[IS_IPv4], b->searches_x[IS_IPv4]));
|
|
NM_CMP_RETURN(nm_strv_ptrarray_cmp(a->dns_options_x[IS_IPv4], b->dns_options_x[IS_IPv4]));
|
|
|
|
if (NM_FLAGS_ANY(a->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY(IS_IPv4)))
|
|
NM_CMP_DIRECT(a->dns_priority_x[IS_IPv4], b->dns_priority_x[IS_IPv4]);
|
|
|
|
NM_CMP_DIRECT(a->route_table_sync_x[IS_IPv4], b->route_table_sync_x[IS_IPv4]);
|
|
}
|
|
|
|
NM_CMP_RETURN(_garray_inaddr_cmp(a->wins, b->wins, AF_INET));
|
|
NM_CMP_RETURN(_garray_inaddr_cmp(a->nis_servers, b->nis_servers, AF_INET));
|
|
NM_CMP_DIRECT_REF_STRING(a->nis_domain, b->nis_domain);
|
|
NM_CMP_DIRECT(a->mdns, b->mdns);
|
|
NM_CMP_DIRECT(a->llmnr, b->llmnr);
|
|
NM_CMP_DIRECT(a->mtu, b->mtu);
|
|
NM_CMP_DIRECT(a->ip6_mtu, b->ip6_mtu);
|
|
NM_CMP_DIRECT_UNSAFE(a->metered, b->metered);
|
|
NM_CMP_DIRECT_UNSAFE(a->proxy_browser_only, b->proxy_browser_only);
|
|
NM_CMP_DIRECT_UNSAFE(a->proxy_method, b->proxy_method);
|
|
NM_CMP_DIRECT_REF_STRING(a->proxy_pac_url, b->proxy_pac_url);
|
|
NM_CMP_DIRECT_REF_STRING(a->proxy_pac_script, b->proxy_pac_script);
|
|
NM_CMP_DIRECT_UNSAFE(a->ip6_privacy, b->ip6_privacy);
|
|
|
|
NM_CMP_DIRECT_UNSAFE(a->ndisc_hop_limit_set, b->ndisc_hop_limit_set);
|
|
if (a->ndisc_hop_limit_set)
|
|
NM_CMP_DIRECT(a->ndisc_hop_limit_val, b->ndisc_hop_limit_val);
|
|
|
|
NM_CMP_DIRECT_UNSAFE(a->ndisc_reachable_time_msec_set, b->ndisc_reachable_time_msec_set);
|
|
if (a->ndisc_reachable_time_msec_set)
|
|
NM_CMP_DIRECT(a->ndisc_reachable_time_msec_val, b->ndisc_reachable_time_msec_val);
|
|
|
|
NM_CMP_DIRECT_UNSAFE(a->ndisc_retrans_timer_msec_set, b->ndisc_retrans_timer_msec_set);
|
|
if (a->ndisc_retrans_timer_msec_set)
|
|
NM_CMP_DIRECT(a->ndisc_retrans_timer_msec_val, b->ndisc_retrans_timer_msec_val);
|
|
|
|
NM_CMP_FIELD(a, b, source);
|
|
|
|
/* these fields are not considered by cmp():
|
|
*
|
|
* - multi_idx
|
|
* - ref_count
|
|
* - is_sealed
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const NMPObject *
|
|
_data_get_direct_route_for_host(const NML3ConfigData *self,
|
|
int addr_family,
|
|
gconstpointer host,
|
|
guint32 route_table)
|
|
{
|
|
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
const NMPObject * best_route_obj = NULL;
|
|
const NMPlatformIPXRoute *best_route = NULL;
|
|
const NMPObject * item_obj;
|
|
NMDedupMultiIter ipconf_iter;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(host);
|
|
|
|
if (nm_ip_addr_is_null(addr_family, host))
|
|
return NULL;
|
|
|
|
nm_l3_config_data_iter_obj_for_each (&ipconf_iter,
|
|
self,
|
|
&item_obj,
|
|
NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) {
|
|
const NMPlatformIPXRoute *item = NMP_OBJECT_CAST_IPX_ROUTE(item_obj);
|
|
|
|
if (nm_ip_addr_is_null(addr_family,
|
|
nm_platform_ip_route_get_gateway(addr_family, &item->rx)))
|
|
continue;
|
|
|
|
if (best_route && best_route->rx.plen > item->rx.plen)
|
|
continue;
|
|
|
|
if (nm_platform_route_table_uncoerce(item->rx.table_coerced, TRUE) != route_table)
|
|
continue;
|
|
|
|
if (!nm_utils_ip_address_same_prefix(addr_family,
|
|
host,
|
|
item->rx.network_ptr,
|
|
item->rx.plen))
|
|
continue;
|
|
|
|
if (best_route && best_route->rx.metric <= item->rx.metric)
|
|
continue;
|
|
|
|
best_route_obj = item_obj;
|
|
best_route = item;
|
|
}
|
|
return best_route_obj;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Kernel likes to add device routes for all addresses. Normally, we want to suppress that
|
|
* with IFA_F_NOPREFIXROUTE. But we also want to support kernels that don't support that
|
|
* flag. So, we collect here all those routes that kernel might add but we don't want.
|
|
* If the route shows up within a certain timeout of us configuring the address, we assume
|
|
* that it was (undesirably) added by kernel and we remove it.
|
|
*
|
|
* The most common reason is that for each IPv4 address we want to add a corresponding device
|
|
* route with the right ipv4.route-metric. The route that kernel adds has metric 0, so it is
|
|
* undesired.
|
|
*
|
|
* FIXME(l3cfg): implement handling blacklisted routes.
|
|
*
|
|
* For IPv6, IFA_F_NOPREFIXROUTE is supported for a longer time and we don't do such a hack.
|
|
*/
|
|
GPtrArray *
|
|
nm_l3_config_data_get_blacklisted_ip4_routes(const NML3ConfigData *self, gboolean is_vrf)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
|
|
const NMPObject * my_addr_obj;
|
|
NMDedupMultiIter iter;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
|
|
/* For IPv6 slaac, we explicitly add the device-routes (onlink).
|
|
* As we don't do that for IPv4 and manual IPv6 addresses. Add them here
|
|
* as dependent routes. */
|
|
|
|
nm_l3_config_data_iter_obj_for_each (&iter, self, &my_addr_obj, NMP_OBJECT_TYPE_IP4_ADDRESS) {
|
|
const NMPlatformIP4Address *const my_addr = NMP_OBJECT_CAST_IP4_ADDRESS(my_addr_obj);
|
|
in_addr_t network_4;
|
|
NMPlatformIPXRoute rx;
|
|
|
|
if (my_addr->external)
|
|
continue;
|
|
|
|
nm_assert(my_addr->plen <= 32);
|
|
if (my_addr->plen == 0)
|
|
continue;
|
|
|
|
network_4 = nm_utils_ip4_address_clear_host_address(my_addr->peer_address, my_addr->plen);
|
|
|
|
if (nm_utils_ip4_address_is_zeronet(network_4)) {
|
|
/* 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;
|
|
}
|
|
|
|
rx.r4 = (NMPlatformIP4Route){
|
|
.ifindex = self->ifindex,
|
|
.rt_source = NM_IP_CONFIG_SOURCE_KERNEL,
|
|
.network = network_4,
|
|
.plen = my_addr->plen,
|
|
.pref_src = my_addr->address,
|
|
.table_coerced = nm_platform_route_table_coerce(RT_TABLE_MAIN),
|
|
.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE,
|
|
.scope_inv = nm_platform_route_scope_inv(NM_RT_SCOPE_LINK),
|
|
};
|
|
nm_platform_ip_route_normalize(AF_INET, &rx.rx);
|
|
|
|
if (nm_l3_config_data_lookup_route(self, AF_INET, &rx.rx)) {
|
|
/* we track such a route explicitly. Don't blacklist it. */
|
|
continue;
|
|
}
|
|
|
|
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, nmp_object_new(NMP_OBJECT_TYPE_IP4_ROUTE, &rx));
|
|
}
|
|
|
|
return g_steal_pointer(&ip4_dev_route_blacklist);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_l3_config_data_add_dependent_routes(NML3ConfigData *self,
|
|
int addr_family,
|
|
guint32 route_table,
|
|
guint32 route_metric,
|
|
gboolean is_vrf)
|
|
{
|
|
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
gs_unref_ptrarray GPtrArray *extra_onlink_routes = NULL;
|
|
const NMPObject * my_addr_obj;
|
|
const NMPObject * my_route_obj;
|
|
NMPlatformIPXRoute rx;
|
|
NMDedupMultiIter iter;
|
|
in_addr_t network_4 = 0;
|
|
guint i;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
/* For IPv6 slaac, we explicitly add the device-routes (onlink).
|
|
* As we don't do that for IPv4 and manual IPv6 addresses. Add them here
|
|
* as dependent routes. */
|
|
|
|
if (!IS_IPv4) {
|
|
/* Pre-generate multicast route */
|
|
rx.r6 = (NMPlatformIP6Route){
|
|
.ifindex = self->ifindex,
|
|
.network.s6_addr[0] = 0xffu,
|
|
.plen = 8,
|
|
.table_coerced = nm_platform_route_table_coerce(RT_TABLE_LOCAL),
|
|
.type_coerced = nm_platform_route_type_coerce(RTN_UNICAST),
|
|
.metric = 256,
|
|
};
|
|
nm_l3_config_data_add_route(self, addr_family, NULL, &rx.rx);
|
|
}
|
|
|
|
nm_l3_config_data_iter_obj_for_each (&iter,
|
|
self,
|
|
&my_addr_obj,
|
|
NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) {
|
|
const NMPlatformIPXAddress *const my_addr = NMP_OBJECT_CAST_IPX_ADDRESS(my_addr_obj);
|
|
|
|
if (my_addr->ax.external)
|
|
continue;
|
|
|
|
if (IS_IPv4) {
|
|
nm_assert(my_addr->a4.plen <= 32);
|
|
if (my_addr->a4.plen == 0)
|
|
continue;
|
|
}
|
|
|
|
if (IS_IPv4) {
|
|
rx.r4 = (NMPlatformIP4Route){
|
|
.ifindex = self->ifindex,
|
|
.rt_source = NM_IP_CONFIG_SOURCE_KERNEL,
|
|
.network = my_addr->a4.address,
|
|
.plen = 32,
|
|
.pref_src = my_addr->a4.address,
|
|
.type_coerced = nm_platform_route_type_coerce(RTN_LOCAL),
|
|
.scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST),
|
|
.table_coerced =
|
|
nm_platform_route_table_coerce(is_vrf ? route_table : RT_TABLE_LOCAL),
|
|
};
|
|
} else {
|
|
rx.r6 = (NMPlatformIP6Route){
|
|
.ifindex = self->ifindex,
|
|
.network = my_addr->a6.address,
|
|
.plen = 128,
|
|
.type_coerced = nm_platform_route_type_coerce(RTN_LOCAL),
|
|
.metric = 0,
|
|
.table_coerced =
|
|
nm_platform_route_table_coerce(is_vrf ? route_table : RT_TABLE_LOCAL),
|
|
};
|
|
}
|
|
nm_l3_config_data_add_route(self, addr_family, NULL, &rx.rx);
|
|
|
|
if (my_addr->ax.plen == 0)
|
|
continue;
|
|
|
|
if (IS_IPv4) {
|
|
network_4 =
|
|
nm_utils_ip4_address_clear_host_address(my_addr->a4.peer_address, my_addr->a4.plen);
|
|
|
|
if (nm_utils_ip4_address_is_zeronet(network_4)) {
|
|
/* Kernel doesn't add device-routes for destinations that
|
|
* start with 0.x.y.z. Skip them. */
|
|
continue;
|
|
}
|
|
|
|
if (my_addr->a4.plen == 32 && my_addr->a4.address == my_addr->a4.peer_address) {
|
|
/* Kernel doesn't add device-routes for /32 addresses unless
|
|
* they have a peer. */
|
|
continue;
|
|
}
|
|
} else {
|
|
if (NM_FLAGS_HAS(my_addr->a6.n_ifa_flags, IFA_F_NOPREFIXROUTE))
|
|
continue;
|
|
}
|
|
|
|
if (IS_IPv4) {
|
|
rx.r4 = (NMPlatformIP4Route){
|
|
.ifindex = self->ifindex,
|
|
.rt_source = NM_IP_CONFIG_SOURCE_KERNEL,
|
|
.network = network_4,
|
|
.plen = my_addr->a4.plen,
|
|
.pref_src = my_addr->a4.address,
|
|
.table_coerced = nm_platform_route_table_coerce(route_table),
|
|
.metric = route_metric,
|
|
.scope_inv = nm_platform_route_scope_inv(NM_RT_SCOPE_LINK),
|
|
};
|
|
nm_platform_ip_route_normalize(addr_family, &rx.rx);
|
|
nm_l3_config_data_add_route(self, addr_family, NULL, &rx.rx);
|
|
} else {
|
|
const gboolean has_peer = !IN6_IS_ADDR_UNSPECIFIED(&my_addr->a6.peer_address);
|
|
int routes_i;
|
|
|
|
/* If we have an IPv6 peer, we add two /128 routes
|
|
* (unless, both addresses are identical). */
|
|
for (routes_i = 0; routes_i < 2; routes_i++) {
|
|
struct in6_addr a6_stack;
|
|
const struct in6_addr *a6;
|
|
guint8 plen;
|
|
|
|
if (routes_i == 1 && has_peer
|
|
&& IN6_ARE_ADDR_EQUAL(&my_addr->a6.address, &my_addr->a6.peer_address))
|
|
break;
|
|
|
|
if (has_peer) {
|
|
if (routes_i == 0)
|
|
a6 = &my_addr->a6.address;
|
|
else
|
|
a6 = &my_addr->a6.peer_address;
|
|
plen = 128;
|
|
} else {
|
|
a6 = nm_utils_ip6_address_clear_host_address(&a6_stack,
|
|
&my_addr->a6.address,
|
|
my_addr->a6.plen);
|
|
plen = my_addr->a6.plen;
|
|
}
|
|
|
|
rx.r6 = (NMPlatformIP6Route){
|
|
.ifindex = self->ifindex,
|
|
.rt_source = NM_IP_CONFIG_SOURCE_KERNEL,
|
|
.table_coerced = nm_platform_route_table_coerce(route_table),
|
|
.metric = route_metric,
|
|
.network = *a6,
|
|
.plen = plen,
|
|
};
|
|
nm_platform_ip_route_normalize(addr_family, &rx.rx);
|
|
nm_l3_config_data_add_route(self, addr_family, NULL, &rx.rx);
|
|
}
|
|
}
|
|
}
|
|
|
|
nm_l3_config_data_iter_obj_for_each (&iter,
|
|
self,
|
|
&my_route_obj,
|
|
NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) {
|
|
const NMPlatformIPXRoute *my_route = NMP_OBJECT_CAST_IPX_ROUTE(my_route_obj);
|
|
NMPObject * new_route;
|
|
NMPlatformIPXRoute * new_r;
|
|
const NMIPAddr * p_gateway;
|
|
|
|
if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(my_route)
|
|
|| NM_IS_IP_CONFIG_SOURCE_RTPROT(my_route->rx.rt_source))
|
|
continue;
|
|
|
|
p_gateway = nm_platform_ip_route_get_gateway(addr_family, &my_route->rx);
|
|
|
|
if (nm_ip_addr_is_null(addr_family, p_gateway))
|
|
continue;
|
|
|
|
if (_data_get_direct_route_for_host(
|
|
self,
|
|
addr_family,
|
|
p_gateway,
|
|
nm_platform_route_table_uncoerce(my_route->rx.table_coerced, TRUE)))
|
|
continue;
|
|
|
|
new_route = nmp_object_clone(my_route_obj, FALSE);
|
|
new_r = NMP_OBJECT_CAST_IPX_ROUTE(new_route);
|
|
if (IS_IPv4) {
|
|
new_r->r4.network = my_route->r4.gateway;
|
|
new_r->r4.plen = 32;
|
|
new_r->r4.gateway = 0;
|
|
} else {
|
|
new_r->r6.network = my_route->r6.gateway;
|
|
new_r->r6.plen = 128;
|
|
new_r->r6.gateway = in6addr_any;
|
|
}
|
|
|
|
/* we cannot add the route right away, because that invalidates the iteration. */
|
|
if (!extra_onlink_routes)
|
|
extra_onlink_routes = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
|
|
g_ptr_array_add(extra_onlink_routes, new_route);
|
|
}
|
|
if (extra_onlink_routes) {
|
|
for (i = 0; i < extra_onlink_routes->len; i++) {
|
|
nm_l3_config_data_add_route_full(self,
|
|
addr_family,
|
|
extra_onlink_routes->pdata[i],
|
|
NULL,
|
|
NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_init_from_connection_ip(NML3ConfigData *self,
|
|
int addr_family,
|
|
NMConnection * connection,
|
|
guint32 route_table,
|
|
guint32 route_metric)
|
|
{
|
|
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
NMSettingIPConfig *s_ip;
|
|
guint naddresses;
|
|
guint nroutes;
|
|
guint nnameservers;
|
|
guint nsearches;
|
|
const char * gateway_str;
|
|
NMIPAddr gateway_bin;
|
|
guint i;
|
|
int idx;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
nm_assert(!connection || NM_IS_CONNECTION(connection));
|
|
|
|
if (!connection)
|
|
return;
|
|
|
|
s_ip = nm_connection_get_setting_ip_config(connection, addr_family);
|
|
if (!s_ip)
|
|
return;
|
|
|
|
if (!nm_setting_ip_config_get_never_default(s_ip)
|
|
&& (gateway_str = nm_setting_ip_config_get_gateway(s_ip))
|
|
&& inet_pton(addr_family, gateway_str, &gateway_bin) == 1
|
|
&& !nm_ip_addr_is_null(addr_family, &gateway_bin)) {
|
|
NMPlatformIPXRoute r;
|
|
|
|
if (IS_IPv4) {
|
|
r.r4 = (NMPlatformIP4Route){
|
|
.rt_source = NM_IP_CONFIG_SOURCE_USER,
|
|
.gateway = gateway_bin.addr4,
|
|
.table_coerced = nm_platform_route_table_coerce(route_table),
|
|
.metric = route_metric,
|
|
};
|
|
} else {
|
|
r.r6 = (NMPlatformIP6Route){
|
|
.rt_source = NM_IP_CONFIG_SOURCE_USER,
|
|
.gateway = gateway_bin.addr6,
|
|
.table_coerced = nm_platform_route_table_coerce(route_table),
|
|
.metric = route_metric,
|
|
};
|
|
}
|
|
|
|
nm_l3_config_data_add_route(self, addr_family, NULL, &r.rx);
|
|
}
|
|
|
|
naddresses = nm_setting_ip_config_get_num_addresses(s_ip);
|
|
for (i = 0; i < naddresses; i++) {
|
|
NMIPAddress * s_addr = nm_setting_ip_config_get_address(s_ip, i);
|
|
NMPlatformIPXAddress a;
|
|
NMIPAddr addr_bin;
|
|
GVariant * label;
|
|
|
|
nm_assert(nm_ip_address_get_family(s_addr) == addr_family);
|
|
|
|
nm_ip_address_get_address_binary(s_addr, &addr_bin);
|
|
|
|
if (IS_IPv4) {
|
|
a.a4 = (NMPlatformIP4Address){
|
|
.address = addr_bin.addr4,
|
|
.peer_address = addr_bin.addr4,
|
|
.plen = nm_ip_address_get_prefix(s_addr),
|
|
.lifetime = NM_PLATFORM_LIFETIME_PERMANENT,
|
|
.preferred = NM_PLATFORM_LIFETIME_PERMANENT,
|
|
.addr_source = NM_IP_CONFIG_SOURCE_USER,
|
|
};
|
|
label = nm_ip_address_get_attribute(s_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
|
|
if (label)
|
|
g_strlcpy(a.a4.label, g_variant_get_string(label, NULL), sizeof(a.a4.label));
|
|
|
|
nm_assert(a.a4.plen <= 32);
|
|
} else {
|
|
a.a6 = (NMPlatformIP6Address){
|
|
.address = addr_bin.addr6,
|
|
.plen = nm_ip_address_get_prefix(s_addr),
|
|
.lifetime = NM_PLATFORM_LIFETIME_PERMANENT,
|
|
.preferred = NM_PLATFORM_LIFETIME_PERMANENT,
|
|
.addr_source = NM_IP_CONFIG_SOURCE_USER,
|
|
};
|
|
|
|
nm_assert(a.a6.plen <= 128);
|
|
}
|
|
|
|
nm_l3_config_data_add_address(self, addr_family, NULL, &a.ax);
|
|
}
|
|
|
|
nroutes = nm_setting_ip_config_get_num_routes(s_ip);
|
|
for (i = 0; i < nroutes; i++) {
|
|
NMIPRoute * s_route = nm_setting_ip_config_get_route(s_ip, i);
|
|
NMPlatformIPXRoute r;
|
|
NMIPAddr network_bin;
|
|
NMIPAddr next_hop_bin;
|
|
gint64 metric64;
|
|
guint32 metric;
|
|
guint plen;
|
|
|
|
nm_assert(nm_ip_route_get_family(s_route) == addr_family);
|
|
|
|
nm_ip_route_get_dest_binary(s_route, &network_bin);
|
|
nm_ip_route_get_next_hop_binary(s_route, &next_hop_bin);
|
|
|
|
metric64 = nm_ip_route_get_metric(s_route);
|
|
if (metric64 < 0)
|
|
metric = route_metric;
|
|
else
|
|
metric = metric64;
|
|
metric = nm_utils_ip_route_metric_normalize(addr_family, metric);
|
|
|
|
plen = nm_ip_route_get_prefix(s_route);
|
|
|
|
nm_utils_ipx_address_clear_host_address(addr_family, &network_bin, &network_bin, plen);
|
|
|
|
if (IS_IPv4) {
|
|
r.r4 = (NMPlatformIP4Route){
|
|
.network = network_bin.addr4,
|
|
.plen = nm_ip_route_get_prefix(s_route),
|
|
.gateway = next_hop_bin.addr4,
|
|
.metric = metric,
|
|
.rt_source = NM_IP_CONFIG_SOURCE_USER,
|
|
};
|
|
nm_assert(r.r4.plen <= 32);
|
|
} else {
|
|
r.r6 = (NMPlatformIP6Route){
|
|
.network = network_bin.addr6,
|
|
.plen = nm_ip_route_get_prefix(s_route),
|
|
.gateway = next_hop_bin.addr6,
|
|
.metric = metric,
|
|
.rt_source = NM_IP_CONFIG_SOURCE_USER,
|
|
};
|
|
nm_assert(r.r6.plen <= 128);
|
|
}
|
|
|
|
nm_utils_ip_route_attribute_to_platform(addr_family, s_route, &r.rx, route_table);
|
|
|
|
nm_l3_config_data_add_route(self, addr_family, NULL, &r.rx);
|
|
}
|
|
|
|
nnameservers = nm_setting_ip_config_get_num_dns(s_ip);
|
|
for (i = 0; i < nnameservers; i++) {
|
|
const char *s;
|
|
NMIPAddr ip;
|
|
|
|
s = nm_setting_ip_config_get_dns(s_ip, i);
|
|
if (!nm_utils_parse_inaddr_bin(addr_family, s, NULL, &ip))
|
|
continue;
|
|
nm_l3_config_data_add_nameserver(self, addr_family, &ip);
|
|
}
|
|
|
|
nsearches = nm_setting_ip_config_get_num_dns_searches(s_ip);
|
|
for (i = 0; i < nsearches; i++) {
|
|
nm_l3_config_data_add_search(self,
|
|
addr_family,
|
|
nm_setting_ip_config_get_dns_search(s_ip, i));
|
|
}
|
|
|
|
idx = 0;
|
|
while ((idx = nm_setting_ip_config_next_valid_dns_option(s_ip, i)) >= 0) {
|
|
nm_l3_config_data_add_dns_option(self,
|
|
addr_family,
|
|
nm_setting_ip_config_get_dns_option(s_ip, i));
|
|
idx++;
|
|
}
|
|
|
|
nm_l3_config_data_set_dns_priority(self,
|
|
addr_family,
|
|
nm_setting_ip_config_get_dns_priority(s_ip));
|
|
}
|
|
|
|
NML3ConfigData *
|
|
nm_l3_config_data_new_from_connection(NMDedupMultiIndex *multi_idx,
|
|
int ifindex,
|
|
NMConnection * connection,
|
|
guint32 route_table_4,
|
|
guint32 route_table_6,
|
|
guint32 route_metric_4,
|
|
guint32 route_metric_6)
|
|
{
|
|
NML3ConfigData *self;
|
|
NMSettingProxy *s_proxy;
|
|
|
|
self = nm_l3_config_data_new(multi_idx, ifindex);
|
|
|
|
_init_from_connection_ip(self, AF_INET, connection, route_table_4, route_metric_4);
|
|
_init_from_connection_ip(self, AF_INET6, connection, route_table_6, route_metric_6);
|
|
|
|
s_proxy = _nm_connection_get_setting(connection, NM_TYPE_SETTING_PROXY);
|
|
if (s_proxy) {
|
|
switch (nm_setting_proxy_get_method(s_proxy)) {
|
|
case NM_SETTING_PROXY_METHOD_NONE:
|
|
self->proxy_method = NM_PROXY_CONFIG_METHOD_NONE;
|
|
break;
|
|
case NM_SETTING_PROXY_METHOD_AUTO:
|
|
self->proxy_method = NM_PROXY_CONFIG_METHOD_AUTO;
|
|
self->proxy_pac_url = nm_ref_string_new(nm_setting_proxy_get_pac_url(s_proxy));
|
|
self->proxy_pac_script = nm_ref_string_new(nm_setting_proxy_get_pac_script(s_proxy));
|
|
break;
|
|
}
|
|
self->proxy_browser_only = (!!nm_setting_proxy_get_browser_only(s_proxy));
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
static int
|
|
sort_captured_addresses_4(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);
|
|
}
|
|
|
|
static int
|
|
sort_captured_addresses_6(const CList *lst_a, const CList *lst_b, gconstpointer user_data)
|
|
{
|
|
NMSettingIP6ConfigPrivacy ipv6_privacy_rfc4941 = GPOINTER_TO_INT(user_data);
|
|
const NMPlatformIP6Address *addr_a =
|
|
NMP_OBJECT_CAST_IP6_ADDRESS(c_list_entry(lst_a, NMDedupMultiEntry, lst_entries)->obj);
|
|
const NMPlatformIP6Address *addr_b =
|
|
NMP_OBJECT_CAST_IP6_ADDRESS(c_list_entry(lst_b, NMDedupMultiEntry, lst_entries)->obj);
|
|
|
|
return nm_platform_ip6_address_pretty_sort_cmp(
|
|
addr_a,
|
|
addr_b,
|
|
ipv6_privacy_rfc4941 == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR);
|
|
}
|
|
|
|
static void
|
|
_init_from_platform(NML3ConfigData * self,
|
|
int addr_family,
|
|
NMPlatform * platform,
|
|
NMSettingIP6ConfigPrivacy ipv6_privacy_rfc4941)
|
|
{
|
|
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
|
const NMDedupMultiHeadEntry *head_entry;
|
|
const NMPObject * plobj = NULL;
|
|
NMDedupMultiIter iter;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert_addr_family(addr_family);
|
|
|
|
head_entry = nm_platform_lookup_object(platform,
|
|
IS_IPv4 ? NMP_OBJECT_TYPE_IP4_ADDRESS
|
|
: NMP_OBJECT_TYPE_IP6_ADDRESS,
|
|
self->ifindex);
|
|
if (head_entry) {
|
|
if (IS_IPv4)
|
|
self->has_routes_with_type_local_4_set = FALSE;
|
|
else
|
|
self->has_routes_with_type_local_6_set = FALSE;
|
|
nmp_cache_iter_for_each (&iter, head_entry, &plobj) {
|
|
if (!_l3_config_data_add_obj(self->multi_idx,
|
|
&self->idx_addresses_x[IS_IPv4],
|
|
self->ifindex,
|
|
plobj,
|
|
NULL,
|
|
NM_L3_CONFIG_ADD_FLAGS_APPEND_FORCE,
|
|
NULL,
|
|
NULL))
|
|
nm_assert_not_reached();
|
|
}
|
|
head_entry = nm_l3_config_data_lookup_addresses(self, addr_family);
|
|
nm_assert(head_entry);
|
|
nm_dedup_multi_head_entry_sort(head_entry,
|
|
IS_IPv4 ? sort_captured_addresses_4
|
|
: sort_captured_addresses_6,
|
|
GINT_TO_POINTER(ipv6_privacy_rfc4941));
|
|
}
|
|
|
|
head_entry =
|
|
nm_platform_lookup_object(platform,
|
|
IS_IPv4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE,
|
|
self->ifindex);
|
|
nmp_cache_iter_for_each (&iter, head_entry, &plobj)
|
|
nm_l3_config_data_add_route(self, addr_family, plobj, NULL);
|
|
}
|
|
|
|
NML3ConfigData *
|
|
nm_l3_config_data_new_from_platform(NMDedupMultiIndex * multi_idx,
|
|
int ifindex,
|
|
NMPlatform * platform,
|
|
NMSettingIP6ConfigPrivacy ipv6_privacy_rfc4941)
|
|
{
|
|
NML3ConfigData *self;
|
|
|
|
nm_assert(NM_IS_PLATFORM(platform));
|
|
nm_assert(ifindex > 0);
|
|
|
|
/* Slaves have no IP configuration */
|
|
if (nm_platform_link_get_master(platform, ifindex) > 0)
|
|
return NULL;
|
|
|
|
self = nm_l3_config_data_new(multi_idx, ifindex);
|
|
|
|
_init_from_platform(self, AF_INET, platform, ipv6_privacy_rfc4941);
|
|
_init_from_platform(self, AF_INET6, platform, ipv6_privacy_rfc4941);
|
|
|
|
return self;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_l3_config_data_merge(NML3ConfigData * self,
|
|
const NML3ConfigData *src,
|
|
NML3ConfigMergeFlags merge_flags,
|
|
const guint32 * default_route_table_x /* length 2, for IS_IPv4 */,
|
|
const guint32 * default_route_metric_x /* length 2, for IS_IPv4 */,
|
|
const guint32 * default_route_penalty_x /* length 2, for IS_IPv4 */,
|
|
NML3ConfigMergeHookAddObj hook_add_addr,
|
|
gpointer hook_user_data)
|
|
{
|
|
static const guint32 x_default_route_table_x[2] = {RT_TABLE_MAIN, RT_TABLE_MAIN};
|
|
static const guint32 x_default_route_metric_x[2] = {NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6,
|
|
NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4};
|
|
static const guint32 x_default_route_penalty_x[2] = {0, 0};
|
|
NMDedupMultiIter iter;
|
|
const NMPObject * obj;
|
|
int IS_IPv4;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(src, TRUE));
|
|
|
|
if (!default_route_table_x)
|
|
default_route_table_x = x_default_route_table_x;
|
|
if (!default_route_metric_x)
|
|
default_route_metric_x = x_default_route_metric_x;
|
|
if (!default_route_penalty_x)
|
|
default_route_penalty_x = x_default_route_penalty_x;
|
|
|
|
nm_assert(default_route_table_x[0] != 0);
|
|
nm_assert(default_route_table_x[1] != 0);
|
|
nm_assert(default_route_metric_x[0] != 0); /* IPv6 route metric cannot be zero. */
|
|
|
|
nm_assert(!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)
|
|
|| nm_utils_is_power_of_two(merge_flags));
|
|
|
|
for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
|
|
const int addr_family = IS_IPv4 ? AF_INET : AF_INET6;
|
|
const NML3ConfigDatFlags has_dns_priority_flag =
|
|
NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY(IS_IPv4);
|
|
|
|
nm_l3_config_data_iter_obj_for_each (&iter,
|
|
src,
|
|
&obj,
|
|
NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) {
|
|
NMPlatformIPXAddress addr_stack;
|
|
const NMPlatformIPAddress *addr = NULL;
|
|
NMTernary ip4acd_not_ready = NM_TERNARY_DEFAULT;
|
|
|
|
if (hook_add_addr && !hook_add_addr(src, obj, &ip4acd_not_ready, hook_user_data))
|
|
continue;
|
|
|
|
if (IS_IPv4 && ip4acd_not_ready != NM_TERNARY_DEFAULT
|
|
&& (!!ip4acd_not_ready) != NMP_OBJECT_CAST_IP4_ADDRESS(obj)->ip4acd_not_ready) {
|
|
addr_stack.a4 = *NMP_OBJECT_CAST_IP4_ADDRESS(obj);
|
|
addr_stack.a4.ip4acd_not_ready = (!!ip4acd_not_ready);
|
|
addr = &addr_stack.ax;
|
|
} else
|
|
nm_assert(IS_IPv4 || ip4acd_not_ready == NM_TERNARY_DEFAULT);
|
|
|
|
nm_l3_config_data_add_address_full(self,
|
|
addr_family,
|
|
addr ? NULL : obj,
|
|
addr,
|
|
NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE,
|
|
NULL);
|
|
}
|
|
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES)) {
|
|
nm_l3_config_data_iter_obj_for_each (&iter,
|
|
src,
|
|
&obj,
|
|
NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) {
|
|
const NMPlatformIPRoute *r_src = NMP_OBJECT_CAST_IP_ROUTE(obj);
|
|
NMPlatformIPXRoute r;
|
|
|
|
#define _ensure_r() \
|
|
G_STMT_START \
|
|
{ \
|
|
if (r_src != &r.rx) { \
|
|
r_src = &r.rx; \
|
|
if (IS_IPv4) \
|
|
r.r4 = *NMP_OBJECT_CAST_IP4_ROUTE(obj); \
|
|
else \
|
|
r.r6 = *NMP_OBJECT_CAST_IP6_ROUTE(obj); \
|
|
r.rx.ifindex = self->ifindex; \
|
|
} \
|
|
} \
|
|
G_STMT_END
|
|
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)) {
|
|
if (r_src->table_any) {
|
|
_ensure_r();
|
|
r.rx.table_any = FALSE;
|
|
r.rx.table_coerced = default_route_table_x[IS_IPv4];
|
|
}
|
|
|
|
if (r_src->metric_any) {
|
|
_ensure_r();
|
|
r.rx.metric_any = FALSE;
|
|
r.rx.metric =
|
|
nm_add_clamped_u32(r.rx.metric, default_route_metric_x[IS_IPv4]);
|
|
}
|
|
|
|
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r_src)) {
|
|
if (NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES)
|
|
&& !NM_FLAGS_HAS(src->flags,
|
|
NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES))
|
|
continue;
|
|
if (default_route_penalty_x && default_route_penalty_x[IS_IPv4] > 0) {
|
|
_ensure_r();
|
|
r.rx.metric =
|
|
nm_utils_ip_route_metric_penalize(r.rx.metric,
|
|
default_route_penalty_x[IS_IPv4]);
|
|
}
|
|
}
|
|
}
|
|
|
|
nm_l3_config_data_add_route_full(self,
|
|
addr_family,
|
|
r_src == &r.rx ? NULL : obj,
|
|
r_src == &r.rx ? r_src : NULL,
|
|
NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
#undef _ensure_r
|
|
}
|
|
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS))
|
|
_garray_inaddr_merge(&self->nameservers_x[IS_IPv4],
|
|
src->nameservers_x[IS_IPv4],
|
|
addr_family);
|
|
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS))
|
|
_strv_ptrarray_merge(&self->domains_x[IS_IPv4], src->domains_x[IS_IPv4]);
|
|
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS))
|
|
_strv_ptrarray_merge(&self->searches_x[IS_IPv4], src->searches_x[IS_IPv4]);
|
|
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS))
|
|
_strv_ptrarray_merge(&self->dns_options_x[IS_IPv4], src->dns_options_x[IS_IPv4]);
|
|
|
|
if (!NM_FLAGS_ANY(self->flags, has_dns_priority_flag)
|
|
&& NM_FLAGS_ANY(src->flags, has_dns_priority_flag)) {
|
|
self->dns_priority_x[IS_IPv4] = src->dns_priority_x[IS_IPv4];
|
|
self->flags |= has_dns_priority_flag;
|
|
}
|
|
|
|
if (self->route_table_sync_x[IS_IPv4] == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE)
|
|
self->route_table_sync_x[IS_IPv4] = src->route_table_sync_x[IS_IPv4];
|
|
}
|
|
|
|
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) {
|
|
_garray_inaddr_merge(&self->wins, src->wins, AF_INET);
|
|
_garray_inaddr_merge(&self->nis_servers, src->nis_servers, AF_INET);
|
|
|
|
if (!self->nis_domain)
|
|
self->nis_domain = nm_ref_string_ref(src->nis_domain);
|
|
}
|
|
|
|
if (self->mdns == NM_SETTING_CONNECTION_MDNS_DEFAULT)
|
|
self->mdns = src->mdns;
|
|
|
|
if (self->llmnr == NM_SETTING_CONNECTION_LLMNR_DEFAULT)
|
|
self->llmnr = src->llmnr;
|
|
|
|
self->metered = NM_MAX((NMTernary) self->metered, (NMTernary) src->metered);
|
|
|
|
if (self->proxy_method == NM_PROXY_CONFIG_METHOD_UNKNOWN)
|
|
self->proxy_method = src->proxy_method;
|
|
|
|
if (self->proxy_browser_only == NM_TERNARY_DEFAULT)
|
|
self->proxy_browser_only = src->proxy_browser_only;
|
|
|
|
if (!self->proxy_pac_url)
|
|
self->proxy_pac_url = nm_ref_string_ref(src->proxy_pac_url);
|
|
|
|
if (!self->proxy_pac_script)
|
|
self->proxy_pac_script = nm_ref_string_ref(src->proxy_pac_script);
|
|
|
|
if (self->ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)
|
|
self->ip6_privacy = src->ip6_privacy;
|
|
|
|
if (!self->ndisc_hop_limit_set && src->ndisc_hop_limit_set) {
|
|
self->ndisc_hop_limit_set = TRUE;
|
|
self->ndisc_hop_limit_val = src->ndisc_hop_limit_val;
|
|
}
|
|
|
|
if (!self->ndisc_reachable_time_msec_set && src->ndisc_reachable_time_msec_set) {
|
|
self->ndisc_reachable_time_msec_set = TRUE;
|
|
self->ndisc_reachable_time_msec_val = src->ndisc_reachable_time_msec_val;
|
|
}
|
|
|
|
if (!self->ndisc_retrans_timer_msec_set && src->ndisc_retrans_timer_msec_set) {
|
|
self->ndisc_retrans_timer_msec_set = TRUE;
|
|
self->ndisc_retrans_timer_msec_val = src->ndisc_retrans_timer_msec_val;
|
|
}
|
|
|
|
if (self->mtu == 0u)
|
|
self->mtu = src->mtu;
|
|
|
|
if (self->ip6_mtu == 0u)
|
|
self->ip6_mtu = src->ip6_mtu;
|
|
|
|
if (NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)) {
|
|
_nm_unused nm_auto_unref_dhcplease NMDhcpLease *dhcp_lease_6 =
|
|
g_steal_pointer(&self->dhcp_lease_x[0]);
|
|
_nm_unused nm_auto_unref_dhcplease NMDhcpLease *dhcp_lease_4 =
|
|
g_steal_pointer(&self->dhcp_lease_x[1]);
|
|
|
|
self->source = src->source;
|
|
self->dhcp_lease_x[0] = nm_dhcp_lease_ref(self->dhcp_lease_x[0]);
|
|
self->dhcp_lease_x[1] = nm_dhcp_lease_ref(self->dhcp_lease_x[1]);
|
|
}
|
|
}
|
|
|
|
NML3ConfigData *
|
|
nm_l3_config_data_new_clone(const NML3ConfigData *src, int ifindex)
|
|
{
|
|
NML3ConfigData *self;
|
|
|
|
nm_assert(_NM_IS_L3_CONFIG_DATA(src, TRUE));
|
|
|
|
/* pass 0, to use the original ifindex. You can also use this function to
|
|
* copy the configuration for a different ifindex. */
|
|
nm_assert(ifindex >= 0);
|
|
if (ifindex <= 0)
|
|
ifindex = src->ifindex;
|
|
|
|
self = nm_l3_config_data_new(src->multi_idx, ifindex);
|
|
nm_l3_config_data_merge(self,
|
|
src,
|
|
NM_L3_CONFIG_MERGE_FLAGS_CLONE,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
nm_assert(nm_l3_config_data_cmp_full(src, self, NM_L3_CONFIG_CMP_FLAGS_IGNORE_IFINDEX) == 0);
|
|
nm_assert(nm_l3_config_data_get_ifindex(self) == ifindex);
|
|
|
|
return self;
|
|
}
|