From 64f1a1d42397910bbc1a30e2a19b57480f7e7b44 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 9 Jul 2008 14:05:49 +0000 Subject: [PATCH] 2008-07-09 Dan Williams * 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 --- ChangeLog | 17 ++ callouts/Makefile.am | 22 +- callouts/nm-avahi-autoipd-action.c | 151 ++++++++++++ callouts/nm-avahi-autoipd.conf | 14 ++ src/Makefile.am | 2 - src/autoip.c | 379 ----------------------------- src/autoip.h | 8 - src/nm-device-private.h | 4 + src/nm-device.c | 359 +++++++++++++++++++++++---- src/nm-manager.c | 68 ++++++ 10 files changed, 584 insertions(+), 440 deletions(-) create mode 100644 callouts/nm-avahi-autoipd-action.c create mode 100644 callouts/nm-avahi-autoipd.conf delete mode 100644 src/autoip.c delete mode 100644 src/autoip.h diff --git a/ChangeLog b/ChangeLog index 9deb295b74..a354398291 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2008-07-09 Dan Williams + + * 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 * system-settings/plugins/ifcfg-fedora/reader.c diff --git a/callouts/Makefile.am b/callouts/Makefile.am index 70ce1fe6ea..2974645a91 100644 --- a/callouts/Makefile.am +++ b/callouts/Makefile.am @@ -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 diff --git a/callouts/nm-avahi-autoipd-action.c b/callouts/nm-avahi-autoipd-action.c new file mode 100644 index 0000000000..82508e1291 --- /dev/null +++ b/callouts/nm-avahi-autoipd-action.c @@ -0,0 +1,151 @@ +/* NetworkManager -- Network link manager + * + * Dan Williams + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#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; +} + diff --git a/callouts/nm-avahi-autoipd.conf b/callouts/nm-avahi-autoipd.conf new file mode 100644 index 0000000000..97d9ff5397 --- /dev/null +++ b/callouts/nm-avahi-autoipd.conf @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 00a0036be1..bdce5d6aef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/autoip.c b/src/autoip.c deleted file mode 100644 index 93ac1fa6f7..0000000000 --- a/src/autoip.c +++ /dev/null @@ -1,379 +0,0 @@ -// Based upon http://www.zeroconf.org/AVH-IPv4LL.c -// Merged into NetworkManager by Tom Parker -// Original copyright continues below -// -// ---------------------------------- -// Simple IPv4 Link-Local addressing (see ) -// @(#)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 -// -// 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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; -} diff --git a/src/autoip.h b/src/autoip.h deleted file mode 100644 index 53b43edc87..0000000000 --- a/src/autoip.h +++ /dev/null @@ -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 */ diff --git a/src/nm-device-private.h b/src/nm-device-private.h index c73e682981..41ba564348 100644 --- a/src/nm-device-private.h +++ b/src/nm-device-private.h @@ -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 */ diff --git a/src/nm-device.c b/src/nm-device.c index 3f50823e56..22a7501631 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -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 @@ -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 @@ -29,6 +29,10 @@ #include #include #include +#include +#include +#include +#include #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 diff --git a/src/nm-manager.c b/src/nm-manager.c index cef870c0a5..10149cbb5f 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -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