diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2018-03-29 18:31:52 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2018-03-30 18:44:18 +0200 |
commit | 46bed9d47f30da778e34503d32fcdcd1e37927ae (patch) | |
tree | 3016da5c84268440e3bfb226c94647930003651c | |
parent | 267ddc3569fd1515ede2ee2444481abd8a044aab (diff) | |
download | NetworkManager-bg/n-acd.tar.gz |
core: arping: use n-acdbg/n-acd
-rw-r--r-- | src/devices/nm-arping-manager.c | 348 | ||||
-rw-r--r-- | src/devices/nm-arping-manager.h | 2 | ||||
-rw-r--r-- | src/devices/nm-device.c | 7 | ||||
-rw-r--r-- | src/devices/tests/test-arping.c | 42 |
4 files changed, 224 insertions, 175 deletions
diff --git a/src/devices/nm-arping-manager.c b/src/devices/nm-arping-manager.c index eb5b0574e1..03b5382cc0 100644 --- a/src/devices/nm-arping-manager.c +++ b/src/devices/nm-arping-manager.c @@ -11,7 +11,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * Copyright (C) 2015 Red Hat, Inc. + * Copyright (C) 2015-2018 Red Hat, Inc. */ #include "nm-default.h" @@ -25,6 +25,7 @@ #include "platform/nm-platform.h" #include "nm-utils.h" #include "NetworkManagerUtils.h" +#include "n-acd/src/n-acd.h" /*****************************************************************************/ @@ -37,14 +38,13 @@ typedef enum { typedef struct { in_addr_t address; - GPid pid; - guint watch; gboolean duplicate; NMArpingManager *manager; + NAcd *acd; + GIOChannel *channel; + guint event_id; } AddressInfo; -/*****************************************************************************/ - enum { PROBE_TERMINATED, LAST_SIGNAL, @@ -54,6 +54,8 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { int ifindex; + const guint8 * hwaddr; + size_t hwaddr_len; State state; GHashTable *addresses; guint completed; @@ -94,6 +96,53 @@ G_DEFINE_TYPE (NMArpingManager, nm_arping_manager, G_TYPE_OBJECT) /*****************************************************************************/ +static const char * +_acd_event_to_string (unsigned int event) +{ + switch (event) { + case N_ACD_EVENT_READY: + return "ready"; + case N_ACD_EVENT_USED: + return "used"; + case N_ACD_EVENT_DEFENDED: + return "defended"; + case N_ACD_EVENT_CONFLICT: + return "conflict"; + case N_ACD_EVENT_DOWN: + return "down"; + } + return NULL; +} + +#define acd_event_to_string(event) NM_UTILS_LOOKUP_STR (_acd_event_to_string, event) + +static const char * +_acd_error_to_string (int error) +{ + if (error < 0) + return strerror(-error); + + switch (error) { + case _N_ACD_E_SUCCESS: + return "success"; + case N_ACD_E_DONE: + return "no more events (engine running)"; + case N_ACD_E_STOPPED: + return "no more events (engine stopped)"; + case N_ACD_E_PREEMPTED: + return "preempted"; + case N_ACD_E_INVALID_ARGUMENT: + return "invalid argument"; + case N_ACD_E_BUSY: + return "busy"; + } + return NULL; +} + +#define acd_error_to_string(error) NM_UTILS_LOOKUP_STR (_acd_error_to_string, error) + +/*****************************************************************************/ + /** * nm_arping_manager_add_address: * @self: a #NMArpingManager @@ -127,62 +176,106 @@ nm_arping_manager_add_address (NMArpingManager *self, in_addr_t address) return TRUE; } -static void -arping_watch_cb (GPid pid, gint status, gpointer user_data) +static gboolean +acd_event (GIOChannel *source, GIOCondition condition, gpointer data) { - AddressInfo *info = user_data; + AddressInfo *info = data; NMArpingManager *self = info->manager; NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - const char *addr; - - info->pid = 0; - info->watch = 0; - addr = nm_utils_inet4_ntop (info->address, NULL); - - if (WIFEXITED (status)) { - if (WEXITSTATUS (status) != 0) { - _LOGD ("%s already used in the %s network", - addr, nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex)); - info->duplicate = TRUE; - } else - _LOGD ("DAD succeeded for %s", addr); - } else { - _LOGD ("stopped unexpectedly with status %d for %s", status, addr); + NAcdEvent *event; + char address_str[INET_ADDRSTRLEN]; + int r; + + if ( n_acd_dispatch (info->acd) + || n_acd_pop_event (info->acd, &event)) + return G_SOURCE_CONTINUE; + + nm_utils_inet4_ntop (info->address, address_str); + + switch (event->event) { + case N_ACD_EVENT_READY: + info->duplicate = FALSE; + if (priv->state == STATE_ANNOUNCING) { + r = n_acd_announce (info->acd, N_ACD_DEFEND_ONCE); + if (r) { + _LOGW ("couldn't announce address %s on interface '%s': %s", + address_str, + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + acd_error_to_string (r)); + } else + _LOGD ("announcing address %s", address_str); + } + break; + case N_ACD_EVENT_USED: + info->duplicate = TRUE; + break; + case N_ACD_EVENT_DEFENDED: + _LOGD ("defended address %s", address_str); + break; + case N_ACD_EVENT_CONFLICT: + _LOGW ("conflict detected for address %s on interface '%s'", + address_str, + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex)); + break; + default: + _LOGD ("event '%s' for address %s", + acd_event_to_string (event->event), + address_str); + return G_SOURCE_CONTINUE; } - if (++priv->completed == g_hash_table_size (priv->addresses)) { + if ( priv->state == STATE_PROBING + && ++priv->completed == g_hash_table_size (priv->addresses)) { priv->state = STATE_PROBE_DONE; - nm_clear_g_source (&priv->timer); g_signal_emit (self, signals[PROBE_TERMINATED], 0); } + + return G_SOURCE_CONTINUE; } static gboolean -arping_timeout_cb (gpointer user_data) +acd_probe_start (NMArpingManager *self, + AddressInfo *info, + guint64 timeout) { - NMArpingManager *self = user_data; NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - GHashTableIter iter; - AddressInfo *info; - - priv->timer = 0; + NAcdConfig *config; + int r, fd; + + r = n_acd_new (&info->acd); + if (r) { + _LOGW ("could not create ACD for %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, NULL), + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + acd_error_to_string (r)); + return FALSE; + } - g_hash_table_iter_init (&iter, priv->addresses); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { - nm_clear_g_source (&info->watch); - if (info->pid) { - _LOGD ("DAD timed out for %s", - nm_utils_inet4_ntop (info->address, NULL)); - nm_utils_kill_child_async (info->pid, SIGTERM, LOGD_IP4, - "arping", 1000, NULL, NULL); - info->pid = 0; - } + n_acd_get_fd (info->acd, &fd); + info->channel = g_io_channel_unix_new (fd); + info->event_id = g_io_add_watch (info->channel, G_IO_IN, acd_event, info); + + config = &(NAcdConfig) { + .ifindex = priv->ifindex, + .mac = priv->hwaddr, + .n_mac = priv->hwaddr_len, + .ip = info->address, + .timeout_msec = timeout, + .transport = N_ACD_TRANSPORT_ETHERNET, + }; + + r = n_acd_start (info->acd, config); + if (r) { + _LOGW ("could not start probe for %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, NULL), + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + acd_error_to_string (r)); + return FALSE; } - priv->state = STATE_PROBE_DONE; - g_signal_emit (self, signals[PROBE_TERMINATED], 0); + _LOGD ("start probe for %s", nm_utils_inet4_ntop (info->address, NULL)); - return G_SOURCE_REMOVE; + return TRUE; } /** @@ -199,7 +292,6 @@ arping_timeout_cb (gpointer user_data) gboolean nm_arping_manager_start_probe (NMArpingManager *self, guint timeout, GError **error) { - const char *argv[] = { NULL, "-D", "-q", "-I", NULL, "-c", NULL, "-w", NULL, NULL, NULL }; NMArpingManagerPrivate *priv; GHashTableIter iter; AddressInfo *info; @@ -208,57 +300,21 @@ nm_arping_manager_start_probe (NMArpingManager *self, guint timeout, GError **er g_return_val_if_fail (NM_IS_ARPING_MANAGER (self), FALSE); g_return_val_if_fail (!error || !*error, FALSE); - g_return_val_if_fail (timeout, FALSE); priv = NM_ARPING_MANAGER_GET_PRIVATE (self); g_return_val_if_fail (priv->state == STATE_INIT, FALSE); - argv[4] = nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex); - if (!argv[4]) { - /* The device was probably just removed. */ - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, - "can't find a name for ifindex %d", priv->ifindex); - return FALSE; - } - priv->completed = 0; - argv[0] = nm_utils_find_helper ("arping", NULL, NULL); - if (!argv[0]) { - g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, - "arping could not be found"); - return FALSE; - } - - timeout_str = g_strdup_printf ("%u", timeout / 1000 + 2); - argv[6] = timeout_str; - argv[8] = timeout_str; - g_hash_table_iter_init (&iter, priv->addresses); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) + success |= acd_probe_start (self, info, timeout); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { - gs_free char *tmp_str = NULL; - - argv[9] = nm_utils_inet4_ntop (info->address, NULL); - _LOGD ("run %s", (tmp_str = g_strjoinv (" ", (char **) argv))); - - if (g_spawn_async (NULL, (char **) argv, NULL, - G_SPAWN_STDOUT_TO_DEV_NULL | - G_SPAWN_STDERR_TO_DEV_NULL | - G_SPAWN_DO_NOT_REAP_CHILD, - NULL, NULL, &info->pid, NULL)) { - info->watch = g_child_watch_add (info->pid, arping_watch_cb, info); - success = TRUE; - } - } - - if (success) { - priv->timer = g_timeout_add (timeout, arping_timeout_cb, self); + if (success) priv->state = STATE_PROBING; - } else { + else g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, - "could not spawn arping process"); - } + "could not start probing"); return success; } @@ -277,8 +333,6 @@ nm_arping_manager_reset (NMArpingManager *self) g_return_if_fail (NM_IS_ARPING_MANAGER (self)); priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - nm_clear_g_source (&priv->timer); - nm_clear_g_source (&priv->round2_id); g_hash_table_remove_all (priv->addresses); priv->state = STATE_INIT; @@ -326,66 +380,6 @@ nm_arping_manager_check_address (NMArpingManager *self, in_addr_t address) return !info->duplicate; } -static void -send_announcements (NMArpingManager *self, const char *mode_arg) -{ - NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - const char *argv[] = { NULL, mode_arg, "-q", "-I", NULL, "-c", "1", NULL, NULL }; - int ip_arg = G_N_ELEMENTS (argv) - 2; - GError *error = NULL; - GHashTableIter iter; - AddressInfo *info; - - argv[4] = nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex); - if (!argv[4]) { - /* The device was probably just removed. */ - _LOGW ("can't find a name for ifindex %d", priv->ifindex); - return; - } - - argv[0] = nm_utils_find_helper ("arping", NULL, NULL); - if (!argv[0]) { - _LOGW ("arping could not be found; no ARPs will be sent"); - return; - } - - g_hash_table_iter_init (&iter, priv->addresses); - - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { - gs_free char *tmp_str = NULL; - gboolean success; - - if (info->duplicate) - continue; - - argv[ip_arg] = nm_utils_inet4_ntop (info->address, NULL); - _LOGD ("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 ("could not send ARP for address %s: %s", argv[ip_arg], - error->message); - g_clear_error (&error); - } - } -} - -static gboolean -arp_announce_round2 (gpointer self) -{ - NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE ((NMArpingManager *) self); - - priv->round2_id = 0; - send_announcements (self, "-U"); - priv->state = STATE_INIT; - g_hash_table_remove_all (priv->addresses); - - return G_SOURCE_REMOVE; -} - /** * nm_arping_manager_announce_addresses: * @self: a #NMArpingManager @@ -396,14 +390,39 @@ void nm_arping_manager_announce_addresses (NMArpingManager *self) { NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - - g_return_if_fail ( priv->state == STATE_INIT - || priv->state == STATE_PROBE_DONE); - - send_announcements (self, "-A"); - nm_clear_g_source (&priv->round2_id); - priv->round2_id = g_timeout_add_seconds (2, arp_announce_round2, self); - priv->state = STATE_ANNOUNCING; + GHashTableIter iter; + AddressInfo *info; + int r; + + if (priv->state == STATE_INIT) { + /* n-acd can't announce without probing, therefore let's + * start a fake probe with zero timeout and then perform + * the announce. */ + priv->state = STATE_ANNOUNCING; + g_hash_table_iter_init (&iter, priv->addresses); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { + if (!acd_probe_start (self, info, 0)) + _LOGW ("couldn't announce address %s on interface '%s'", + nm_utils_inet4_ntop (info->address, NULL), + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex)); + } + } else if (priv->state == STATE_PROBE_DONE) { + priv->state = STATE_ANNOUNCING; + g_hash_table_iter_init (&iter, priv->addresses); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { + if (info->duplicate) + continue; + r = n_acd_announce (info->acd, N_ACD_DEFEND_ONCE); + if (r) { + _LOGW ("couldn't announce address %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, NULL), + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + acd_error_to_string (r)); + } else + _LOGD ("announcing address %s", nm_utils_inet4_ntop (info->address, NULL)); + } + } else + nm_assert_not_reached (); } static void @@ -411,12 +430,9 @@ destroy_address_info (gpointer data) { AddressInfo *info = (AddressInfo *) data; - nm_clear_g_source (&info->watch); - - if (info->pid) { - nm_utils_kill_child_async (info->pid, SIGTERM, LOGD_IP4, "arping", - 1000, NULL, NULL); - } + g_clear_pointer (&info->channel, g_io_channel_unref); + g_clear_pointer (&info->acd, n_acd_free); + nm_clear_g_source (&info->event_id); g_slice_free (AddressInfo, info); } @@ -434,14 +450,20 @@ nm_arping_manager_init (NMArpingManager *self) } NMArpingManager * -nm_arping_manager_new (int ifindex) +nm_arping_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len) { NMArpingManager *self; NMArpingManagerPrivate *priv; + nm_assert (hwaddr); + nm_assert (hwaddr_len); + self = g_object_new (NM_TYPE_ARPING_MANAGER, NULL); priv = NM_ARPING_MANAGER_GET_PRIVATE (self); priv->ifindex = ifindex; + priv->hwaddr = hwaddr; + priv->hwaddr_len = hwaddr_len; + return self; } diff --git a/src/devices/nm-arping-manager.h b/src/devices/nm-arping-manager.h index c8a86af030..604d4d1922 100644 --- a/src/devices/nm-arping-manager.h +++ b/src/devices/nm-arping-manager.h @@ -32,7 +32,7 @@ typedef struct _NMArpingManagerClass NMArpingManagerClass; GType nm_arping_manager_get_type (void); -NMArpingManager *nm_arping_manager_new (int ifindex); +NMArpingManager *nm_arping_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len); void nm_arping_manager_destroy (NMArpingManager *self); gboolean nm_arping_manager_add_address (NMArpingManager *self, in_addr_t address); gboolean nm_arping_manager_start_probe (NMArpingManager *self, guint timeout, GError **error); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index d6ac3f9df5..63cd9b1ff1 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -5770,6 +5770,7 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) guint timeout; gboolean ret, addr_found; const guint8 *hwaddr_arr; + size_t length; GError *error = NULL; guint i; @@ -5787,7 +5788,7 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) timeout = get_ipv4_dad_timeout (self); hwaddr_arr = nm_platform_link_get_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), - NULL); + &length); if ( !timeout || !hwaddr_arr @@ -5807,7 +5808,7 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) /* 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)); + arping_manager = nm_arping_manager_new (nm_device_get_ip_ifindex (self), hwaddr_arr, length); priv->arping.dad_list = g_slist_append (priv->arping.dad_list, arping_manager); data = g_slice_new0 (ArpingData); @@ -8900,7 +8901,7 @@ arp_announce (NMDevice *self) if (num == 0) return; - priv->arping.announcing = nm_arping_manager_new (nm_device_get_ip_ifindex (self)); + priv->arping.announcing = nm_arping_manager_new (nm_device_get_ip_ifindex (self), hw_addr, hw_addr_len); for (i = 0; i < num; i++) { NMIPAddress *ip = nm_setting_ip_config_get_address (s_ip4, i); diff --git a/src/devices/tests/test-arping.c b/src/devices/tests/test-arping.c index f053754523..1313c4daee 100644 --- a/src/devices/tests/test-arping.c +++ b/src/devices/tests/test-arping.c @@ -34,6 +34,10 @@ typedef struct { int ifindex0; int ifindex1; + const guint8 *hwaddr0; + const guint8 *hwaddr1; + size_t hwaddr0_len; + size_t hwaddr1_len; } test_fixture; static void @@ -45,6 +49,9 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data) g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL)); g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL)); + + fixture->hwaddr0 = nm_platform_link_get_address (NM_PLATFORM_GET, fixture->ifindex0, &fixture->hwaddr0_len); + fixture->hwaddr1 = nm_platform_link_get_address (NM_PLATFORM_GET, fixture->ifindex1, &fixture->hwaddr1_len); } typedef struct { @@ -56,6 +63,7 @@ typedef struct { static void arping_manager_probe_terminated (NMArpingManager *arping_manager, GMainLoop *loop) { + g_print ("\n probe terminated \n"); g_main_loop_quit (loop); } @@ -69,11 +77,6 @@ test_arping_common (test_fixture *fixture, TestInfo *info) guint wait_time; gulong signal_id; - if (!nm_utils_find_helper ("arping", NULL, NULL)) { - g_test_skip ("arping binary is missing"); - return; - } - /* first, try with a short waittime. We hope that this is long enough * to successfully complete the test. Only if that's not the case, we * assume the computer is currently busy (high load) and we retry with @@ -81,7 +84,7 @@ test_arping_common (test_fixture *fixture, TestInfo *info) wait_time = WAIT_TIME_OPTIMISTIC; again: - manager = nm_arping_manager_new (fixture->ifindex0); + manager = nm_arping_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len); g_assert (manager != NULL); for (i = 0; info->addresses[i]; i++) @@ -126,7 +129,8 @@ test_arping_1 (test_fixture *fixture, gconstpointer user_data) { TestInfo info = { .addresses = { ADDR1, ADDR2, ADDR3 }, .peer_addresses = { ADDR4 }, - .expected_result = { TRUE, TRUE, TRUE } }; + .expected_result = { TRUE, TRUE, TRUE }, + }; test_arping_common (fixture, &info); } @@ -136,12 +140,31 @@ test_arping_2 (test_fixture *fixture, gconstpointer user_data) { TestInfo info = { .addresses = { ADDR1, ADDR2, ADDR3, ADDR4 }, .peer_addresses = { ADDR3, ADDR2 }, - .expected_result = { TRUE, FALSE, FALSE, TRUE } }; + .expected_result = { TRUE, FALSE, FALSE, TRUE }, + }; test_arping_common (fixture, &info); } static void +test_arping_announce (test_fixture *fixture, gconstpointer user_data) +{ + gs_unref_object NMArpingManager *manager = NULL; + GMainLoop *loop; + + manager = nm_arping_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len); + g_assert (manager != NULL); + + g_assert (nm_arping_manager_add_address (manager, ADDR1)); + g_assert (nm_arping_manager_add_address (manager, ADDR2)); + + loop = g_main_loop_new (NULL, FALSE); + nm_arping_manager_announce_addresses (manager); + g_assert (!nmtst_main_loop_run (loop, 200)); + g_main_loop_unref (loop); +} + +static void fixture_teardown (test_fixture *fixture, gconstpointer user_data) { nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex0); @@ -159,6 +182,9 @@ _nmtstp_init_tests (int *argc, char ***argv) void _nmtstp_setup_tests (void) { + //if (0) g_test_add ("/arping/1", test_fixture, NULL, fixture_setup, test_arping_1, fixture_teardown); + //if (0) g_test_add ("/arping/2", test_fixture, NULL, fixture_setup, test_arping_2, fixture_teardown); + g_test_add ("/arping/3", test_fixture, NULL, fixture_setup, test_arping_announce, fixture_teardown); } |