mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-25 10:40:08 +01:00
merge: branch 'bg/builtin-ping'
Use an internal "ping" implementation https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2328
This commit is contained in:
commit
f21f9eb172
5 changed files with 475 additions and 264 deletions
3
NEWS
3
NEWS
|
|
@ -23,6 +23,9 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
|
|||
the 802.1X certificates and keys set in the connection.
|
||||
* Introduce a libnm function that can be used by VPN plugins to check
|
||||
user permissions on certificate and keys.
|
||||
* Use an internal implementation of the ping functionality when the
|
||||
"connection.gateway-ping-timeout" or "connection.ip-ping-addresses"
|
||||
properties are set, instead of relying on the "ping" tool.
|
||||
|
||||
=============================================
|
||||
NetworkManager-1.56
|
||||
|
|
|
|||
|
|
@ -199,8 +199,6 @@ Requires: dbus >= %{dbus_version}
|
|||
Requires: glib2 >= %{glib2_version}
|
||||
Requires: %{name}-libnm%{?_isa} = %{epoch}:%{version}-%{release}
|
||||
|
||||
Recommends: iputils
|
||||
|
||||
%if 0%{?rhel} == 8
|
||||
# Older libndp versions use select() (rh#1933041). On well known distros,
|
||||
# choose a version that has the necessary fix.
|
||||
|
|
|
|||
|
|
@ -805,7 +805,7 @@ typedef struct _NMDevicePrivate {
|
|||
|
||||
GVariant *ports_variant; /* Array of port devices D-Bus path */
|
||||
char *prop_ip_iface; /* IP interface D-Bus property */
|
||||
GList *ping_operations;
|
||||
CList ping_ops_lst_head;
|
||||
GSource *ping_timeout;
|
||||
} NMDevicePrivate;
|
||||
|
||||
|
|
@ -850,7 +850,6 @@ static const char *_activation_func_to_string(ActivationHandleFunc func);
|
|||
static void
|
||||
_set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, gboolean quitting);
|
||||
static void queued_state_clear(NMDevice *device);
|
||||
static void ip_check_ping_watch_cb(GPid pid, int status, gpointer user_data);
|
||||
static void nm_device_start_ip_check(NMDevice *self);
|
||||
static void realize_start_setup(NMDevice *self,
|
||||
const NMPlatformLink *plink,
|
||||
|
|
@ -15381,36 +15380,16 @@ _dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_da
|
|||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
NMLogDomain log_domain;
|
||||
NMDevice *device;
|
||||
gboolean ping_addresses_require_all;
|
||||
GSource *watch;
|
||||
GPid pid;
|
||||
char *binary;
|
||||
char *address;
|
||||
guint deadline;
|
||||
char *addr_str;
|
||||
NMIPAddrTyped addr_bin;
|
||||
|
||||
NMLogDomain log_domain;
|
||||
NMDevice *device;
|
||||
GCancellable *cancellable;
|
||||
gboolean require_all;
|
||||
CList ping_ops_lst;
|
||||
} PingOperation;
|
||||
|
||||
static PingOperation *
|
||||
ping_operation_new(NMDevice *self,
|
||||
NMLogDomain log_domain,
|
||||
const char *address,
|
||||
const char *ping_binary,
|
||||
guint ping_timeout,
|
||||
gboolean ip_ping_addresses_require_all)
|
||||
{
|
||||
PingOperation *ping_op = g_new0(PingOperation, 1);
|
||||
|
||||
ping_op->device = self;
|
||||
ping_op->log_domain = log_domain;
|
||||
ping_op->address = g_strdup(address);
|
||||
ping_op->binary = g_strdup(ping_binary);
|
||||
ping_op->deadline = ping_timeout + 10;
|
||||
ping_op->ping_addresses_require_all = ip_ping_addresses_require_all;
|
||||
|
||||
return ping_op;
|
||||
}
|
||||
|
||||
static void
|
||||
ip_check_pre_up(NMDevice *self)
|
||||
{
|
||||
|
|
@ -15433,188 +15412,154 @@ ip_check_pre_up(NMDevice *self)
|
|||
}
|
||||
|
||||
static void
|
||||
cleanup_ping_operation(PingOperation *ping_op)
|
||||
ping_op_cleanup(PingOperation *ping_op)
|
||||
{
|
||||
if (ping_op->watch) {
|
||||
nm_clear_g_source_inst(&ping_op->watch);
|
||||
}
|
||||
|
||||
if (ping_op->pid) {
|
||||
nm_utils_kill_child_async(ping_op->pid,
|
||||
SIGTERM,
|
||||
ping_op->log_domain,
|
||||
"ping",
|
||||
1000,
|
||||
NULL,
|
||||
NULL);
|
||||
ping_op->pid = 0;
|
||||
}
|
||||
|
||||
nm_clear_g_free(&ping_op->binary);
|
||||
nm_clear_g_free(&ping_op->address);
|
||||
nm_clear_g_cancellable(&ping_op->cancellable);
|
||||
nm_clear_g_free(&ping_op->addr_str);
|
||||
c_list_unlink_stale(&ping_op->ping_ops_lst);
|
||||
|
||||
g_free(ping_op);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
spawn_ping_for_operation(NMDevice *self, PingOperation *ping_op)
|
||||
static void
|
||||
ping_cleanup(NMDevice *self)
|
||||
{
|
||||
gs_free char *str_timeout = NULL;
|
||||
gs_free char *tmp_str = NULL;
|
||||
const char *args[] = {ping_op->binary,
|
||||
"-I",
|
||||
nm_device_get_ip_iface(self),
|
||||
"-c",
|
||||
"1",
|
||||
"-w",
|
||||
NULL,
|
||||
ping_op->address,
|
||||
NULL};
|
||||
gs_free_error GError *error = NULL;
|
||||
gboolean ret;
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
|
||||
PingOperation *ping_op;
|
||||
|
||||
args[6] = str_timeout = g_strdup_printf("%u", ping_op->deadline);
|
||||
|
||||
tmp_str = g_strjoinv(" ", (char **) args);
|
||||
_LOGD(ping_op->log_domain, "ping: running '%s'", tmp_str);
|
||||
|
||||
ret = g_spawn_async("/",
|
||||
(char **) args,
|
||||
NULL,
|
||||
G_SPAWN_DO_NOT_REAP_CHILD,
|
||||
NULL,
|
||||
NULL,
|
||||
&ping_op->pid,
|
||||
&error);
|
||||
|
||||
if (ret) {
|
||||
ping_op->watch = nm_g_child_watch_add_source(ping_op->pid, ip_check_ping_watch_cb, ping_op);
|
||||
} else {
|
||||
_LOGD(ping_op->log_domain, "ping: could not spawn %s: %s", ping_op->binary, error->message);
|
||||
while ((ping_op = c_list_first_entry(&priv->ping_ops_lst_head, PingOperation, ping_ops_lst))) {
|
||||
ping_op_cleanup(ping_op);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
respawn_ping_cb(gpointer user_data)
|
||||
{
|
||||
PingOperation *ping_op = (PingOperation *) user_data;
|
||||
NMDevice *self = ping_op->device;
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
|
||||
|
||||
nm_clear_g_source_inst(&ping_op->watch);
|
||||
|
||||
if (!spawn_ping_for_operation(self, ping_op)) {
|
||||
priv->ping_operations = g_list_remove(priv->ping_operations, ping_op);
|
||||
cleanup_ping_operation(ping_op);
|
||||
|
||||
if (g_list_length(priv->ping_operations) == 0) {
|
||||
ip_check_pre_up(self);
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
nm_clear_g_source_inst(&priv->ping_timeout);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_check_ping_watch_cb(GPid pid, int status, gpointer user_data)
|
||||
ping_host_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||
{
|
||||
PingOperation *ping_op = (PingOperation *) user_data;
|
||||
NMDevice *self = ping_op->device;
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
|
||||
gboolean success = FALSE;
|
||||
NMDevice *self;
|
||||
NMDevicePrivate *priv;
|
||||
PingOperation *ping_op = user_data;
|
||||
gs_free_error GError *error = NULL;
|
||||
gboolean success;
|
||||
NMLogDomain log_domain;
|
||||
gs_free char *addr_str = NULL;
|
||||
|
||||
if (!ping_op->watch)
|
||||
success = nm_utils_ping_host_finish(result, &error);
|
||||
if (nm_utils_error_is_cancelled(error))
|
||||
return;
|
||||
|
||||
nm_clear_g_source_inst(&ping_op->watch);
|
||||
ping_op->pid = 0;
|
||||
self = NM_DEVICE(ping_op->device);
|
||||
priv = NM_DEVICE_GET_PRIVATE(self);
|
||||
log_domain = ping_op->log_domain;
|
||||
addr_str = g_steal_pointer(&ping_op->addr_str);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) == 0) {
|
||||
_LOGD(ping_op->log_domain, "ping: ping succeeded on %s", ping_op->address);
|
||||
success = TRUE;
|
||||
} else {
|
||||
_LOGD(ping_op->log_domain,
|
||||
"ping: ping failed with error code %d on %s",
|
||||
WEXITSTATUS(status),
|
||||
ping_op->address);
|
||||
}
|
||||
} else {
|
||||
_LOGD(ping_op->log_domain,
|
||||
"ping: stopped unexpectedly with status %d on %s",
|
||||
status,
|
||||
ping_op->address);
|
||||
if (!success) {
|
||||
/* it should never fail because we set an infinite timeout */
|
||||
nm_assert_not_reached();
|
||||
return;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
if (ping_op->ping_addresses_require_all) {
|
||||
priv->ping_operations = g_list_remove(priv->ping_operations, ping_op);
|
||||
if (g_list_length(priv->ping_operations) == 0) {
|
||||
_LOGD(ping_op->log_domain,
|
||||
"ping: ip-ping-addresses requires all, all ping checks on ip-ping-addresses "
|
||||
"succeeded");
|
||||
if (priv->ping_timeout)
|
||||
nm_clear_g_source_inst(&priv->ping_timeout);
|
||||
ip_check_pre_up(self);
|
||||
}
|
||||
cleanup_ping_operation(ping_op);
|
||||
} else {
|
||||
nm_assert(priv->ping_operations);
|
||||
|
||||
g_list_free_full(priv->ping_operations, (GDestroyNotify) cleanup_ping_operation);
|
||||
priv->ping_operations = NULL;
|
||||
|
||||
if (priv->ping_timeout)
|
||||
nm_clear_g_source_inst(&priv->ping_timeout);
|
||||
|
||||
_LOGD(ping_op->log_domain,
|
||||
"ping: ip-ping-addresses requires any, one ping check on ip-ping-addresses "
|
||||
"succeeded");
|
||||
ip_check_pre_up(self);
|
||||
if (ping_op->require_all) {
|
||||
ping_op_cleanup(ping_op);
|
||||
if (!c_list_is_empty(&priv->ping_ops_lst_head)) {
|
||||
_LOGD(log_domain,
|
||||
"ping: check on address %s succeeded, waiting for other addresses",
|
||||
addr_str);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* If ping exited with an error it may have returned early,
|
||||
* wait 1 second and restart it */
|
||||
ping_op->watch = nm_g_timeout_add_seconds_source(1, respawn_ping_cb, ping_op);
|
||||
}
|
||||
|
||||
_LOGD(log_domain, "ping: check on address %s succeeded, continuing the activation", addr_str);
|
||||
|
||||
ping_cleanup(self);
|
||||
ip_check_pre_up(self);
|
||||
}
|
||||
|
||||
static void
|
||||
ping_operation_start(NMDevice *self,
|
||||
const char *addr_str,
|
||||
NMIPAddrTyped *addr_bin,
|
||||
gboolean require_all)
|
||||
{
|
||||
PingOperation *ping_op;
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
|
||||
NMIPAddrTyped addr_bin_local;
|
||||
char buf[NM_INET_ADDRSTRLEN];
|
||||
int addr_family;
|
||||
int ret;
|
||||
|
||||
/* Exactly one of the two must be set */
|
||||
nm_assert(!!addr_str ^ !!addr_bin);
|
||||
|
||||
/* Derive the string address from the binary and vice versa */
|
||||
if (addr_str) {
|
||||
ret = nm_inet_parse_bin_full(AF_UNSPEC,
|
||||
FALSE,
|
||||
addr_str,
|
||||
&addr_family,
|
||||
&addr_bin_local.addr.addr_ptr);
|
||||
nm_assert(ret);
|
||||
addr_bin_local.addr_family = addr_family;
|
||||
addr_bin = &addr_bin_local;
|
||||
} else {
|
||||
nm_inet_ntop(addr_bin->addr_family, addr_bin->addr.addr_ptr, buf);
|
||||
addr_str = buf;
|
||||
}
|
||||
|
||||
/* When pinging the gateway, the caller must ensure that the IP configuration is ready.
|
||||
* For the ip-ping-addresses property, a valid connection always has may-fail=no for
|
||||
* the families of all the target addresses. Thus, at this point the IP configuration
|
||||
* must also be ready. */
|
||||
nm_assert(priv->ip_data_x[NM_IS_IPv4(addr_bin->addr_family)].state == NM_DEVICE_IP_STATE_READY);
|
||||
|
||||
ping_op = g_new(PingOperation, 1);
|
||||
*ping_op = (PingOperation) {
|
||||
.device = self,
|
||||
.cancellable = g_cancellable_new(),
|
||||
.require_all = require_all,
|
||||
.addr_bin = *addr_bin,
|
||||
.addr_str = g_strdup(addr_str),
|
||||
.log_domain = (addr_bin->addr_family == AF_INET) ? LOGD_IP4 : LOGD_IP6,
|
||||
};
|
||||
|
||||
/* Start the asynchronous ping operation */
|
||||
nm_utils_ping_host(ping_op->addr_bin,
|
||||
nm_device_get_ip_ifindex(self),
|
||||
0, /* try forever */
|
||||
ping_op->cancellable,
|
||||
ping_host_cb,
|
||||
ping_op);
|
||||
|
||||
c_list_link_tail(&priv->ping_ops_lst_head, &ping_op->ping_ops_lst);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ip_check_ping_timeout_cb(gpointer user_data)
|
||||
{
|
||||
NMDevice *self = NM_DEVICE(user_data);
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
|
||||
NMDevice *self = NM_DEVICE(user_data);
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
|
||||
PingOperation *ping_op;
|
||||
nm_auto_free_gstring GString *str = NULL;
|
||||
|
||||
_LOGW(LOGD_DEVICE, "ping timeout: unreachable gateway or ip-ping-addresses");
|
||||
|
||||
if (priv->ping_operations) {
|
||||
g_list_free_full(priv->ping_operations, (GDestroyNotify) cleanup_ping_operation);
|
||||
priv->ping_operations = NULL;
|
||||
if (_LOGW_ENABLED(LOGD_DEVICE)) {
|
||||
str = g_string_new("");
|
||||
c_list_for_each_entry (ping_op, &priv->ping_ops_lst_head, ping_ops_lst) {
|
||||
if (str->len != 0)
|
||||
g_string_append(str, ", ");
|
||||
g_string_append(str, ping_op->addr_str);
|
||||
}
|
||||
_LOGW(LOGD_DEVICE,
|
||||
"ping: the following addresses were not reachable within the timeout: %s",
|
||||
str->str);
|
||||
}
|
||||
|
||||
if (priv->ping_timeout)
|
||||
nm_clear_g_source_inst(&priv->ping_timeout);
|
||||
ping_cleanup(self);
|
||||
ip_check_pre_up(self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
start_ping(NMDevice *self, PingOperation *ping_op)
|
||||
{
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
|
||||
|
||||
if (spawn_ping_for_operation(self, ping_op)) {
|
||||
priv->ping_operations = g_list_append(priv->ping_operations, ping_op);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cleanup_ping_operation(ping_op);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
nm_device_start_ip_check(NMDevice *self)
|
||||
{
|
||||
|
|
@ -15623,17 +15568,13 @@ nm_device_start_ip_check(NMDevice *self)
|
|||
NMSettingConnection *s_con;
|
||||
guint gw_ping_timeout = 0;
|
||||
guint ip_ping_timeout = 0;
|
||||
const char *ping_binary = NULL;
|
||||
char buf[NM_INET_ADDRSTRLEN];
|
||||
NMLogDomain log_domain = LOGD_IP4;
|
||||
gboolean ip_ping_addresses_require_all;
|
||||
gboolean ping_started = FALSE;
|
||||
gboolean require_all;
|
||||
NMIPAddrTyped addr_bin = {};
|
||||
|
||||
/* Shouldn't be any active ping here, since IP_CHECK happens after the
|
||||
* first IP method completes. Any subsequently completing IP method doesn't
|
||||
* get checked.
|
||||
*/
|
||||
g_return_if_fail(priv->ping_operations == NULL);
|
||||
* get checked. */
|
||||
g_return_if_fail(c_list_is_empty(&priv->ping_ops_lst_head));
|
||||
g_return_if_fail(priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY
|
||||
|| priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY);
|
||||
|
||||
|
|
@ -15642,100 +15583,71 @@ nm_device_start_ip_check(NMDevice *self)
|
|||
|
||||
s_con = nm_connection_get_setting_connection(connection);
|
||||
g_assert(s_con);
|
||||
gw_ping_timeout = nm_setting_connection_get_gateway_ping_timeout(s_con);
|
||||
ip_ping_addresses_require_all = _prop_get_connection_ip_ping_addresses_require_all(self, s_con);
|
||||
ip_ping_timeout = nm_setting_connection_get_ip_ping_timeout(s_con);
|
||||
gw_ping_timeout = nm_setting_connection_get_gateway_ping_timeout(s_con);
|
||||
ip_ping_timeout = nm_setting_connection_get_ip_ping_timeout(s_con);
|
||||
require_all = _prop_get_connection_ip_ping_addresses_require_all(self, s_con);
|
||||
|
||||
buf[0] = '\0';
|
||||
if (gw_ping_timeout != 0 && ip_ping_timeout == 0) {
|
||||
/* the timeouts are mutually exclusive */
|
||||
nm_assert(gw_ping_timeout == 0 || ip_ping_timeout == 0);
|
||||
|
||||
if (gw_ping_timeout > 0) {
|
||||
const NMPObject *gw;
|
||||
const NML3ConfigData *l3cd;
|
||||
|
||||
_LOGD(LOGD_DEVICE, "starting ping gateway...");
|
||||
|
||||
l3cd = priv->l3cfg ? nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE) : NULL;
|
||||
if (!l3cd) {
|
||||
/* pass */
|
||||
} else if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY) {
|
||||
gw = nm_l3_config_data_get_best_default_route(l3cd, AF_INET);
|
||||
if (gw) {
|
||||
nm_inet4_ntop(NMP_OBJECT_CAST_IP4_ROUTE(gw)->gateway, buf);
|
||||
ping_binary = nm_utils_find_helper("ping", "/usr/bin/ping", NULL);
|
||||
log_domain = LOGD_IP4;
|
||||
addr_bin.addr_family = AF_INET;
|
||||
addr_bin.addr.addr4 = NMP_OBJECT_CAST_IP4_ROUTE(gw)->gateway;
|
||||
}
|
||||
} else if (priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY) {
|
||||
gw = nm_l3_config_data_get_best_default_route(l3cd, AF_INET6);
|
||||
if (gw) {
|
||||
nm_inet6_ntop(&NMP_OBJECT_CAST_IP6_ROUTE(gw)->gateway, buf);
|
||||
ping_binary = nm_utils_find_helper("ping6", "/usr/bin/ping6", NULL);
|
||||
log_domain = LOGD_IP6;
|
||||
addr_bin.addr_family = AF_INET6;
|
||||
addr_bin.addr.addr6 = NMP_OBJECT_CAST_IP6_ROUTE(gw)->gateway;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buf[0]) {
|
||||
PingOperation *ping_op = ping_operation_new(self,
|
||||
log_domain,
|
||||
buf,
|
||||
ping_binary,
|
||||
gw_ping_timeout,
|
||||
ip_ping_addresses_require_all);
|
||||
|
||||
if (start_ping(self, ping_op))
|
||||
ping_started = TRUE;
|
||||
}
|
||||
|
||||
if (gw_ping_timeout == 0 && ip_ping_timeout != 0) {
|
||||
if (addr_bin.addr_family != AF_UNSPEC) {
|
||||
_LOGD(LOGD_DEVICE,
|
||||
"starting ping on the IPv%c gateway with a %u seconds timeout",
|
||||
nm_utils_addr_family_to_char(addr_bin.addr_family),
|
||||
gw_ping_timeout);
|
||||
ping_operation_start(self, NULL, &addr_bin, require_all);
|
||||
}
|
||||
} else if (ip_ping_timeout > 0) {
|
||||
const NML3ConfigData *l3cd;
|
||||
GArray *ip_ping_addresses;
|
||||
const char *const *strv;
|
||||
guint i;
|
||||
GArray *ip_ping_addresses = _nm_setting_connection_get_ip_ping_addresses(s_con);
|
||||
const char *const *strv = nm_strvarray_get_strv_notempty(ip_ping_addresses, NULL);
|
||||
|
||||
_LOGD(LOGD_DEVICE, "starting ping ip addresses...");
|
||||
ip_ping_addresses = _nm_setting_connection_get_ip_ping_addresses(s_con);
|
||||
strv = nm_strvarray_get_strv_notnull(ip_ping_addresses, NULL);
|
||||
|
||||
_LOGD(LOGD_DEVICE,
|
||||
"starting ping on the ip-ping-addresses with a %u seconds timeout",
|
||||
ip_ping_timeout);
|
||||
|
||||
l3cd = priv->l3cfg ? nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE) : NULL;
|
||||
|
||||
if (l3cd) {
|
||||
for (i = 0; strv[i]; i++) {
|
||||
const char *s = strv[i];
|
||||
struct in_addr ipv4_addr;
|
||||
struct in6_addr ipv6_addr;
|
||||
|
||||
if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY
|
||||
&& inet_pton(AF_INET, (const char *) s, &ipv4_addr)) {
|
||||
ping_binary = nm_utils_find_helper("ping", "/usr/bin/ping", NULL);
|
||||
log_domain = LOGD_IP4;
|
||||
} else if (priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY
|
||||
&& inet_pton(AF_INET6, (const char *) s, &ipv6_addr)) {
|
||||
ping_binary = nm_utils_find_helper("ping6", "/usr/bin/ping6", NULL);
|
||||
log_domain = LOGD_IP6;
|
||||
} else
|
||||
continue;
|
||||
|
||||
if (s[0]) {
|
||||
PingOperation *ping_op = ping_operation_new(self,
|
||||
log_domain,
|
||||
s,
|
||||
ping_binary,
|
||||
ip_ping_timeout,
|
||||
ip_ping_addresses_require_all);
|
||||
|
||||
if (start_ping(self, ping_op))
|
||||
ping_started = TRUE;
|
||||
}
|
||||
ping_operation_start(self, strv[i], NULL, require_all);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ping_started) {
|
||||
if (c_list_is_empty(&priv->ping_ops_lst_head)) {
|
||||
/* No ping operation in progress, advance to pre-up */
|
||||
ip_check_pre_up(self);
|
||||
} else {
|
||||
priv->ping_timeout =
|
||||
nm_g_timeout_add_seconds_source(gw_ping_timeout ? gw_ping_timeout : ip_ping_timeout,
|
||||
nm_g_timeout_add_seconds_source(ip_ping_timeout > 0 ? ip_ping_timeout : gw_ping_timeout,
|
||||
ip_check_ping_timeout_cb,
|
||||
self);
|
||||
}
|
||||
/* If no ping was started, just advance to pre_up. */
|
||||
else
|
||||
ip_check_pre_up(self);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -17194,17 +17106,11 @@ _cancel_activation(NMDevice *self)
|
|||
|
||||
_dispatcher_cleanup(self);
|
||||
|
||||
if (priv->ping_operations) {
|
||||
g_list_free_full(priv->ping_operations, (GDestroyNotify) cleanup_ping_operation);
|
||||
priv->ping_operations = NULL;
|
||||
}
|
||||
|
||||
if (priv->ping_timeout)
|
||||
nm_clear_g_source_inst(&priv->ping_timeout);
|
||||
|
||||
_dev_ip_state_cleanup(self, AF_INET, FALSE);
|
||||
_dev_ip_state_cleanup(self, AF_INET6, FALSE);
|
||||
|
||||
ping_cleanup(self);
|
||||
|
||||
/* Break the activation chain */
|
||||
activation_source_clear(self);
|
||||
}
|
||||
|
|
@ -17960,13 +17866,8 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
|
|||
break;
|
||||
}
|
||||
case NM_DEVICE_STATE_SECONDARIES:
|
||||
if (priv->ping_operations) {
|
||||
g_list_free_full(priv->ping_operations, (GDestroyNotify) cleanup_ping_operation);
|
||||
priv->ping_operations = NULL;
|
||||
}
|
||||
if (priv->ping_timeout)
|
||||
nm_clear_g_source_inst(&priv->ping_timeout);
|
||||
_LOGD(LOGD_DEVICE, "device entered SECONDARIES state");
|
||||
ping_cleanup(self);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -19631,6 +19532,8 @@ nm_device_init(NMDevice *self)
|
|||
priv->available_connections = g_hash_table_new_full(nm_direct_hash, NULL, g_object_unref, NULL);
|
||||
priv->ip6_saved_properties = g_hash_table_new_full(nm_str_hash, g_str_equal, NULL, g_free);
|
||||
|
||||
c_list_init(&priv->ping_ops_lst_head);
|
||||
|
||||
priv->managed_type_ = NM_DEVICE_MANAGED_TYPE_EXTERNAL;
|
||||
/* If networking is already disabled at boot, we want to manage all devices
|
||||
* after re-enabling networking; hence, the initial state is MANAGED. */
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
#include <linux/if_infiniband.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include "libnm-glib-aux/nm-uuid.h"
|
||||
#include "libnm-platform/nmp-base.h"
|
||||
|
|
@ -5002,6 +5004,297 @@ NM_UTILS_LOOKUP_STR_DEFINE(nm_activation_type_to_string,
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
NMIPAddrTyped address;
|
||||
char *addr_str;
|
||||
GTask *task;
|
||||
GSource *timeout_source;
|
||||
GSource *retry_source;
|
||||
GSource *input_source;
|
||||
gulong cancellable_id;
|
||||
int ifindex;
|
||||
int socket;
|
||||
guint16 seq;
|
||||
} PingInfo;
|
||||
|
||||
#define _NMLOG2_PREFIX_NAME "ping"
|
||||
#define _NMLOG2_DOMAIN LOGD_CORE
|
||||
#define _NMLOG2(level, info, ...) \
|
||||
G_STMT_START \
|
||||
{ \
|
||||
if (nm_logging_enabled((level), (_NMLOG2_DOMAIN))) { \
|
||||
PingInfo *_info = (info); \
|
||||
\
|
||||
_nm_log((level), \
|
||||
(_NMLOG2_DOMAIN), \
|
||||
0, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
_NMLOG2_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT \
|
||||
",if=%d,%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
|
||||
NM_HASH_OBFUSCATE_PTR(_info), \
|
||||
_info->ifindex, \
|
||||
_info->addr_str _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
|
||||
} \
|
||||
} \
|
||||
G_STMT_END
|
||||
|
||||
static void
|
||||
ping_complete(PingInfo *info, GError *error)
|
||||
{
|
||||
nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task), &info->cancellable_id);
|
||||
|
||||
if (error && !nm_utils_error_is_cancelled(error)) {
|
||||
_LOG2T(info, "terminated with error: %s", error->message);
|
||||
}
|
||||
|
||||
if (error)
|
||||
g_task_return_error(info->task, error);
|
||||
else
|
||||
g_task_return_boolean(info->task, TRUE);
|
||||
|
||||
nm_clear_g_source_inst(&info->timeout_source);
|
||||
nm_clear_g_source_inst(&info->retry_source);
|
||||
nm_clear_g_source_inst(&info->input_source);
|
||||
nm_clear_g_free(&info->addr_str);
|
||||
nm_clear_fd(&info->socket);
|
||||
g_object_unref(info->task);
|
||||
|
||||
g_free(info);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ping_socket_data_cb(int fd, GIOCondition condition, gpointer user_data)
|
||||
{
|
||||
PingInfo *info = user_data;
|
||||
ssize_t len;
|
||||
union {
|
||||
struct icmphdr icmph;
|
||||
struct icmp6_hdr icmp6h;
|
||||
} pkt;
|
||||
|
||||
len = recv(fd, &pkt, sizeof(pkt), 0);
|
||||
|
||||
if (len < 0)
|
||||
return G_SOURCE_CONTINUE;
|
||||
|
||||
if (info->address.addr_family == AF_INET) {
|
||||
if (len >= sizeof(struct icmphdr) && pkt.icmph.type == ICMP_ECHOREPLY) {
|
||||
_LOG2T(info, "received echo-reply with seq %hu", ntohs(pkt.icmph.un.echo.sequence));
|
||||
ping_complete(info, NULL);
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
} else {
|
||||
if (len >= sizeof(struct icmp6_hdr) && pkt.icmp6h.icmp6_type == ICMP6_ECHO_REPLY) {
|
||||
_LOG2T(info, "received echo-reply with seq %hu", ntohs(pkt.icmp6h.icmp6_seq));
|
||||
ping_complete(info, NULL);
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
ping_send(PingInfo *info)
|
||||
{
|
||||
const bool IS_IPv4 = NM_IS_IPv4(info->address.addr_family);
|
||||
union {
|
||||
struct sockaddr_in6 sa6;
|
||||
struct sockaddr_in sa4;
|
||||
} sa;
|
||||
union {
|
||||
struct icmphdr icmph;
|
||||
struct icmp6_hdr icmp6h;
|
||||
} pkt;
|
||||
socklen_t sa_len;
|
||||
size_t pkt_len;
|
||||
nm_be32_t ifindex_be;
|
||||
int errsv;
|
||||
|
||||
info->seq++;
|
||||
|
||||
if (info->socket < 0) {
|
||||
info->socket = socket(info->address.addr_family,
|
||||
SOCK_DGRAM | SOCK_CLOEXEC,
|
||||
IS_IPv4 ? IPPROTO_ICMP : IPPROTO_ICMPV6);
|
||||
if (info->socket < 0) {
|
||||
errsv = errno;
|
||||
_LOG2T(info, "socket creation failed: %s", nm_strerror_native(errsv));
|
||||
/* Try again at the next iteration */
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
if (IS_IPv4) {
|
||||
sa.sa4.sin_family = AF_INET;
|
||||
sa.sa4.sin_addr.s_addr = info->address.addr.addr4;
|
||||
sa_len = sizeof(struct sockaddr_in);
|
||||
} else {
|
||||
sa.sa6.sin6_family = AF_INET6;
|
||||
sa.sa6.sin6_addr = info->address.addr.addr6;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&info->address.addr.addr6))
|
||||
sa.sa6.sin6_scope_id = info->ifindex;
|
||||
sa_len = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
|
||||
/* setsockopt(IP*_UNICAST_IF) must be called *before* connecting
|
||||
* the socket, otherwise it doesn't have any effect */
|
||||
ifindex_be = htonl(info->ifindex);
|
||||
if (setsockopt(info->socket,
|
||||
IS_IPv4 ? IPPROTO_IP : IPPROTO_IPV6,
|
||||
IS_IPv4 ? IP_UNICAST_IF : IPV6_UNICAST_IF,
|
||||
&ifindex_be,
|
||||
sizeof(ifindex_be))) {
|
||||
errsv = errno;
|
||||
_LOG2T(info,
|
||||
"failed to bind the socket to the interface: %s",
|
||||
nm_strerror_native(errsv));
|
||||
/* Try again at the next iteration */
|
||||
nm_clear_fd(&info->socket);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Connect the socket so that the kernel only delivers us packets
|
||||
* coming from the given remote address */
|
||||
if (connect(info->socket, (struct sockaddr *) &sa, sa_len) < 0) {
|
||||
errsv = errno;
|
||||
_LOG2T(info, "failed to connect the socket: %s", nm_strerror_native(errsv));
|
||||
/* try again at the next iteration */
|
||||
nm_clear_fd(&info->socket);
|
||||
return;
|
||||
}
|
||||
|
||||
info->input_source = nm_g_unix_fd_source_new(info->socket,
|
||||
G_IO_IN,
|
||||
G_PRIORITY_DEFAULT,
|
||||
ping_socket_data_cb,
|
||||
info,
|
||||
NULL);
|
||||
g_source_attach(info->input_source, g_task_get_context(info->task));
|
||||
}
|
||||
|
||||
if (IS_IPv4) {
|
||||
memset(&pkt.icmph, 0, sizeof(struct icmphdr));
|
||||
pkt.icmph.type = ICMP_ECHO;
|
||||
pkt.icmph.un.echo.sequence = htons(info->seq);
|
||||
pkt_len = sizeof(struct icmphdr);
|
||||
} else {
|
||||
memset(&pkt.icmp6h, 0, sizeof(struct icmp6_hdr));
|
||||
pkt.icmp6h.icmp6_type = ICMP6_ECHO_REQUEST;
|
||||
pkt.icmp6h.icmp6_seq = htons(info->seq);
|
||||
pkt_len = sizeof(struct icmp6_hdr);
|
||||
}
|
||||
/* The kernel will automatically set the ID ICMP field and filter
|
||||
* incoming packets by the same ID */
|
||||
|
||||
if (send(info->socket, &pkt, pkt_len, 0) < 0) {
|
||||
errsv = errno;
|
||||
_LOG2T(info, "error sending echo-request #%u: %s", info->seq, nm_strerror_native(errsv));
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG2T(info, "sent echo-request #%u", info->seq);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ping_timeout_cb(gpointer user_data)
|
||||
{
|
||||
PingInfo *info = user_data;
|
||||
|
||||
_LOG2T(info, "timeout");
|
||||
|
||||
nm_clear_g_source_inst(&info->timeout_source);
|
||||
ping_complete(info, g_error_new_literal(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "timeout"));
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ping_retry_cb(gpointer user_data)
|
||||
{
|
||||
PingInfo *info = user_data;
|
||||
|
||||
ping_send(info);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
ping_cancelled(GObject *object, gpointer user_data)
|
||||
{
|
||||
PingInfo *info = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
nm_clear_g_signal_handler(g_task_get_cancellable(info->task), &info->cancellable_id);
|
||||
nm_utils_error_set_cancelled(&error, FALSE, NULL);
|
||||
ping_complete(info, error);
|
||||
}
|
||||
|
||||
void
|
||||
nm_utils_ping_host(NMIPAddrTyped address,
|
||||
int ifindex,
|
||||
guint timeout_sec,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer cb_data)
|
||||
{
|
||||
PingInfo *info;
|
||||
char buf[NM_INET_ADDRSTRLEN];
|
||||
gulong signal_id;
|
||||
|
||||
nm_assert(ifindex > 0);
|
||||
nm_assert(G_IS_CANCELLABLE(cancellable));
|
||||
nm_assert(callback);
|
||||
nm_assert(cb_data);
|
||||
|
||||
info = g_new0(PingInfo, 1);
|
||||
info->address = address;
|
||||
info->ifindex = ifindex;
|
||||
info->task = nm_g_task_new(NULL, cancellable, nm_utils_ping_host, callback, cb_data);
|
||||
info->socket = -1;
|
||||
|
||||
nm_inet_ntop(address.addr_family, address.addr.addr_ptr, buf);
|
||||
info->addr_str = g_strdup(buf);
|
||||
|
||||
_LOG2T(info, "started");
|
||||
|
||||
if (timeout_sec > 0) {
|
||||
info->timeout_source = nm_g_timeout_source_new_seconds(timeout_sec,
|
||||
G_PRIORITY_DEFAULT,
|
||||
ping_timeout_cb,
|
||||
info,
|
||||
NULL);
|
||||
g_source_attach(info->timeout_source, g_task_get_context(info->task));
|
||||
}
|
||||
|
||||
info->retry_source =
|
||||
nm_g_timeout_source_new_seconds(1, G_PRIORITY_DEFAULT, ping_retry_cb, info, NULL);
|
||||
g_source_attach(info->retry_source, g_task_get_context(info->task));
|
||||
|
||||
signal_id = g_cancellable_connect(cancellable, G_CALLBACK(ping_cancelled), info, NULL);
|
||||
if (signal_id == 0) {
|
||||
/* the callback was invoked synchronously, which destroyed @info.
|
||||
* We must not touch it anymore. */
|
||||
return;
|
||||
}
|
||||
info->cancellable_id = signal_id;
|
||||
|
||||
ping_send(info);
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_utils_ping_host_finish(GAsyncResult *result, GError **error)
|
||||
{
|
||||
GTask *task = G_TASK(result);
|
||||
|
||||
nm_assert(nm_g_task_is_valid(result, NULL, nm_utils_ping_host));
|
||||
|
||||
return g_task_propagate_boolean(task, error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
GPid pid;
|
||||
GTask *task;
|
||||
|
|
@ -5023,6 +5316,9 @@ typedef struct {
|
|||
gsize out_buffer_offset;
|
||||
} HelperInfo;
|
||||
|
||||
#undef _NMLOG2_PREFIX_NAME
|
||||
#undef _NMLOG2_DOMAIN
|
||||
#undef _NMLOG2
|
||||
#define _NMLOG2_PREFIX_NAME "nm-daemon-helper"
|
||||
#define _NMLOG2_DOMAIN LOGD_CORE
|
||||
#define _NMLOG2(level, info, ...) \
|
||||
|
|
|
|||
|
|
@ -520,4 +520,15 @@ void nm_utils_read_private_files(const char *const *paths,
|
|||
gpointer cb_data);
|
||||
GHashTable *nm_utils_read_private_files_finish(GAsyncResult *result, GError **error);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void nm_utils_ping_host(NMIPAddrTyped address,
|
||||
int ifindex,
|
||||
guint timeout_sec,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer cb_data);
|
||||
|
||||
gboolean nm_utils_ping_host_finish(GAsyncResult *result, GError **error);
|
||||
|
||||
#endif /* __NM_CORE_UTILS_H__ */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue