2008-07-09 Dan Williams <dcbw@redhat.com>

* callouts/Makefile.am
	  callouts/nm-avahi-autoipd-action.c
	  callouts/nm-avahi-autoipd.conf
		- avahi-autoipd callout to send options back to NM

	* src/autoip.c
	  src/autoip.h
		- remove

	* src/nm-device.c
	  src/nm-device-private.h
	  src/nm-manager.c
		- Use avahi-autoipd for IPv4LL functionality rather than really crappy
			old custom stuff



git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@3816 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
This commit is contained in:
Dan Williams
2008-07-09 14:05:49 +00:00
parent e059e6176d
commit 64f1a1d423
10 changed files with 584 additions and 440 deletions

View File

@@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* Dan Williams <dcbw@redhat.com>
@@ -17,7 +17,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2005 Red Hat, Inc.
* (C) Copyright 2005 - 2008 Red Hat, Inc.
*/
#include <glib.h>
@@ -29,6 +29,10 @@
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include "nm-device-interface.h"
#include "nm-device.h"
@@ -40,7 +44,6 @@
#include "nm-dbus-manager.h"
#include "nm-named-manager.h"
#include "nm-utils.h"
#include "autoip.h"
#include "nm-netlink.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-connection.h"
@@ -88,8 +91,15 @@ struct _NMDevicePrivate
gulong dhcp_state_sigid;
gulong dhcp_timeout_sigid;
/* dnsmasq stuff for shared connections */
NMDnsMasqManager * dnsmasq_manager;
gulong dnsmasq_state_id;
/* avahi-autoipd stuff */
GPid aipd_pid;
guint aipd_watch;
guint aipd_timeout;
guint32 aipd_addr;
};
static gboolean check_connection_compatible (NMDeviceInterface *device,
@@ -521,6 +531,271 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self)
nm_device_get_iface (self));
}
static void
aipd_timeout_remove (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->aipd_timeout) {
g_source_remove (priv->aipd_timeout);
priv->aipd_timeout = 0;
}
}
static void
aipd_cleanup (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->aipd_pid > 0) {
kill (priv->aipd_pid, SIGKILL);
priv->aipd_pid = -1;
}
if (priv->aipd_watch) {
g_source_remove (priv->aipd_watch);
priv->aipd_watch = 0;
}
aipd_timeout_remove (self);
priv->aipd_addr = 0;
}
static NMIP4Config *
aipd_get_ip4_config (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMIP4Config *config = NULL;
NMSettingIP4Address *addr;
g_return_val_if_fail (priv->aipd_addr > 0, NULL);
config = nm_ip4_config_new ();
addr = g_malloc0 (sizeof (NMSettingIP4Address));
addr->address = (guint32) priv->aipd_addr;
addr->prefix = 16;
nm_ip4_config_take_address (config, addr);
return config;
}
static gboolean
handle_autoip_change (NMDevice *self)
{
NMActRequest *req;
NMConnection *connection;
NMIP4Config *config;
config = aipd_get_ip4_config (self);
if (!config) {
nm_warning ("failed to get autoip config for rebind");
return FALSE;
}
req = nm_device_get_act_request (self);
g_assert (req);
connection = nm_act_request_get_connection (req);
g_assert (connection);
g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config);
if (!nm_device_set_ip4_config (self, config)) {
nm_warning ("(%s): failed to update IP4 config in response to autoip event.",
nm_device_get_iface (self));
return FALSE;
}
return TRUE;
}
#define IPV4LL_NETWORK (htonl (0xA9FE0000L))
#define IPV4LL_NETMASK (htonl (0xFFFF0000L))
void
nm_device_handle_autoip4_event (NMDevice *self,
const char *event,
const char *address)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMActRequest *req;
NMConnection *connection = NULL;
NMSettingIP4Config *s_ip4 = NULL;
NMDeviceState state;
const char *iface;
g_return_if_fail (event != NULL);
req = nm_device_get_act_request (self);
if (!req)
return;
connection = nm_act_request_get_connection (req);
if (!connection)
return;
/* Ignore if the connection isn't an AutoIP connection */
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
if (!s_ip4 || !s_ip4->method || strcmp (s_ip4->method, NM_SETTING_IP4_CONFIG_METHOD_AUTOIP))
return;
iface = nm_device_get_iface (self);
state = nm_device_get_state (self);
if (strcmp (event, "BIND") == 0) {
struct in_addr ip;
if (inet_pton (AF_INET, address, &ip) <= 0) {
nm_warning ("(%s): invalid address %s received from avahi-autoipd.",
iface, address);
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
return;
}
if ((ip.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) {
nm_warning ("(%s): invalid address %s received from avahi-autoipd.",
iface, address);
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
return;
}
switch (state) {
case NM_DEVICE_STATE_IP_CONFIG:
if (priv->aipd_addr) {
nm_warning ("(%s): already have autoip address!", iface);
return;
}
priv->aipd_addr = ip.s_addr;
aipd_timeout_remove (self);
nm_device_activate_schedule_stage4_ip_config_get (self);
break;
case NM_DEVICE_STATE_ACTIVATED:
priv->aipd_addr = ip.s_addr;
if (!handle_autoip_change (self))
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
break;
default:
nm_warning ("(%s): unexpected avahi-autoip event %s for %s.",
iface, event, address);
break;
}
} else {
nm_warning ("%s: autoip address %s no longer valid because '%s'.",
iface, address, event);
/* The address is gone; terminate the connection or fail activation */
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
}
}
static void
aipd_watch_cb (GPid pid, gint status, gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMDeviceState state;
const char *iface;
if (!priv->aipd_watch)
return;
priv->aipd_watch = 0;
iface = nm_device_get_iface (self);
if (WIFEXITED (status))
nm_warning ("%s: avahi-autoipd exited with error code %d", iface, WEXITSTATUS (status));
else if (WIFSTOPPED (status))
nm_warning ("%s: avahi-autoipd stopped unexpectedly with signal %d", iface, WSTOPSIG (status));
else if (WIFSIGNALED (status))
nm_warning ("%s: avahi-autoipd died with signal %d", iface, WTERMSIG (status));
else
nm_warning ("%s: avahi-autoipd died from an unknown cause", iface);
aipd_cleanup (self);
state = nm_device_get_state (self);
if (nm_device_is_activating (self) || (state == NM_DEVICE_STATE_ACTIVATED))
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
}
static gboolean
aipd_timeout_cb (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (!priv->aipd_timeout)
return FALSE;
priv->aipd_timeout = 0;
nm_info ("%s: avahi-autoipd timed out.", nm_device_get_iface (self));
aipd_cleanup (self);
if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
nm_device_activate_schedule_stage4_ip_config_timeout (self);
return FALSE;
}
static void
aipd_child_setup (gpointer user_data G_GNUC_UNUSED)
{
/* We are in the child process at this point.
* Give child it's own program group for signal
* separation.
*/
pid_t pid = getpid ();
setpgid (pid, pid);
}
static gboolean
aipd_exec (NMDevice *self, GError **error)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
char *argv[5];
gboolean success = FALSE;
const char **aipd_binary = NULL;
static const char *aipd_paths[] = {
"/usr/sbin/avahi-autoipd",
"/usr/local/sbin/avahi-autoipd",
NULL
};
aipd_cleanup (self);
/* Find avahi-autoipd */
aipd_binary = aipd_paths;
while (*aipd_binary != NULL) {
if (g_file_test (*aipd_binary, G_FILE_TEST_EXISTS))
break;
aipd_binary++;
}
if (!*aipd_binary) {
g_set_error (error, 0, 0, "%s", "couldn't find avahi-autoipd");
return FALSE;
}
argv[0] = (char *) (*aipd_binary);
argv[1] = "--script";
argv[2] = LIBEXECDIR "/nm-avahi-autoipd.action";
argv[3] = (char *) nm_device_get_iface (self);
argv[4] = NULL;
success = g_spawn_async ("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
&aipd_child_setup, NULL, &(priv->aipd_pid), error);
if (!success)
return FALSE;
/* Monitor the child process so we know when it dies */
priv->aipd_watch = g_child_watch_add (priv->aipd_pid, aipd_watch_cb, self);
/* Start a timeout to bound the address attempt */
priv->aipd_timeout = g_timeout_add (20000, aipd_timeout_cb, self);
return TRUE;
}
static NMActStageReturn
real_act_stage3_ip_config_start (NMDevice *self)
@@ -528,6 +803,9 @@ real_act_stage3_ip_config_start (NMDevice *self)
NMSettingIP4Config *s_ip4;
NMActRequest *req;
NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
const char *iface;
iface = nm_device_get_iface (self);
req = nm_device_get_act_request (self);
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (nm_act_request_get_connection (req),
@@ -544,12 +822,7 @@ real_act_stage3_ip_config_start (NMDevice *self)
/* DHCP manager will cancel any transaction already in progress and we do not
want to cancel this activation if we get "down" state from that. */
g_signal_handler_block (priv->dhcp_manager, priv->dhcp_state_sigid);
success = nm_dhcp_manager_begin_transaction (priv->dhcp_manager,
nm_device_get_iface (self),
s_ip4,
45);
success = nm_dhcp_manager_begin_transaction (priv->dhcp_manager, iface, s_ip4, 45);
g_signal_handler_unblock (priv->dhcp_manager, priv->dhcp_state_sigid);
if (success) {
@@ -559,6 +832,21 @@ real_act_stage3_ip_config_start (NMDevice *self)
ret = NM_ACT_STAGE_RETURN_POSTPONE;
} else
ret = NM_ACT_STAGE_RETURN_FAILURE;
} else if (s_ip4 && !strcmp (s_ip4->method, NM_SETTING_IP4_CONFIG_METHOD_AUTOIP)) {
GError *error = NULL;
/* Start avahi-autoipd */
if (aipd_exec (self, &error)) {
nm_info ("Activation (%s) Stage 3 of 5 (IP Configure Start) started"
" avahi-autoipd...", iface);
ret = NM_ACT_STAGE_RETURN_POSTPONE;
} else {
nm_info ("Activation (%s) Stage 3 of 5 (IP Configure Start) failed"
" to start avahi-autoipd: %s", iface, error->message);
g_error_free (error);
aipd_cleanup (self);
ret = NM_ACT_STAGE_RETURN_FAILURE;
}
}
return ret;
@@ -625,35 +913,6 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
nm_device_get_iface (self));
}
/*
* nm_device_new_ip4_autoip_config
*
* Build up an IP config with a Link Local address
*
*/
static NMIP4Config *
nm_device_new_ip4_autoip_config (NMDevice *self)
{
struct in_addr ip;
NMIP4Config *config = NULL;
NMSettingIP4Address *addr;
g_return_val_if_fail (self != NULL, NULL);
// FIXME: make our autoip implementation not suck; use avahi-autoip
if (!get_autoip (self, &ip))
return NULL;
config = nm_ip4_config_new ();
addr = g_malloc0 (sizeof (NMSettingIP4Address));
addr->address = (guint32) ip.s_addr;
addr->prefix = 16;
nm_ip4_config_take_address (config, addr);
return config;
}
static GHashTable *shared_ips = NULL;
static void
@@ -735,7 +994,7 @@ real_act_stage4_get_ip4_config (NMDevice *self,
g_assert (s_ip4);
if (!strcmp (s_ip4->method, NM_SETTING_IP4_CONFIG_METHOD_AUTOIP)) {
*config = nm_device_new_ip4_autoip_config (self);
*config = aipd_get_ip4_config (self);
} else if (!strcmp (s_ip4->method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
*config = nm_ip4_config_new ();
if (*config)
@@ -879,7 +1138,7 @@ out:
/*
* nm_device_activate_schedule_stage4_ip_config_timeout
*
* Deal with a timed out DHCP transaction
* Deal with a timeout of the IP configuration
*
*/
void
@@ -1057,6 +1316,8 @@ nm_device_deactivate_quickly (NMDevice *self)
}
}
aipd_cleanup (self);
/* Tear down an existing activation request */
clear_act_request (self);
@@ -1343,7 +1604,7 @@ dhcp_timeout (NMDHCPManager *dhcp_manager,
return;
if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
nm_device_activate_schedule_stage4_ip_config_timeout (device);
nm_device_activate_schedule_stage4_ip_config_timeout (device);
}
gboolean
@@ -1494,7 +1755,7 @@ nm_device_is_up (NMDevice *self)
}
gboolean
nm_device_hw_bring_up (NMDevice *self, gboolean wait)
nm_device_hw_bring_up (NMDevice *self, gboolean do_wait)
{
gboolean success;
guint32 tries = 0;
@@ -1513,7 +1774,7 @@ nm_device_hw_bring_up (NMDevice *self, gboolean wait)
}
/* Wait for the device to come up if requested */
while (wait && !nm_device_hw_is_up (self) && (tries++ < 50))
while (do_wait && !nm_device_hw_is_up (self) && (tries++ < 50))
g_usleep (200);
if (!nm_device_hw_is_up (self)) {
@@ -1531,7 +1792,7 @@ out:
}
void
nm_device_hw_take_down (NMDevice *self, gboolean wait)
nm_device_hw_take_down (NMDevice *self, gboolean do_wait)
{
guint32 tries = 0;
@@ -1546,18 +1807,18 @@ nm_device_hw_take_down (NMDevice *self, gboolean wait)
NM_DEVICE_GET_CLASS (self)->hw_take_down (self);
/* Wait for the device to come up if requested */
while (wait && nm_device_hw_is_up (self) && (tries++ < 50))
while (do_wait && nm_device_hw_is_up (self) && (tries++ < 50))
g_usleep (200);
}
static gboolean
nm_device_bring_up (NMDevice *self, gboolean wait)
nm_device_bring_up (NMDevice *self, gboolean do_wait)
{
gboolean success;
g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
if (!nm_device_hw_bring_up (self, wait))
if (!nm_device_hw_bring_up (self, do_wait))
return FALSE;
if (nm_device_is_up (self))
@@ -1575,7 +1836,7 @@ nm_device_bring_up (NMDevice *self, gboolean wait)
}
void
nm_device_take_down (NMDevice *self, gboolean wait)
nm_device_take_down (NMDevice *self, gboolean do_wait)
{
g_return_if_fail (NM_IS_DEVICE (self));
@@ -1589,7 +1850,7 @@ nm_device_take_down (NMDevice *self, gboolean wait)
NM_DEVICE_GET_CLASS (self)->take_down (self);
}
nm_device_hw_take_down (self, wait);
nm_device_hw_take_down (self, do_wait);
}
static void