Files
NetworkManager/libnm-core/nm-setting-ip-config.c
Beniamino Galvani af13081bec dns: change default DNS priority of VPNs to -50
Change the default DNS priority of VPNs to -50, to avoid leaking
queries out of full-tunnel VPNs.

This is a change in behavior. In particular:

 - when using dns=default (i.e. no split-dns) before this patch both
   VPN and the local name server were added (in this order) to
   resolv.conf; the result was that depending on resolv.conf options
   and resolver implementation, the name servers were tried in a
   certain manner which does not prevent DNS leaks.
   With this change, only the VPN name server is added to resolv.conf.

 - When using a split-dns plugin (systemd-resolved or dnsmasq), before
   this patch the full-tunnel VPN would get all queries except those
   ending in a local domain, that would instead be directed to the
   local server.
   After this patch, the VPN gets all queries.

To revert to the old behavior, set the DNS priority to 50 in the
connection profile.
2020-10-09 10:29:00 +02:00

6281 lines
203 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (C) 2007 - 2017 Red Hat, Inc.
* Copyright (C) 2007 - 2008 Novell, Inc.
*/
#include "nm-default.h"
#include "nm-setting-ip-config.h"
#include <arpa/inet.h>
#include <linux/fib_rules.h>
#include "nm-glib-aux/nm-str-buf.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
#include "nm-utils.h"
#include "nm-setting-private.h"
#include "nm-utils-private.h"
/**
* SECTION:nm-setting-ip-config
* @short_description: Abstract base class for IPv4 and IPv6
* addressing, routing, and name service properties
* @include: nm-setting-ip-config.h
* @see_also: #NMSettingIP4Config, #NMSettingIP6Config
*
* #NMSettingIPConfig is the abstract base class of
* #NMSettingIP4Config and #NMSettingIP6Config, providing properties
* related to IP addressing, routing, and Domain Name Service.
**/
/*****************************************************************************/
const NMUtilsDNSOptionDesc _nm_utils_dns_option_descs[] = {
{NM_SETTING_DNS_OPTION_DEBUG, FALSE, FALSE},
{NM_SETTING_DNS_OPTION_NDOTS, TRUE, FALSE},
{NM_SETTING_DNS_OPTION_TIMEOUT, TRUE, FALSE},
{NM_SETTING_DNS_OPTION_ATTEMPTS, TRUE, FALSE},
{NM_SETTING_DNS_OPTION_ROTATE, FALSE, FALSE},
{NM_SETTING_DNS_OPTION_NO_CHECK_NAMES, FALSE, FALSE},
{NM_SETTING_DNS_OPTION_INET6, FALSE, TRUE},
{NM_SETTING_DNS_OPTION_IP6_BYTESTRING, FALSE, TRUE},
{NM_SETTING_DNS_OPTION_IP6_DOTINT, FALSE, TRUE},
{NM_SETTING_DNS_OPTION_NO_IP6_DOTINT, FALSE, TRUE},
{NM_SETTING_DNS_OPTION_EDNS0, FALSE, FALSE},
{NM_SETTING_DNS_OPTION_SINGLE_REQUEST, FALSE, FALSE},
{NM_SETTING_DNS_OPTION_SINGLE_REQUEST_REOPEN, FALSE, FALSE},
{NM_SETTING_DNS_OPTION_NO_TLD_QUERY, FALSE, FALSE},
{NM_SETTING_DNS_OPTION_USE_VC, FALSE, FALSE},
{NM_SETTING_DNS_OPTION_NO_RELOAD, FALSE, FALSE},
{NM_SETTING_DNS_OPTION_TRUST_AD, FALSE, FALSE},
{NULL, FALSE, FALSE}};
static char *
canonicalize_ip(int family, const char *ip, gboolean null_any)
{
guint8 addr_bytes[sizeof(struct in6_addr)];
char addr_str[NM_UTILS_INET_ADDRSTRLEN];
int ret;
if (!ip) {
if (null_any)
return NULL;
if (family == AF_INET)
return g_strdup("0.0.0.0");
if (family == AF_INET6)
return g_strdup("::");
g_return_val_if_reached(NULL);
}
ret = inet_pton(family, ip, addr_bytes);
g_return_val_if_fail(ret == 1, NULL);
if (null_any) {
if (!memcmp(addr_bytes, &in6addr_any, nm_utils_addr_family_to_size(family)))
return NULL;
}
return g_strdup(inet_ntop(family, addr_bytes, addr_str, sizeof(addr_str)));
}
static char *
canonicalize_ip_binary(int family, gconstpointer ip, gboolean null_any)
{
char string[NM_UTILS_INET_ADDRSTRLEN];
if (!ip) {
if (null_any)
return NULL;
if (family == AF_INET)
return g_strdup("0.0.0.0");
if (family == AF_INET6)
return g_strdup("::");
g_return_val_if_reached(NULL);
}
if (null_any) {
if (!memcmp(ip, &in6addr_any, nm_utils_addr_family_to_size(family)))
return NULL;
}
return g_strdup(inet_ntop(family, ip, string, sizeof(string)));
}
static gboolean
valid_ip(int family, const char *ip, GError **error)
{
if (!ip) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
family == AF_INET ? _("Missing IPv4 address") : _("Missing IPv6 address"));
return FALSE;
}
if (!nm_utils_ipaddr_is_valid(family, ip)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
family == AF_INET ? _("Invalid IPv4 address '%s'")
: _("Invalid IPv6 address '%s'"),
ip);
return FALSE;
} else
return TRUE;
}
static gboolean
valid_prefix(int family, guint prefix, GError **error)
{
if ((family == AF_INET && prefix <= 32) || (family == AF_INET6 && prefix <= 128))
return TRUE;
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
family == AF_INET ? _("Invalid IPv4 address prefix '%u'")
: _("Invalid IPv6 address prefix '%u'"),
prefix);
return FALSE;
}
static gboolean
valid_metric(gint64 metric, GError **error)
{
if (metric < -1 || metric > G_MAXUINT32) {
if (error) {
char buf[64];
/* We can't concatenate G_GINT64_FORMAT into a translatable string */
g_snprintf(buf, sizeof(buf), "%" G_GINT64_FORMAT, metric);
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("Invalid routing metric '%s'"),
buf);
}
return FALSE;
}
return TRUE;
}
/*****************************************************************************
* NMIPAddress
*****************************************************************************/
G_DEFINE_BOXED_TYPE(NMIPAddress, nm_ip_address, nm_ip_address_dup, nm_ip_address_unref)
struct NMIPAddress {
guint refcount;
char *address;
int prefix, family;
GHashTable *attributes;
};
/**
* nm_ip_address_new:
* @family: the IP address family (<literal>AF_INET</literal> or
* <literal>AF_INET6</literal>)
* @addr: the IP address
* @prefix: the address prefix length
* @error: location to store error, or %NULL
*
* Creates a new #NMIPAddress object.
*
* Returns: (transfer full): the new #NMIPAddress object, or %NULL on error
**/
NMIPAddress *
nm_ip_address_new(int family, const char *addr, guint prefix, GError **error)
{
NMIPAddress *address;
g_return_val_if_fail(family == AF_INET || family == AF_INET6, NULL);
g_return_val_if_fail(addr != NULL, NULL);
if (!valid_ip(family, addr, error))
return NULL;
if (!valid_prefix(family, prefix, error))
return NULL;
address = g_slice_new0(NMIPAddress);
address->refcount = 1;
address->family = family;
address->address = canonicalize_ip(family, addr, FALSE);
address->prefix = prefix;
return address;
}
/**
* nm_ip_address_new_binary:
* @family: the IP address family (<literal>AF_INET</literal> or
* <literal>AF_INET6</literal>)
* @addr: the IP address
* @prefix: the address prefix length
* @error: location to store error, or %NULL
*
* Creates a new #NMIPAddress object. @addr must point to a buffer of the
* correct size for @family.
*
* Returns: (transfer full): the new #NMIPAddress object, or %NULL on error
**/
NMIPAddress *
nm_ip_address_new_binary(int family, gconstpointer addr, guint prefix, GError **error)
{
NMIPAddress *address;
char string[NM_UTILS_INET_ADDRSTRLEN];
g_return_val_if_fail(family == AF_INET || family == AF_INET6, NULL);
g_return_val_if_fail(addr != NULL, NULL);
if (!valid_prefix(family, prefix, error))
return NULL;
address = g_slice_new0(NMIPAddress);
address->refcount = 1;
address->family = family;
address->address = g_strdup(inet_ntop(family, addr, string, sizeof(string)));
address->prefix = prefix;
return address;
}
/**
* nm_ip_address_ref:
* @address: the #NMIPAddress
*
* Increases the reference count of the object.
**/
void
nm_ip_address_ref(NMIPAddress *address)
{
g_return_if_fail(address != NULL);
g_return_if_fail(address->refcount > 0);
address->refcount++;
}
/**
* nm_ip_address_unref:
* @address: the #NMIPAddress
*
* Decreases the reference count of the object. If the reference count
* reaches zero, the object will be destroyed.
**/
void
nm_ip_address_unref(NMIPAddress *address)
{
g_return_if_fail(address != NULL);
g_return_if_fail(address->refcount > 0);
address->refcount--;
if (address->refcount == 0) {
g_free(address->address);
if (address->attributes)
g_hash_table_unref(address->attributes);
g_slice_free(NMIPAddress, address);
}
}
/**
* nm_ip_address_cmp_full:
* @a: the #NMIPAddress
* @b: the #NMIPAddress to compare @address to.
* @cmp_flags: the #NMIPAddressCmpFlags that indicate what to compare.
*
* Note that with @cmp_flags #NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS, there
* is no total order for comparing GVariant. That means, if the two addresses
* only differ by their attributes, the sort order is undefined and the return
* value only indicates equality.
*
* Returns: 0 if the two objects have the same values (according to their flags)
* or a integer indicating the compare order.
**/
int
nm_ip_address_cmp_full(const NMIPAddress *a, const NMIPAddress *b, NMIPAddressCmpFlags cmp_flags)
{
g_return_val_if_fail(!a || a->refcount > 0, 0);
g_return_val_if_fail(!b || b->refcount > 0, 0);
g_return_val_if_fail(!NM_FLAGS_ANY(cmp_flags, ~NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS), 0);
NM_CMP_SELF(a, b);
NM_CMP_FIELD(a, b, family);
NM_CMP_FIELD(a, b, prefix);
NM_CMP_FIELD_STR(a, b, address);
if (NM_FLAGS_HAS(cmp_flags, NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS)) {
GHashTableIter iter;
const char * key;
GVariant * value, *value2;
guint n;
n = a->attributes ? g_hash_table_size(a->attributes) : 0u;
NM_CMP_DIRECT(n, (b->attributes ? g_hash_table_size(b->attributes) : 0u));
if (n > 0) {
g_hash_table_iter_init(&iter, a->attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) {
value2 = g_hash_table_lookup(b->attributes, key);
/* We cannot really compare GVariants, because g_variant_compare() does
* not work in general. So, don't bother. NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS is
* documented to not provide a total order for the attribute contents.
*
* Theoretically, we can implement also a total order. However we should
* not do that by default because it would require us to sort the keys
* first. Most callers don't care about total order, so they shouldn't
* pay the overhead. */
if (!value2)
return -2;
if (!g_variant_equal(value, value2))
return -2;
}
}
}
return 0;
}
/**
* nm_ip_address_equal:
* @address: the #NMIPAddress
* @other: the #NMIPAddress to compare @address to.
*
* Determines if two #NMIPAddress objects contain the same address and prefix
* (attributes are not compared).
*
* Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
**/
gboolean
nm_ip_address_equal(NMIPAddress *address, NMIPAddress *other)
{
return nm_ip_address_cmp_full(address, other, NM_IP_ADDRESS_CMP_FLAGS_NONE) == 0;
}
/**
* nm_ip_address_dup:
* @address: the #NMIPAddress
*
* Creates a copy of @address
*
* Returns: (transfer full): a copy of @address
**/
NMIPAddress *
nm_ip_address_dup(NMIPAddress *address)
{
NMIPAddress *copy;
g_return_val_if_fail(address != NULL, NULL);
g_return_val_if_fail(address->refcount > 0, NULL);
copy = nm_ip_address_new(address->family, address->address, address->prefix, NULL);
if (address->attributes) {
GHashTableIter iter;
const char * key;
GVariant * value;
g_hash_table_iter_init(&iter, address->attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value))
nm_ip_address_set_attribute(copy, key, value);
}
return copy;
}
/**
* nm_ip_address_get_family:
* @address: the #NMIPAddress
*
* Gets the IP address family (eg, AF_INET) property of this address
* object.
*
* Returns: the IP address family
**/
int
nm_ip_address_get_family(NMIPAddress *address)
{
g_return_val_if_fail(address != NULL, 0);
g_return_val_if_fail(address->refcount > 0, 0);
return address->family;
}
/**
* nm_ip_address_get_address:
* @address: the #NMIPAddress
*
* Gets the IP address property of this address object.
*
* Returns: the IP address
**/
const char *
nm_ip_address_get_address(NMIPAddress *address)
{
g_return_val_if_fail(address != NULL, NULL);
g_return_val_if_fail(address->refcount > 0, NULL);
return address->address;
}
/**
* nm_ip_address_set_address:
* @address: the #NMIPAddress
* @addr: the IP address, as a string
*
* Sets the IP address property of this address object.
*
* @addr must be a valid address of @address's family. If you aren't sure you
* have a valid address, use nm_utils_ipaddr_valid() to check it.
**/
void
nm_ip_address_set_address(NMIPAddress *address, const char *addr)
{
g_return_if_fail(address != NULL);
g_return_if_fail(addr != NULL);
g_return_if_fail(nm_utils_ipaddr_is_valid(address->family, addr));
g_free(address->address);
address->address = canonicalize_ip(address->family, addr, FALSE);
}
/**
* nm_ip_address_get_address_binary: (skip)
* @address: the #NMIPAddress
* @addr: a buffer in which to store the address in binary format.
*
* Gets the IP address property of this address object.
*
* @addr must point to a buffer that is the correct size for @address's family.
**/
void
nm_ip_address_get_address_binary(NMIPAddress *address, gpointer addr)
{
g_return_if_fail(address != NULL);
g_return_if_fail(addr != NULL);
inet_pton(address->family, address->address, addr);
}
/**
* nm_ip_address_set_address_binary: (skip)
* @address: the #NMIPAddress
* @addr: the address, in binary format
*
* Sets the IP address property of this address object.
*
* @addr must point to a buffer that is the correct size for @address's family.
**/
void
nm_ip_address_set_address_binary(NMIPAddress *address, gconstpointer addr)
{
char string[NM_UTILS_INET_ADDRSTRLEN];
g_return_if_fail(address != NULL);
g_return_if_fail(addr != NULL);
g_free(address->address);
address->address = g_strdup(inet_ntop(address->family, addr, string, sizeof(string)));
}
/**
* nm_ip_address_get_prefix:
* @address: the #NMIPAddress
*
* Gets the IP address prefix (ie "24" or "30" etc) property of this address
* object.
*
* Returns: the IP address prefix
**/
guint
nm_ip_address_get_prefix(NMIPAddress *address)
{
g_return_val_if_fail(address != NULL, 0);
g_return_val_if_fail(address->refcount > 0, 0);
return address->prefix;
}
/**
* nm_ip_address_set_prefix:
* @address: the #NMIPAddress
* @prefix: the IP address prefix
*
* Sets the IP address prefix property of this address object.
**/
void
nm_ip_address_set_prefix(NMIPAddress *address, guint prefix)
{
g_return_if_fail(address != NULL);
g_return_if_fail(valid_prefix(address->family, prefix, NULL));
address->prefix = prefix;
}
const char **
_nm_ip_address_get_attribute_names(const NMIPAddress *address, gboolean sorted, guint *out_length)
{
nm_assert(address);
return nm_utils_strdict_get_keys(address->attributes, sorted, out_length);
}
/**
* nm_ip_address_get_attribute_names:
* @address: the #NMIPAddress
*
* Gets an array of attribute names defined on @address.
*
* Returns: (transfer full): a %NULL-terminated array of attribute names,
**/
char **
nm_ip_address_get_attribute_names(NMIPAddress *address)
{
const char **names;
g_return_val_if_fail(address, NULL);
names = _nm_ip_address_get_attribute_names(address, TRUE, NULL);
return nm_utils_strv_make_deep_copied_nonnull(names);
}
/**
* nm_ip_address_get_attribute:
* @address: the #NMIPAddress
* @name: the name of an address attribute
*
* Gets the value of the attribute with name @name on @address
*
* Returns: (transfer none): the value of the attribute with name @name on
* @address, or %NULL if @address has no such attribute.
**/
GVariant *
nm_ip_address_get_attribute(NMIPAddress *address, const char *name)
{
g_return_val_if_fail(address != NULL, NULL);
g_return_val_if_fail(name != NULL && *name != '\0', NULL);
if (address->attributes)
return g_hash_table_lookup(address->attributes, name);
else
return NULL;
}
/**
* nm_ip_address_set_attribute:
* @address: the #NMIPAddress
* @name: the name of an address attribute
* @value: (transfer none) (allow-none): the value
*
* Sets or clears the named attribute on @address to the given value.
**/
void
nm_ip_address_set_attribute(NMIPAddress *address, const char *name, GVariant *value)
{
g_return_if_fail(address != NULL);
g_return_if_fail(name != NULL && *name != '\0');
g_return_if_fail(strcmp(name, "address") != 0 && strcmp(name, "prefix") != 0);
if (!address->attributes) {
address->attributes = g_hash_table_new_full(nm_str_hash,
g_str_equal,
g_free,
(GDestroyNotify) g_variant_unref);
}
if (value)
g_hash_table_insert(address->attributes, g_strdup(name), g_variant_ref_sink(value));
else
g_hash_table_remove(address->attributes, name);
}
/*****************************************************************************
* NMIPRoute
*****************************************************************************/
G_DEFINE_BOXED_TYPE(NMIPRoute, nm_ip_route, nm_ip_route_dup, nm_ip_route_unref)
struct NMIPRoute {
guint refcount;
int family;
char * dest;
guint prefix;
char * next_hop;
gint64 metric;
GHashTable *attributes;
};
/**
* nm_ip_route_new:
* @family: the IP address family (<literal>AF_INET</literal> or
* <literal>AF_INET6</literal>)
* @dest: the IP address of the route's destination
* @prefix: the address prefix length
* @next_hop: (allow-none): the IP address of the next hop (or %NULL)
* @metric: the route metric (or -1 for "default")
* @error: location to store error, or %NULL
*
* Creates a new #NMIPRoute object.
*
* Returns: (transfer full): the new #NMIPRoute object, or %NULL on error
**/
NMIPRoute *
nm_ip_route_new(int family,
const char *dest,
guint prefix,
const char *next_hop,
gint64 metric,
GError ** error)
{
NMIPRoute *route;
g_return_val_if_fail(family == AF_INET || family == AF_INET6, NULL);
g_return_val_if_fail(dest, NULL);
if (!valid_ip(family, dest, error))
return NULL;
if (!valid_prefix(family, prefix, error))
return NULL;
if (next_hop && !valid_ip(family, next_hop, error))
return NULL;
if (!valid_metric(metric, error))
return NULL;
route = g_slice_new0(NMIPRoute);
route->refcount = 1;
route->family = family;
route->dest = canonicalize_ip(family, dest, FALSE);
route->prefix = prefix;
route->next_hop = canonicalize_ip(family, next_hop, TRUE);
route->metric = metric;
return route;
}
/**
* nm_ip_route_new_binary:
* @family: the IP address family (<literal>AF_INET</literal> or
* <literal>AF_INET6</literal>)
* @dest: the IP address of the route's destination
* @prefix: the address prefix length
* @next_hop: (allow-none): the IP address of the next hop (or %NULL)
* @metric: the route metric (or -1 for "default")
* @error: location to store error, or %NULL
*
* Creates a new #NMIPRoute object. @dest and @next_hop (if non-%NULL) must
* point to buffers of the correct size for @family.
*
* Returns: (transfer full): the new #NMIPRoute object, or %NULL on error
**/
NMIPRoute *
nm_ip_route_new_binary(int family,
gconstpointer dest,
guint prefix,
gconstpointer next_hop,
gint64 metric,
GError ** error)
{
NMIPRoute *route;
g_return_val_if_fail(family == AF_INET || family == AF_INET6, NULL);
g_return_val_if_fail(dest, NULL);
if (!valid_prefix(family, prefix, error))
return NULL;
if (!valid_metric(metric, error))
return NULL;
route = g_slice_new0(NMIPRoute);
route->refcount = 1;
route->family = family;
route->dest = canonicalize_ip_binary(family, dest, FALSE);
route->prefix = prefix;
route->next_hop = canonicalize_ip_binary(family, next_hop, TRUE);
route->metric = metric;
return route;
}
/**
* nm_ip_route_ref:
* @route: the #NMIPRoute
*
* Increases the reference count of the object.
**/
void
nm_ip_route_ref(NMIPRoute *route)
{
g_return_if_fail(route != NULL);
g_return_if_fail(route->refcount > 0);
route->refcount++;
}
/**
* nm_ip_route_unref:
* @route: the #NMIPRoute
*
* Decreases the reference count of the object. If the reference count
* reaches zero, the object will be destroyed.
**/
void
nm_ip_route_unref(NMIPRoute *route)
{
g_return_if_fail(route != NULL);
g_return_if_fail(route->refcount > 0);
route->refcount--;
if (route->refcount == 0) {
g_free(route->dest);
g_free(route->next_hop);
if (route->attributes)
g_hash_table_unref(route->attributes);
g_slice_free(NMIPRoute, route);
}
}
/**
* nm_ip_route_equal_full:
* @route: the #NMIPRoute
* @other: the #NMIPRoute to compare @route to.
* @cmp_flags: tune how to compare attributes. Currently, only
* NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE (0) and NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS (1)
* is supported.
*
* Determines if two #NMIPRoute objects contain the same destination, prefix,
* next hop, and metric.
*
* Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
*
* Since: 1.10
**/
gboolean
nm_ip_route_equal_full(NMIPRoute *route, NMIPRoute *other, guint cmp_flags)
{
g_return_val_if_fail(route != NULL, FALSE);
g_return_val_if_fail(route->refcount > 0, FALSE);
g_return_val_if_fail(other != NULL, FALSE);
g_return_val_if_fail(other->refcount > 0, FALSE);
g_return_val_if_fail(NM_IN_SET(cmp_flags,
NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE,
NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS),
FALSE);
if (route->prefix != other->prefix || route->metric != other->metric
|| strcmp(route->dest, other->dest) != 0
|| g_strcmp0(route->next_hop, other->next_hop) != 0)
return FALSE;
if (cmp_flags == NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS) {
GHashTableIter iter;
const char * key;
GVariant * value, *value2;
guint n;
n = route->attributes ? g_hash_table_size(route->attributes) : 0u;
if (n != (other->attributes ? g_hash_table_size(other->attributes) : 0u))
return FALSE;
if (n) {
g_hash_table_iter_init(&iter, route->attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) {
value2 = g_hash_table_lookup(other->attributes, key);
if (!value2)
return FALSE;
if (!g_variant_equal(value, value2))
return FALSE;
}
}
}
return TRUE;
}
/**
* nm_ip_route_equal:
* @route: the #NMIPRoute
* @other: the #NMIPRoute to compare @route to.
*
* Determines if two #NMIPRoute objects contain the same destination, prefix,
* next hop, and metric. (Attributes are not compared.)
*
* Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
**/
gboolean
nm_ip_route_equal(NMIPRoute *route, NMIPRoute *other)
{
return nm_ip_route_equal_full(route, other, NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE);
}
/**
* nm_ip_route_dup:
* @route: the #NMIPRoute
*
* Creates a copy of @route
*
* Returns: (transfer full): a copy of @route
**/
NMIPRoute *
nm_ip_route_dup(NMIPRoute *route)
{
NMIPRoute *copy;
g_return_val_if_fail(route != NULL, NULL);
g_return_val_if_fail(route->refcount > 0, NULL);
copy = nm_ip_route_new(route->family,
route->dest,
route->prefix,
route->next_hop,
route->metric,
NULL);
if (route->attributes) {
GHashTableIter iter;
const char * key;
GVariant * value;
g_hash_table_iter_init(&iter, route->attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value))
nm_ip_route_set_attribute(copy, key, value);
}
return copy;
}
/**
* nm_ip_route_get_family:
* @route: the #NMIPRoute
*
* Gets the IP address family (eg, AF_INET) property of this route
* object.
*
* Returns: the IP address family
**/
int
nm_ip_route_get_family(NMIPRoute *route)
{
g_return_val_if_fail(route != NULL, 0);
g_return_val_if_fail(route->refcount > 0, 0);
return route->family;
}
/**
* nm_ip_route_get_dest:
* @route: the #NMIPRoute
*
* Gets the IP destination address property of this route object.
*
* Returns: the IP address of the route's destination
**/
const char *
nm_ip_route_get_dest(NMIPRoute *route)
{
g_return_val_if_fail(route != NULL, NULL);
g_return_val_if_fail(route->refcount > 0, NULL);
return route->dest;
}
/**
* nm_ip_route_set_dest:
* @route: the #NMIPRoute
* @dest: the route's destination, as a string
*
* Sets the destination property of this route object.
*
* @dest must be a valid address of @route's family. If you aren't sure you
* have a valid address, use nm_utils_ipaddr_is_valid() to check it.
**/
void
nm_ip_route_set_dest(NMIPRoute *route, const char *dest)
{
g_return_if_fail(route != NULL);
g_return_if_fail(nm_utils_ipaddr_is_valid(route->family, dest));
g_free(route->dest);
route->dest = canonicalize_ip(route->family, dest, FALSE);
}
/**
* nm_ip_route_get_dest_binary: (skip)
* @route: the #NMIPRoute
* @dest: a buffer in which to store the destination in binary format.
*
* Gets the destination property of this route object.
*
* @dest must point to a buffer that is the correct size for @route's family.
**/
void
nm_ip_route_get_dest_binary(NMIPRoute *route, gpointer dest)
{
g_return_if_fail(route != NULL);
g_return_if_fail(dest != NULL);
inet_pton(route->family, route->dest, dest);
}
/**
* nm_ip_route_set_dest_binary: (skip)
* @route: the #NMIPRoute
* @dest: the route's destination, in binary format
*
* Sets the destination property of this route object.
*
* @dest must point to a buffer that is the correct size for @route's family.
**/
void
nm_ip_route_set_dest_binary(NMIPRoute *route, gconstpointer dest)
{
char string[NM_UTILS_INET_ADDRSTRLEN];
g_return_if_fail(route != NULL);
g_return_if_fail(dest != NULL);
g_free(route->dest);
route->dest = g_strdup(inet_ntop(route->family, dest, string, sizeof(string)));
}
/**
* nm_ip_route_get_prefix:
* @route: the #NMIPRoute
*
* Gets the IP prefix (ie "24" or "30" etc) of this route.
*
* Returns: the IP prefix
**/
guint
nm_ip_route_get_prefix(NMIPRoute *route)
{
g_return_val_if_fail(route != NULL, 0);
g_return_val_if_fail(route->refcount > 0, 0);
return route->prefix;
}
/**
* nm_ip_route_set_prefix:
* @route: the #NMIPRoute
* @prefix: the route prefix
*
* Sets the prefix property of this route object.
**/
void
nm_ip_route_set_prefix(NMIPRoute *route, guint prefix)
{
g_return_if_fail(route != NULL);
g_return_if_fail(valid_prefix(route->family, prefix, NULL));
route->prefix = prefix;
}
/**
* nm_ip_route_get_next_hop:
* @route: the #NMIPRoute
*
* Gets the IP address of the next hop of this route; this will be %NULL if the
* route has no next hop.
*
* Returns: the IP address of the next hop, or %NULL if this is a device route.
**/
const char *
nm_ip_route_get_next_hop(NMIPRoute *route)
{
g_return_val_if_fail(route != NULL, NULL);
g_return_val_if_fail(route->refcount > 0, NULL);
return route->next_hop;
}
/**
* nm_ip_route_set_next_hop:
* @route: the #NMIPRoute
* @next_hop: (allow-none): the route's next hop, as a string
*
* Sets the next-hop property of this route object.
*
* @next_hop (if non-%NULL) must be a valid address of @route's family. If you
* aren't sure you have a valid address, use nm_utils_ipaddr_valid() to check
* it.
**/
void
nm_ip_route_set_next_hop(NMIPRoute *route, const char *next_hop)
{
g_return_if_fail(route != NULL);
g_return_if_fail(!next_hop || nm_utils_ipaddr_is_valid(route->family, next_hop));
g_free(route->next_hop);
route->next_hop = canonicalize_ip(route->family, next_hop, TRUE);
}
/**
* nm_ip_route_get_next_hop_binary: (skip)
* @route: the #NMIPRoute
* @next_hop: a buffer in which to store the next hop in binary format.
*
* Gets the next hop property of this route object.
*
* @next_hop must point to a buffer that is the correct size for @route's family.
*
* Returns: %TRUE if @route has a next hop, %FALSE if not (in which case
* @next_hop will be zeroed out)
**/
gboolean
nm_ip_route_get_next_hop_binary(NMIPRoute *route, gpointer next_hop)
{
g_return_val_if_fail(route != NULL, FALSE);
g_return_val_if_fail(next_hop != NULL, FALSE);
if (route->next_hop) {
inet_pton(route->family, route->next_hop, next_hop);
return TRUE;
} else {
memset(next_hop, 0, nm_utils_addr_family_to_size(route->family));
return FALSE;
}
}
/**
* nm_ip_route_set_next_hop_binary: (skip)
* @route: the #NMIPRoute
* @next_hop: the route's next hop, in binary format
*
* Sets the destination property of this route object.
*
* @next_hop (if non-%NULL) must point to a buffer that is the correct size for
* @route's family.
**/
void
nm_ip_route_set_next_hop_binary(NMIPRoute *route, gconstpointer next_hop)
{
g_return_if_fail(route != NULL);
g_free(route->next_hop);
route->next_hop = canonicalize_ip_binary(route->family, next_hop, TRUE);
}
/**
* nm_ip_route_get_metric:
* @route: the #NMIPRoute
*
* Gets the route metric property of this route object; lower values
* indicate "better" or more preferred routes; -1 indicates "default"
* (meaning NetworkManager will set it appropriately).
*
* Returns: the route metric
**/
gint64
nm_ip_route_get_metric(NMIPRoute *route)
{
g_return_val_if_fail(route != NULL, 0);
g_return_val_if_fail(route->refcount > 0, 0);
return route->metric;
}
/**
* nm_ip_route_set_metric:
* @route: the #NMIPRoute
* @metric: the route metric (or -1 for "default")
*
* Sets the metric property of this route object.
**/
void
nm_ip_route_set_metric(NMIPRoute *route, gint64 metric)
{
g_return_if_fail(route != NULL);
g_return_if_fail(valid_metric(metric, NULL));
route->metric = metric;
}
GHashTable *
_nm_ip_route_get_attributes(NMIPRoute *route)
{
nm_assert(route);
return route->attributes;
}
/**
* _nm_ip_route_get_attribute_names:
* @route: the #NMIPRoute
* @sorted: whether to sort the names. Otherwise, their order is
* undefined and unstable.
* @out_length: (allow-none) (out): the number of elements
*
* Gets an array of attribute names defined on @route.
*
* Returns: (array length=out_length) (transfer container): a %NULL-terminated array
* of attribute names or %NULL if there are no attributes. The order of the returned
* names depends on @sorted.
**/
const char **
_nm_ip_route_get_attribute_names(const NMIPRoute *route, gboolean sorted, guint *out_length)
{
nm_assert(route);
return nm_utils_strdict_get_keys(route->attributes, sorted, out_length);
}
/**
* nm_ip_route_get_attribute_names:
* @route: the #NMIPRoute
*
* Gets an array of attribute names defined on @route.
*
* Returns: (transfer full): a %NULL-terminated array of attribute names
**/
char **
nm_ip_route_get_attribute_names(NMIPRoute *route)
{
const char **names;
g_return_val_if_fail(route != NULL, NULL);
names = _nm_ip_route_get_attribute_names(route, TRUE, NULL);
return nm_utils_strv_make_deep_copied_nonnull(names);
}
/**
* nm_ip_route_get_attribute:
* @route: the #NMIPRoute
* @name: the name of an route attribute
*
* Gets the value of the attribute with name @name on @route
*
* Returns: (transfer none): the value of the attribute with name @name on
* @route, or %NULL if @route has no such attribute.
**/
GVariant *
nm_ip_route_get_attribute(NMIPRoute *route, const char *name)
{
g_return_val_if_fail(route != NULL, NULL);
g_return_val_if_fail(name != NULL && *name != '\0', NULL);
if (route->attributes)
return g_hash_table_lookup(route->attributes, name);
else
return NULL;
}
/**
* nm_ip_route_set_attribute:
* @route: the #NMIPRoute
* @name: the name of a route attribute
* @value: (transfer none) (allow-none): the value
*
* Sets the named attribute on @route to the given value.
**/
void
nm_ip_route_set_attribute(NMIPRoute *route, const char *name, GVariant *value)
{
g_return_if_fail(route != NULL);
g_return_if_fail(name != NULL && *name != '\0');
g_return_if_fail(strcmp(name, "dest") != 0 && strcmp(name, "prefix") != 0
&& strcmp(name, "next-hop") != 0 && strcmp(name, "metric") != 0);
if (!route->attributes) {
route->attributes = g_hash_table_new_full(nm_str_hash,
g_str_equal,
g_free,
(GDestroyNotify) g_variant_unref);
}
if (value)
g_hash_table_insert(route->attributes, g_strdup(name), g_variant_ref_sink(value));
else
g_hash_table_remove(route->attributes, name);
}
static const NMVariantAttributeSpec *const ip_route_attribute_spec[] = {
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_CWND,
G_VARIANT_TYPE_UINT32,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_FROM,
G_VARIANT_TYPE_STRING,
.v6 = TRUE,
.str_type = 'p', ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_INITCWND,
G_VARIANT_TYPE_UINT32,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_INITRWND,
G_VARIANT_TYPE_UINT32,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND,
G_VARIANT_TYPE_BOOLEAN,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND,
G_VARIANT_TYPE_BOOLEAN,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND,
G_VARIANT_TYPE_BOOLEAN,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU,
G_VARIANT_TYPE_BOOLEAN,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW,
G_VARIANT_TYPE_BOOLEAN,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_MTU,
G_VARIANT_TYPE_UINT32,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_ONLINK,
G_VARIANT_TYPE_BOOLEAN,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_SCOPE,
G_VARIANT_TYPE_BYTE,
.v4 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_SRC,
G_VARIANT_TYPE_STRING,
.v4 = TRUE,
.v6 = TRUE,
.str_type = 'a', ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_TABLE,
G_VARIANT_TYPE_UINT32,
.v4 = TRUE,
.v6 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_TOS, G_VARIANT_TYPE_BYTE, .v4 = TRUE, ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_TYPE,
G_VARIANT_TYPE_STRING,
.v4 = TRUE,
.v6 = TRUE,
.str_type = 'T', ),
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_WINDOW,
G_VARIANT_TYPE_UINT32,
.v4 = TRUE,
.v6 = TRUE, ),
NULL,
};
/**
* nm_ip_route_get_variant_attribute_spec:
*
* Returns: the specifiers for route attributes
*
* Since: 1.8
*/
const NMVariantAttributeSpec *const *
nm_ip_route_get_variant_attribute_spec(void)
{
return ip_route_attribute_spec;
}
/**
* nm_ip_route_attribute_validate:
* @name: the attribute name
* @value: the attribute value
* @family: IP address family of the route
* @known: (out): on return, whether the attribute name is a known one
* @error: (allow-none): return location for a #GError, or %NULL
*
* Validates a route attribute, i.e. checks that the attribute is a known one
* and the value is of the correct type and well-formed.
*
* Returns: %TRUE if the attribute is valid, %FALSE otherwise
*
* Since: 1.8
*/
gboolean
nm_ip_route_attribute_validate(const char *name,
GVariant * value,
int family,
gboolean * known,
GError ** error)
{
const NMVariantAttributeSpec *spec;
g_return_val_if_fail(name, FALSE);
g_return_val_if_fail(value, FALSE);
g_return_val_if_fail(family == AF_INET || family == AF_INET6, FALSE);
g_return_val_if_fail(!error || !*error, FALSE);
spec = _nm_variant_attribute_spec_find_binary_search(ip_route_attribute_spec,
G_N_ELEMENTS(ip_route_attribute_spec) - 1,
name);
if (!spec) {
NM_SET_OUT(known, FALSE);
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("unknown attribute"));
return FALSE;
}
NM_SET_OUT(known, TRUE);
if (!g_variant_is_of_type(value, spec->type)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("invalid attribute type '%s'"),
g_variant_get_type_string(value));
return FALSE;
}
if ((family == AF_INET && !spec->v4) || (family == AF_INET6 && !spec->v6)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
family == AF_INET ? _("attribute is not valid for a IPv4 route")
: _("attribute is not valid for a IPv6 route"));
return FALSE;
}
if (g_variant_type_equal(spec->type, G_VARIANT_TYPE_STRING)) {
const char *string = g_variant_get_string(value, NULL);
switch (spec->str_type) {
case 'a': /* IP address */
if (!nm_utils_ipaddr_is_valid(family, string)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
family == AF_INET ? _("'%s' is not a valid IPv4 address")
: _("'%s' is not a valid IPv6 address"),
string);
return FALSE;
}
break;
case 'p': /* IP address + optional prefix */
{
gs_free char *addr_free = NULL;
const char * addr = string;
const char * str;
str = strchr(addr, '/');
if (str) {
addr = nm_strndup_a(200, addr, str - addr, &addr_free);
str++;
if (_nm_utils_ascii_str_to_int64(str, 10, 0, family == AF_INET ? 32 : 128, -1)
< 0) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("invalid prefix %s"),
str);
return FALSE;
}
}
if (!nm_utils_ipaddr_is_valid(family, addr)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
family == AF_INET ? _("'%s' is not a valid IPv4 address")
: _("'%s' is not a valid IPv6 address"),
string);
return FALSE;
}
break;
}
case 'T': /* route type. */
if (!NM_IN_SET(nm_utils_route_type_by_name(string), RTN_UNICAST, RTN_LOCAL)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%s is not a valid route type"),
string);
return FALSE;
}
break;
default:
break;
}
}
return TRUE;
}
gboolean
_nm_ip_route_attribute_validate_all(const NMIPRoute *route, GError **error)
{
NMUtilsNamedValue attrs_static[G_N_ELEMENTS(ip_route_attribute_spec)];
gs_free NMUtilsNamedValue *attrs_free = NULL;
const NMUtilsNamedValue * attrs;
guint attrs_len;
GVariant * val;
guint i;
guint8 u8;
g_return_val_if_fail(route, FALSE);
g_return_val_if_fail(!error || !*error, FALSE);
if (!route->attributes)
return TRUE;
attrs = nm_utils_named_values_from_strdict(route->attributes,
&attrs_len,
attrs_static,
&attrs_free);
for (i = 0; i < attrs_len; i++) {
const char *key = attrs[i].name;
GVariant * val2 = attrs[i].value_ptr;
if (!nm_ip_route_attribute_validate(key, val2, route->family, NULL, NULL))
return FALSE;
}
if ((val = g_hash_table_lookup(route->attributes, NM_IP_ROUTE_ATTRIBUTE_TYPE))) {
nm_assert(g_variant_is_of_type(val, G_VARIANT_TYPE_STRING));
u8 = nm_utils_route_type_by_name(g_variant_get_string(val, NULL));
if (u8 == RTN_LOCAL && route->family == AF_INET
&& (val = g_hash_table_lookup(route->attributes, NM_IP_ROUTE_ATTRIBUTE_SCOPE))) {
nm_assert(g_variant_is_of_type(val, G_VARIANT_TYPE_BYTE));
u8 = g_variant_get_byte(val);
if (!NM_IN_SET(u8, RT_SCOPE_HOST, RT_SCOPE_NOWHERE)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("route scope is invalid"));
return FALSE;
}
}
}
return TRUE;
}
/*****************************************************************************/
struct NMIPRoutingRule {
NMIPAddr from_bin;
NMIPAddr to_bin;
char * from_str;
char * to_str;
char * iifname;
char * oifname;
guint ref_count;
guint32 priority;
guint32 table;
gint32 suppress_prefixlength;
guint32 fwmark;
guint32 fwmask;
guint16 sport_start;
guint16 sport_end;
guint16 dport_start;
guint16 dport_end;
guint8 action;
guint8 from_len;
guint8 to_len;
guint8 tos;
guint8 ipproto;
bool is_v4 : 1;
bool sealed : 1;
bool priority_has : 1;
bool from_has : 1;
bool from_valid : 1;
bool to_has : 1;
bool to_valid : 1;
bool invert : 1;
};
static NMIPRoutingRule *_ip_routing_rule_dup(const NMIPRoutingRule *rule);
G_DEFINE_BOXED_TYPE(NMIPRoutingRule,
nm_ip_routing_rule,
_ip_routing_rule_dup,
nm_ip_routing_rule_unref)
static gboolean
NM_IS_IP_ROUTING_RULE(const NMIPRoutingRule *self, gboolean also_sealed)
{
return self && self->ref_count > 0 && (also_sealed || !self->sealed);
}
static int
_ip_routing_rule_get_addr_family(const NMIPRoutingRule *self)
{
nm_assert(NM_IS_IP_ROUTING_RULE(self, TRUE));
return self->is_v4 ? AF_INET : AF_INET6;
}
static int
_ip_routing_rule_get_addr_size(const NMIPRoutingRule *self)
{
nm_assert(NM_IS_IP_ROUTING_RULE(self, TRUE));
return self->is_v4 ? sizeof(struct in_addr) : sizeof(struct in6_addr);
}
static NMIPRoutingRule *
_ip_routing_rule_dup(const NMIPRoutingRule *rule)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(rule, TRUE), NULL);
if (rule->sealed)
return nm_ip_routing_rule_ref((NMIPRoutingRule *) rule);
return nm_ip_routing_rule_new_clone(rule);
}
/**
* nm_ip_routing_rule_new:
* @addr_family: the address family of the routing rule. Must be either
* %AF_INET (2) or %AF_INET6 (10).
*
* Returns: (transfer full): a newly created rule instance with the
* provided address family. The instance is unsealed.
*
* Since: 1.18
*/
NMIPRoutingRule *
nm_ip_routing_rule_new(int addr_family)
{
NMIPRoutingRule *self;
g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), NULL);
self = g_slice_new(NMIPRoutingRule);
*self = (NMIPRoutingRule){
.ref_count = 1,
.is_v4 = (addr_family == AF_INET),
.action = FR_ACT_TO_TBL,
.table = RT_TABLE_MAIN,
.suppress_prefixlength = -1,
};
return self;
}
/**
* nm_ip_routing_rule_new_clone:
* @rule: the #NMIPRoutingRule to clone.
*
* Returns: (transfer full): a newly created rule instance with
* the same settings as @rule. Note that the instance will
* always be unsealred.
*
* Since: 1.18
*/
NMIPRoutingRule *
nm_ip_routing_rule_new_clone(const NMIPRoutingRule *rule)
{
NMIPRoutingRule *self;
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(rule, TRUE), NULL);
self = g_slice_new(NMIPRoutingRule);
*self = (NMIPRoutingRule){
.ref_count = 1,
.sealed = FALSE,
.is_v4 = rule->is_v4,
.priority = rule->priority,
.priority_has = rule->priority_has,
.invert = rule->invert,
.tos = rule->tos,
.fwmark = rule->fwmark,
.fwmask = rule->fwmask,
.sport_start = rule->sport_start,
.sport_end = rule->sport_end,
.dport_start = rule->dport_start,
.dport_end = rule->dport_end,
.ipproto = rule->ipproto,
.from_len = rule->from_len,
.from_bin = rule->from_bin,
.from_str = (rule->from_has && !rule->from_valid) ? g_strdup(rule->from_str) : NULL,
.from_has = rule->from_has,
.from_valid = rule->from_valid,
.to_len = rule->to_len,
.to_bin = rule->to_bin,
.to_str = (rule->to_has && !rule->to_valid) ? g_strdup(rule->to_str) : NULL,
.to_has = rule->to_has,
.to_valid = rule->to_valid,
.iifname = g_strdup(rule->iifname),
.oifname = g_strdup(rule->oifname),
.action = rule->action,
.table = rule->table,
.suppress_prefixlength = rule->suppress_prefixlength,
};
return self;
}
/**
* nm_ip_routing_rule_ref:
* @self: (allow-none): the #NMIPRoutingRule instance
*
* Increases the reference count of the instance.
* This is not thread-safe.
*
* Returns: (transfer full): the @self argument with incremented
* reference count.
*
* Since: 1.18
*/
NMIPRoutingRule *
nm_ip_routing_rule_ref(NMIPRoutingRule *self)
{
if (!self)
return NULL;
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
nm_assert(self->ref_count < G_MAXUINT);
self->ref_count++;
return self;
}
/**
* nm_ip_routing_rule_unref:
* @self: (allow-none): the #NMIPRoutingRule instance
*
* Decreases the reference count of the instance and destroys
* the instance if the reference count reaches zero.
* This is not thread-safe.
*
* Since: 1.18
*/
void
nm_ip_routing_rule_unref(NMIPRoutingRule *self)
{
if (!self)
return;
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE));
if (--self->ref_count > 0)
return;
g_free(self->from_str);
g_free(self->to_str);
g_free(self->iifname);
g_free(self->oifname);
g_slice_free(NMIPRoutingRule, self);
}
/**
* nm_ip_routing_rule_is_sealed:
* @self: the #NMIPRoutingRule instance
*
* Returns: whether @self is sealed. Once sealed, an instance
* cannot be modified nor unsealed.
*
* Since: 1.18
*/
gboolean
nm_ip_routing_rule_is_sealed(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), FALSE);
return self->sealed;
}
/**
* nm_ip_routing_rule_seal:
* @self: the #NMIPRoutingRule instance
*
* Seals the routing rule. Afterwards, the instance can no longer be
* modified, and it is a bug to call any of the accessors that would
* modify the rule. If @self was already sealed, this has no effect.
*
* Since: 1.18
*/
void
nm_ip_routing_rule_seal(NMIPRoutingRule *self)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE));
self->sealed = TRUE;
}
/**
* nm_ip_routing_rule_get_addr_family:
* @self: the #NMIPRoutingRule instance
*
* Returns: the address family of the rule. Either %AF_INET or %AF_INET6.
*
* Since: 1.18
*/
int
nm_ip_routing_rule_get_addr_family(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), AF_UNSPEC);
return _ip_routing_rule_get_addr_family(self);
}
/**
* nm_ip_routing_rule_get_priority:
* @self: the #NMIPRoutingRule instance
*
* Returns: the priority. A valid priority is in the range from
* 0 to %G_MAXUINT32. If unset, -1 is returned.
*
* Since: 1.18
*/
gint64
nm_ip_routing_rule_get_priority(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), -1);
return self->priority_has ? (gint64) self->priority : (gint64) -1;
}
/**
* nm_ip_routing_rule_set_priority:
* @self: the #NMIPRoutingRule instance
* @priority: the priority to set
*
* A valid priority ranges from 0 to %G_MAXUINT32. "-1" is also allowed
* to reset the priority. It is a bug calling this function with any
* other value.
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_priority(NMIPRoutingRule *self, gint64 priority)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
if (priority >= 0 && priority <= (gint64) G_MAXUINT32) {
self->priority = (guint32) priority;
self->priority_has = TRUE;
} else {
g_return_if_fail(priority == -1);
self->priority = 0;
self->priority_has = FALSE;
}
}
/**
* nm_ip_routing_rule_get_invert:
* @self: the #NMIPRoutingRule instance
*
* Returns: the "invert" setting of the rule.
*
* Since: 1.18
*/
gboolean
nm_ip_routing_rule_get_invert(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), FALSE);
return self->invert;
}
/**
* nm_ip_routing_rule_set_invert:
* @self: the #NMIPRoutingRule instance
* @invert: the new value to set
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_invert(NMIPRoutingRule *self, gboolean invert)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
self->invert = invert;
}
/**
* nm_ip_routing_rule_get_from_len:
* @self: the #NMIPRoutingRule instance
*
* Returns: the set prefix length for the from/src parameter.
*
* Since: 1.18
*/
guint8
nm_ip_routing_rule_get_from_len(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->from_len;
}
/**
* nm_ip_routing_rule_get_from:
* @self: the #NMIPRoutingRule instance
*
* Returns: (transfer none): the set from/src parameter or
* %NULL, if no value is set.
*
* Since: 1.18
*/
const char *
nm_ip_routing_rule_get_from(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
if (!self->from_has)
return NULL;
if (!self->from_str) {
nm_assert(self->from_valid);
((NMIPRoutingRule *) self)->from_str =
nm_utils_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->from_bin);
}
return self->from_str;
}
const NMIPAddr *
nm_ip_routing_rule_get_from_bin(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
return (self->from_has && self->from_valid) ? &self->from_bin : NULL;
}
void
nm_ip_routing_rule_set_from_bin(NMIPRoutingRule *self, gconstpointer from, guint8 len)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
nm_clear_g_free(&self->from_str);
if (!from) {
self->from_has = FALSE;
self->from_len = len;
return;
}
self->from_has = TRUE;
self->from_len = len;
self->from_valid = TRUE;
nm_ip_addr_set(_ip_routing_rule_get_addr_family(self), &self->from_bin, from);
}
/**
* nm_ip_routing_rule_set_from:
* @self: the #NMIPRoutingRule instance
* @from: (allow-none): the from/src address to set.
* The address family must match.
* @len: the corresponding prefix length of the address.
*
* Setting invalid values is accepted, but will later fail
* during nm_ip_routing_rule_validate().
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_from(NMIPRoutingRule *self, const char *from, guint8 len)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
if (!from) {
nm_clear_g_free(&self->from_str);
self->from_has = FALSE;
self->from_len = len;
return;
}
nm_clear_g_free(&self->from_str);
self->from_has = TRUE;
self->from_len = len;
self->from_valid = nm_utils_parse_inaddr_bin(_ip_routing_rule_get_addr_family(self),
from,
NULL,
&self->from_bin);
if (!self->from_valid)
self->from_str = g_strdup(from);
}
/**
* nm_ip_routing_rule_get_to_len:
* @self: the #NMIPRoutingRule instance
*
* Returns: the set prefix length for the to/dst parameter.
*
* Since: 1.18
*/
guint8
nm_ip_routing_rule_get_to_len(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->to_len;
}
/**
* nm_ip_routing_rule_get_to:
* @self: the #NMIPRoutingRule instance
*
* Returns: (transfer none): the set to/dst parameter or
* %NULL, if no value is set.
*
* Since: 1.18
*/
const char *
nm_ip_routing_rule_get_to(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
if (!self->to_has)
return NULL;
if (!self->to_str) {
nm_assert(self->to_valid);
((NMIPRoutingRule *) self)->to_str =
nm_utils_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->to_bin);
}
return self->to_str;
}
const NMIPAddr *
nm_ip_routing_rule_get_to_bin(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
return (self->to_has && self->to_valid) ? &self->to_bin : NULL;
}
void
nm_ip_routing_rule_set_to_bin(NMIPRoutingRule *self, gconstpointer to, guint8 len)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
nm_clear_g_free(&self->to_str);
if (!to) {
self->to_has = FALSE;
self->to_len = len;
return;
}
self->to_has = TRUE;
self->to_len = len;
self->to_valid = TRUE;
nm_ip_addr_set(_ip_routing_rule_get_addr_family(self), &self->to_bin, to);
}
/**
* nm_ip_routing_rule_set_to:
* @self: the #NMIPRoutingRule instance
* @to: (allow-none): the to/dst address to set.
* The address family must match.
* @len: the corresponding prefix length of the address.
* If @to is %NULL, this valid is ignored.
*
* Setting invalid values is accepted, but will later fail
* during nm_ip_routing_rule_validate().
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_to(NMIPRoutingRule *self, const char *to, guint8 len)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
if (!to) {
nm_clear_g_free(&self->to_str);
self->to_has = FALSE;
self->to_len = len;
return;
}
nm_clear_g_free(&self->to_str);
self->to_has = TRUE;
self->to_len = len;
self->to_valid =
nm_utils_parse_inaddr_bin(_ip_routing_rule_get_addr_family(self), to, NULL, &self->to_bin);
if (!self->to_valid)
self->to_str = g_strdup(to);
}
/**
* nm_ip_routing_rule_get_tos:
* @self: the #NMIPRoutingRule instance
*
* Returns: the tos of the rule.
*
* Since: 1.18
*/
guint8
nm_ip_routing_rule_get_tos(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->tos;
}
/**
* nm_ip_routing_rule_set_tos:
* @self: the #NMIPRoutingRule instance
* @tos: the tos to set
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_tos(NMIPRoutingRule *self, guint8 tos)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
self->tos = tos;
}
/**
* nm_ip_routing_rule_get_ipproto:
* @self: the #NMIPRoutingRule instance
*
* Returns: the ipproto of the rule.
*
* Since: 1.18
*/
guint8
nm_ip_routing_rule_get_ipproto(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->ipproto;
}
/**
* nm_ip_routing_rule_set_ipproto:
* @self: the #NMIPRoutingRule instance
* @ipproto: the ipproto to set
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_ipproto(NMIPRoutingRule *self, guint8 ipproto)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
self->ipproto = ipproto;
}
/**
* nm_ip_routing_rule_get_source_port_start:
* @self: the #NMIPRoutingRule instance
*
* Returns: the source port start setting.
*
* Since: 1.18
*/
guint16
nm_ip_routing_rule_get_source_port_start(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->sport_start;
}
/**
* nm_ip_routing_rule_get_source_port_end:
* @self: the #NMIPRoutingRule instance
*
* Returns: the source port end setting.
*
* Since: 1.18
*/
guint16
nm_ip_routing_rule_get_source_port_end(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->sport_end;
}
/**
* nm_ip_routing_rule_set_source_port:
* @self: the #NMIPRoutingRule instance
* @start: the start port to set.
* @end: the end port to set.
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_source_port(NMIPRoutingRule *self, guint16 start, guint16 end)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
self->sport_start = start;
self->sport_end = end;
}
/**
* nm_ip_routing_rule_get_destination_port_start:
* @self: the #NMIPRoutingRule instance
*
* Returns: the destination port start setting.
*
* Since: 1.18
*/
guint16
nm_ip_routing_rule_get_destination_port_start(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->dport_start;
}
/**
* nm_ip_routing_rule_get_destination_port_end:
* @self: the #NMIPRoutingRule instance
*
* Returns: the destination port end setting.
*
* Since: 1.18
*/
guint16
nm_ip_routing_rule_get_destination_port_end(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->dport_end;
}
/**
* nm_ip_routing_rule_set_destination_port:
* @self: the #NMIPRoutingRule instance
* @start: the start port to set.
* @end: the end port to set.
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_destination_port(NMIPRoutingRule *self, guint16 start, guint16 end)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
self->dport_start = start;
self->dport_end = end;
}
/**
* nm_ip_routing_rule_get_fwmark:
* @self: the #NMIPRoutingRule instance
*
* Returns: the fwmark setting.
*
* Since: 1.18
*/
guint32
nm_ip_routing_rule_get_fwmark(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->fwmark;
}
/**
* nm_ip_routing_rule_get_fwmask:
* @self: the #NMIPRoutingRule instance
*
* Returns: the fwmask setting.
*
* Since: 1.18
*/
guint32
nm_ip_routing_rule_get_fwmask(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->fwmask;
}
/**
* nm_ip_routing_rule_set_fwmark:
* @self: the #NMIPRoutingRule instance
* @fwmark: the fwmark
* @fwmask: the fwmask
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_fwmark(NMIPRoutingRule *self, guint32 fwmark, guint32 fwmask)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
self->fwmark = fwmark;
self->fwmask = fwmask;
}
/**
* nm_ip_routing_rule_get_iifname:
* @self: the #NMIPRoutingRule instance.
*
* Returns: (transfer none): the set iifname or %NULL if unset.
*
* Since: 1.18
*/
const char *
nm_ip_routing_rule_get_iifname(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
return self->iifname;
}
gboolean
nm_ip_routing_rule_get_xifname_bin(const NMIPRoutingRule *self,
gboolean iif /* or else oif */,
char out_xifname[static 16 /* IFNAMSIZ */])
{
gs_free gpointer bin_to_free = NULL;
const char * xifname;
gconstpointer bin;
gsize len;
nm_assert(NM_IS_IP_ROUTING_RULE(self, TRUE));
nm_assert(out_xifname);
xifname = iif ? self->iifname : self->oifname;
if (!xifname)
return FALSE;
bin = nm_utils_buf_utf8safe_unescape(xifname,
NM_UTILS_STR_UTF8_SAFE_FLAG_NONE,
&len,
&bin_to_free);
strncpy(out_xifname, bin, 16 /* IFNAMSIZ */);
out_xifname[15] = '\0';
return TRUE;
}
/**
* nm_ip_routing_rule_set_iifname:
* @self: the #NMIPRoutingRule instance.
* @iifname: (allow-none): the iifname to set or %NULL to unset.
*
* The name supports C backslash escaping for non-UTF-8 characters.
* Note that nm_ip_routing_rule_from_string() too uses backslash
* escaping when tokenizing the words by whitespace. So, in string
* representation you'd get double backslashes.
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_iifname(NMIPRoutingRule *self, const char *iifname)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
g_free(self->iifname);
self->iifname = g_strdup(iifname);
}
/**
* nm_ip_routing_rule_get_oifname:
* @self: the #NMIPRoutingRule instance.
*
* Returns: (transfer none): the set oifname or %NULL if unset.
*
* Since: 1.18
*/
const char *
nm_ip_routing_rule_get_oifname(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
return self->oifname;
}
/**
* nm_ip_routing_rule_set_oifname:
* @self: the #NMIPRoutingRule instance.
* @oifname: (allow-none): the oifname to set or %NULL to unset.
*
* The name supports C backslash escaping for non-UTF-8 characters.
* Note that nm_ip_routing_rule_from_string() too uses backslash
* escaping when tokenizing the words by whitespace. So, in string
* representation you'd get double backslashes.
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_oifname(NMIPRoutingRule *self, const char *oifname)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
g_free(self->oifname);
self->oifname = g_strdup(oifname);
}
/**
* nm_ip_routing_rule_get_action:
* @self: the #NMIPRoutingRule instance
*
* Returns: the set action.
*
* Since: 1.18
*/
guint8
nm_ip_routing_rule_get_action(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->action;
}
/**
* nm_ip_routing_rule_set_action:
* @self: the #NMIPRoutingRule instance
* @action: the action to set
*
* Note that currently only certain actions are allowed. nm_ip_routing_rule_validate()
* will reject unsupported actions as invalid.
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_action(NMIPRoutingRule *self, guint8 action)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
self->action = action;
}
/**
* nm_ip_routing_rule_get_table:
* @self: the #NMIPRoutingRule instance
*
* Returns: the set table.
*
* Since: 1.18
*/
guint32
nm_ip_routing_rule_get_table(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), 0);
return self->table;
}
/**
* nm_ip_routing_rule_set_table:
* @self: the #NMIPRoutingRule instance
* @table: the table to set
*
* Since: 1.18
*/
void
nm_ip_routing_rule_set_table(NMIPRoutingRule *self, guint32 table)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
self->table = table;
}
/**
* nm_ip_routing_rule_get_suppress_prefixlength:
* @self: the #NMIPRoutingRule instance
*
* Returns: the suppress_prefixlength of the rule. -1 means that the value is unset.
*
* Since: 1.20
*/
gint32
nm_ip_routing_rule_get_suppress_prefixlength(const NMIPRoutingRule *self)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), -1);
return self->suppress_prefixlength;
}
/**
* nm_ip_routing_rule_set_suppress_prefixlength:
* @self: the #NMIPRoutingRule instance
* @suppress_prefixlength: the suppress_prefixlength to set. The value -1 means
* unset.
*
* Since: 1.20
*/
void
nm_ip_routing_rule_set_suppress_prefixlength(NMIPRoutingRule *self, gint32 suppress_prefixlength)
{
g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, FALSE));
self->suppress_prefixlength = suppress_prefixlength;
}
/**
* nm_ip_routing_rule_cmp:
* @rule: (allow-none): the #NMIPRoutingRule instance to compare
* @other: (allow-none): the other #NMIPRoutingRule instance to compare
*
* Returns: zero, a positive, or a negative integer to indicate
* equality or how the arguments compare.
*
* Since: 1.18
*/
int
nm_ip_routing_rule_cmp(const NMIPRoutingRule *rule, const NMIPRoutingRule *other)
{
NM_CMP_SELF(rule, other);
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(rule, TRUE), 0);
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(other, TRUE), 0);
NM_CMP_FIELD_UNSAFE(rule, other, priority_has);
if (rule->priority_has)
NM_CMP_FIELD(rule, other, priority);
NM_CMP_FIELD_UNSAFE(rule, other, is_v4);
NM_CMP_FIELD_UNSAFE(rule, other, invert);
NM_CMP_FIELD(rule, other, tos);
NM_CMP_FIELD(rule, other, fwmark);
NM_CMP_FIELD(rule, other, fwmask);
NM_CMP_FIELD(rule, other, action);
NM_CMP_FIELD(rule, other, table);
NM_CMP_FIELD(rule, other, suppress_prefixlength);
NM_CMP_FIELD(rule, other, sport_start);
NM_CMP_FIELD(rule, other, sport_end);
NM_CMP_FIELD(rule, other, dport_start);
NM_CMP_FIELD(rule, other, dport_end);
NM_CMP_FIELD(rule, other, ipproto);
/* We compare the plain strings, not the binary values after utf8safe unescaping.
*
* The reason is, that the rules differ already when the direct strings differ, not
* only when the unescaped names differ. */
NM_CMP_FIELD_STR0(rule, other, iifname);
NM_CMP_FIELD_STR0(rule, other, oifname);
NM_CMP_FIELD(rule, other, from_len);
NM_CMP_FIELD_UNSAFE(rule, other, from_has);
if (rule->from_has) {
NM_CMP_FIELD_UNSAFE(rule, other, from_valid);
if (rule->from_valid) {
NM_CMP_RETURN(
memcmp(&rule->from_bin, &other->from_bin, _ip_routing_rule_get_addr_size(rule)));
} else
NM_CMP_FIELD_STR(rule, other, from_str);
}
NM_CMP_FIELD(rule, other, to_len);
NM_CMP_FIELD_UNSAFE(rule, other, to_has);
if (rule->to_has) {
NM_CMP_FIELD_UNSAFE(rule, other, to_valid);
if (rule->to_valid) {
NM_CMP_RETURN(
memcmp(&rule->to_bin, &other->to_bin, _ip_routing_rule_get_addr_size(rule)));
} else
NM_CMP_FIELD_STR(rule, other, to_str);
}
return 0;
}
static gboolean
_rr_xport_range_valid(guint16 xport_start, guint16 xport_end)
{
if (xport_start == 0)
return (xport_end == 0);
return xport_start <= xport_end && xport_end < 0xFFFFu;
}
static gboolean
_rr_xport_range_parse(char *str, gint64 *out_start, guint16 *out_end)
{
guint16 start, end;
gint64 i64;
char * s;
s = strchr(str, '-');
if (s)
*(s++) = '\0';
i64 = _nm_utils_ascii_str_to_int64(str, 10, 0, 0xFFFF, -1);
if (i64 == -1)
return FALSE;
start = i64;
if (s) {
i64 = _nm_utils_ascii_str_to_int64(s, 10, 0, 0xFFFF, -1);
if (i64 == -1)
return FALSE;
end = i64;
} else
end = start;
*out_start = start;
*out_end = end;
return TRUE;
}
/**
* nm_ip_routing_rule_validate:
* @self: the #NMIPRoutingRule instance to validate
* @error: (allow-none) (out): the error result if validation fails.
*
* Returns: %TRUE if the rule validates.
*
* Since: 1.18
*/
gboolean
nm_ip_routing_rule_validate(const NMIPRoutingRule *self, GError **error)
{
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), FALSE);
g_return_val_if_fail(!error || !*error, FALSE);
/* Kernel may be more flexible about validating. We do a strict validation
* here and reject certain settings eagerly. We can always relax it later. */
if (!self->priority_has) {
/* iproute2 accepts not specifying the priority, in which case kernel will select
* an unused priority. We don't allow for that, and will always require the user to
* select a priority.
*
* Note that if the user selects priority 0 or a non-unique priority, this is problematic
* due to kernel bugs rh#1685816 and rh#1685816. It may result in NetworkManager wrongly being
* unable to add a rule or deleting the wrong rule.
* This problem is not at all specific to the priority, it affects all rules that
* have default values which confuse kernel. But setting a unique priority avoids
* this problem nicely. */
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid priority"));
return FALSE;
}
if (NM_IN_SET(self->action, FR_ACT_TO_TBL)) {
if (self->table == 0) {
/* with IPv4, kernel allows a table (in RTM_NEWRULE) of zero to automatically select
* an unused table. We don't. The user needs to specify the table.
*
* For IPv6, kernel doesn't allow a table of zero, so we are consistent here. */
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("missing table"));
return FALSE;
}
} else {
/* whitelist the actions that we currently. */
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid action"));
return FALSE;
}
if (self->from_len == 0) {
if (self->from_has) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("has from/src but the prefix-length is zero"));
return FALSE;
}
} else if (self->from_len > 0 && self->from_len <= 8 * _ip_routing_rule_get_addr_size(self)) {
if (!self->from_has) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("missing from/src for a non zero prefix-length"));
return FALSE;
}
if (!self->from_valid) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid from/src"));
return FALSE;
}
} else {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid prefix length for from/src"));
return FALSE;
}
if (self->to_len == 0) {
if (self->to_has) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("has to/dst but the prefix-length is zero"));
return FALSE;
}
} else if (self->to_len > 0 && self->to_len <= 8 * _ip_routing_rule_get_addr_size(self)) {
if (!self->to_has) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("missing to/dst for a non zero prefix-length"));
return FALSE;
}
if (!self->to_valid) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid to/dst"));
return FALSE;
}
} else {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid prefix length for to/dst"));
return FALSE;
}
if (self->iifname
&& (!g_utf8_validate(self->iifname, -1, NULL)
|| !nm_utils_is_valid_iface_name_utf8safe(self->iifname))) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid iifname"));
return FALSE;
}
if (self->oifname
&& (!g_utf8_validate(self->oifname, -1, NULL)
|| !nm_utils_is_valid_iface_name_utf8safe(self->oifname))) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid oifname"));
return FALSE;
}
if (!_rr_xport_range_valid(self->sport_start, self->sport_end)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid source port range"));
return FALSE;
}
if (!_rr_xport_range_valid(self->dport_start, self->dport_end)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid destination port range"));
return FALSE;
}
if (self->suppress_prefixlength != -1) {
if (self->suppress_prefixlength < -1
|| self->suppress_prefixlength > (self->is_v4 ? 32 : 128)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("suppress_prefixlength out of range"));
return FALSE;
}
if (self->action != FR_ACT_TO_TBL) {
g_set_error_literal(
error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("suppress_prefixlength is only allowed with the to-table action"));
return FALSE;
}
}
return TRUE;
}
/*****************************************************************************/
typedef enum {
RR_DBUS_ATTR_ACTION,
RR_DBUS_ATTR_DPORT_END,
RR_DBUS_ATTR_DPORT_START,
RR_DBUS_ATTR_FAMILY,
RR_DBUS_ATTR_FROM,
RR_DBUS_ATTR_FROM_LEN,
RR_DBUS_ATTR_FWMARK,
RR_DBUS_ATTR_FWMASK,
RR_DBUS_ATTR_IIFNAME,
RR_DBUS_ATTR_INVERT,
RR_DBUS_ATTR_IPPROTO,
RR_DBUS_ATTR_OIFNAME,
RR_DBUS_ATTR_PRIORITY,
RR_DBUS_ATTR_SPORT_END,
RR_DBUS_ATTR_SPORT_START,
RR_DBUS_ATTR_TABLE,
RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH,
RR_DBUS_ATTR_TO,
RR_DBUS_ATTR_TOS,
RR_DBUS_ATTR_TO_LEN,
_RR_DBUS_ATTR_NUM,
} RRDbusAttr;
typedef struct {
const char * name;
const GVariantType *dbus_type;
} RRDbusData;
static const RRDbusData rr_dbus_data[_RR_DBUS_ATTR_NUM] = {
#define _D(attr, _name, type) \
[attr] = { \
.name = _name, \
.dbus_type = type, \
}
_D(RR_DBUS_ATTR_ACTION, NM_IP_ROUTING_RULE_ATTR_ACTION, G_VARIANT_TYPE_BYTE),
_D(RR_DBUS_ATTR_DPORT_END, NM_IP_ROUTING_RULE_ATTR_DPORT_END, G_VARIANT_TYPE_UINT16),
_D(RR_DBUS_ATTR_DPORT_START, NM_IP_ROUTING_RULE_ATTR_DPORT_START, G_VARIANT_TYPE_UINT16),
_D(RR_DBUS_ATTR_FAMILY, NM_IP_ROUTING_RULE_ATTR_FAMILY, G_VARIANT_TYPE_INT32),
_D(RR_DBUS_ATTR_FROM, NM_IP_ROUTING_RULE_ATTR_FROM, G_VARIANT_TYPE_STRING),
_D(RR_DBUS_ATTR_FROM_LEN, NM_IP_ROUTING_RULE_ATTR_FROM_LEN, G_VARIANT_TYPE_BYTE),
_D(RR_DBUS_ATTR_FWMARK, NM_IP_ROUTING_RULE_ATTR_FWMARK, G_VARIANT_TYPE_UINT32),
_D(RR_DBUS_ATTR_FWMASK, NM_IP_ROUTING_RULE_ATTR_FWMASK, G_VARIANT_TYPE_UINT32),
_D(RR_DBUS_ATTR_IIFNAME, NM_IP_ROUTING_RULE_ATTR_IIFNAME, G_VARIANT_TYPE_STRING),
_D(RR_DBUS_ATTR_INVERT, NM_IP_ROUTING_RULE_ATTR_INVERT, G_VARIANT_TYPE_BOOLEAN),
_D(RR_DBUS_ATTR_IPPROTO, NM_IP_ROUTING_RULE_ATTR_IPPROTO, G_VARIANT_TYPE_BYTE),
_D(RR_DBUS_ATTR_OIFNAME, NM_IP_ROUTING_RULE_ATTR_OIFNAME, G_VARIANT_TYPE_STRING),
_D(RR_DBUS_ATTR_PRIORITY, NM_IP_ROUTING_RULE_ATTR_PRIORITY, G_VARIANT_TYPE_UINT32),
_D(RR_DBUS_ATTR_SPORT_END, NM_IP_ROUTING_RULE_ATTR_SPORT_END, G_VARIANT_TYPE_UINT16),
_D(RR_DBUS_ATTR_SPORT_START, NM_IP_ROUTING_RULE_ATTR_SPORT_START, G_VARIANT_TYPE_UINT16),
_D(RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH,
NM_IP_ROUTING_RULE_ATTR_SUPPRESS_PREFIXLENGTH,
G_VARIANT_TYPE_INT32),
_D(RR_DBUS_ATTR_TABLE, NM_IP_ROUTING_RULE_ATTR_TABLE, G_VARIANT_TYPE_UINT32),
_D(RR_DBUS_ATTR_TO, NM_IP_ROUTING_RULE_ATTR_TO, G_VARIANT_TYPE_STRING),
_D(RR_DBUS_ATTR_TOS, NM_IP_ROUTING_RULE_ATTR_TOS, G_VARIANT_TYPE_BYTE),
_D(RR_DBUS_ATTR_TO_LEN, NM_IP_ROUTING_RULE_ATTR_TO_LEN, G_VARIANT_TYPE_BYTE),
#undef _D
};
static void
_rr_variants_free(GVariant *(*p_variants)[])
{
int i;
for (i = 0; i < _RR_DBUS_ATTR_NUM; i++) {
if ((*p_variants)[i])
g_variant_unref((*p_variants)[i]);
}
}
NMIPRoutingRule *
nm_ip_routing_rule_from_dbus(GVariant *variant, gboolean strict, GError **error)
{
nm_auto(_rr_variants_free) GVariant *variants[_RR_DBUS_ATTR_NUM] = {};
nm_auto_unref_ip_routing_rule NMIPRoutingRule *self = NULL;
RRDbusAttr attr;
GVariantIter iter;
const char * iter_key;
GVariant * iter_val;
int addr_family;
int i;
g_variant_iter_init(&iter, variant);
#if NM_MORE_ASSERTS > 10
for (attr = 0; attr < _RR_DBUS_ATTR_NUM; attr++) {
nm_assert(rr_dbus_data[attr].name);
nm_assert(g_variant_type_string_is_valid((const char *) rr_dbus_data[attr].dbus_type));
}
#endif
while (g_variant_iter_next(&iter, "{&sv}", &iter_key, &iter_val)) {
gs_unref_variant GVariant *iter_val2 = iter_val;
for (attr = 0; attr < _RR_DBUS_ATTR_NUM; attr++) {
if (nm_streq(iter_key, rr_dbus_data[attr].name)) {
if (variants[attr]) {
if (strict) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("duplicate key %s"),
iter_key);
return NULL;
}
g_variant_unref(variants[attr]);
}
variants[attr] = g_steal_pointer(&iter_val2);
break;
}
}
if (attr >= _RR_DBUS_ATTR_NUM && strict) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid key \"%s\""),
iter_key);
return NULL;
}
}
for (attr = 0; attr < _RR_DBUS_ATTR_NUM; attr++) {
if (!variants[attr])
continue;
if (!g_variant_is_of_type(variants[attr], rr_dbus_data[attr].dbus_type)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid variant type '%s' for \"%s\""),
(const char *) rr_dbus_data[attr].dbus_type,
rr_dbus_data[attr].name);
return NULL;
}
}
if (!variants[RR_DBUS_ATTR_FAMILY]) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("missing \"" NM_IP_ROUTING_RULE_ATTR_FAMILY "\""));
return NULL;
}
addr_family = g_variant_get_int32(variants[RR_DBUS_ATTR_FAMILY]);
if (!NM_IN_SET(addr_family, AF_INET, AF_INET6)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid \"" NM_IP_ROUTING_RULE_ATTR_FAMILY "\""));
return NULL;
}
self = nm_ip_routing_rule_new(addr_family);
if (variants[RR_DBUS_ATTR_PRIORITY])
nm_ip_routing_rule_set_priority(self,
g_variant_get_uint32(variants[RR_DBUS_ATTR_PRIORITY]));
if (variants[RR_DBUS_ATTR_INVERT])
nm_ip_routing_rule_set_invert(self, g_variant_get_boolean(variants[RR_DBUS_ATTR_INVERT]));
if (variants[RR_DBUS_ATTR_TOS])
nm_ip_routing_rule_set_tos(self, g_variant_get_byte(variants[RR_DBUS_ATTR_TOS]));
if (variants[RR_DBUS_ATTR_IPPROTO])
nm_ip_routing_rule_set_ipproto(self, g_variant_get_byte(variants[RR_DBUS_ATTR_IPPROTO]));
for (i = 0; i < 2; i++) {
GVariant *v_start = variants[i ? RR_DBUS_ATTR_SPORT_START : RR_DBUS_ATTR_DPORT_START];
GVariant *v_end = variants[i ? RR_DBUS_ATTR_SPORT_END : RR_DBUS_ATTR_DPORT_END];
guint16 start, end;
if (!v_start && !v_end)
continue;
/* if start or end is missing, it defaults to the other parameter, respectively. */
if (v_start)
start = g_variant_get_uint16(v_start);
else
start = g_variant_get_uint16(v_end);
if (v_end)
end = g_variant_get_uint16(v_end);
else
end = g_variant_get_uint16(v_start);
if (i)
nm_ip_routing_rule_set_source_port(self, start, end);
else
nm_ip_routing_rule_set_destination_port(self, start, end);
}
if (variants[RR_DBUS_ATTR_FWMARK] || variants[RR_DBUS_ATTR_FWMASK]) {
nm_ip_routing_rule_set_fwmark(
self,
variants[RR_DBUS_ATTR_FWMARK] ? g_variant_get_uint32(variants[RR_DBUS_ATTR_FWMARK])
: 0u,
variants[RR_DBUS_ATTR_FWMASK] ? g_variant_get_uint32(variants[RR_DBUS_ATTR_FWMASK])
: 0u);
}
if (variants[RR_DBUS_ATTR_FROM] || variants[RR_DBUS_ATTR_FROM_LEN]) {
nm_ip_routing_rule_set_from(
self,
variants[RR_DBUS_ATTR_FROM] ? g_variant_get_string(variants[RR_DBUS_ATTR_FROM], NULL)
: NULL,
variants[RR_DBUS_ATTR_FROM_LEN] ? g_variant_get_byte(variants[RR_DBUS_ATTR_FROM_LEN])
: 0u);
}
if (variants[RR_DBUS_ATTR_TO] || variants[RR_DBUS_ATTR_TO_LEN]) {
nm_ip_routing_rule_set_to(
self,
variants[RR_DBUS_ATTR_TO] ? g_variant_get_string(variants[RR_DBUS_ATTR_TO], NULL)
: NULL,
variants[RR_DBUS_ATTR_TO_LEN] ? g_variant_get_byte(variants[RR_DBUS_ATTR_TO_LEN]) : 0u);
}
if (variants[RR_DBUS_ATTR_IIFNAME])
nm_ip_routing_rule_set_iifname(self,
g_variant_get_string(variants[RR_DBUS_ATTR_IIFNAME], NULL));
if (variants[RR_DBUS_ATTR_OIFNAME])
nm_ip_routing_rule_set_oifname(self,
g_variant_get_string(variants[RR_DBUS_ATTR_OIFNAME], NULL));
if (variants[RR_DBUS_ATTR_ACTION])
nm_ip_routing_rule_set_action(self, g_variant_get_byte(variants[RR_DBUS_ATTR_ACTION]));
if (variants[RR_DBUS_ATTR_TABLE])
nm_ip_routing_rule_set_table(self, g_variant_get_uint32(variants[RR_DBUS_ATTR_TABLE]));
if (variants[RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH])
nm_ip_routing_rule_set_suppress_prefixlength(
self,
g_variant_get_int32(variants[RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH]));
if (strict && !nm_ip_routing_rule_validate(self, error))
return NULL;
return g_steal_pointer(&self);
}
static void
_rr_to_dbus_add(GVariantBuilder *builder, RRDbusAttr attr, GVariant *value)
{
nm_assert(builder);
nm_assert(value);
nm_assert(g_variant_is_floating(value));
nm_assert(g_variant_is_of_type(value, rr_dbus_data[attr].dbus_type));
g_variant_builder_add(builder, "{sv}", rr_dbus_data[attr].name, value);
}
GVariant *
nm_ip_routing_rule_to_dbus(const NMIPRoutingRule *self)
{
GVariantBuilder builder;
char addr_str[NM_UTILS_INET_ADDRSTRLEN];
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
_rr_to_dbus_add(&builder,
RR_DBUS_ATTR_FAMILY,
g_variant_new_int32(_ip_routing_rule_get_addr_family(self)));
if (self->invert)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_INVERT, g_variant_new_boolean(TRUE));
if (self->priority_has)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_PRIORITY, g_variant_new_uint32(self->priority));
if (self->tos != 0)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_TOS, g_variant_new_byte(self->tos));
if (self->ipproto != 0)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_IPPROTO, g_variant_new_byte(self->ipproto));
if (self->fwmark != 0)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_FWMARK, g_variant_new_uint32(self->fwmark));
if (self->fwmask != 0)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_FWMASK, g_variant_new_uint32(self->fwmask));
if (self->sport_start != 0 || self->sport_end != 0) {
_rr_to_dbus_add(&builder,
RR_DBUS_ATTR_SPORT_START,
g_variant_new_uint16(self->sport_start));
if (self->sport_start != self->sport_end)
_rr_to_dbus_add(&builder,
RR_DBUS_ATTR_SPORT_END,
g_variant_new_uint16(self->sport_end));
}
if (self->dport_start != 0 || self->dport_end != 0) {
_rr_to_dbus_add(&builder,
RR_DBUS_ATTR_DPORT_START,
g_variant_new_uint16(self->dport_start));
if (self->dport_start != self->dport_end)
_rr_to_dbus_add(&builder,
RR_DBUS_ATTR_DPORT_END,
g_variant_new_uint16(self->dport_end));
}
if (self->from_has || self->from_len != 0) {
_rr_to_dbus_add(
&builder,
RR_DBUS_ATTR_FROM,
g_variant_new_string(self->from_str
?: nm_utils_inet_ntop(_ip_routing_rule_get_addr_family(self),
&self->from_bin,
addr_str)));
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_FROM_LEN, g_variant_new_byte(self->from_len));
}
if (self->to_has || self->to_len != 0) {
_rr_to_dbus_add(
&builder,
RR_DBUS_ATTR_TO,
g_variant_new_string(self->to_str
?: nm_utils_inet_ntop(_ip_routing_rule_get_addr_family(self),
&self->to_bin,
addr_str)));
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_TO_LEN, g_variant_new_byte(self->to_len));
}
if (self->iifname)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_IIFNAME, g_variant_new_string(self->iifname));
if (self->oifname)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_OIFNAME, g_variant_new_string(self->oifname));
if (self->action != FR_ACT_TO_TBL)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_ACTION, g_variant_new_byte(self->action));
if (self->table != 0)
_rr_to_dbus_add(&builder, RR_DBUS_ATTR_TABLE, g_variant_new_uint32(self->table));
if (self->suppress_prefixlength != -1)
_rr_to_dbus_add(&builder,
RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH,
g_variant_new_int32(self->suppress_prefixlength));
return g_variant_builder_end(&builder);
}
/*****************************************************************************/
static gboolean
_rr_string_validate(gboolean for_from /* or else to-string */,
NMIPRoutingRuleAsStringFlags to_string_flags,
GHashTable * extra_args,
GError ** error)
{
if (NM_FLAGS_ANY(to_string_flags,
~(NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET
| NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6
| NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE))) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("Unsupported to-string-flags argument"));
return FALSE;
}
if (extra_args && g_hash_table_size(extra_args) > 0) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("Unsupported extra-argument"));
return FALSE;
}
return TRUE;
}
static int
_rr_string_addr_family_from_flags(NMIPRoutingRuleAsStringFlags to_string_flags)
{
if (NM_FLAGS_HAS(to_string_flags, NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET)) {
if (!NM_FLAGS_HAS(to_string_flags, NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6))
return AF_INET;
} else if (NM_FLAGS_HAS(to_string_flags, NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6))
return AF_INET6;
return AF_UNSPEC;
}
/**
* nm_ip_routing_rule_from_string:
* @str: the string representation to convert to an #NMIPRoutingRule
* @to_string_flags: #NMIPRoutingRuleAsStringFlags for controlling the
* string conversion.
* @extra_args: (allow-none): extra arguments for controlling the string
* conversion. Currently, not extra arguments are supported.
* @error: (allow-none) (out): the error reason.
*
* Returns: (transfer full): the new #NMIPRoutingRule or %NULL on error.
*
* Since: 1.18
*/
NMIPRoutingRule *
nm_ip_routing_rule_from_string(const char * str,
NMIPRoutingRuleAsStringFlags to_string_flags,
GHashTable * extra_args,
GError ** error)
{
nm_auto_unref_ip_routing_rule NMIPRoutingRule *self = NULL;
gs_free const char ** tokens = NULL;
gsize i_token;
gboolean any_words = FALSE;
char * word0 = NULL;
char * word1 = NULL;
char * word_from = NULL;
char * word_to = NULL;
char * word_iifname = NULL;
char * word_oifname = NULL;
gint64 i64_priority = -1;
gint64 i64_table = -1;
gint64 i64_tos = -1;
gint64 i64_fwmark = -1;
gint64 i64_fwmask = -1;
gint64 i64_sport_start = -1;
gint64 i64_ipproto = -1;
gint64 i64_suppress_prefixlength = -1;
guint16 sport_end = 0;
gint64 i64_dport_start = -1;
guint16 dport_end = 0;
gboolean val_invert = FALSE;
int addr_family = AF_UNSPEC;
NMIPAddr val_from = {};
NMIPAddr val_to = {};
int val_from_len = -1;
int val_to_len = -1;
char * s;
g_return_val_if_fail(str, NULL);
if (!_rr_string_validate(TRUE, to_string_flags, extra_args, error))
return NULL;
/* NM_IP_ROUTING_RULE_TO_STRING_TYPE_IPROUTE gives a string representation that is
* partly compatibly with iproute2. That is, the part after `ip -[46] rule add $ARGS`.
* There are differences though:
*
* - trying to convert an invalid rule to string may not be possible. The reason is for
* example that an invalid rule can have nm_ip_routing_rule_get_from() like "bogus",
* but we don't write that as "from bogus". In general, if you try to convert an invalid
* rule to string, the operation may fail or the result may itself not be parsable.
* Of course, valid rules can be converted to string and read back the same (round-trip).
*
* - iproute2 in may regards is flexible about the command lines. For example
* - for tables it accepts table names from /etc/iproute2/rt_tables. We only
* accept the special aliases "main", "local", and "default".
* - key names like "preference" can be abbreviated to "pref", we don't do that.
* - the "preference"/"priority" may be unspecified, in which kernel automatically
* chooses an unused priority (during `ip rule add`). We don't allow for that, the
* priority must be explicitly set.
*
* - iproute2 does not support any escaping. Well, it's the shell that supports quoting
* and escaping and splits the command line. We need to split the command line ourself,
* but we don't support full shell quotation.
* from-string tokenizes words at (ASCII) whitespaces (removing the whitespaces).
* It also supports backslash escaping (e.g. to contain whitespace), but it does
* not support special escape sequences. Values are taken literally, meaning
* "\n\ \111" gives results in "n 111".
* The strings really shouldn't contain any special characters that require escaping,
* but that's the rule.
* This also goes together with the @allow_escaping parameter of nm_utils_strsplit_set().
* If you concatenate multiple rule expressions with a delimiter, the delimiter inside
* each word can be backslash escaped, and nm_utils_strsplit_set(allow_escaping=TRUE) will
* properly split the words, preserving the backslashes, which then will be removed by
* nm_ip_routing_rule_from_string().
*/
addr_family = _rr_string_addr_family_from_flags(to_string_flags);
tokens = nm_utils_escaped_tokens_split(str, NM_ASCII_SPACES);
for (i_token = 0; tokens && tokens[i_token]; i_token++) {
char *str_word = (char *) tokens[i_token];
any_words = TRUE;
if (!word0)
word0 = str_word;
else {
nm_assert(!word1);
word1 = str_word;
}
/* iproute2 matches keywords with any partial prefix. We don't allow
* for that flexibility. */
if (NM_IN_STRSET(word0, "from")) {
if (!word1)
continue;
if (word_from)
goto next_fail_word0_duplicate_key;
word_from = word1;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "to")) {
if (!word1)
continue;
if (word_to)
goto next_fail_word0_duplicate_key;
word_to = word1;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "not")) {
/* we accept multiple "not" specifiers. "not not" still means
* not. */
val_invert = TRUE;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "priority", "order", "pref", "preference")) {
if (!word1)
continue;
if (i64_priority != -1)
goto next_fail_word0_duplicate_key;
i64_priority = _nm_utils_ascii_str_to_int64(word1, 0, 0, G_MAXUINT32, -1);
if (i64_priority == -1)
goto next_fail_word1_invalid_value;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "table", "lookup")) {
if (!word1)
continue;
if (i64_table != -1)
goto next_fail_word0_duplicate_key;
i64_table = _nm_utils_ascii_str_to_int64(word1, 0, 1, G_MAXUINT32, -1);
if (i64_table == -1) {
if (nm_streq(word1, "main"))
i64_table = RT_TABLE_MAIN;
else if (nm_streq(word1, "local"))
i64_table = RT_TABLE_LOCAL;
else if (nm_streq(word1, "default"))
i64_table = RT_TABLE_DEFAULT;
else
goto next_fail_word1_invalid_value;
}
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "tos", "dsfield")) {
if (!word1)
continue;
if (i64_tos != -1)
goto next_fail_word0_duplicate_key;
i64_tos = _nm_utils_ascii_str_to_int64(word1, 16, 0, G_MAXUINT8, -1);
if (i64_tos == -1)
goto next_fail_word1_invalid_value;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "ipproto")) {
if (!word1)
continue;
if (i64_ipproto != -1)
goto next_fail_word0_duplicate_key;
i64_ipproto = _nm_utils_ascii_str_to_int64(word1, 10, 0, G_MAXUINT8, -1);
if (i64_ipproto == -1)
goto next_fail_word1_invalid_value;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "sport")) {
if (!word1)
continue;
if (i64_sport_start != -1)
goto next_fail_word0_duplicate_key;
if (!_rr_xport_range_parse(word1, &i64_sport_start, &sport_end))
goto next_fail_word1_invalid_value;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "dport")) {
if (!word1)
continue;
if (i64_dport_start != -1)
goto next_fail_word0_duplicate_key;
if (!_rr_xport_range_parse(word1, &i64_dport_start, &dport_end))
goto next_fail_word1_invalid_value;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "fwmark")) {
if (!word1)
continue;
if (i64_fwmark != -1)
goto next_fail_word0_duplicate_key;
s = strchr(word1, '/');
if (s)
*(s++) = '\0';
i64_fwmark = _nm_utils_ascii_str_to_int64(word1, 0, 0, G_MAXUINT32, -1);
if (i64_fwmark == -1)
goto next_fail_word1_invalid_value;
if (s) {
i64_fwmask = _nm_utils_ascii_str_to_int64(s, 0, 0, G_MAXUINT32, -1);
if (i64_fwmask == -1)
goto next_fail_word1_invalid_value;
} else
i64_fwmask = 0xFFFFFFFFu;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "iif", "dev")) {
if (!word1)
continue;
if (word_iifname)
goto next_fail_word0_duplicate_key;
word_iifname = word1;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "oif")) {
if (!word1)
continue;
if (word_oifname)
goto next_fail_word0_duplicate_key;
word_oifname = word1;
goto next_words_consumed;
}
if (NM_IN_STRSET(word0, "suppress_prefixlength", "sup_pl")) {
if (!word1)
continue;
if (i64_suppress_prefixlength != -1)
goto next_fail_word0_duplicate_key;
i64_suppress_prefixlength = _nm_utils_ascii_str_to_int64(word1, 0, 0, G_MAXINT32, -1);
if (i64_suppress_prefixlength == -1)
goto next_fail_word1_invalid_value;
goto next_words_consumed;
}
/* also the action is still unsupported. For the moment, we only support
* FR_ACT_TO_TBL, which is the default (by not expressing it on the command
* line). */
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("unsupported key \"%s\""),
word0);
return FALSE;
next_fail_word0_duplicate_key:
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("duplicate key \"%s\""),
word0);
return FALSE;
next_fail_word1_invalid_value:
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("invalid value for \"%s\""),
word0);
return FALSE;
next_words_consumed:
word0 = NULL;
word1 = NULL;
}
if (!any_words) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("empty text does not describe a rule"));
return FALSE;
}
if (word0) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("missing argument for \"%s\""),
word0);
return FALSE;
}
if (!NM_IN_STRSET(word_from, NULL, "all")) {
if (!nm_utils_parse_inaddr_prefix_bin(addr_family,
word_from,
&addr_family,
&val_from,
&val_from_len)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("invalid \"from\" part"));
return FALSE;
}
if (val_from_len == -1)
val_from_len = nm_utils_addr_family_to_size(addr_family) * 8;
}
if (!NM_IN_STRSET(word_to, NULL, "all")) {
if (!nm_utils_parse_inaddr_prefix_bin(addr_family,
word_to,
&addr_family,
&val_to,
&val_to_len)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("invalid \"to\" part"));
return FALSE;
}
if (val_to_len == -1)
val_to_len = nm_utils_addr_family_to_size(addr_family) * 8;
}
if (!NM_IN_SET(addr_family, AF_INET, AF_INET6)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("cannot detect address family for rule"));
return FALSE;
}
self = nm_ip_routing_rule_new(addr_family);
if (val_invert)
self->invert = TRUE;
if (i64_priority != -1)
nm_ip_routing_rule_set_priority(self, i64_priority);
if (i64_tos != -1)
nm_ip_routing_rule_set_tos(self, i64_tos);
if (i64_ipproto != -1)
nm_ip_routing_rule_set_ipproto(self, i64_ipproto);
if (i64_fwmark != -1)
nm_ip_routing_rule_set_fwmark(self, i64_fwmark, i64_fwmask);
if (i64_sport_start != -1)
nm_ip_routing_rule_set_source_port(self, i64_sport_start, sport_end);
if (i64_dport_start != -1)
nm_ip_routing_rule_set_destination_port(self, i64_dport_start, dport_end);
if (i64_suppress_prefixlength != -1)
nm_ip_routing_rule_set_suppress_prefixlength(self, i64_suppress_prefixlength);
if (val_from_len > 0 || (val_from_len == 0 && !nm_ip_addr_is_null(addr_family, &val_from))) {
nm_ip_routing_rule_set_from_bin(self, &val_from, val_from_len);
}
if (val_to_len > 0 || (val_to_len == 0 && !nm_ip_addr_is_null(addr_family, &val_to))) {
nm_ip_routing_rule_set_to_bin(self, &val_to, val_to_len);
}
if (word_iifname)
nm_ip_routing_rule_set_iifname(self, word_iifname);
if (word_oifname)
nm_ip_routing_rule_set_oifname(self, word_oifname);
if (i64_table != -1)
nm_ip_routing_rule_set_table(self, i64_table);
if (NM_FLAGS_HAS(to_string_flags, NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE)) {
gs_free_error GError *local = NULL;
if (!nm_ip_routing_rule_validate(self, &local)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("rule is invalid: %s"),
local->message);
return NULL;
}
}
return g_steal_pointer(&self);
}
static void
_rr_string_append_inet_addr(NMStrBuf * str,
gboolean is_from /* or else is-to */,
gboolean required,
int addr_family,
const NMIPAddr *addr_bin,
guint8 addr_len)
{
char addr_str[NM_UTILS_INET_ADDRSTRLEN];
if (addr_len == 0) {
if (required) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(str, ' '),
"%s %s/0",
is_from ? "from" : "to",
(addr_family == AF_INET) ? "0.0.0.0" : "::");
}
return;
}
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(str, ' '),
"%s %s",
is_from ? "from" : "to",
nm_utils_inet_ntop(addr_family, addr_bin, addr_str));
if (addr_len != nm_utils_addr_family_to_size(addr_family) * 8) {
nm_str_buf_append_printf(str, "/%u", addr_len);
}
}
/**
* nm_ip_routing_rule_to_string:
* @self: the #NMIPRoutingRule instance to convert to string.
* @to_string_flags: #NMIPRoutingRuleAsStringFlags for controlling the
* string conversion.
* @extra_args: (allow-none): extra arguments for controlling the string
* conversion. Currently, not extra arguments are supported.
* @error: (allow-none) (out): the error reason.
*
* Returns: (transfer full): the string representation or %NULL on error.
*
* Since: 1.18
*/
char *
nm_ip_routing_rule_to_string(const NMIPRoutingRule * self,
NMIPRoutingRuleAsStringFlags to_string_flags,
GHashTable * extra_args,
GError ** error)
{
int addr_family;
NMStrBuf str;
g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
if (!_rr_string_validate(FALSE, to_string_flags, extra_args, error))
return NULL;
addr_family = nm_ip_routing_rule_get_addr_family(self);
if (!NM_IN_SET(_rr_string_addr_family_from_flags(to_string_flags), AF_UNSPEC, addr_family)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("invalid address family"));
return NULL;
}
/* It is only guaranteed that valid rules can be expressed as string.
*
* Still, unless requested proceed to convert to string without validating and
* hope for the best.
*
* That is, because self->from_str might contain an invalid IP address (indicated
* by self->from_valid). But we don't support serializing such arbitrary strings
* as "from %s". */
if (NM_FLAGS_HAS(to_string_flags, NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE)) {
gs_free_error GError *local = NULL;
if (!nm_ip_routing_rule_validate(self, &local)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
_("rule is invalid: %s"),
local->message);
return NULL;
}
}
nm_str_buf_init(&str, NM_UTILS_GET_NEXT_REALLOC_SIZE_32, FALSE);
if (self->priority_has) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),
"priority %u",
(guint) self->priority);
}
if (self->invert)
nm_str_buf_append(nm_str_buf_append_required_delimiter(&str, ' '), "not");
_rr_string_append_inet_addr(&str,
TRUE,
(!self->to_has || !self->to_valid),
addr_family,
&self->from_bin,
(self->from_has && self->from_valid) ? self->from_len : 0);
_rr_string_append_inet_addr(&str,
FALSE,
FALSE,
addr_family,
&self->to_bin,
(self->to_has && self->to_valid) ? self->to_len : 0);
if (self->tos != 0) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),
"tos 0x%02x",
(guint) self->tos);
}
if (self->ipproto != 0) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),
"ipproto %u",
(guint) self->ipproto);
}
if (self->fwmark != 0 || self->fwmask != 0) {
if (self->fwmark != 0) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),
"fwmark 0x%x",
self->fwmark);
} else {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '), "fwmark 0");
}
if (self->fwmask != 0xFFFFFFFFu) {
if (self->fwmask != 0)
nm_str_buf_append_printf(&str, "/0x%x", self->fwmask);
else
nm_str_buf_append_printf(&str, "/0");
}
}
if (self->sport_start != 0 || self->sport_end != 0) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),
"sport %u",
self->sport_start);
if (self->sport_start != self->sport_end) {
nm_str_buf_append_printf(&str, "-%u", self->sport_end);
}
}
if (self->dport_start != 0 || self->dport_end != 0) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),
"dport %u",
self->dport_start);
if (self->dport_start != self->dport_end) {
nm_str_buf_append_printf(&str, "-%u", self->dport_end);
}
}
if (self->iifname) {
nm_str_buf_append(nm_str_buf_append_required_delimiter(&str, ' '), "iif ");
nm_utils_escaped_tokens_escape_strbuf(self->iifname, NM_ASCII_SPACES, &str);
}
if (self->oifname) {
nm_str_buf_append(nm_str_buf_append_required_delimiter(&str, ' '), "oif ");
nm_utils_escaped_tokens_escape_strbuf(self->oifname, NM_ASCII_SPACES, &str);
}
if (self->table != 0) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),
"table %u",
(guint) self->table);
}
if (self->suppress_prefixlength != -1) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),
"suppress_prefixlength %d",
(int) self->suppress_prefixlength);
}
return nm_str_buf_finalize(&str, NULL);
}
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE(NMSettingIPConfig,
PROP_METHOD,
PROP_DNS,
PROP_DNS_SEARCH,
PROP_DNS_OPTIONS,
PROP_DNS_PRIORITY,
PROP_ADDRESSES,
PROP_GATEWAY,
PROP_ROUTES,
PROP_ROUTE_METRIC,
PROP_ROUTE_TABLE,
PROP_IGNORE_AUTO_ROUTES,
PROP_IGNORE_AUTO_DNS,
PROP_DHCP_HOSTNAME,
PROP_DHCP_HOSTNAME_FLAGS,
PROP_DHCP_SEND_HOSTNAME,
PROP_NEVER_DEFAULT,
PROP_MAY_FAIL,
PROP_DAD_TIMEOUT,
PROP_DHCP_TIMEOUT,
PROP_DHCP_IAID,
PROP_DHCP_REJECT_SERVERS, );
typedef struct {
GPtrArray *dns; /* array of IP address strings */
GPtrArray *dns_search; /* array of domain name strings */
GPtrArray *dns_options; /* array of DNS options */
GPtrArray *addresses; /* array of NMIPAddress */
GPtrArray *routes; /* array of NMIPRoute */
GPtrArray *routing_rules;
GArray * dhcp_reject_servers;
char * method;
char * gateway;
char * dhcp_hostname;
char * dhcp_iaid;
gint64 route_metric;
guint dhcp_hostname_flags;
int dns_priority;
int dad_timeout;
int dhcp_timeout;
guint32 route_table;
bool ignore_auto_routes : 1;
bool ignore_auto_dns : 1;
bool dhcp_send_hostname : 1;
bool never_default : 1;
bool may_fail : 1;
} NMSettingIPConfigPrivate;
G_DEFINE_ABSTRACT_TYPE(NMSettingIPConfig, nm_setting_ip_config, NM_TYPE_SETTING)
#define NM_SETTING_IP_CONFIG_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE((o), NM_TYPE_SETTING_IP_CONFIG, NMSettingIPConfigPrivate))
/*****************************************************************************/
#define NM_SETTING_IP_CONFIG_GET_FAMILY(setting) \
(NM_IS_SETTING_IP4_CONFIG(setting) ? AF_INET : AF_INET6)
/**
* nm_setting_ip_config_get_method:
* @setting: the #NMSettingIPConfig
*
* Returns: the #NMSettingIPConfig:method property of the setting; see
* #NMSettingIP4Config and #NMSettingIP6Config for details of the
* methods available with each type.
**/
const char *
nm_setting_ip_config_get_method(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->method;
}
/**
* nm_setting_ip_config_get_num_dns:
* @setting: the #NMSettingIPConfig
*
* Returns: the number of configured DNS servers
**/
guint
nm_setting_ip_config_get_num_dns(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dns->len;
}
/**
* nm_setting_ip_config_get_dns:
* @setting: the #NMSettingIPConfig
* @idx: index number of the DNS server to return
*
* Returns: the IP address of the DNS server at index @idx
**/
const char *
nm_setting_ip_config_get_dns(NMSettingIPConfig *setting, int idx)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_val_if_fail(idx >= 0 && idx < priv->dns->len, NULL);
return priv->dns->pdata[idx];
}
/**
* nm_setting_ip_config_add_dns:
* @setting: the #NMSettingIPConfig
* @dns: the IP address of the DNS server to add
*
* Adds a new DNS server to the setting.
*
* Returns: %TRUE if the DNS server was added; %FALSE if the server was already
* known
**/
gboolean
nm_setting_ip_config_add_dns(NMSettingIPConfig *setting, const char *dns)
{
NMSettingIPConfigPrivate *priv;
char * dns_canonical;
guint i;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(dns != NULL, FALSE);
g_return_val_if_fail(nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), dns),
FALSE);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
dns_canonical = canonicalize_ip(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), dns, FALSE);
for (i = 0; i < priv->dns->len; i++) {
if (!strcmp(dns_canonical, priv->dns->pdata[i])) {
g_free(dns_canonical);
return FALSE;
}
}
g_ptr_array_add(priv->dns, dns_canonical);
_notify(setting, PROP_DNS);
return TRUE;
}
/**
* nm_setting_ip_config_remove_dns:
* @setting: the #NMSettingIPConfig
* @idx: index number of the DNS server to remove
*
* Removes the DNS server at index @idx.
**/
void
nm_setting_ip_config_remove_dns(NMSettingIPConfig *setting, int idx)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(idx >= 0 && idx < priv->dns->len);
g_ptr_array_remove_index(priv->dns, idx);
_notify(setting, PROP_DNS);
}
/**
* nm_setting_ip_config_remove_dns_by_value:
* @setting: the #NMSettingIPConfig
* @dns: the DNS server to remove
*
* Removes the DNS server @dns.
*
* Returns: %TRUE if the DNS server was found and removed; %FALSE if it was not.
**/
gboolean
nm_setting_ip_config_remove_dns_by_value(NMSettingIPConfig *setting, const char *dns)
{
NMSettingIPConfigPrivate *priv;
char * dns_canonical;
guint i;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(dns != NULL, FALSE);
g_return_val_if_fail(nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), dns),
FALSE);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
dns_canonical = canonicalize_ip(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), dns, FALSE);
for (i = 0; i < priv->dns->len; i++) {
if (!strcmp(dns_canonical, priv->dns->pdata[i])) {
g_ptr_array_remove_index(priv->dns, i);
_notify(setting, PROP_DNS);
g_free(dns_canonical);
return TRUE;
}
}
g_free(dns_canonical);
return FALSE;
}
/**
* nm_setting_ip_config_clear_dns:
* @setting: the #NMSettingIPConfig
*
* Removes all configured DNS servers.
**/
void
nm_setting_ip_config_clear_dns(NMSettingIPConfig *setting)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
if (priv->dns->len != 0) {
g_ptr_array_set_size(priv->dns, 0);
_notify(setting, PROP_DNS);
}
}
/**
* nm_setting_ip_config_get_num_dns_searches:
* @setting: the #NMSettingIPConfig
*
* Returns: the number of configured DNS search domains
**/
guint
nm_setting_ip_config_get_num_dns_searches(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dns_search->len;
}
/**
* nm_setting_ip_config_get_dns_search:
* @setting: the #NMSettingIPConfig
* @idx: index number of the DNS search domain to return
*
* Returns: the DNS search domain at index @idx
**/
const char *
nm_setting_ip_config_get_dns_search(NMSettingIPConfig *setting, int idx)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_val_if_fail(idx >= 0 && idx < priv->dns_search->len, NULL);
return priv->dns_search->pdata[idx];
}
/**
* nm_setting_ip_config_add_dns_search:
* @setting: the #NMSettingIPConfig
* @dns_search: the search domain to add
*
* Adds a new DNS search domain to the setting.
*
* Returns: %TRUE if the DNS search domain was added; %FALSE if the search
* domain was already known
**/
gboolean
nm_setting_ip_config_add_dns_search(NMSettingIPConfig *setting, const char *dns_search)
{
NMSettingIPConfigPrivate *priv;
guint i;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(dns_search != NULL, FALSE);
g_return_val_if_fail(dns_search[0] != '\0', FALSE);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
for (i = 0; i < priv->dns_search->len; i++) {
if (!strcmp(dns_search, priv->dns_search->pdata[i]))
return FALSE;
}
g_ptr_array_add(priv->dns_search, g_strdup(dns_search));
_notify(setting, PROP_DNS_SEARCH);
return TRUE;
}
/**
* nm_setting_ip_config_remove_dns_search:
* @setting: the #NMSettingIPConfig
* @idx: index number of the DNS search domain
*
* Removes the DNS search domain at index @idx.
**/
void
nm_setting_ip_config_remove_dns_search(NMSettingIPConfig *setting, int idx)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(idx >= 0 && idx < priv->dns_search->len);
g_ptr_array_remove_index(priv->dns_search, idx);
_notify(setting, PROP_DNS_SEARCH);
}
/**
* nm_setting_ip_config_remove_dns_search_by_value:
* @setting: the #NMSettingIPConfig
* @dns_search: the search domain to remove
*
* Removes the DNS search domain @dns_search.
*
* Returns: %TRUE if the DNS search domain was found and removed; %FALSE if it was not.
**/
gboolean
nm_setting_ip_config_remove_dns_search_by_value(NMSettingIPConfig *setting, const char *dns_search)
{
NMSettingIPConfigPrivate *priv;
guint i;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(dns_search != NULL, FALSE);
g_return_val_if_fail(dns_search[0] != '\0', FALSE);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
for (i = 0; i < priv->dns_search->len; i++) {
if (!strcmp(dns_search, priv->dns_search->pdata[i])) {
g_ptr_array_remove_index(priv->dns_search, i);
_notify(setting, PROP_DNS_SEARCH);
return TRUE;
}
}
return FALSE;
}
/**
* nm_setting_ip_config_clear_dns_searches:
* @setting: the #NMSettingIPConfig
*
* Removes all configured DNS search domains.
**/
void
nm_setting_ip_config_clear_dns_searches(NMSettingIPConfig *setting)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
if (priv->dns_search->len != 0) {
g_ptr_array_set_size(priv->dns_search, 0);
_notify(setting, PROP_DNS_SEARCH);
}
}
/**
* nm_setting_ip_config_get_num_dns_options:
* @setting: the #NMSettingIPConfig
*
* Returns: the number of configured DNS options
*
* Since: 1.2
**/
guint
nm_setting_ip_config_get_num_dns_options(NMSettingIPConfig *setting)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
return priv->dns_options ? priv->dns_options->len : 0;
}
/**
* nm_setting_ip_config_has_dns_options:
* @setting: the #NMSettingIPConfig
*
* NMSettingIPConfig can have a list of dns-options. If the list
* is empty, there are two similar (but differentiated) states.
* Either the options are explicitly set to have no values,
* or the options are left undefined. The latter means to use
* a default configuration, while the former explicitly means "no-options".
*
* Returns: whether DNS options are initialized or left unset (the default).
**/
gboolean
nm_setting_ip_config_has_dns_options(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
return !!NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dns_options;
}
/**
* nm_setting_ip_config_get_dns_option:
* @setting: the #NMSettingIPConfig
* @idx: index number of the DNS option
*
* Returns: the DNS option at index @idx
*
* Since: 1.2
**/
const char *
nm_setting_ip_config_get_dns_option(NMSettingIPConfig *setting, guint idx)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_val_if_fail(priv->dns_options, NULL);
g_return_val_if_fail(idx < priv->dns_options->len, NULL);
return priv->dns_options->pdata[idx];
}
/**
* nm_setting_ip_config_next_valid_dns_option:
* @setting: the #NMSettingIPConfig
* @idx: index to start the search from
*
* Returns: the index, greater or equal than @idx, of the first valid
* DNS option, or -1 if no valid option is found
**/
int
nm_setting_ip_config_next_valid_dns_option(NMSettingIPConfig *setting, guint idx)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), -1);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
if (!priv->dns_options)
return -1;
for (; idx < priv->dns_options->len; idx++) {
if (_nm_utils_dns_option_validate(priv->dns_options->pdata[idx],
NULL,
NULL,
NM_IS_SETTING_IP6_CONFIG(setting),
_nm_utils_dns_option_descs))
return idx;
}
return -1;
}
/**
* nm_setting_ip_config_add_dns_option:
* @setting: the #NMSettingIPConfig
* @dns_option: the DNS option to add
*
* Adds a new DNS option to the setting.
*
* Returns: %TRUE if the DNS option was added; %FALSE otherwise
*
* Since: 1.2
**/
gboolean
nm_setting_ip_config_add_dns_option(NMSettingIPConfig *setting, const char *dns_option)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(dns_option != NULL, FALSE);
g_return_val_if_fail(dns_option[0] != '\0', FALSE);
if (!_nm_utils_dns_option_validate(dns_option, NULL, NULL, FALSE, NULL))
return FALSE;
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
if (!priv->dns_options)
priv->dns_options = g_ptr_array_new_with_free_func(g_free);
else {
if (_nm_utils_dns_option_find_idx(priv->dns_options, dns_option) >= 0)
return FALSE;
}
g_ptr_array_add(priv->dns_options, g_strdup(dns_option));
_notify(setting, PROP_DNS_OPTIONS);
return TRUE;
}
/**
* nm_setting_ip_config_remove_dns_option:
* @setting: the #NMSettingIPConfig
* @idx: index number of the DNS option
*
* Removes the DNS option at index @idx.
*
* Since: 1.2
**/
void
nm_setting_ip_config_remove_dns_option(NMSettingIPConfig *setting, int idx)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(priv->dns_options);
g_return_if_fail(idx >= 0 && idx < priv->dns_options->len);
g_ptr_array_remove_index(priv->dns_options, idx);
_notify(setting, PROP_DNS_OPTIONS);
}
/**
* nm_setting_ip_config_remove_dns_option_by_value:
* @setting: the #NMSettingIPConfig
* @dns_option: the DNS option to remove
*
* Removes the DNS option @dns_option.
*
* Returns: %TRUE if the DNS option was found and removed; %FALSE if it was not.
*
* Since: 1.2
**/
gboolean
nm_setting_ip_config_remove_dns_option_by_value(NMSettingIPConfig *setting, const char *dns_option)
{
NMSettingIPConfigPrivate *priv;
gssize i;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(dns_option != NULL, FALSE);
g_return_val_if_fail(dns_option[0] != '\0', FALSE);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
if (!priv->dns_options)
return FALSE;
i = _nm_utils_dns_option_find_idx(priv->dns_options, dns_option);
if (i >= 0) {
g_ptr_array_remove_index(priv->dns_options, i);
_notify(setting, PROP_DNS_OPTIONS);
return TRUE;
}
return FALSE;
}
/**
* nm_setting_ip_config_clear_dns_options:
* @setting: the #NMSettingIPConfig
* @is_set: the dns-options can be either empty or unset (default).
* Specify how to clear the options.
*
* Removes all configured DNS options.
*
* Since: 1.2
**/
void
nm_setting_ip_config_clear_dns_options(NMSettingIPConfig *setting, gboolean is_set)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
if (!priv->dns_options) {
if (!is_set)
return;
priv->dns_options = g_ptr_array_new_with_free_func(g_free);
} else {
if (!is_set) {
g_ptr_array_unref(priv->dns_options);
priv->dns_options = NULL;
} else {
if (priv->dns_options->len == 0)
return;
g_ptr_array_set_size(priv->dns_options, 0);
}
}
_notify(setting, PROP_DNS_OPTIONS);
}
/**
* nm_setting_ip_config_get_dns_priority:
* @setting: the #NMSettingIPConfig
*
* Returns: the priority of DNS servers
*
* Since: 1.4
**/
int
nm_setting_ip_config_get_dns_priority(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dns_priority;
}
/**
* nm_setting_ip_config_get_num_addresses:
* @setting: the #NMSettingIPConfig
*
* Returns: the number of configured addresses
**/
guint
nm_setting_ip_config_get_num_addresses(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->addresses->len;
}
/**
* nm_setting_ip_config_get_address:
* @setting: the #NMSettingIPConfig
* @idx: index number of the address to return
*
* Returns: (transfer none): the address at index @idx
**/
NMIPAddress *
nm_setting_ip_config_get_address(NMSettingIPConfig *setting, int idx)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_val_if_fail(idx >= 0 && idx < priv->addresses->len, NULL);
return priv->addresses->pdata[idx];
}
/**
* nm_setting_ip_config_add_address:
* @setting: the #NMSettingIPConfig
* @address: the new address to add
*
* Adds a new IP address and associated information to the setting. The
* given address is duplicated internally and is not changed by this function.
*
* Returns: %TRUE if the address was added; %FALSE if the address was already
* known.
**/
gboolean
nm_setting_ip_config_add_address(NMSettingIPConfig *setting, NMIPAddress *address)
{
NMSettingIPConfigPrivate *priv;
guint i;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(address != NULL, FALSE);
g_return_val_if_fail(address->family == NM_SETTING_IP_CONFIG_GET_FAMILY(setting), FALSE);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
for (i = 0; i < priv->addresses->len; i++) {
if (nm_ip_address_equal(priv->addresses->pdata[i], address))
return FALSE;
}
g_ptr_array_add(priv->addresses, nm_ip_address_dup(address));
_notify(setting, PROP_ADDRESSES);
return TRUE;
}
/**
* nm_setting_ip_config_remove_address:
* @setting: the #NMSettingIPConfig
* @idx: index number of the address to remove
*
* Removes the address at index @idx.
**/
void
nm_setting_ip_config_remove_address(NMSettingIPConfig *setting, int idx)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(idx >= 0 && idx < priv->addresses->len);
g_ptr_array_remove_index(priv->addresses, idx);
_notify(setting, PROP_ADDRESSES);
}
/**
* nm_setting_ip_config_remove_address_by_value:
* @setting: the #NMSettingIPConfig
* @address: the IP address to remove
*
* Removes the address @address.
*
* Returns: %TRUE if the address was found and removed; %FALSE if it was not.
**/
gboolean
nm_setting_ip_config_remove_address_by_value(NMSettingIPConfig *setting, NMIPAddress *address)
{
NMSettingIPConfigPrivate *priv;
guint i;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(address != NULL, FALSE);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
for (i = 0; i < priv->addresses->len; i++) {
if (nm_ip_address_equal(priv->addresses->pdata[i], address)) {
g_ptr_array_remove_index(priv->addresses, i);
_notify(setting, PROP_ADDRESSES);
return TRUE;
}
}
return FALSE;
}
/**
* nm_setting_ip_config_clear_addresses:
* @setting: the #NMSettingIPConfig
*
* Removes all configured addresses.
**/
void
nm_setting_ip_config_clear_addresses(NMSettingIPConfig *setting)
{
NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
if (priv->addresses->len != 0) {
g_ptr_array_set_size(priv->addresses, 0);
_notify(setting, PROP_ADDRESSES);
}
}
/**
* nm_setting_ip_config_get_gateway:
* @setting: the #NMSettingIPConfig
*
* Returns: the IP address of the gateway associated with this configuration, or
* %NULL.
**/
const char *
nm_setting_ip_config_get_gateway(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->gateway;
}
/**
* nm_setting_ip_config_get_num_routes:
* @setting: the #NMSettingIPConfig
*
* Returns: the number of configured routes
**/
guint
nm_setting_ip_config_get_num_routes(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->routes->len;
}
/**
* nm_setting_ip_config_get_route:
* @setting: the #NMSettingIPConfig
* @idx: index number of the route to return
*
* Returns: (transfer none): the route at index @idx
**/
NMIPRoute *
nm_setting_ip_config_get_route(NMSettingIPConfig *setting, int idx)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_val_if_fail(idx >= 0 && idx < priv->routes->len, NULL);
return priv->routes->pdata[idx];
}
/**
* nm_setting_ip_config_add_route:
* @setting: the #NMSettingIPConfig
* @route: the route to add
*
* Appends a new route and associated information to the setting. The
* given route is duplicated internally and is not changed by this function.
* If an identical route (considering attributes as well) already exists, the
* route is not added and the function returns %FALSE.
*
* Note that before 1.10, this function would not consider route attributes
* and not add a route that has an existing route with same dest/prefix,next_hop,metric
* parameters.
*
* Returns: %TRUE if the route was added; %FALSE if the route was already known.
**/
gboolean
nm_setting_ip_config_add_route(NMSettingIPConfig *setting, NMIPRoute *route)
{
NMSettingIPConfigPrivate *priv;
guint i;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(route != NULL, FALSE);
g_return_val_if_fail(route->family == NM_SETTING_IP_CONFIG_GET_FAMILY(setting), FALSE);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
for (i = 0; i < priv->routes->len; i++) {
if (nm_ip_route_equal_full(priv->routes->pdata[i],
route,
NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS))
return FALSE;
}
g_ptr_array_add(priv->routes, nm_ip_route_dup(route));
_notify(setting, PROP_ROUTES);
return TRUE;
}
/**
* nm_setting_ip_config_remove_route:
* @setting: the #NMSettingIPConfig
* @idx: index number of the route
*
* Removes the route at index @idx.
**/
void
nm_setting_ip_config_remove_route(NMSettingIPConfig *setting, int idx)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(idx >= 0 && idx < priv->routes->len);
g_ptr_array_remove_index(priv->routes, idx);
_notify(setting, PROP_ROUTES);
}
/**
* nm_setting_ip_config_remove_route_by_value:
* @setting: the #NMSettingIPConfig
* @route: the route to remove
*
* Removes the first matching route that matches @route.
* Note that before 1.10, this function would only compare dest/prefix,next_hop,metric
* and ignore route attributes. Now, @route must match exactly.
*
* Returns: %TRUE if the route was found and removed; %FALSE if it was not.
**/
gboolean
nm_setting_ip_config_remove_route_by_value(NMSettingIPConfig *setting, NMIPRoute *route)
{
NMSettingIPConfigPrivate *priv;
guint i;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
g_return_val_if_fail(route != NULL, FALSE);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
for (i = 0; i < priv->routes->len; i++) {
if (nm_ip_route_equal_full(priv->routes->pdata[i],
route,
NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS)) {
g_ptr_array_remove_index(priv->routes, i);
_notify(setting, PROP_ROUTES);
return TRUE;
}
}
return FALSE;
}
/**
* nm_setting_ip_config_clear_routes:
* @setting: the #NMSettingIPConfig
*
* Removes all configured routes.
**/
void
nm_setting_ip_config_clear_routes(NMSettingIPConfig *setting)
{
NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
if (priv->routes->len != 0) {
g_ptr_array_set_size(priv->routes, 0);
_notify(setting, PROP_ROUTES);
}
}
/**
* nm_setting_ip_config_get_route_metric:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:route-metric
* property.
*
* Returns: the route metric that is used for routes that don't explicitly
* specify a metric. See #NMSettingIPConfig:route-metric for more details.
**/
gint64
nm_setting_ip_config_get_route_metric(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), -1);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->route_metric;
}
/**
* nm_setting_ip_config_get_route_table:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:route-table
* property.
*
* Returns: the configured route-table.
*
* Since: 1.10
**/
guint32
nm_setting_ip_config_get_route_table(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->route_table;
}
/*****************************************************************************/
static void
_routing_rules_notify(NMSettingIPConfig *setting)
{
_nm_setting_emit_property_changed(NM_SETTING(setting));
}
/**
* nm_setting_ip_config_get_num_routing_rules:
* @setting: the #NMSettingIPConfig
*
* Returns: the number of configured routing rules
*
* Since: 1.18
**/
guint
nm_setting_ip_config_get_num_routing_rules(NMSettingIPConfig *setting)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
return priv->routing_rules ? priv->routing_rules->len : 0u;
}
/**
* nm_setting_ip_config_get_routing_rule:
* @setting: the #NMSettingIPConfig
* @idx: index number of the routing_rule to return
*
* Returns: (transfer none): the routing rule at index @idx
*
* Since: 1.18
**/
NMIPRoutingRule *
nm_setting_ip_config_get_routing_rule(NMSettingIPConfig *setting, guint idx)
{
NMSettingIPConfigPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_val_if_fail(priv->routing_rules && idx < priv->routing_rules->len, NULL);
return priv->routing_rules->pdata[idx];
}
/**
* nm_setting_ip_config_add_routing_rule:
* @setting: the #NMSettingIPConfig
* @routing_rule: the #NMIPRoutingRule to add. The address family
* of the added rule must be compatible with the setting.
*
* Appends a new routing-rule and associated information to the setting. The
* given routing rules gets sealed and the reference count is incremented.
* The function does not check whether an identical rule already exists
* and always appends the rule to the end of the list.
*
* Since: 1.18
**/
void
nm_setting_ip_config_add_routing_rule(NMSettingIPConfig *setting, NMIPRoutingRule *routing_rule)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
g_return_if_fail(NM_IS_IP_ROUTING_RULE(routing_rule, TRUE));
g_return_if_fail(_ip_routing_rule_get_addr_family(routing_rule)
== NM_SETTING_IP_CONFIG_GET_FAMILY(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
if (!priv->routing_rules)
priv->routing_rules =
g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_routing_rule_unref);
nm_ip_routing_rule_seal(routing_rule);
g_ptr_array_add(priv->routing_rules, nm_ip_routing_rule_ref(routing_rule));
_routing_rules_notify(setting);
}
/**
* nm_setting_ip_config_remove_routing_rule:
* @setting: the #NMSettingIPConfig
* @idx: index number of the routing_rule
*
* Removes the routing_rule at index @idx.
*
* Since: 1.18
**/
void
nm_setting_ip_config_remove_routing_rule(NMSettingIPConfig *setting, guint idx)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(priv->routing_rules && idx < priv->routing_rules->len);
g_ptr_array_remove_index(priv->routing_rules, idx);
_routing_rules_notify(setting);
}
/**
* nm_setting_ip_config_clear_routing_rules:
* @setting: the #NMSettingIPConfig
*
* Removes all configured routing rules.
*
* Since: 1.18
**/
void
nm_setting_ip_config_clear_routing_rules(NMSettingIPConfig *setting)
{
NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
if (priv->routing_rules && priv->routing_rules->len > 0) {
g_ptr_array_set_size(priv->routing_rules, 0);
_routing_rules_notify(setting);
}
}
static GVariant *
_routing_rules_dbus_only_synth(const NMSettInfoSetting * sett_info,
guint property_idx,
NMConnection * connection,
NMSetting * setting,
NMConnectionSerializationFlags flags,
const NMConnectionSerializationOptions *options)
{
NMSettingIPConfig * self = NM_SETTING_IP_CONFIG(setting);
NMSettingIPConfigPrivate *priv;
GVariantBuilder builder;
gboolean any = FALSE;
guint i;
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(self);
if (!priv->routing_rules || priv->routing_rules->len == 0)
return NULL;
for (i = 0; i < priv->routing_rules->len; i++) {
GVariant *variant;
variant = nm_ip_routing_rule_to_dbus(priv->routing_rules->pdata[i]);
if (!variant)
continue;
if (!any) {
any = TRUE;
g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
}
g_variant_builder_add(&builder, "@a{sv}", variant);
}
return any ? g_variant_builder_end(&builder) : NULL;
}
static gboolean
_routing_rules_dbus_only_set(NMSetting * setting,
GVariant * connection_dict,
const char * property,
GVariant * value,
NMSettingParseFlags parse_flags,
GError ** error)
{
GVariantIter iter_rules;
GVariant * rule_var;
guint i_rule;
gboolean success = FALSE;
gboolean rules_changed = FALSE;
nm_assert(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")));
g_variant_iter_init(&iter_rules, value);
i_rule = 0;
while (g_variant_iter_next(&iter_rules, "@a{sv}", &rule_var)) {
_nm_unused gs_unref_variant GVariant *rule_var_unref = rule_var;
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL;
gs_free_error GError *local = NULL;
i_rule++;
rule =
nm_ip_routing_rule_from_dbus(rule_var,
NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT),
&local);
if (!rule) {
if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_MISSING_PROPERTY,
_("rule #%u is invalid: %s"),
i_rule,
local->message);
goto out;
}
continue;
}
nm_setting_ip_config_add_routing_rule(NM_SETTING_IP_CONFIG(setting), rule);
rules_changed = TRUE;
}
success = TRUE;
out:
if (rules_changed)
_routing_rules_notify(NM_SETTING_IP_CONFIG(setting));
return success;
}
/*****************************************************************************/
/**
* nm_setting_ip_config_get_ignore_auto_routes:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:ignore-auto-routes
* property.
*
* Returns: %TRUE if automatically configured (ie via DHCP) routes should be
* ignored.
**/
gboolean
nm_setting_ip_config_get_ignore_auto_routes(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->ignore_auto_routes;
}
/**
* nm_setting_ip_config_get_ignore_auto_dns:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:ignore-auto-dns
* property.
*
* Returns: %TRUE if automatically configured (ie via DHCP) DNS information
* should be ignored.
**/
gboolean
nm_setting_ip_config_get_ignore_auto_dns(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->ignore_auto_dns;
}
/**
* nm_setting_ip_config_get_dhcp_hostname:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:dhcp-hostname
* property.
*
* Returns: the configured hostname to send to the DHCP server
**/
const char *
nm_setting_ip_config_get_dhcp_hostname(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_hostname;
}
/**
* nm_setting_ip_config_get_dhcp_send_hostname:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:dhcp-send-hostname
* property.
*
* Returns: %TRUE if NetworkManager should send the machine hostname to the
* DHCP server when requesting addresses to allow the server to automatically
* update DNS information for this machine.
**/
gboolean
nm_setting_ip_config_get_dhcp_send_hostname(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_send_hostname;
}
/**
* nm_setting_ip_config_get_never_default:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:never-default
* property.
*
* Returns: %TRUE if this connection should never be the default
* connection
**/
gboolean
nm_setting_ip_config_get_never_default(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->never_default;
}
/**
* nm_setting_ip_config_get_may_fail:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:may-fail
* property.
*
* Returns: %TRUE if this connection doesn't require this type of IP
* addressing to complete for the connection to succeed.
**/
gboolean
nm_setting_ip_config_get_may_fail(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->may_fail;
}
/**
* nm_setting_ip_config_get_dad_timeout:
* @setting: the #NMSettingIPConfig
*
* Returns: the #NMSettingIPConfig:dad-timeout property.
*
* Since: 1.2
**/
int
nm_setting_ip_config_get_dad_timeout(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dad_timeout;
}
/**
* nm_setting_ip_config_get_dhcp_hostname_flags:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:dhcp-hostname-flags
* property.
*
* Returns: flags for the DHCP hostname and FQDN
*
* Since: 1.22
*/
NMDhcpHostnameFlags
nm_setting_ip_config_get_dhcp_hostname_flags(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NM_DHCP_HOSTNAME_FLAG_NONE);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_hostname_flags;
}
/**
* nm_setting_ip_config_get_dhcp_timeout:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:dhcp-timeout
* property.
*
* Returns: the configured DHCP timeout in seconds. 0 = default for
* the particular kind of device.
*
* Since: 1.2
**/
int
nm_setting_ip_config_get_dhcp_timeout(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_timeout;
}
/**
* nm_setting_ip_config_get_dhcp_iaid:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:dhcp-iaid
* property.
*
* Returns: the configured DHCP IAID (Identity Association Identifier)
*
* Since: 1.22
**/
const char *
nm_setting_ip_config_get_dhcp_iaid(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_iaid;
}
/**
* nm_setting_ip_config_get_dhcp_reject_servers:
* @setting: the #NMSettingIPConfig
* @out_len: (allow-none) (out): the number of returned elements
*
* Returns: (array length=out_len zero-terminated=1) (transfer none):
* A %NULL terminated array of DHCP reject servers. Even if no reject
* servers are configured, this always returns a non %NULL value.
*
* Since: 1.28
*/
const char *const *
nm_setting_ip_config_get_dhcp_reject_servers(NMSettingIPConfig *setting, guint *out_len)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
return nm_strvarray_get_strv(&NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_reject_servers,
out_len);
}
/**
* nm_setting_ip_config_add_dhcp_reject_server:
* @setting: the #NMSettingIPConfig
* @server: the DHCP reject server to add
*
* Adds a new DHCP reject server to the setting.
*
* Since: 1.28
**/
void
nm_setting_ip_config_add_dhcp_reject_server(NMSettingIPConfig *setting, const char *server)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
g_return_if_fail(server != NULL);
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
nm_strvarray_add(nm_strvarray_ensure(&priv->dhcp_reject_servers), server);
_notify(setting, PROP_DHCP_REJECT_SERVERS);
}
/**
* nm_setting_ip_config_remove_dhcp_reject_server:
* @setting: the #NMSettingIPConfig
* @idx: index number of the DHCP reject server
*
* Removes the DHCP reject server at index @idx.
*
* Since: 1.28
**/
void
nm_setting_ip_config_remove_dhcp_reject_server(NMSettingIPConfig *setting, guint idx)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
g_return_if_fail(priv->dhcp_reject_servers && idx < priv->dhcp_reject_servers->len);
g_array_remove_index(priv->dhcp_reject_servers, idx);
_notify(setting, PROP_DHCP_REJECT_SERVERS);
}
/**
* nm_setting_ip_config_clear_dhcp_reject_servers:
* @setting: the #NMSettingIPConfig
*
* Removes all configured DHCP reject servers.
*
* Since: 1.28
**/
void
nm_setting_ip_config_clear_dhcp_reject_servers(NMSettingIPConfig *setting)
{
NMSettingIPConfigPrivate *priv;
g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
if (nm_g_array_len(priv->dhcp_reject_servers) != 0) {
nm_clear_pointer(&priv->dhcp_reject_servers, g_array_unref);
_notify(setting, PROP_DHCP_REJECT_SERVERS);
}
}
static gboolean
verify_label(const char *label)
{
const char *p;
char * iface;
p = strchr(label, ':');
if (!p)
return FALSE;
iface = g_strndup(label, p - label);
if (!nm_utils_ifname_valid_kernel(iface, NULL)) {
g_free(iface);
return FALSE;
}
g_free(iface);
for (p++; *p; p++) {
if (!g_ascii_isalnum(*p) && *p != '_')
return FALSE;
}
return TRUE;
}
static gboolean
verify(NMSetting *setting, NMConnection *connection, GError **error)
{
NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
guint i;
if (!priv->method) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_MISSING_PROPERTY,
_("property is missing"));
g_prefix_error(error, "%s.%s: ", nm_setting_get_name(setting), NM_SETTING_IP_CONFIG_METHOD);
return FALSE;
}
if (priv->dhcp_hostname && !*priv->dhcp_hostname) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("property is empty"));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_DHCP_HOSTNAME);
return FALSE;
}
/* Validate DNS */
for (i = 0; i < priv->dns->len; i++) {
const char *dns = priv->dns->pdata[i];
if (!nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), dns)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%d. DNS server address is invalid"),
(int) (i + 1));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_DNS);
return FALSE;
}
}
/* Validate addresses */
for (i = 0; i < priv->addresses->len; i++) {
NMIPAddress *addr = (NMIPAddress *) priv->addresses->pdata[i];
GVariant * label;
if (nm_ip_address_get_family(addr) != NM_SETTING_IP_CONFIG_GET_FAMILY(setting)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%d. IP address is invalid"),
(int) (i + 1));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_ADDRESSES);
return FALSE;
}
label = nm_ip_address_get_attribute(addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
if (label) {
if (!g_variant_is_of_type(label, G_VARIANT_TYPE_STRING)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%d. IP address has 'label' property with invalid type"),
(int) (i + 1));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_ADDRESSES);
return FALSE;
}
if (!verify_label(g_variant_get_string(label, NULL))) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%d. IP address has invalid label '%s'"),
(int) (i + 1),
g_variant_get_string(label, NULL));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_ADDRESSES);
return FALSE;
}
}
}
/* Validate gateway */
if (priv->gateway) {
if (!priv->addresses->len) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("gateway cannot be set if there are no addresses configured"));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_GATEWAY);
return FALSE;
}
if (!nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), priv->gateway)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("gateway is invalid"));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_GATEWAY);
return FALSE;
}
}
/* Validate routes */
for (i = 0; i < priv->routes->len; i++) {
gs_free_error GError *local = NULL;
NMIPRoute * route = (NMIPRoute *) priv->routes->pdata[i];
if (nm_ip_route_get_family(route) != NM_SETTING_IP_CONFIG_GET_FAMILY(setting)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%d. route is invalid"),
(int) (i + 1));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_ROUTES);
return FALSE;
}
if (!_nm_ip_route_attribute_validate_all(route, &local)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid attribute: %s"),
local->message);
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_ROUTES);
return FALSE;
}
}
if (priv->routing_rules) {
for (i = 0; i < priv->routing_rules->len; i++) {
NMIPRoutingRule *rule = priv->routing_rules->pdata[i];
gs_free_error GError *local = NULL;
if (_ip_routing_rule_get_addr_family(rule)
!= NM_SETTING_IP_CONFIG_GET_FAMILY(setting)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%u. rule has wrong address-family"),
i + 1);
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_ROUTING_RULES);
return FALSE;
}
if (!nm_ip_routing_rule_validate(rule, &local)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%u. rule is invalid: %s"),
i + 1,
local->message);
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_ROUTES);
return FALSE;
}
}
}
if (priv->dhcp_iaid && !_nm_utils_iaid_verify(priv->dhcp_iaid, NULL)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' is not a valid IAID"),
priv->dhcp_iaid);
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_DHCP_IAID);
return FALSE;
}
/* Validate DHCP hostname flags */
if (priv->dhcp_hostname_flags != NM_DHCP_HOSTNAME_FLAG_NONE && !priv->dhcp_send_hostname) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("the property cannot be set when '%s' is disabled"),
NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME);
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS);
return FALSE;
}
if (!_nm_utils_validate_dhcp_hostname_flags(priv->dhcp_hostname_flags,
NM_SETTING_IP_CONFIG_GET_FAMILY(setting),
error)) {
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS);
return FALSE;
}
/* Validate reject servers */
if (priv->dhcp_reject_servers && priv->dhcp_reject_servers->len != 0) {
if (NM_SETTING_IP_CONFIG_GET_FAMILY(setting) != AF_INET) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("the property is currently supported only for DHCPv4"));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS);
return FALSE;
}
for (i = 0; i < priv->dhcp_reject_servers->len; i++) {
if (!nm_utils_parse_inaddr_prefix(
NM_SETTING_IP_CONFIG_GET_FAMILY(setting),
g_array_index(priv->dhcp_reject_servers, const char *, i),
NULL,
NULL)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' is not a valid IP or subnet"),
g_array_index(priv->dhcp_reject_servers, const char *, i));
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS);
return FALSE;
}
}
}
/* Normalizable errors */
if (priv->gateway && priv->never_default) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("a gateway is incompatible with '%s'"),
NM_SETTING_IP_CONFIG_NEVER_DEFAULT);
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_GATEWAY);
return NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
}
return TRUE;
}
static NMTernary
compare_property(const NMSettInfoSetting *sett_info,
guint property_idx,
NMConnection * con_a,
NMSetting * set_a,
NMConnection * con_b,
NMSetting * set_b,
NMSettingCompareFlags flags)
{
NMSettingIPConfigPrivate *a_priv;
NMSettingIPConfigPrivate *b_priv;
guint i;
if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_IP_CONFIG_ADDRESSES)) {
if (set_b) {
a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a);
b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b);
if (a_priv->addresses->len != b_priv->addresses->len)
return FALSE;
for (i = 0; i < a_priv->addresses->len; i++) {
if (nm_ip_address_cmp_full(a_priv->addresses->pdata[i],
b_priv->addresses->pdata[i],
NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS)
!= 0)
return FALSE;
}
}
return TRUE;
}
if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_IP_CONFIG_ROUTES)) {
if (set_b) {
a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a);
b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b);
if (a_priv->routes->len != b_priv->routes->len)
return FALSE;
for (i = 0; i < a_priv->routes->len; i++) {
if (!nm_ip_route_equal_full(a_priv->routes->pdata[i],
b_priv->routes->pdata[i],
NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS))
return FALSE;
}
}
return TRUE;
}
if (nm_streq(sett_info->property_infos[property_idx].name,
NM_SETTING_IP_CONFIG_ROUTING_RULES)) {
if (set_b) {
guint n;
a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a);
b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b);
n = (a_priv->routing_rules) ? a_priv->routing_rules->len : 0u;
if (n != (b_priv->routing_rules ? b_priv->routing_rules->len : 0u))
return FALSE;
for (i = 0; i < n; i++) {
if (nm_ip_routing_rule_cmp(a_priv->routing_rules->pdata[i],
b_priv->routing_rules->pdata[i])
!= 0)
return FALSE;
}
}
return TRUE;
}
return NM_SETTING_CLASS(nm_setting_ip_config_parent_class)
->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
}
static void
duplicate_copy_properties(const NMSettInfoSetting *sett_info, NMSetting *src, NMSetting *dst)
{
NMSettingIPConfigPrivate *priv_src = NM_SETTING_IP_CONFIG_GET_PRIVATE(src);
NMSettingIPConfigPrivate *priv_dst = NM_SETTING_IP_CONFIG_GET_PRIVATE(dst);
guint i;
gboolean changed = FALSE;
NM_SETTING_CLASS(nm_setting_ip_config_parent_class)
->duplicate_copy_properties(sett_info, src, dst);
if (priv_dst->routing_rules && priv_dst->routing_rules->len > 0) {
changed = TRUE;
g_ptr_array_set_size(priv_dst->routing_rules, 0);
}
if (priv_src->routing_rules && priv_src->routing_rules->len > 0) {
changed = TRUE;
if (!priv_dst->routing_rules)
priv_dst->routing_rules =
g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_routing_rule_unref);
for (i = 0; i < priv_src->routing_rules->len; i++) {
g_ptr_array_add(priv_dst->routing_rules,
nm_ip_routing_rule_ref(priv_src->routing_rules->pdata[i]));
}
}
if (changed)
_routing_rules_notify(NM_SETTING_IP_CONFIG(dst));
}
static void
enumerate_values(const NMSettInfoProperty *property_info,
NMSetting * setting,
NMSettingValueIterFn func,
gpointer user_data)
{
if (nm_streq(property_info->name, NM_SETTING_IP_CONFIG_ROUTING_RULES)) {
NMSettingIPConfigPrivate * priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
nm_auto_unset_gvalue GValue value = G_VALUE_INIT;
GPtrArray * ptr = NULL;
guint i;
if (priv->routing_rules && priv->routing_rules->len > 0) {
ptr = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_routing_rule_unref);
for (i = 0; i < priv->routing_rules->len; i++)
g_ptr_array_add(ptr, nm_ip_routing_rule_ref(priv->routing_rules->pdata[i]));
}
g_value_init(&value, G_TYPE_PTR_ARRAY);
g_value_take_boxed(&value, ptr);
func(setting, property_info->name, &value, 0, user_data);
return;
}
NM_SETTING_CLASS(nm_setting_ip_config_parent_class)
->enumerate_values(property_info, setting, func, user_data);
}
/*****************************************************************************/
static gboolean
ip_gateway_set(NMSetting * setting,
GVariant * connection_dict,
const char * property,
GVariant * value,
NMSettingParseFlags parse_flags,
GError ** error)
{
/* FIXME: properly handle errors */
/* Don't set from 'gateway' if we're going to use the gateway in 'addresses' */
if (_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "gateway"))
return TRUE;
g_object_set(setting, property, g_variant_get_string(value, NULL), NULL);
return TRUE;
}
GArray *
_nm_sett_info_property_override_create_array_ip_config(void)
{
GArray *properties_override = _nm_sett_info_property_override_create_array();
_nm_properties_override_gobj(properties_override,
obj_properties[PROP_GATEWAY],
NM_SETT_INFO_PROPERT_TYPE(.dbus_type = G_VARIANT_TYPE_STRING,
.from_dbus_fcn = ip_gateway_set, ));
/* ---dbus---
* property: routing-rules
* format: array of 'a{sv}'
* description: Array of dictionaries for routing rules.
* ---end---
*/
_nm_properties_override_dbus(
properties_override,
NM_SETTING_IP_CONFIG_ROUTING_RULES,
NM_SETT_INFO_PROPERT_TYPE(.dbus_type = NM_G_VARIANT_TYPE("aa{sv}"),
.to_dbus_fcn = _routing_rules_dbus_only_synth,
.from_dbus_fcn = _routing_rules_dbus_only_set, ));
return properties_override;
}
/*****************************************************************************/
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMSettingIPConfig * setting = NM_SETTING_IP_CONFIG(object);
NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
switch (prop_id) {
case PROP_METHOD:
g_value_set_string(value, nm_setting_ip_config_get_method(setting));
break;
case PROP_DNS:
g_value_take_boxed(value, _nm_utils_ptrarray_to_strv(priv->dns));
break;
case PROP_DNS_SEARCH:
g_value_take_boxed(value, _nm_utils_ptrarray_to_strv(priv->dns_search));
break;
case PROP_DNS_OPTIONS:
g_value_take_boxed(value,
priv->dns_options ? _nm_utils_ptrarray_to_strv(priv->dns_options)
: NULL);
break;
case PROP_DNS_PRIORITY:
g_value_set_int(value, priv->dns_priority);
break;
case PROP_ADDRESSES:
g_value_take_boxed(value,
_nm_utils_copy_array(priv->addresses,
(NMUtilsCopyFunc) nm_ip_address_dup,
(GDestroyNotify) nm_ip_address_unref));
break;
case PROP_GATEWAY:
g_value_set_string(value, nm_setting_ip_config_get_gateway(setting));
break;
case PROP_ROUTES:
g_value_take_boxed(value,
_nm_utils_copy_array(priv->routes,
(NMUtilsCopyFunc) nm_ip_route_dup,
(GDestroyNotify) nm_ip_route_unref));
break;
case PROP_ROUTE_METRIC:
g_value_set_int64(value, priv->route_metric);
break;
case PROP_ROUTE_TABLE:
g_value_set_uint(value, priv->route_table);
break;
case PROP_IGNORE_AUTO_ROUTES:
g_value_set_boolean(value, nm_setting_ip_config_get_ignore_auto_routes(setting));
break;
case PROP_IGNORE_AUTO_DNS:
g_value_set_boolean(value, nm_setting_ip_config_get_ignore_auto_dns(setting));
break;
case PROP_DHCP_HOSTNAME:
g_value_set_string(value, nm_setting_ip_config_get_dhcp_hostname(setting));
break;
case PROP_DHCP_SEND_HOSTNAME:
g_value_set_boolean(value, nm_setting_ip_config_get_dhcp_send_hostname(setting));
break;
case PROP_NEVER_DEFAULT:
g_value_set_boolean(value, priv->never_default);
break;
case PROP_MAY_FAIL:
g_value_set_boolean(value, priv->may_fail);
break;
case PROP_DAD_TIMEOUT:
g_value_set_int(value, nm_setting_ip_config_get_dad_timeout(setting));
break;
case PROP_DHCP_TIMEOUT:
g_value_set_int(value, nm_setting_ip_config_get_dhcp_timeout(setting));
break;
case PROP_DHCP_IAID:
g_value_set_string(value, nm_setting_ip_config_get_dhcp_iaid(setting));
break;
case PROP_DHCP_HOSTNAME_FLAGS:
g_value_set_uint(value, nm_setting_ip_config_get_dhcp_hostname_flags(setting));
break;
case PROP_DHCP_REJECT_SERVERS:
g_value_set_boxed(value, nm_strvarray_get_strv_non_empty(priv->dhcp_reject_servers, NULL));
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)
{
NMSettingIPConfig * setting = NM_SETTING_IP_CONFIG(object);
NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
const char * gateway;
char ** strv;
guint i;
switch (prop_id) {
case PROP_METHOD:
g_free(priv->method);
priv->method = g_value_dup_string(value);
break;
case PROP_DNS:
g_ptr_array_unref(priv->dns);
priv->dns = _nm_utils_strv_to_ptrarray(g_value_get_boxed(value));
break;
case PROP_DNS_SEARCH:
g_ptr_array_unref(priv->dns_search);
priv->dns_search = _nm_utils_strv_to_ptrarray(g_value_get_boxed(value));
break;
case PROP_DNS_OPTIONS:
strv = g_value_get_boxed(value);
if (!strv) {
if (priv->dns_options) {
g_ptr_array_unref(priv->dns_options);
priv->dns_options = NULL;
}
} else {
if (priv->dns_options)
g_ptr_array_set_size(priv->dns_options, 0);
else
priv->dns_options = g_ptr_array_new_with_free_func(g_free);
for (i = 0; strv[i]; i++) {
if (_nm_utils_dns_option_validate(strv[i], NULL, NULL, FALSE, NULL)
&& _nm_utils_dns_option_find_idx(priv->dns_options, strv[i]) < 0)
g_ptr_array_add(priv->dns_options, g_strdup(strv[i]));
}
}
break;
case PROP_DNS_PRIORITY:
priv->dns_priority = g_value_get_int(value);
break;
case PROP_ADDRESSES:
g_ptr_array_unref(priv->addresses);
priv->addresses = _nm_utils_copy_array(g_value_get_boxed(value),
(NMUtilsCopyFunc) nm_ip_address_dup,
(GDestroyNotify) nm_ip_address_unref);
break;
case PROP_GATEWAY:
gateway = g_value_get_string(value);
g_return_if_fail(
!gateway
|| nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), gateway));
g_free(priv->gateway);
priv->gateway = canonicalize_ip(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), gateway, TRUE);
break;
case PROP_ROUTES:
g_ptr_array_unref(priv->routes);
priv->routes = _nm_utils_copy_array(g_value_get_boxed(value),
(NMUtilsCopyFunc) nm_ip_route_dup,
(GDestroyNotify) nm_ip_route_unref);
break;
case PROP_ROUTE_METRIC:
priv->route_metric = g_value_get_int64(value);
break;
case PROP_ROUTE_TABLE:
priv->route_table = g_value_get_uint(value);
break;
case PROP_IGNORE_AUTO_ROUTES:
priv->ignore_auto_routes = g_value_get_boolean(value);
break;
case PROP_IGNORE_AUTO_DNS:
priv->ignore_auto_dns = g_value_get_boolean(value);
break;
case PROP_DHCP_HOSTNAME:
g_free(priv->dhcp_hostname);
priv->dhcp_hostname = g_value_dup_string(value);
break;
case PROP_DHCP_SEND_HOSTNAME:
priv->dhcp_send_hostname = g_value_get_boolean(value);
break;
case PROP_NEVER_DEFAULT:
priv->never_default = g_value_get_boolean(value);
break;
case PROP_MAY_FAIL:
priv->may_fail = g_value_get_boolean(value);
break;
case PROP_DAD_TIMEOUT:
priv->dad_timeout = g_value_get_int(value);
break;
case PROP_DHCP_TIMEOUT:
priv->dhcp_timeout = g_value_get_int(value);
break;
case PROP_DHCP_IAID:
priv->dhcp_iaid = g_value_dup_string(value);
break;
case PROP_DHCP_HOSTNAME_FLAGS:
priv->dhcp_hostname_flags = g_value_get_uint(value);
break;
case PROP_DHCP_REJECT_SERVERS:
nm_strvarray_set_strv(&priv->dhcp_reject_servers, g_value_get_boxed(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
/*****************************************************************************/
static void
nm_setting_ip_config_init(NMSettingIPConfig *setting)
{
NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
priv->dns = g_ptr_array_new_with_free_func(g_free);
priv->dns_search = g_ptr_array_new_with_free_func(g_free);
priv->addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref);
priv->routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref);
priv->route_metric = -1;
priv->dhcp_send_hostname = TRUE;
priv->may_fail = TRUE;
priv->dad_timeout = -1;
}
static void
finalize(GObject *object)
{
NMSettingIPConfig * self = NM_SETTING_IP_CONFIG(object);
NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(self);
g_free(priv->method);
g_free(priv->gateway);
g_free(priv->dhcp_hostname);
g_free(priv->dhcp_iaid);
g_ptr_array_unref(priv->dns);
g_ptr_array_unref(priv->dns_search);
if (priv->dns_options)
g_ptr_array_unref(priv->dns_options);
g_ptr_array_unref(priv->addresses);
g_ptr_array_unref(priv->routes);
if (priv->routing_rules)
g_ptr_array_unref(priv->routing_rules);
nm_clear_pointer(&priv->dhcp_reject_servers, g_array_unref);
G_OBJECT_CLASS(nm_setting_ip_config_parent_class)->finalize(object);
}
static void
nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
{
GObjectClass * object_class = G_OBJECT_CLASS(klass);
NMSettingClass *setting_class = NM_SETTING_CLASS(klass);
g_type_class_add_private(klass, sizeof(NMSettingIPConfigPrivate));
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->finalize = finalize;
setting_class->verify = verify;
setting_class->compare_property = compare_property;
setting_class->duplicate_copy_properties = duplicate_copy_properties;
setting_class->enumerate_values = enumerate_values;
/**
* NMSettingIPConfig:method:
*
* IP configuration method.
*
* #NMSettingIP4Config and #NMSettingIP6Config both support "disabled",
* "auto", "manual", and "link-local". See the subclass-specific
* documentation for other values.
*
* In general, for the "auto" method, properties such as
* #NMSettingIPConfig:dns and #NMSettingIPConfig:routes specify information
* that is added on to the information returned from automatic
* configuration. The #NMSettingIPConfig:ignore-auto-routes and
* #NMSettingIPConfig:ignore-auto-dns properties modify this behavior.
*
* For methods that imply no upstream network, such as "shared" or
* "link-local", these properties must be empty.
*
* For IPv4 method "shared", the IP subnet can be configured by adding one
* manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the
* shared method must be configured on the interface which shares the internet
* to a subnet, not on the uplink which is shared.
**/
obj_properties[PROP_METHOD] = g_param_spec_string(
NM_SETTING_IP_CONFIG_METHOD,
"",
"",
NULL,
G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dns:
*
* Array of IP addresses of DNS servers.
**/
obj_properties[PROP_DNS] = g_param_spec_boxed(NM_SETTING_IP_CONFIG_DNS,
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dns-search:
*
* Array of DNS search domains. Domains starting with a tilde ('~')
* are considered 'routing' domains and are used only to decide the
* interface over which a query must be forwarded; they are not used
* to complete unqualified host names.
*
* When using a DNS plugin that supports Conditional Forwarding or
* Split DNS, then the search domains specify which name servers to
* query. This makes the behavior different from running with plain
* /etc/resolv.conf. For more information see also the dns-priority setting.
**/
obj_properties[PROP_DNS_SEARCH] =
g_param_spec_boxed(NM_SETTING_IP_CONFIG_DNS_SEARCH,
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dns-options:
*
* Array of DNS options as described in man 5 resolv.conf.
*
* %NULL means that the options are unset and left at the default.
* In this case NetworkManager will use default options. This is
* distinct from an empty list of properties.
*
* The currently supported options are "attempts", "debug", "edns0",
* "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names",
* "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request",
* "single-request-reopen", "timeout", "trust-ad", "use-vc".
*
* The "trust-ad" setting is only honored if the profile contributes
* name servers to resolv.conf, and if all contributing profiles have
* "trust-ad" enabled.
*
* Since: 1.2
**/
obj_properties[PROP_DNS_OPTIONS] =
g_param_spec_boxed(NM_SETTING_IP_CONFIG_DNS_OPTIONS,
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dns-priority:
*
* DNS servers priority.
*
* The relative priority for DNS servers specified by this setting. A lower
* numerical value is better (higher priority).
*
* Negative values have the special effect of excluding other configurations
* with a greater numerical priority value; so in presence of at least one negative
* priority, only DNS servers from connections with the lowest priority value will be used.
* To avoid all DNS leaks, set the priority of the profile that should be used
* to the most negative value of all active connections profiles.
*
* Zero selects a globally configured default value. If the latter is missing
* or zero too, it defaults to -50 for VPNs (including WireGuard) and 100 for
* other connections.
*
* Note that the priority is to order DNS settings for multiple active
* connections. It does not disambiguate multiple DNS servers within the
* same connection profile.
*
* When multiple devices have configurations with the same priority, VPNs will be
* considered first, then devices with the best (lowest metric) default
* route and then all other devices.
*
* When using dns=default, servers with higher priority will be on top of
* resolv.conf. To prioritize a given server over another one within the
* same connection, just specify them in the desired order.
* Note that commonly the resolver tries name servers in /etc/resolv.conf
* in the order listed, proceeding with the next server in the list
* on failure. See for example the "rotate" option of the dns-options setting.
* If there are any negative DNS priorities, then only name servers from
* the devices with that lowest priority will be considered.
*
* When using a DNS resolver that supports Conditional Forwarding or
* Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection
* is used to query domains in its search list. The search domains determine which
* name servers to ask, and the DNS priority is used to prioritize
* name servers based on the domain. Queries for domains not present in any
* search list are routed through connections having the '~.' special wildcard
* domain, which is added automatically to connections with the default route
* (or can be added manually). When multiple connections specify the same domain, the
* one with the best priority (lowest numerical value) wins. If a sub domain
* is configured on another interface it will be accepted regardless the priority,
* unless parent domain on the other interface has a negative priority, which causes
* the sub domain to be shadowed.
* With Split DNS one can avoid undesired DNS leaks by properly configuring
* DNS priorities and the search domains, so that only name servers of the desired
* interface are configured.
*
* Since: 1.4
**/
obj_properties[PROP_DNS_PRIORITY] =
g_param_spec_int(NM_SETTING_IP_CONFIG_DNS_PRIORITY,
"",
"",
G_MININT32,
G_MAXINT32,
0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:addresses: (type GPtrArray(NMIPAddress))
*
* Array of IP addresses.
**/
obj_properties[PROP_ADDRESSES] =
g_param_spec_boxed(NM_SETTING_IP_CONFIG_ADDRESSES,
"",
"",
G_TYPE_PTR_ARRAY,
/* "addresses" is a legacy D-Bus property, because the
* "addresses" GObject property normally gets set from
* the "address-data" D-Bus property...
*/
G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | NM_SETTING_PARAM_LEGACY
| G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:gateway:
*
* The gateway associated with this configuration. This is only meaningful
* if #NMSettingIPConfig:addresses is also set.
*
* The gateway's main purpose is to control the next hop of the standard default route on the device.
* Hence, the gateway property conflicts with #NMSettingIPConfig:never-default and will be
* automatically dropped if the IP configuration is set to never-default.
*
* As an alternative to set the gateway, configure a static default route with /0 as prefix
* length.
**/
obj_properties[PROP_GATEWAY] = g_param_spec_string(
NM_SETTING_IP_CONFIG_GATEWAY,
"",
"",
NULL,
G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:routes: (type GPtrArray(NMIPRoute))
*
* Array of IP routes.
**/
obj_properties[PROP_ROUTES] =
g_param_spec_boxed(NM_SETTING_IP_CONFIG_ROUTES,
"",
"",
G_TYPE_PTR_ARRAY,
G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE |
/* See :addresses above Re: LEGACY */
NM_SETTING_PARAM_LEGACY | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:route-metric:
*
* The default metric for routes that don't explicitly specify a metric.
* The default value -1 means that the metric is chosen automatically
* based on the device type.
* The metric applies to dynamic routes, manual (static) routes that
* don't have an explicit metric setting, address prefix routes, and
* the default route.
* Note that for IPv6, the kernel accepts zero (0) but coerces it to
* 1024 (user default). Hence, setting this property to zero effectively
* mean setting it to 1024.
* For IPv4, zero is a regular value for the metric.
**/
obj_properties[PROP_ROUTE_METRIC] =
g_param_spec_int64(NM_SETTING_IP_CONFIG_ROUTE_METRIC,
"",
"",
-1,
G_MAXUINT32,
-1,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:route-table:
*
* Enable policy routing (source routing) and set the routing table used when adding routes.
*
* This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes
* and static routes. But note that static routes can individually overwrite the setting
* by explicitly specifying a non-zero routing table.
*
* If the table setting is left at zero, it is eligible to be overwritten via global
* configuration. If the property is zero even after applying the global configuration
* value, policy routing is disabled for the address family of this connection.
*
* Policy routing disabled means that NetworkManager will add all routes to the main
* table (except static routes that explicitly configure a different table). Additionally,
* NetworkManager will not delete any extraneous routes from tables except the main table.
* This is to preserve backward compatibility for users who manage routing tables outside
* of NetworkManager.
*
* Since: 1.10
**/
obj_properties[PROP_ROUTE_TABLE] = g_param_spec_uint(
NM_SETTING_IP_CONFIG_ROUTE_TABLE,
"",
"",
0,
G_MAXUINT32,
0,
G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:ignore-auto-routes:
*
* When #NMSettingIPConfig:method is set to "auto" and this property to
* %TRUE, automatically configured routes are ignored and only routes
* specified in the #NMSettingIPConfig:routes property, if any, are used.
**/
obj_properties[PROP_IGNORE_AUTO_ROUTES] =
g_param_spec_boolean(NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES,
"",
"",
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:ignore-auto-dns:
*
* When #NMSettingIPConfig:method is set to "auto" and this property to
* %TRUE, automatically configured name servers and search domains are
* ignored and only name servers and search domains specified in the
* #NMSettingIPConfig:dns and #NMSettingIPConfig:dns-search properties, if
* any, are used.
**/
obj_properties[PROP_IGNORE_AUTO_DNS] =
g_param_spec_boolean(NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS,
"",
"",
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dhcp-hostname:
*
* If the #NMSettingIPConfig:dhcp-send-hostname property is %TRUE, then the
* specified name will be sent to the DHCP server when acquiring a lease.
* This property and #NMSettingIP4Config:dhcp-fqdn are mutually exclusive and
* cannot be set at the same time.
**/
obj_properties[PROP_DHCP_HOSTNAME] =
g_param_spec_string(NM_SETTING_IP_CONFIG_DHCP_HOSTNAME,
"",
"",
NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dhcp-send-hostname:
*
* If %TRUE, a hostname is sent to the DHCP server when acquiring a lease.
* Some DHCP servers use this hostname to update DNS databases, essentially
* providing a static hostname for the computer. If the
* #NMSettingIPConfig:dhcp-hostname property is %NULL and this property is
* %TRUE, the current persistent hostname of the computer is sent.
**/
obj_properties[PROP_DHCP_SEND_HOSTNAME] =
g_param_spec_boolean(NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME,
"",
"",
TRUE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:never-default:
*
* If %TRUE, this connection will never be the default connection for this
* IP type, meaning it will never be assigned the default route by
* NetworkManager.
**/
obj_properties[PROP_NEVER_DEFAULT] =
g_param_spec_boolean(NM_SETTING_IP_CONFIG_NEVER_DEFAULT,
"",
"",
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:may-fail:
*
* If %TRUE, allow overall network configuration to proceed even if the
* configuration specified by this property times out. Note that at least
* one IP configuration must succeed or overall network configuration will
* still fail. For example, in IPv6-only networks, setting this property to
* %TRUE on the #NMSettingIP4Config allows the overall network configuration
* to succeed if IPv4 configuration fails but IPv6 configuration completes
* successfully.
**/
obj_properties[PROP_MAY_FAIL] =
g_param_spec_boolean(NM_SETTING_IP_CONFIG_MAY_FAIL,
"",
"",
TRUE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dad-timeout:
*
* Timeout in milliseconds used to check for the presence of duplicate IP
* addresses on the network. If an address conflict is detected, the
* activation will fail. A zero value means that no duplicate address
* detection is performed, -1 means the default value (either configuration
* ipvx.dad-timeout override or zero). A value greater than zero is a
* timeout in milliseconds.
*
* The property is currently implemented only for IPv4.
*
* Since: 1.2
**/
obj_properties[PROP_DAD_TIMEOUT] = g_param_spec_int(
NM_SETTING_IP_CONFIG_DAD_TIMEOUT,
"",
"",
-1,
NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX,
-1,
G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dhcp-timeout:
*
* A timeout for a DHCP transaction in seconds. If zero (the default), a
* globally configured default is used. If still unspecified, a device specific
* timeout is used (usually 45 seconds).
*
* Set to 2147483647 (MAXINT32) for infinity.
**/
obj_properties[PROP_DHCP_TIMEOUT] = g_param_spec_int(
NM_SETTING_IP_CONFIG_DHCP_TIMEOUT,
"",
"",
0,
G_MAXINT32,
0,
G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dhcp-iaid:
*
* A string containing the "Identity Association Identifier" (IAID) used
* by the DHCP client. The property is a 32-bit decimal value or a
* special value among "mac", "perm-mac", "ifname" and "stable". When
* set to "mac" (or "perm-mac"), the last 4 bytes of the current (or
* permanent) MAC address are used as IAID. When set to "ifname", the
* IAID is computed by hashing the interface name. The special value
* "stable" can be used to generate an IAID based on the stable-id (see
* connection.stable-id), a per-host key and the interface name. When
* the property is unset, the value from global configuration is used;
* if no global default is set then the IAID is assumed to be
* "ifname". Note that at the moment this property is ignored for IPv6
* by dhclient, which always derives the IAID from the MAC address.
*
* Since: 1.22
**/
obj_properties[PROP_DHCP_IAID] =
g_param_spec_string(NM_SETTING_IP_CONFIG_DHCP_IAID,
"",
"",
NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dhcp-hostname-flags:
*
* Flags for the DHCP hostname and FQDN.
*
* Currently, this property only includes flags to control the FQDN flags
* set in the DHCP FQDN option. Supported FQDN flags are
* %NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE,
* %NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED and
* %NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE. When no FQDN flag is set and
* %NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS is set, the DHCP FQDN option will
* contain no flag. Otherwise, if no FQDN flag is set and
* %NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS is not set, the standard FQDN flags
* are set in the request:
* %NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE,
* %NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED for IPv4 and
* %NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE for IPv6.
*
* When this property is set to the default value %NM_DHCP_HOSTNAME_FLAG_NONE,
* a global default is looked up in NetworkManager configuration. If that value
* is unset or also %NM_DHCP_HOSTNAME_FLAG_NONE, then the standard FQDN flags
* described above are sent in the DHCP requests.
*
* Since: 1.22
*/
obj_properties[PROP_DHCP_HOSTNAME_FLAGS] =
g_param_spec_uint(NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS,
"",
"",
0,
G_MAXUINT32,
NM_DHCP_HOSTNAME_FLAG_NONE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dhcp-reject-servers:
*
* Array of servers from which DHCP offers must be rejected. This property
* is useful to avoid getting a lease from misconfigured or rogue servers.
*
* For DHCPv4, each element must be an IPv4 address, optionally
* followed by a slash and a prefix length (e.g. "192.168.122.0/24").
*
* This property is currently not implemented for DHCPv6.
*
* Since: 1.28
**/
obj_properties[PROP_DHCP_REJECT_SERVERS] =
g_param_spec_boxed(NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS,
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}