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,3 +1,20 @@
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
2008-07-07 Dan Williams <dcbw@redhat.com>
* system-settings/plugins/ifcfg-fedora/reader.c

View file

@ -1,11 +1,13 @@
dbusservicedir = $(DBUS_SYS_DIR)
dbusservice_DATA = \
nm-dhcp-client.conf \
nm-dispatcher.conf
nm-dispatcher.conf \
nm-avahi-autoipd.conf
libexec_PROGRAMS = \
nm-dhcp-client.action \
nm-dispatcher.action
nm-dispatcher.action \
nm-avahi-autoipd.action
nm_dhcp_client_action_SOURCES = \
nm-dhcp-client-action.c
@ -23,6 +25,22 @@ nm_dhcp_client_action_LDADD = \
$(GTHREAD_LIBS)
nm_avahi_autoipd_action_SOURCES = \
nm-avahi-autoipd-action.c
nm_avahi_autoipd_action_CPPFLAGS = \
$(DBUS_CFLAGS) \
$(GTHREAD_CFLAGS) \
-DDBUS_API_SUBJECT_TO_CHANGE \
-DG_DISABLE_DEPRECATED \
-DSYSCONFDIR=\"$(sysconfdir)\" \
-DLIBEXECDIR=\"$(libexecdir)\"
nm_avahi_autoipd_action_LDADD = \
$(DBUS_LIBS) \
$(GTHREAD_LIBS)
nm_dispatcher_action_SOURCES = \
nm-dispatcher-action.c \
nm-dispatcher-action.h

View file

@ -0,0 +1,151 @@
/* NetworkManager -- Network link manager
*
* Dan Williams <dcbw@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2008 Red Hat, Inc.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <dbus/dbus-glib.h>
#define NM_AVAHI_AUTOIPD_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
#define NM_AVAHI_AUTOIPD_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd"
static DBusConnection *
dbus_init (void)
{
DBusConnection * connection;
DBusError error;
int ret, flags;
dbus_connection_set_change_sigpipe (TRUE);
dbus_error_init (&error);
connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
if (dbus_error_is_set (&error)) {
fprintf (stderr, "Error: could not get the system bus. Make sure "
"the message bus daemon is running! Message: (%s) %s\n",
error.name,
error.message);
goto error;
}
dbus_connection_set_exit_on_disconnect (connection, FALSE);
#if (DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR < 60)
flags = DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT;
#else
flags = DBUS_NAME_FLAG_DO_NOT_QUEUE;
#endif
dbus_error_init (&error);
ret = dbus_bus_request_name (connection,
NM_AVAHI_AUTOIPD_DBUS_SERVICE,
flags,
&error);
if (dbus_error_is_set (&error)) {
fprintf (stderr, "Error: Could not acquire the NM DHCP client service. "
"Message: (%s) %s\n",
error.name,
error.message);
goto error;
}
if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
fprintf (stderr, "Error: Could not acquire the NM DHCP client service "
"as it is already taken. Return: %d\n",
ret);
goto error;
}
return connection;
error:
if (dbus_error_is_set (&error))
dbus_error_free (&error);
if (connection)
dbus_connection_unref (connection);
return NULL;
}
int
main (int argc, char *argv[])
{
DBusConnection *connection;
DBusMessage *message;
dbus_bool_t result;
char *event, *iface, *address;
g_type_init ();
if (argc != 4) {
fprintf (stderr, "Error: expected 3 arguments (event, interface, address).\n");
exit (1);
}
event = argv[1];
iface = argv[2];
address = argv[3] ? argv[3] : "";
if (!event || !iface || !strlen (event) || !strlen (iface)) {
fprintf (stderr, "Error: unexpected arguments received from avahi-autoipd.\n");
exit (1);
}
/* Get a connection to the system bus */
connection = dbus_init ();
if (connection == NULL)
exit (1);
message = dbus_message_new_signal ("/", NM_AVAHI_AUTOIPD_DBUS_IFACE, "Event");
if (message == NULL) {
fprintf (stderr, "Error: not enough memory to send autoip Event signal.\n");
exit (1);
}
if (!dbus_message_append_args (message,
DBUS_TYPE_STRING, &event,
DBUS_TYPE_STRING, &iface,
DBUS_TYPE_STRING, &address,
DBUS_TYPE_INVALID)) {
fprintf (stderr, "Error: failed to construct autoip Event signal.\n");
exit (1);
}
/* queue the message */
result = dbus_connection_send (connection, message, NULL);
if (!result) {
fprintf (stderr, "Error: could not send send autoip Event signal.\n");
exit (1);
}
dbus_message_unref (message);
/* Send out the message */
dbus_connection_flush (connection);
return 0;
}

View file

@ -0,0 +1,14 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="org.freedesktop.nm_avahi_autoipd"/>
<allow send_interface="org.freedesktop.nm_avahi_autoipd"/>
</policy>
<policy context="default">
<deny own="org.freedesktop.nm_avahi_autoipd"/>
<deny send_interface="org.freedesktop.nm_avahi_autoipd"/>
</policy>
</busconfig>

View file

@ -65,8 +65,6 @@ NetworkManager_SOURCES = \
nm-gsm-device.h \
nm-cdma-device.c \
nm-cdma-device.h \
autoip.c \
autoip.h \
wpa.c \
wpa.h \
nm-netlink.c \

View file

@ -1,379 +0,0 @@
// Based upon http://www.zeroconf.org/AVH-IPv4LL.c
// Merged into NetworkManager by Tom Parker <palfrey@tevp.net>
// Original copyright continues below
//
// ----------------------------------
// Simple IPv4 Link-Local addressing (see <http://www.zeroconf.org/>)
// @(#)llip.c, 1.5, Copyright 2003 by Arthur van Hoff (avh@strangeberry.com)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// See <http://www.gnu.org/copyleft/lesser.html>
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <netinet/ether.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <syslog.h>
#include <glib.h>
#include <unistd.h>
#include "NetworkManager.h"
#include "nm-device.h"
#include "nm-device-ethernet.h"
#include "nm-device-wifi.h"
#include "NetworkManagerUtils.h"
#include "nm-utils.h"
#include "autoip.h"
// Times here are in seconds
#define LINKLOCAL_ADDR 0xa9fe0000
#define LINKLOCAL_BCAST 0xa9feffff
#define PROBE_NUM 3
#define PROBE_MIN 1
#define PROBE_MAX 2
#define ANNOUNCE_NUM 3
#define ANNOUNCE_INTERVAL 2
#define ANNOUNCE_WAIT 2
#define FAILURE_TIMEOUT 14
typedef struct EtherHeader
{
u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */
u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */
u_int16_t ether_type; /* packet type ID field */
} __attribute__((packed)) EtherHeader;
typedef struct ARPMessage
{
EtherHeader ethhdr;
u_short htype; /* hardware type (must be ARPHRD_ETHER) */
u_short ptype; /* protocol type (must be ETHERTYPE_IP) */
u_char hlen; /* hardware address length (must be 6) */
u_char plen; /* protocol address length (must be 4) */
u_short operation; /* ARP opcode */
u_char sHaddr[ETH_ALEN]; /* sender's hardware address */
u_char sInaddr[4]; /* sender's IP address */
u_char tHaddr[ETH_ALEN]; /* target's hardware address */
u_char tInaddr[4]; /* target's IP address */
u_char pad[18]; /* pad for min. Ethernet payload (60 bytes) */
} __attribute__((packed)) ARPMessage;
// Times here are in seconds
#define ARP_DEFAULT_LEASETIME 100
static struct in_addr null_ip = {0};
static struct ether_addr null_addr = {{0, 0, 0, 0, 0, 0}};
static struct ether_addr broadcast_addr = {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
/**
* Pick a random link local IP address.
*/
static void pick (struct in_addr *ip)
{
ip->s_addr = htonl (LINKLOCAL_ADDR | ((abs(random()) % 0xFD00) + (abs(random()) % 0x0100)));
/* Make sure we don't use 0xFF or 0x00 anywhere */
while (((ip->s_addr & 0x0000FF00) == 0xFF00) || ((ip->s_addr & 0x0000FF00) == 0x0000))
ip->s_addr = (ip->s_addr & 0xFFFF00FF) + (abs(random()) && 0xFFFF);
while (((ip->s_addr & 0x000000FF) == 0xFF) || ((ip->s_addr & 0x000000FF) == 0x00))
ip->s_addr = (ip->s_addr & 0xFFFFFF00) + (abs(random()) && 0xFF);
}
/**
* Send out an ARP packet.
*/
static gboolean arp(int fd, struct sockaddr *saddr, int op,
struct ether_addr *source_addr, struct in_addr source_ip,
struct ether_addr *target_addr, struct in_addr target_ip)
{
struct ARPMessage p;
gboolean success = FALSE;
memset (&p, 0, sizeof (p));
/* ether header */
p.ethhdr.ether_type = htons (ETHERTYPE_ARP);
memcpy (p.ethhdr.ether_shost, source_addr, ETH_ALEN);
memcpy (p.ethhdr.ether_dhost, &broadcast_addr, ETH_ALEN);
/* arp request */
p.htype = htons (ARPHRD_ETHER);
p.ptype = htons (ETHERTYPE_IP);
p.hlen = ETH_ALEN;
p.plen = 4;
p.operation = htons (op);
memcpy (&p.sHaddr, source_addr, ETH_ALEN);
memcpy (&p.sInaddr, &source_ip, sizeof (p.sInaddr));
memcpy (&p.tHaddr, target_addr, ETH_ALEN);
memcpy (&p.tInaddr, &target_ip, sizeof (p.tInaddr));
/* send it */
if (sendto (fd, &p, sizeof (p), 0, saddr, sizeof (*saddr)) < 0)
nm_warning ("autoip ARP sendto() failed.");
else
success = TRUE;
return success;
}
/*****************************************************************************/
/* Subtract the `struct timeval' values X and Y,
storing the result in RESULT.
Return 1 if the difference is negative, otherwise 0. */
static int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
{
/* Perform the carry for the later subtraction by updating Y. */
if (x->tv_usec < y->tv_usec)
{
int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
y->tv_usec -= 1000000 * nsec;
y->tv_sec += nsec;
}
if (x->tv_usec - y->tv_usec > 1000000)
{
int nsec = (x->tv_usec - y->tv_usec) / 1000000;
y->tv_usec += 1000000 * nsec;
y->tv_sec -= nsec;
}
/* Compute the time remaining to wait.
`tv_usec' is certainly positive. */
result->tv_sec = x->tv_sec - y->tv_sec;
result->tv_usec = x->tv_usec - y->tv_usec;
/* Return 1 if result is negative. */
return x->tv_sec < y->tv_sec;
}
enum return_vals
{
RET_ERROR = 0,
RET_TIMEOUT,
RET_CEASED,
RET_SUCCESS
};
/*****************************************************************************/
/* "timeout" should be the future point in time when we wish to stop
* checking for data on the socket.
*/
static int peekfd (NMDevice *dev, int sk, struct timeval *timeout)
{
struct timeval diff;
struct timeval now;
/* Wake up each second to check whether or not we've been told
* to stop with iface->cease and check our timeout.
*/
gettimeofday (&now, NULL);
// nm_info ("autoip waiting for data, overall timeout = {%ds, %dus}\n", (int)timeout->tv_sec, (int)timeout->tv_usec);
while (timeval_subtract (&diff, timeout, &now) == 0)
{
fd_set fs;
struct timeval wait = {1, 0};
// nm_info ("autoip waiting for data, remaining timeout = {%ds, %dus}\n", (int)diff.tv_sec, (int)diff.tv_usec);
FD_ZERO (&fs);
FD_SET (sk, &fs);
if (select (sk+1, &fs, NULL, NULL, &wait) == -1)
return RET_ERROR;
if (FD_ISSET(sk, &fs))
return RET_SUCCESS;
gettimeofday (&now, NULL);
};
return RET_TIMEOUT;
}
gboolean get_autoip (NMDevice *dev, struct in_addr *out_ip)
{
struct sockaddr saddr;
ARPMessage p;
struct ether_addr addr;
struct in_addr ip = {0};
int fd = -1;
int nprobes = 0;
int nannounce = 0;
gboolean success = FALSE;
const char *iface;
char buf[INET_ADDRSTRLEN+1];
memset(&buf, '\0', sizeof (buf));
g_return_val_if_fail (dev != NULL, FALSE);
g_return_val_if_fail (out_ip != NULL, FALSE);
out_ip->s_addr = 0;
iface = nm_device_get_iface (dev);
/* initialize saddr */
memset (&saddr, 0, sizeof (saddr));
strncpy (saddr.sa_data, iface, sizeof (saddr.sa_data));
if (NM_IS_DEVICE_ETHERNET (dev))
nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (dev), &addr);
else if (NM_IS_DEVICE_WIFI (dev))
nm_device_wifi_get_address (NM_DEVICE_WIFI (dev), &addr);
else
goto out;
/* open an ARP socket */
fd = socket (AF_PACKET, SOCK_PACKET, htons (ETH_P_ALL));
if (fd < 0) {
nm_warning ("%s: Couldn't open network control socket.", iface);
goto out;
}
/* bind to the ARP socket */
if (bind (fd, &saddr, sizeof (saddr)) < 0) {
nm_warning ("%s: Couldn't bind to the device.", iface);
goto out;
}
/* initialize pseudo random selection of IP addresses */
srandom ( (addr.ether_addr_octet[ETHER_ADDR_LEN-4] << 24) |
(addr.ether_addr_octet[ETHER_ADDR_LEN-3] << 16) |
(addr.ether_addr_octet[ETHER_ADDR_LEN-2] << 8) |
(addr.ether_addr_octet[ETHER_ADDR_LEN-1] << 0));
/* pick an ip address */
if (ip.s_addr == 0)
pick (&ip);
while (1)
{
struct timeval timeout;
int err;
if (nprobes < PROBE_NUM)
{
if (!inet_ntop (AF_INET, &ip, buf, INET_ADDRSTRLEN)) {
nm_warning ("%s: error converting IP4 address 0x%X",
__func__, ntohl (ip.s_addr));
continue;
}
nm_info ("autoip: Sending probe #%d for IP address %s.", nprobes, buf);
arp (fd, &saddr, ARPOP_REQUEST, &addr, null_ip, &null_addr, ip);
nprobes++;
gettimeofday (&timeout, NULL);
if (nprobes == PROBE_NUM)
{
/* Link local specifies a different interval between
* the end of probe requests and announce packets.
*/
timeout.tv_sec += ANNOUNCE_WAIT;
}
else
{
unsigned int usecs_to_sleep = ((PROBE_MAX - PROBE_MIN) * 1000000) - 1;
/* We want to sleep between PROBE_MIN and PROBE_MAX seconds, exclusive */
timeout.tv_sec += PROBE_MIN;
timeout.tv_usec += 1 + (random () % usecs_to_sleep);
}
}
else if (nannounce < ANNOUNCE_NUM)
{
if (!inet_ntop (AF_INET, &ip, buf, INET_ADDRSTRLEN)) {
nm_warning ("%s: error converting IP4 address 0x%X",
__func__, ntohl (ip.s_addr));
continue;
}
nm_info ("autoip: Sending announce #%d for IP address %s.", nannounce, buf);
arp (fd, &saddr, ARPOP_REQUEST, &addr, ip, &addr, ip);
nannounce++;
gettimeofday (&timeout, NULL);
timeout.tv_sec += ANNOUNCE_INTERVAL;
timeout.tv_usec += (random () % 200000);
}
else
{
/* Use our address! */
memcpy (out_ip, &ip, sizeof (ip));
success = TRUE;
goto out;
}
nm_info ("autoip: Waiting for reply...");
err = peekfd (dev, fd, &timeout);
if ((err == RET_ERROR) || (err == RET_CEASED))
goto out;
/* There's some data waiting for us */
if (err == RET_SUCCESS) {
nm_info ("autoip: Got some data to check for reply packet.");
/* read ARP packet */
if (recv (fd, &p, sizeof (p), 0) < 0) {
nm_warning ("autoip: packet receive failure, ignoring it.");
continue;
}
#ifdef ARP_DEBUG
nm_warning ("autoip: (%s) recv arp type=%d, op=%d, ", iface, ntohs(p.ethhdr.ether_type), ntohs(p.operation));
{
struct in_addr a;
memcpy (&(a.s_addr), &(p.sInaddr), sizeof (a.s_addr));
if (!inet_ntop (AF_INET, &a, buf, INET_ADDRSTRLEN)) {
nm_warning ("%s: error converting IP4 address 0x%X",
__func__, ntohl (a.s_addr));
continue;
}
nm_warning (" source = %s %02X:%02X:%02X:%02X:%02X:%02X, ", buf,
p.sHaddr[0], p.sHaddr[1], p.sHaddr[2], p.sHaddr[3], p.sHaddr[4], p.sHaddr[5]);
memcpy (&(a.s_addr), &(p.tInaddr), sizeof (a.s_addr));
if (!inet_ntop (AF_INET, &a, buf, INET_ADDRSTRLEN)) {
nm_warning ("%s: error converting IP4 address 0x%X",
__func__, ntohl (a.s_addr));
continue;
}
nm_warning (" target = %s %02X:%02X:%02X:%02X:%02X:%02X\n", buf,
p.tHaddr[0], p.tHaddr[1], p.tHaddr[2], p.tHaddr[3], p.tHaddr[4], p.tHaddr[5]);
}
#endif
if ( (ntohs (p.ethhdr.ether_type) == ETHERTYPE_ARP)
&& (ntohs (p.operation) == ARPOP_REPLY)
&& ((uint32_t)(*p.tInaddr) == ip.s_addr)
&& (memcmp (&addr, &p.tHaddr, ETH_ALEN) != 0))
{
#ifdef ARP_DEBUG
if (!inet_ntop (AF_INET, &ip, buf, INET_ADDRSTRLEN)) {
nm_warning ("%s: error converting IP4 address 0x%X",
__func__, ntohl (ip.s_addr));
continue;
}
nm_warning ("autoip: (%s) ARP conflict for IP address %s.\n", iface, buf);
#endif
/* Ok, start all over again */
pick (&ip);
nprobes = 0;
nannounce = 0;
}
}
}
out:
if (fd >= 0)
close (fd);
return success;
}

View file

@ -1,8 +0,0 @@
#ifndef _SRC_AUTOIP_H
#define _SRC_AUTOIP_H
#include "nm-device.h"
extern gboolean get_autoip (NMDevice *dev, struct in_addr *out_ip);
#endif /* _SRC_AUTOIP_H */

View file

@ -36,4 +36,8 @@ gboolean nm_device_hw_bring_up (NMDevice *self, gboolean wait);
void nm_device_hw_take_down (NMDevice *self, gboolean wait);
void nm_device_handle_autoip4_event (NMDevice *self,
const char *event,
const char *address);
#endif /* NM_DEVICE_PRIVATE_H */

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

View file

@ -19,6 +19,9 @@
#include "nm-dbus-glib-types.h"
#include "nm-hal-manager.h"
#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
#define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd"
static gboolean impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err);
static void impl_manager_activate_connection (NMManager *manager,
const char *service_name,
@ -108,6 +111,8 @@ typedef struct {
NMVPNManager *vpn_manager;
guint vpn_manager_id;
DBusGProxy *aipd_proxy;
gboolean disposed;
} NMManagerPrivate;
@ -210,10 +215,50 @@ vpn_manager_connection_deactivated_cb (NMVPNManager *manager,
g_object_notify (G_OBJECT (user_data), NM_MANAGER_ACTIVE_CONNECTIONS);
}
static void
aipd_handle_event (DBusGProxy *proxy,
const char *event,
const char *iface,
const char *address,
gpointer user_data)
{
NMManager *manager = NM_MANAGER (user_data);
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
GSList *iter;
gboolean handled;
if (!event || !iface) {
nm_warning ("Incomplete message received from avahi-autoipd");
return;
}
if ( (strcmp (event, "BIND") != 0)
&& (strcmp (event, "CONFLICT") != 0)
&& (strcmp (event, "UNBIND") != 0)
&& (strcmp (event, "STOP") != 0)) {
nm_warning ("Unknown event '%s' received from avahi-autoipd", event);
return;
}
for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
NMDevice *candidate = NM_DEVICE (iter->data);
if (!strcmp (nm_device_get_iface (candidate), iface)) {
nm_device_handle_autoip4_event (candidate, event, address);
handled = TRUE;
break;
}
}
if (!handled)
nm_warning ("Unhandled avahi-autoipd event for '%s'", iface);
}
static void
nm_manager_init (NMManager *manager)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
DBusGConnection *g_connection;
guint id;
priv->wireless_enabled = TRUE;
@ -237,6 +282,29 @@ nm_manager_init (NMManager *manager)
id = g_signal_connect (G_OBJECT (priv->vpn_manager), "connection-deactivated",
G_CALLBACK (vpn_manager_connection_deactivated_cb), manager);
priv->vpn_manager_id = id;
g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
priv->aipd_proxy = dbus_g_proxy_new_for_name (g_connection,
NM_AUTOIP_DBUS_SERVICE,
"/",
NM_AUTOIP_DBUS_IFACE);
if (priv->aipd_proxy) {
dbus_g_object_register_marshaller (nm_marshal_VOID__STRING_STRING_STRING,
G_TYPE_NONE,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_INVALID);
dbus_g_proxy_add_signal (priv->aipd_proxy,
"Event",
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->aipd_proxy, "Event",
G_CALLBACK (aipd_handle_event),
manager,
NULL);
} else
nm_warning ("Could not initialize avahi-autoipd D-Bus proxy");
}
NMState