mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-04-17 09:10:47 +02:00
device: detect duplicate IPv4 addresses when method=manual
This commit is contained in:
parent
83b712aa42
commit
28f6e8b4d2
2 changed files with 246 additions and 76 deletions
|
|
@ -589,13 +589,16 @@ ipv6.ip6-privacy=1
|
|||
<term><varname>ethernet.wake-on-lan</varname></term>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>ipv4.route-metric</varname></term>
|
||||
<term><varname>ipv4.dad-timeout</varname></term>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>ipv4.dhcp-timeout</varname></term>
|
||||
<listitem><para>If left unspecified, the default value for
|
||||
the interface type is used.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>ipv4.route-metric</varname></term>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>ipv6.ip6-privacy</varname></term>
|
||||
<listitem><para>If <literal>ipv6.ip6-privacy</literal> is unset, use the content of
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
#include "nm-lldp-listener.h"
|
||||
#include "sd-ipv4ll.h"
|
||||
#include "nm-audit-manager.h"
|
||||
#include "nm-arping-manager.h"
|
||||
|
||||
#include "nm-device-logging.h"
|
||||
_LOG_DECLARE_SELF (NMDevice);
|
||||
|
|
@ -193,6 +194,14 @@ typedef struct {
|
|||
int ifindex;
|
||||
} DeleteOnDeactivateData;
|
||||
|
||||
typedef void (*ArpingCallback) (NMDevice *, NMIP4Config **, gboolean);
|
||||
|
||||
typedef struct {
|
||||
ArpingCallback callback;
|
||||
NMDevice *device;
|
||||
NMIP4Config **configs;
|
||||
} ArpingData;
|
||||
|
||||
typedef struct _NMDevicePrivate {
|
||||
gboolean in_state_changed;
|
||||
gboolean initialized;
|
||||
|
|
@ -300,7 +309,6 @@ typedef struct _NMDevicePrivate {
|
|||
NMDhcp4Config * dhcp4_config;
|
||||
guint dhcp4_restart_id;
|
||||
|
||||
guint arp_round2_id;
|
||||
PingInfo gw_ping;
|
||||
|
||||
/* dnsmasq stuff for shared connections */
|
||||
|
|
@ -315,6 +323,12 @@ typedef struct _NMDevicePrivate {
|
|||
sd_ipv4ll * ipv4ll;
|
||||
guint ipv4ll_timeout;
|
||||
|
||||
/* IPv4 DAD stuff */
|
||||
struct {
|
||||
GSList * dad_list;
|
||||
NMArpingManager * announcing;
|
||||
} arping;
|
||||
|
||||
/* IP6 configuration info */
|
||||
NMIP6Config * ip6_config;
|
||||
IpState ip6_state;
|
||||
|
|
@ -3660,6 +3674,190 @@ nm_device_check_ip_failed (NMDevice *self, gboolean may_fail)
|
|||
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* IPv4 DAD stuff */
|
||||
|
||||
static guint
|
||||
get_ipv4_dad_timeout (NMDevice *self)
|
||||
{
|
||||
NMConnection *connection;
|
||||
NMSettingIPConfig *s_ip4 = NULL;
|
||||
gs_free char *value = NULL;
|
||||
gint ret = 0;
|
||||
|
||||
connection = nm_device_get_applied_connection (self);
|
||||
if (connection)
|
||||
s_ip4 = nm_connection_get_setting_ip4_config (connection);
|
||||
|
||||
if (s_ip4) {
|
||||
ret = nm_setting_ip_config_get_dad_timeout (s_ip4);
|
||||
|
||||
if (ret < 0) {
|
||||
value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
|
||||
"ipv4.dad-timeout", self);
|
||||
ret = _nm_utils_ascii_str_to_int64 (value, 10, -1,
|
||||
NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX,
|
||||
-1);
|
||||
ret = ret < 0 ? 0 : ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
arping_data_destroy (gpointer ptr, GClosure *closure)
|
||||
{
|
||||
ArpingData *data = ptr;
|
||||
int i;
|
||||
|
||||
if (data) {
|
||||
for (i = 0; data->configs && data->configs[i]; i++)
|
||||
g_object_unref (data->configs[i]);
|
||||
g_free (data->configs);
|
||||
g_slice_free (ArpingData, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean success)
|
||||
{
|
||||
NMIP4Config *empty;
|
||||
|
||||
if (success) {
|
||||
empty = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
|
||||
nm_device_activate_schedule_ip4_config_result (self, empty);
|
||||
g_object_unref (empty);
|
||||
} else {
|
||||
nm_device_queue_state (self, NM_DEVICE_STATE_FAILED,
|
||||
NM_DEVICE_STATE_REASON_CONFIG_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *data)
|
||||
{
|
||||
NMDevice *self;
|
||||
NMDevicePrivate *priv;
|
||||
const NMPlatformIP4Address *address;
|
||||
gboolean result, success = TRUE;
|
||||
int i, j;
|
||||
|
||||
g_assert (data);
|
||||
self = data->device;
|
||||
priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
for (i = 0; data->configs && data->configs[i]; i++) {
|
||||
for (j = 0; j < nm_ip4_config_get_num_addresses (data->configs[i]); j++) {
|
||||
address = nm_ip4_config_get_address (data->configs[i], j);
|
||||
result = nm_arping_manager_check_address (arping_manager, address->address);
|
||||
success &= result;
|
||||
|
||||
_NMLOG (result ? LOGL_DEBUG : LOGL_WARN,
|
||||
LOGD_DEVICE,
|
||||
"IPv4 DAD result: address %s is %s",
|
||||
nm_utils_inet4_ntop (address->address, NULL),
|
||||
result ? "unique" : "duplicate");
|
||||
}
|
||||
}
|
||||
|
||||
data->callback (self, data->configs, success);
|
||||
|
||||
priv->arping.dad_list = g_slist_remove (priv->arping.dad_list, arping_manager);
|
||||
nm_arping_manager_destroy (arping_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipv4_dad_start:
|
||||
* @self: device instance
|
||||
* @configs: NULL-terminated array of IPv4 configurations
|
||||
* @cb: callback function
|
||||
*
|
||||
* Start IPv4 DAD on device @self, check addresses in @configs and call @cb
|
||||
* when the procedure ends. @cb will be called in any case, even if DAD can't
|
||||
* be started. @configs will be unreferenced after @cb has been called.
|
||||
*/
|
||||
static void
|
||||
ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb)
|
||||
{
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
NMArpingManager *arping_manager;
|
||||
const NMPlatformIP4Address *address;
|
||||
ArpingData *data;
|
||||
guint timeout;
|
||||
gboolean ret, addr_found;
|
||||
const guint8 *hw_addr;
|
||||
size_t hw_addr_len = 0;
|
||||
GError *error = NULL;
|
||||
guint i, j;
|
||||
|
||||
g_return_if_fail (NM_IS_DEVICE (self));
|
||||
g_return_if_fail (configs);
|
||||
g_return_if_fail (cb);
|
||||
|
||||
for (i = 0, addr_found = FALSE; configs[i]; i++) {
|
||||
if (nm_ip4_config_get_num_addresses (configs[i]) > 0) {
|
||||
addr_found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
timeout = get_ipv4_dad_timeout (self);
|
||||
hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET,
|
||||
nm_device_get_ip_ifindex (self),
|
||||
&hw_addr_len);
|
||||
|
||||
if ( !timeout
|
||||
|| !hw_addr
|
||||
|| !hw_addr_len
|
||||
|| !addr_found
|
||||
|| nm_device_uses_assumed_connection (self)) {
|
||||
|
||||
/* DAD not needed, signal success */
|
||||
cb (self, configs, TRUE);
|
||||
|
||||
for (i = 0; configs[i]; i++)
|
||||
g_object_unref (configs[i]);
|
||||
g_free (configs);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* don't take additional references of @arping_manager that outlive @self.
|
||||
* Otherwise, the callback can be invoked on a dangling pointer as we don't
|
||||
* disconnect the handler. */
|
||||
arping_manager = nm_arping_manager_new (nm_device_get_ip_ifindex (self));
|
||||
priv->arping.dad_list = g_slist_append (priv->arping.dad_list, arping_manager);
|
||||
|
||||
data = g_slice_new0 (ArpingData);
|
||||
data->configs = configs;
|
||||
data->callback = cb;
|
||||
data->device = self;
|
||||
|
||||
for (i = 0; configs[i]; i++) {
|
||||
for (j = 0; j < nm_ip4_config_get_num_addresses (configs[i]); j++) {
|
||||
address = nm_ip4_config_get_address (configs[i], j);
|
||||
nm_arping_manager_add_address (arping_manager, address->address);
|
||||
}
|
||||
}
|
||||
|
||||
g_signal_connect_data (arping_manager, NM_ARPING_MANAGER_PROBE_TERMINATED,
|
||||
G_CALLBACK (arping_manager_probe_terminated), data,
|
||||
arping_data_destroy, 0);
|
||||
|
||||
ret = nm_arping_manager_start_probe (arping_manager, timeout, &error);
|
||||
|
||||
if (!ret) {
|
||||
_LOGW (LOGD_DEVICE, "arping probe failed: %s", error->message);
|
||||
|
||||
/* DAD could not be started, signal success */
|
||||
cb (self, configs, TRUE);
|
||||
|
||||
priv->arping.dad_list = g_slist_remove (priv->arping.dad_list, arping_manager);
|
||||
nm_arping_manager_destroy (arping_manager);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* IPv4LL stuff */
|
||||
|
||||
|
|
@ -4625,10 +4823,17 @@ act_stage3_ip4_config_start (NMDevice *self,
|
|||
else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0)
|
||||
ret = ipv4ll_start (self, reason);
|
||||
else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) {
|
||||
/* Use only IPv4 config from the connection data */
|
||||
*out_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
|
||||
g_assert (*out_config);
|
||||
ret = NM_ACT_STAGE_RETURN_SUCCESS;
|
||||
NMIP4Config **configs, *config;
|
||||
|
||||
config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
|
||||
nm_ip4_config_merge_setting (config,
|
||||
nm_connection_get_setting_ip4_config (connection),
|
||||
nm_device_get_ip4_route_metric (self));
|
||||
|
||||
configs = g_new0 (NMIP4Config *, 2);
|
||||
configs[0] = config;
|
||||
ipv4_dad_start (self, configs, ipv4_manual_method_apply);
|
||||
ret = NM_ACT_STAGE_RETURN_POSTPONE;
|
||||
} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
|
||||
*out_config = shared4_new_config (self, connection, reason);
|
||||
if (*out_config) {
|
||||
|
|
@ -6433,78 +6638,15 @@ start_sharing (NMDevice *self, NMIP4Config *config)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
send_arps (NMDevice *self, const char *mode_arg)
|
||||
{
|
||||
const char *argv[] = { NULL, mode_arg, "-q", "-I", nm_device_get_ip_iface (self), "-c", "1", NULL, NULL };
|
||||
int ip_arg = G_N_ELEMENTS (argv) - 2;
|
||||
NMConnection *connection;
|
||||
NMSettingIPConfig *s_ip4;
|
||||
int i, num;
|
||||
NMIPAddress *addr;
|
||||
GError *error = NULL;
|
||||
|
||||
connection = nm_device_get_applied_connection (self);
|
||||
if (!connection)
|
||||
return;
|
||||
s_ip4 = nm_connection_get_setting_ip4_config (connection);
|
||||
if (!s_ip4)
|
||||
return;
|
||||
num = nm_setting_ip_config_get_num_addresses (s_ip4);
|
||||
if (num == 0)
|
||||
return;
|
||||
|
||||
argv[0] = nm_utils_find_helper ("arping", NULL, NULL);
|
||||
if (!argv[0]) {
|
||||
_LOGW (LOGD_DEVICE | LOGD_IP4, "arping could not be found; no ARPs will be sent");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
gs_free char *tmp_str = NULL;
|
||||
gboolean success;
|
||||
|
||||
addr = nm_setting_ip_config_get_address (s_ip4, i);
|
||||
argv[ip_arg] = nm_ip_address_get_address (addr);
|
||||
|
||||
_LOGD (LOGD_DEVICE | LOGD_IP4,
|
||||
"arping: run %s", (tmp_str = g_strjoinv (" ", (char **) argv)));
|
||||
success = g_spawn_async (NULL, (char **) argv, NULL,
|
||||
G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
|
||||
NULL, NULL, NULL, &error);
|
||||
if (!success) {
|
||||
_LOGW (LOGD_DEVICE | LOGD_IP4,
|
||||
"arping: could not send ARP for local address %s: %s",
|
||||
argv[ip_arg], error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
arp_announce_round2 (gpointer user_data)
|
||||
{
|
||||
NMDevice *self = user_data;
|
||||
NMDevicePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
|
||||
|
||||
priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
priv->arp_round2_id = 0;
|
||||
|
||||
if ( priv->state >= NM_DEVICE_STATE_IP_CONFIG
|
||||
&& priv->state <= NM_DEVICE_STATE_ACTIVATED)
|
||||
send_arps (self, "-U");
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
arp_cleanup (NMDevice *self)
|
||||
{
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
nm_clear_g_source (&priv->arp_round2_id);
|
||||
if (priv->arping.announcing) {
|
||||
nm_arping_manager_destroy (priv->arping.announcing);
|
||||
priv->arping.announcing = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -6513,10 +6655,19 @@ arp_announce (NMDevice *self)
|
|||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
NMConnection *connection;
|
||||
NMSettingIPConfig *s_ip4;
|
||||
int num;
|
||||
guint num, i;
|
||||
const guint8 *hw_addr;
|
||||
size_t hw_addr_len = 0;
|
||||
|
||||
arp_cleanup (self);
|
||||
|
||||
hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET,
|
||||
nm_device_get_ip_ifindex (self),
|
||||
&hw_addr_len);
|
||||
|
||||
if (!hw_addr_len || !hw_addr)
|
||||
return;
|
||||
|
||||
/* We only care about manually-configured addresses; DHCP- and autoip-configured
|
||||
* ones should already have been seen on the network at this point.
|
||||
*/
|
||||
|
|
@ -6530,8 +6681,19 @@ arp_announce (NMDevice *self)
|
|||
if (num == 0)
|
||||
return;
|
||||
|
||||
send_arps (self, "-A");
|
||||
priv->arp_round2_id = g_timeout_add_seconds (2, arp_announce_round2, self);
|
||||
priv->arping.announcing = nm_arping_manager_new (nm_device_get_ip_ifindex (self));
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
NMIPAddress *ip = nm_setting_ip_config_get_address (s_ip4, i);
|
||||
in_addr_t addr;
|
||||
|
||||
if (inet_pton (AF_INET, nm_ip_address_get_address (ip), &addr) == 1)
|
||||
nm_arping_manager_add_address (priv->arping.announcing, addr);
|
||||
else
|
||||
g_warn_if_reached ();
|
||||
}
|
||||
|
||||
nm_arping_manager_announce_addresses (priv->arping.announcing);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -10595,6 +10757,11 @@ dispose (GObject *object)
|
|||
|
||||
_LOGD (LOGD_DEVICE, "disposing");
|
||||
|
||||
g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy);
|
||||
priv->arping.dad_list = NULL;
|
||||
|
||||
arp_cleanup (self);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (nm_config_get (), config_changed_update_ignore_carrier, self);
|
||||
|
||||
dispatcher_cleanup (self);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue