mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-04 20:00:32 +01:00
Add NMIP6Manager to handle IPv6 addrconf
Automatic IPv6 configuration is handled by the kernel, but to integrate it properly with NetworkManager, we need to watch what the kernel does to see whether or not it was successful (so that we can let the user know if there is no IPv6 router present, for example). NMIP6Manager takes care of this.
This commit is contained in:
parent
67a5f31fc8
commit
7344cc186d
10 changed files with 1544 additions and 42 deletions
|
|
@ -407,6 +407,7 @@ marshallers/Makefile
|
|||
src/named-manager/Makefile
|
||||
src/vpn-manager/Makefile
|
||||
src/dhcp-manager/Makefile
|
||||
src/ip6-manager/Makefile
|
||||
src/supplicant-manager/Makefile
|
||||
src/supplicant-manager/tests/Makefile
|
||||
src/ppp-manager/Makefile
|
||||
|
|
|
|||
|
|
@ -21,4 +21,5 @@ VOID:POINTER,STRING
|
|||
VOID:STRING,BOXED
|
||||
BOOLEAN:POINTER,STRING,BOOLEAN,UINT,STRING,STRING
|
||||
BOOLEAN:VOID
|
||||
VOID:STRING,BOOLEAN
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ SUBDIRS= \
|
|||
named-manager \
|
||||
vpn-manager \
|
||||
dhcp-manager \
|
||||
ip6-manager \
|
||||
supplicant-manager \
|
||||
ppp-manager \
|
||||
backends \
|
||||
|
|
@ -18,6 +19,7 @@ INCLUDES = -I${top_srcdir} \
|
|||
-I${top_srcdir}/src/named-manager \
|
||||
-I${top_srcdir}/src/vpn-manager \
|
||||
-I${top_srcdir}/src/dhcp-manager \
|
||||
-I${top_srcdir}/src/ip6-manager \
|
||||
-I${top_srcdir}/src/supplicant-manager \
|
||||
-I${top_srcdir}/src/dnsmasq-manager \
|
||||
-I${top_srcdir}/src/modem-manager \
|
||||
|
|
@ -182,6 +184,7 @@ NetworkManager_LDADD = \
|
|||
./named-manager/libnamed-manager.la \
|
||||
./vpn-manager/libvpn-manager.la \
|
||||
./dhcp-manager/libdhcp-manager.la \
|
||||
./ip6-manager/libip6-manager.la \
|
||||
./supplicant-manager/libsupplicant-manager.la \
|
||||
./dnsmasq-manager/libdnsmasq-manager.la \
|
||||
./ppp-manager/libppp-manager.la \
|
||||
|
|
|
|||
26
src/ip6-manager/Makefile.am
Normal file
26
src/ip6-manager/Makefile.am
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
INCLUDES = \
|
||||
-I${top_srcdir} \
|
||||
-I${top_srcdir}/include \
|
||||
-I${top_builddir}/marshallers \
|
||||
-I${top_srcdir}/libnm-util \
|
||||
-I${top_srcdir}/src \
|
||||
-I${top_srcdir}/src/named-manager
|
||||
|
||||
noinst_LTLIBRARIES = libip6-manager.la
|
||||
|
||||
libip6_manager_la_SOURCES = \
|
||||
nm-ip6-manager.c \
|
||||
nm-ip6-manager.h \
|
||||
nm-netlink-listener.c \
|
||||
nm-netlink-listener.h
|
||||
|
||||
libip6_manager_la_CPPFLAGS = \
|
||||
$(DBUS_CFLAGS) \
|
||||
$(GLIB_CFLAGS) \
|
||||
$(HAL_CFLAGS) \
|
||||
-DG_DISABLE_DEPRECATED
|
||||
|
||||
libip6_manager_la_LIBADD = \
|
||||
$(DBUS_LIBS) \
|
||||
$(GLIB_LIBS) \
|
||||
$(top_builddir)/marshallers/libmarshallers.la
|
||||
809
src/ip6-manager/nm-ip6-manager.c
Normal file
809
src/ip6-manager/nm-ip6-manager.c
Normal file
|
|
@ -0,0 +1,809 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* nm-ip6-manager.c - Handle IPv6 address configuration for NetworkManager
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include <netlink/route/rtnl.h>
|
||||
#include <netlink/route/route.h>
|
||||
|
||||
#include "nm-ip6-manager.h"
|
||||
#include "nm-netlink-listener.h"
|
||||
#include "NetworkManagerUtils.h"
|
||||
#include "nm-marshal.h"
|
||||
#include "nm-utils.h"
|
||||
|
||||
/* Pre-DHCP addrconf timeout, in seconds */
|
||||
#define NM_IP6_TIMEOUT 10
|
||||
|
||||
/* FIXME? Stolen from the kernel sources */
|
||||
#define IF_RA_OTHERCONF 0x80
|
||||
#define IF_RA_MANAGED 0x40
|
||||
#define IF_RA_RCVD 0x20
|
||||
#define IF_RS_SENT 0x10
|
||||
|
||||
typedef struct {
|
||||
NMNetlinkListener *netlink;
|
||||
GHashTable *devices_by_iface, *devices_by_index;
|
||||
|
||||
struct nl_handle *nlh;
|
||||
struct nl_cache *addr_cache, *route_cache;
|
||||
} NMIP6ManagerPrivate;
|
||||
|
||||
#define NM_IP6_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_MANAGER, NMIP6ManagerPrivate))
|
||||
|
||||
typedef enum {
|
||||
NM_IP6_DEVICE_UNCONFIGURED,
|
||||
NM_IP6_DEVICE_GOT_LINK_LOCAL,
|
||||
NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT,
|
||||
NM_IP6_DEVICE_GOT_ADDRESS,
|
||||
NM_IP6_DEVICE_WAITING_FOR_DHCP,
|
||||
NM_IP6_DEVICE_GOT_DHCP,
|
||||
NM_IP6_DEVICE_TIMED_OUT
|
||||
} NMIP6DeviceState;
|
||||
|
||||
typedef struct {
|
||||
struct in6_addr addr;
|
||||
time_t expires;
|
||||
} NMIP6RDNSS;
|
||||
|
||||
typedef struct {
|
||||
NMIP6Manager *manager;
|
||||
char *iface;
|
||||
int index;
|
||||
|
||||
guint finish_addrconf_id;
|
||||
guint config_changed_id;
|
||||
|
||||
NMIP6DeviceState state;
|
||||
NMIP6DeviceState target_state;
|
||||
gboolean want_signal;
|
||||
|
||||
GArray *rdnss_servers;
|
||||
guint rdnss_timeout_id;
|
||||
} NMIP6Device;
|
||||
|
||||
G_DEFINE_TYPE (NMIP6Manager, nm_ip6_manager, G_TYPE_OBJECT)
|
||||
|
||||
enum {
|
||||
ADDRCONF_COMPLETE,
|
||||
CONFIG_CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static NMIP6Manager *nm_ip6_manager_new (void);
|
||||
|
||||
static void netlink_notification (NMNetlinkListener *listener, struct nl_msg *msg, gpointer user_data);
|
||||
|
||||
static void nm_ip6_device_destroy (NMIP6Device *device);
|
||||
|
||||
NMIP6Manager *
|
||||
nm_ip6_manager_get (void)
|
||||
{
|
||||
static NMIP6Manager *singleton = NULL;
|
||||
|
||||
if (!singleton)
|
||||
singleton = nm_ip6_manager_new ();
|
||||
g_assert (singleton);
|
||||
|
||||
return g_object_ref (singleton);
|
||||
}
|
||||
|
||||
static void
|
||||
nm_ip6_manager_init (NMIP6Manager *manager)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
|
||||
priv->devices_by_iface = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
NULL,
|
||||
(GDestroyNotify) nm_ip6_device_destroy);
|
||||
priv->devices_by_index = g_hash_table_new (NULL, NULL);
|
||||
|
||||
priv->netlink = nm_netlink_listener_get ();
|
||||
g_signal_connect (priv->netlink, "notification",
|
||||
G_CALLBACK (netlink_notification), manager);
|
||||
nm_netlink_listener_subscribe (priv->netlink, RTNLGRP_IPV6_IFADDR, NULL);
|
||||
nm_netlink_listener_subscribe (priv->netlink, RTNLGRP_IPV6_PREFIX, NULL);
|
||||
nm_netlink_listener_subscribe (priv->netlink, RTNLGRP_ND_USEROPT, NULL);
|
||||
|
||||
priv->nlh = nm_netlink_get_default_handle ();
|
||||
priv->addr_cache = rtnl_addr_alloc_cache (priv->nlh);
|
||||
priv->route_cache = rtnl_route_alloc_cache (priv->nlh);
|
||||
}
|
||||
|
||||
static void
|
||||
finalize (GObject *object)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (object);
|
||||
|
||||
g_hash_table_destroy (priv->devices_by_iface);
|
||||
g_hash_table_destroy (priv->devices_by_index);
|
||||
g_object_unref (priv->netlink);
|
||||
nl_cache_free (priv->addr_cache);
|
||||
nl_cache_free (priv->route_cache);
|
||||
|
||||
G_OBJECT_CLASS (nm_ip6_manager_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
|
||||
|
||||
g_type_class_add_private (manager_class, sizeof (NMIP6ManagerPrivate));
|
||||
|
||||
/* virtual methods */
|
||||
object_class->finalize = finalize;
|
||||
|
||||
/* signals */
|
||||
signals[ADDRCONF_COMPLETE] =
|
||||
g_signal_new ("addrconf-complete",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete),
|
||||
NULL, NULL,
|
||||
_nm_marshal_VOID__STRING_BOOLEAN,
|
||||
G_TYPE_NONE, 2,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_BOOLEAN);
|
||||
|
||||
signals[CONFIG_CHANGED] =
|
||||
g_signal_new ("config-changed",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__STRING,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_STRING);
|
||||
}
|
||||
|
||||
static void
|
||||
nm_ip6_device_destroy (NMIP6Device *device)
|
||||
{
|
||||
if (device->finish_addrconf_id)
|
||||
g_source_remove (device->finish_addrconf_id);
|
||||
if (device->config_changed_id)
|
||||
g_source_remove (device->config_changed_id);
|
||||
g_free (device->iface);
|
||||
if (device->rdnss_servers)
|
||||
g_array_free (device->rdnss_servers, TRUE);
|
||||
if (device->rdnss_timeout_id)
|
||||
g_source_remove (device->rdnss_timeout_id);
|
||||
|
||||
g_slice_free (NMIP6Device, device);
|
||||
}
|
||||
|
||||
static NMIP6Manager *
|
||||
nm_ip6_manager_new (void)
|
||||
{
|
||||
NMIP6Manager *manager;
|
||||
NMIP6ManagerPrivate *priv;
|
||||
|
||||
manager = g_object_new (NM_TYPE_IP6_MANAGER, NULL);
|
||||
priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
|
||||
if (!priv->devices_by_iface || !priv->devices_by_index) {
|
||||
nm_warning ("Error: not enough memory to initialize IP6 manager tables");
|
||||
g_object_unref (manager);
|
||||
manager = NULL;
|
||||
}
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
static NMIP6Device *
|
||||
nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
|
||||
return g_hash_table_lookup (priv->devices_by_index,
|
||||
GINT_TO_POINTER (ifindex));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
finish_addrconf (gpointer user_data)
|
||||
{
|
||||
NMIP6Device *device = user_data;
|
||||
NMIP6Manager *manager = device->manager;
|
||||
char *iface_copy;
|
||||
|
||||
device->finish_addrconf_id = 0;
|
||||
device->want_signal = FALSE;
|
||||
|
||||
if (device->state >= device->target_state) {
|
||||
g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
|
||||
device->iface, TRUE);
|
||||
} else {
|
||||
nm_info ("Device '%s' IP6 addrconf timed out or failed.",
|
||||
device->iface);
|
||||
|
||||
iface_copy = g_strdup (device->iface);
|
||||
|
||||
nm_ip6_manager_cancel_addrconf (manager, device->iface);
|
||||
g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
|
||||
iface_copy, FALSE);
|
||||
|
||||
g_free (iface_copy);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
emit_config_changed (gpointer user_data)
|
||||
{
|
||||
NMIP6Device *device = user_data;
|
||||
NMIP6Manager *manager = device->manager;
|
||||
|
||||
device->config_changed_id = 0;
|
||||
g_signal_emit (manager, signals[CONFIG_CHANGED], 0, device->iface);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void set_rdnss_timeout (NMIP6Device *device);
|
||||
|
||||
static gboolean
|
||||
rdnss_expired (gpointer user_data)
|
||||
{
|
||||
NMIP6Device *device = user_data;
|
||||
|
||||
set_rdnss_timeout (device);
|
||||
emit_config_changed (device);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
set_rdnss_timeout (NMIP6Device *device)
|
||||
{
|
||||
time_t expires = 0, now = time (NULL);
|
||||
NMIP6RDNSS *rdnss;
|
||||
int i;
|
||||
|
||||
if (device->rdnss_timeout_id) {
|
||||
g_source_remove (device->rdnss_timeout_id);
|
||||
device->rdnss_timeout_id = 0;
|
||||
}
|
||||
|
||||
/* Find the soonest expiration time. */
|
||||
for (i = 0; i < device->rdnss_servers->len; i++) {
|
||||
rdnss = &g_array_index (device->rdnss_servers, NMIP6RDNSS, i);
|
||||
if (rdnss->expires == 0)
|
||||
continue;
|
||||
|
||||
/* If the entry has already expired, remove it; the "+ 1" is
|
||||
* because g_timeout_add_seconds() might fudge the timing a
|
||||
* bit.
|
||||
*/
|
||||
if (rdnss->expires <= now + 1) {
|
||||
g_array_remove_index_fast (device->rdnss_servers, i--);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!expires || rdnss->expires < expires)
|
||||
expires = rdnss->expires;
|
||||
}
|
||||
|
||||
if (expires) {
|
||||
device->rdnss_timeout_id = g_timeout_add_seconds (expires - now,
|
||||
rdnss_expired,
|
||||
device);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed)
|
||||
{
|
||||
NMIP6Manager *manager = device->manager;
|
||||
NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
struct rtnl_addr *rtnladdr;
|
||||
struct nl_addr *nladdr;
|
||||
struct in6_addr *addr;
|
||||
struct rtnl_link *link;
|
||||
guint flags;
|
||||
|
||||
for (rtnladdr = (struct rtnl_addr *)nl_cache_get_first (priv->addr_cache);
|
||||
rtnladdr;
|
||||
rtnladdr = (struct rtnl_addr *)nl_cache_get_next ((struct nl_object *)rtnladdr)) {
|
||||
if (rtnl_addr_get_ifindex (rtnladdr) != device->index)
|
||||
continue;
|
||||
|
||||
nladdr = rtnl_addr_get_local (rtnladdr);
|
||||
if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
|
||||
continue;
|
||||
|
||||
addr = nl_addr_get_binary_addr (nladdr);
|
||||
if (IN6_IS_ADDR_LINKLOCAL (addr)) {
|
||||
if (device->state == NM_IP6_DEVICE_UNCONFIGURED)
|
||||
device->state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
|
||||
} else {
|
||||
if (device->state < NM_IP6_DEVICE_GOT_ADDRESS)
|
||||
device->state = NM_IP6_DEVICE_GOT_ADDRESS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: we don't want to keep a cache of links, because the
|
||||
* kernel doesn't send notifications when the flags change, so the
|
||||
* cached rtnl_links would have out-of-date flags.
|
||||
*/
|
||||
link = nm_netlink_index_to_rtnl_link (device->index);
|
||||
flags = rtnl_link_get_flags (link);
|
||||
rtnl_link_put (link);
|
||||
|
||||
if ((flags & IF_RA_RCVD) && device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT)
|
||||
device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT;
|
||||
|
||||
// if (flags & (IF_RA_MANAGED | IF_RA_OTHERCONF))
|
||||
// device->need_dhcp = TRUE;
|
||||
|
||||
if (device->want_signal) {
|
||||
if (device->state >= device->target_state ||
|
||||
device->state == NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT) {
|
||||
/* device->finish_addrconf_id may currently be a timeout
|
||||
* rather than an idle, so we remove the existing source.
|
||||
*/
|
||||
if (device->finish_addrconf_id)
|
||||
g_source_remove (device->finish_addrconf_id);
|
||||
device->finish_addrconf_id = g_idle_add (finish_addrconf,
|
||||
device);
|
||||
}
|
||||
} else if (config_changed) {
|
||||
if (!device->config_changed_id) {
|
||||
device->config_changed_id = g_idle_add (emit_config_changed,
|
||||
device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ref_object (struct nl_object *obj, void *data)
|
||||
{
|
||||
struct nl_object **out = data;
|
||||
|
||||
nl_object_get (obj);
|
||||
*out = obj;
|
||||
}
|
||||
|
||||
static NMIP6Device *
|
||||
process_addr (NMIP6Manager *manager, struct nl_msg *msg)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
NMIP6Device *device;
|
||||
struct rtnl_addr *rtnladdr;
|
||||
int old_size;
|
||||
|
||||
rtnladdr = NULL;
|
||||
nl_msg_parse (msg, ref_object, &rtnladdr);
|
||||
if (!rtnladdr)
|
||||
return NULL;
|
||||
|
||||
device = nm_ip6_manager_get_device (manager, rtnl_addr_get_ifindex (rtnladdr));
|
||||
|
||||
old_size = nl_cache_nitems (priv->addr_cache);
|
||||
nl_cache_include (priv->addr_cache, (struct nl_object *)rtnladdr, NULL);
|
||||
rtnl_addr_put (rtnladdr);
|
||||
|
||||
/* The kernel will re-notify us of automatically-added addresses
|
||||
* every time it gets another router advertisement. We only want
|
||||
* to notify higher levels if we actually changed something.
|
||||
*/
|
||||
if (nl_cache_nitems (priv->addr_cache) == old_size)
|
||||
return NULL;
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
static NMIP6Device *
|
||||
process_route (NMIP6Manager *manager, struct nl_msg *msg)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
NMIP6Device *device;
|
||||
struct rtnl_route *rtnlroute;
|
||||
int old_size;
|
||||
|
||||
rtnlroute = NULL;
|
||||
nl_msg_parse (msg, ref_object, &rtnlroute);
|
||||
if (!rtnlroute)
|
||||
return NULL;
|
||||
|
||||
device = nm_ip6_manager_get_device (manager, rtnl_route_get_oif (rtnlroute));
|
||||
|
||||
old_size = nl_cache_nitems (priv->route_cache);
|
||||
nl_cache_include (priv->route_cache, (struct nl_object *)rtnlroute, NULL);
|
||||
rtnl_route_put (rtnlroute);
|
||||
|
||||
/* As above in process_addr */
|
||||
if (nl_cache_nitems (priv->route_cache) == old_size)
|
||||
return NULL;
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
static NMIP6Device *
|
||||
process_prefix (NMIP6Manager *manager, struct nl_msg *msg)
|
||||
{
|
||||
struct prefixmsg *pmsg;
|
||||
NMIP6Device *device;
|
||||
|
||||
/* We don't care about the prefix itself, but if we receive a
|
||||
* router advertisement telling us to use DHCP, we might not
|
||||
* get any RTM_NEWADDRs or RTM_NEWROUTEs, so this is our only
|
||||
* way to notice immediately that an RA was received.
|
||||
*/
|
||||
|
||||
pmsg = (struct prefixmsg *) NLMSG_DATA (nlmsg_hdr (msg));
|
||||
device = nm_ip6_manager_get_device (manager, pmsg->prefix_ifindex);
|
||||
|
||||
if (!device || !device->want_signal)
|
||||
return NULL;
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
/* RDNSS parsing code based on rdnssd, Copyright 2007 Pierre Ynard,
|
||||
* Rémi Denis-Courmont. GPLv2/3
|
||||
*/
|
||||
|
||||
#define ND_OPT_RDNSS 25
|
||||
struct nd_opt_rdnss {
|
||||
uint8_t nd_opt_rdnss_type;
|
||||
uint8_t nd_opt_rdnss_len;
|
||||
uint16_t nd_opt_rdnss_reserved1;
|
||||
uint32_t nd_opt_rdnss_lifetime;
|
||||
/* followed by one or more IPv6 addresses */
|
||||
};
|
||||
|
||||
static NMIP6Device *
|
||||
process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg)
|
||||
{
|
||||
NMIP6Device *device;
|
||||
struct nduseroptmsg *ndmsg;
|
||||
struct nd_opt_hdr *opt;
|
||||
guint opts_len, i;
|
||||
time_t now = time (NULL);
|
||||
struct nd_opt_rdnss *rdnss_opt;
|
||||
struct in6_addr *addr;
|
||||
GArray *servers;
|
||||
NMIP6RDNSS server, *sa, *sb;
|
||||
gboolean changed;
|
||||
|
||||
ndmsg = (struct nduseroptmsg *) NLMSG_DATA (nlmsg_hdr (msg));
|
||||
|
||||
if (ndmsg->nduseropt_family != AF_INET6 ||
|
||||
ndmsg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
|
||||
ndmsg->nduseropt_icmp_code != 0)
|
||||
return NULL;
|
||||
|
||||
device = nm_ip6_manager_get_device (manager, ndmsg->nduseropt_ifindex);
|
||||
if (!device)
|
||||
return NULL;
|
||||
|
||||
servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
|
||||
|
||||
opt = (struct nd_opt_hdr *) (ndmsg + 1);
|
||||
opts_len = ndmsg->nduseropt_opts_len;
|
||||
|
||||
while (opts_len >= sizeof (struct nd_opt_hdr)) {
|
||||
size_t nd_opt_len = opt->nd_opt_len;
|
||||
|
||||
if (nd_opt_len == 0 || opts_len < (nd_opt_len << 3))
|
||||
break;
|
||||
|
||||
if (opt->nd_opt_type != ND_OPT_RDNSS)
|
||||
goto next;
|
||||
|
||||
if (nd_opt_len < 3 || (nd_opt_len & 1) == 0)
|
||||
goto next;
|
||||
|
||||
rdnss_opt = (struct nd_opt_rdnss *) opt;
|
||||
|
||||
server.expires = now + ntohl (rdnss_opt->nd_opt_rdnss_lifetime);
|
||||
for (addr = (struct in6_addr *) (rdnss_opt + 1); nd_opt_len >= 2; addr++, nd_opt_len -= 2) {
|
||||
server.addr = *addr;
|
||||
g_array_append_val (servers, server);
|
||||
}
|
||||
|
||||
next:
|
||||
opts_len -= opt->nd_opt_len << 3;
|
||||
opt = (struct nd_opt_hdr *) ((uint8_t *) opt + (opt->nd_opt_len << 3));
|
||||
}
|
||||
|
||||
/* See if anything (other than expiration time) changed */
|
||||
if (servers->len != device->rdnss_servers->len)
|
||||
changed = TRUE;
|
||||
else {
|
||||
for (i = 0; i < servers->len; i++) {
|
||||
sa = &(g_array_index (servers, NMIP6RDNSS, i));
|
||||
sb = &(g_array_index (device->rdnss_servers, NMIP6RDNSS, i));
|
||||
if (memcmp (&sa->addr, &sb->addr, sizeof (struct in6_addr)) != 0) {
|
||||
changed = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
changed = FALSE;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
g_array_free (device->rdnss_servers, TRUE);
|
||||
device->rdnss_servers = servers;
|
||||
} else
|
||||
g_array_free (servers, TRUE);
|
||||
|
||||
/* Timeouts may have changed even if IPs didn't */
|
||||
set_rdnss_timeout (device);
|
||||
|
||||
if (changed)
|
||||
return device;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
netlink_notification (NMNetlinkListener *listener, struct nl_msg *msg, gpointer user_data)
|
||||
{
|
||||
NMIP6Manager *manager = (NMIP6Manager *) user_data;
|
||||
NMIP6Device *device;
|
||||
struct nlmsghdr *hdr;
|
||||
gboolean config_changed = FALSE;
|
||||
|
||||
hdr = nlmsg_hdr (msg);
|
||||
switch (hdr->nlmsg_type) {
|
||||
case RTM_NEWADDR:
|
||||
case RTM_DELADDR:
|
||||
device = process_addr (manager, msg);
|
||||
config_changed = TRUE;
|
||||
break;
|
||||
|
||||
case RTM_NEWROUTE:
|
||||
case RTM_DELROUTE:
|
||||
device = process_route (manager, msg);
|
||||
config_changed = TRUE;
|
||||
break;
|
||||
|
||||
case RTM_NEWPREFIX:
|
||||
device = process_prefix (manager, msg);
|
||||
break;
|
||||
|
||||
case RTM_NEWNDUSEROPT:
|
||||
device = process_nduseropt (manager, msg);
|
||||
config_changed = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (device)
|
||||
nm_ip6_device_sync_from_netlink (device, config_changed);
|
||||
}
|
||||
|
||||
static NMIP6Device *
|
||||
nm_ip6_device_new (NMIP6Manager *manager, const char *iface)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
NMIP6Device *device;
|
||||
|
||||
device = g_slice_new0 (NMIP6Device);
|
||||
if (!device) {
|
||||
nm_warning ("%s: Out of memory creating IP6 addrconf object.", iface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device->iface = g_strdup (iface);
|
||||
if (!device->iface) {
|
||||
nm_warning ("%s: Out of memory creating IP6 addrconf object "
|
||||
"property 'iface'.",
|
||||
iface);
|
||||
goto error;
|
||||
}
|
||||
device->index = nm_netlink_iface_to_index (iface);
|
||||
|
||||
device->manager = manager;
|
||||
|
||||
device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
|
||||
|
||||
g_hash_table_replace (priv->devices_by_iface, device->iface, device);
|
||||
g_hash_table_replace (priv->devices_by_index, GINT_TO_POINTER (device->index), device);
|
||||
|
||||
return device;
|
||||
|
||||
error:
|
||||
nm_ip6_device_destroy (device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
nm_ip6_manager_prepare_interface (NMIP6Manager *manager,
|
||||
const char *iface,
|
||||
NMSettingIP6Config *s_ip6)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv;
|
||||
NMIP6Device *device;
|
||||
const char *method = NULL;
|
||||
char *sysctl_path;
|
||||
|
||||
g_return_if_fail (NM_IS_IP6_MANAGER (manager));
|
||||
g_return_if_fail (iface != NULL);
|
||||
|
||||
priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
|
||||
device = nm_ip6_device_new (manager, iface);
|
||||
|
||||
if (s_ip6)
|
||||
method = nm_setting_ip6_config_get_method (s_ip6);
|
||||
if (!method)
|
||||
method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
|
||||
|
||||
if ( !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)
|
||||
|| !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
|
||||
device->target_state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
|
||||
else
|
||||
device->target_state = NM_IP6_DEVICE_GOT_ADDRESS;
|
||||
|
||||
g_return_if_fail (strchr (iface, '/') == NULL &&
|
||||
strcmp (iface, "all") != 0 &&
|
||||
strcmp (iface, "default") != 0);
|
||||
|
||||
sysctl_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra", iface);
|
||||
nm_utils_do_sysctl (sysctl_path,
|
||||
device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS ? "1\n" : "0\n");
|
||||
g_free (sysctl_path);
|
||||
}
|
||||
|
||||
void
|
||||
nm_ip6_manager_begin_addrconf (NMIP6Manager *manager,
|
||||
const char *iface)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv;
|
||||
NMIP6Device *device;
|
||||
|
||||
g_return_if_fail (NM_IS_IP6_MANAGER (manager));
|
||||
g_return_if_fail (iface != NULL);
|
||||
|
||||
priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
|
||||
device = (NMIP6Device *) g_hash_table_lookup (priv->devices_by_iface, iface);
|
||||
g_return_if_fail (device != NULL);
|
||||
|
||||
nm_info ("Activation (%s) Beginning IP6 addrconf.", iface);
|
||||
|
||||
/* Set up a timeout on the transaction to kill it after the timeout */
|
||||
device->finish_addrconf_id = g_timeout_add_seconds (NM_IP6_TIMEOUT,
|
||||
finish_addrconf,
|
||||
device);
|
||||
|
||||
/* Sync flags, etc, from netlink; this will also notice if the
|
||||
* device is already fully configured and schedule the
|
||||
* ADDRCONF_COMPLETE signal in that case.
|
||||
*/
|
||||
nm_ip6_device_sync_from_netlink (device, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager,
|
||||
const char *iface)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv;
|
||||
NMIP6Device *device;
|
||||
|
||||
g_return_if_fail (NM_IS_IP6_MANAGER (manager));
|
||||
g_return_if_fail (iface != NULL);
|
||||
|
||||
priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
|
||||
device = g_hash_table_lookup (priv->devices_by_iface, iface);
|
||||
if (device) {
|
||||
g_hash_table_remove (priv->devices_by_index, GINT_TO_POINTER (device->index));
|
||||
g_hash_table_remove (priv->devices_by_iface, iface);
|
||||
}
|
||||
}
|
||||
|
||||
NMIP6Config *
|
||||
nm_ip6_manager_get_ip6_config (NMIP6Manager *manager,
|
||||
const char *iface)
|
||||
{
|
||||
NMIP6ManagerPrivate *priv;
|
||||
NMIP6Device *device;
|
||||
NMIP6Config *config;
|
||||
struct rtnl_addr *rtnladdr;
|
||||
struct nl_addr *nladdr;
|
||||
struct in6_addr *addr;
|
||||
NMIP6Address *ip6addr;
|
||||
struct rtnl_route *rtnlroute;
|
||||
struct nl_addr *nldest, *nlgateway;
|
||||
struct in6_addr *dest, *gateway;
|
||||
uint32_t metric;
|
||||
NMIP6Route *ip6route;
|
||||
int i;
|
||||
|
||||
g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);
|
||||
g_return_val_if_fail (iface != NULL, NULL);
|
||||
|
||||
priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
|
||||
|
||||
device = (NMIP6Device *) g_hash_table_lookup (priv->devices_by_iface, iface);
|
||||
if (!device) {
|
||||
nm_warning ("Device '%s' addrconf not started.", iface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config = nm_ip6_config_new ();
|
||||
if (!config) {
|
||||
nm_warning ("%s: Out of memory creating IP6 config object.",
|
||||
iface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add addresses */
|
||||
for (rtnladdr = (struct rtnl_addr *)nl_cache_get_first (priv->addr_cache);
|
||||
rtnladdr;
|
||||
rtnladdr = (struct rtnl_addr *)nl_cache_get_next ((struct nl_object *)rtnladdr)) {
|
||||
if (rtnl_addr_get_ifindex (rtnladdr) != device->index)
|
||||
continue;
|
||||
|
||||
nladdr = rtnl_addr_get_local (rtnladdr);
|
||||
if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
|
||||
continue;
|
||||
|
||||
addr = nl_addr_get_binary_addr (nladdr);
|
||||
ip6addr = nm_ip6_address_new ();
|
||||
nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr));
|
||||
nm_ip6_address_set_address (ip6addr, addr);
|
||||
nm_ip6_config_take_address (config, ip6addr);
|
||||
}
|
||||
|
||||
/* Add routes */
|
||||
for (rtnlroute = (struct rtnl_route *)nl_cache_get_first (priv->route_cache);
|
||||
rtnlroute;
|
||||
rtnlroute = (struct rtnl_route *)nl_cache_get_next ((struct nl_object *)rtnlroute)) {
|
||||
if (rtnl_route_get_oif (rtnlroute) != device->index)
|
||||
continue;
|
||||
|
||||
nldest = rtnl_route_get_dst (rtnlroute);
|
||||
if (!nldest || nl_addr_get_family (nldest) != AF_INET6)
|
||||
continue;
|
||||
dest = nl_addr_get_binary_addr (nldest);
|
||||
|
||||
nlgateway = rtnl_route_get_gateway (rtnlroute);
|
||||
if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6)
|
||||
continue;
|
||||
gateway = nl_addr_get_binary_addr (nlgateway);
|
||||
|
||||
ip6route = nm_ip6_route_new ();
|
||||
nm_ip6_route_set_dest (ip6route, dest);
|
||||
nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute));
|
||||
nm_ip6_route_set_next_hop (ip6route, gateway);
|
||||
metric = rtnl_route_get_metric (rtnlroute, 1);
|
||||
if (metric != UINT_MAX)
|
||||
nm_ip6_route_set_metric (ip6route, metric);
|
||||
nm_ip6_config_take_route (config, ip6route);
|
||||
}
|
||||
|
||||
/* Add DNS servers */
|
||||
if (device->rdnss_servers) {
|
||||
NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data);
|
||||
|
||||
for (i = 0; i < device->rdnss_servers->len; i++)
|
||||
nm_ip6_config_add_nameserver (config, &rdnss[i].addr);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
65
src/ip6-manager/nm-ip6-manager.h
Normal file
65
src/ip6-manager/nm-ip6-manager.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* nm-ip6-manager.c - Handle IPv6 address configuration for NetworkManager
|
||||
*
|
||||
* 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, 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.
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef NM_IP6_MANAGER_H
|
||||
#define NM_IP6_MANAGER_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <nm-setting-ip6-config.h>
|
||||
|
||||
#include "nm-ip6-config.h"
|
||||
|
||||
#define NM_TYPE_IP6_MANAGER (nm_ip6_manager_get_type ())
|
||||
#define NM_IP6_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP6_MANAGER, NMIP6Manager))
|
||||
#define NM_IP6_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP6_MANAGER, NMIP6ManagerClass))
|
||||
#define NM_IS_IP6_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IP6_MANAGER))
|
||||
#define NM_IS_IP6_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IP6_MANAGER))
|
||||
#define NM_IP6_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP6_MANAGER, NMIP6ManagerClass))
|
||||
|
||||
typedef struct {
|
||||
GObject parent;
|
||||
} NMIP6Manager;
|
||||
|
||||
typedef struct {
|
||||
GObjectClass parent;
|
||||
|
||||
/* Signals */
|
||||
void (*addrconf_complete) (NMIP6Manager *manager, char *iface, gboolean success);
|
||||
|
||||
void (*config_changed) (NMIP6Manager *manager, char *iface);
|
||||
} NMIP6ManagerClass;
|
||||
|
||||
GType nm_ip6_manager_get_type (void);
|
||||
|
||||
NMIP6Manager *nm_ip6_manager_get (void);
|
||||
void nm_ip6_manager_prepare_interface (NMIP6Manager *manager,
|
||||
const char *iface,
|
||||
NMSettingIP6Config *s_ip6);
|
||||
void nm_ip6_manager_begin_addrconf (NMIP6Manager *manager,
|
||||
const char *iface);
|
||||
void nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager,
|
||||
const char *iface);
|
||||
|
||||
NMIP6Config * nm_ip6_manager_get_ip6_config (NMIP6Manager *manager,
|
||||
const char *iface);
|
||||
|
||||
#endif /* NM_IP6_MANAGER_H */
|
||||
407
src/ip6-manager/nm-netlink-listener.c
Normal file
407
src/ip6-manager/nm-netlink-listener.c
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* NetworkManager -- Network link manager
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright (C) 2005 - 2009 Red Hat, Inc.
|
||||
* Copyright (C) 2005 - 2008 Novell, Inc.
|
||||
* Copyright (C) 2005 Ray Strode
|
||||
*
|
||||
* Some code borrowed from HAL:
|
||||
*
|
||||
* Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
|
||||
* Copyright (C) 2004 Novell, Inc.
|
||||
*/
|
||||
|
||||
/* FIXME: this should be merged with src/nm-netlink-monitor.c */
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "NetworkManager.h"
|
||||
#include "NetworkManagerSystem.h"
|
||||
#include "nm-netlink-listener.h"
|
||||
#include "nm-utils.h"
|
||||
#include "nm-marshal.h"
|
||||
#include "nm-netlink.h"
|
||||
|
||||
#define NM_NETLINK_LISTENER_EVENT_CONDITIONS \
|
||||
((GIOCondition) (G_IO_IN | G_IO_PRI))
|
||||
|
||||
#define NM_NETLINK_LISTENER_ERROR_CONDITIONS \
|
||||
((GIOCondition) (G_IO_ERR | G_IO_NVAL))
|
||||
|
||||
#define NM_NETLINK_LISTENER_DISCONNECT_CONDITIONS \
|
||||
((GIOCondition) (G_IO_HUP))
|
||||
|
||||
#define NM_NETLINK_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
|
||||
NM_TYPE_NETLINK_LISTENER, \
|
||||
NMNetlinkListenerPrivate))
|
||||
|
||||
typedef struct {
|
||||
struct nl_handle *nlh;
|
||||
struct nl_cb * nlh_cb;
|
||||
struct nl_cache * nlh_link_cache;
|
||||
|
||||
GIOChannel * io_channel;
|
||||
guint event_id;
|
||||
|
||||
guint request_status_id;
|
||||
} NMNetlinkListenerPrivate;
|
||||
|
||||
static gboolean nm_netlink_listener_event_handler (GIOChannel *channel,
|
||||
GIOCondition io_condition,
|
||||
gpointer user_data);
|
||||
|
||||
static gboolean nm_netlink_listener_error_handler (GIOChannel *channel,
|
||||
GIOCondition io_condition,
|
||||
NMNetlinkListener *listener);
|
||||
|
||||
static gboolean nm_netlink_listener_disconnect_handler (GIOChannel *channel,
|
||||
GIOCondition io_condition,
|
||||
NMNetlinkListener *listener);
|
||||
|
||||
static void close_connection (NMNetlinkListener *listener);
|
||||
|
||||
enum {
|
||||
NOTIFICATION = 0,
|
||||
ERROR,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (NMNetlinkListener, nm_netlink_listener, G_TYPE_OBJECT);
|
||||
|
||||
NMNetlinkListener *
|
||||
nm_netlink_listener_get (void)
|
||||
{
|
||||
static NMNetlinkListener *singleton = NULL;
|
||||
|
||||
if (!singleton)
|
||||
singleton = NM_NETLINK_LISTENER (g_object_new (NM_TYPE_NETLINK_LISTENER, NULL));
|
||||
else
|
||||
g_object_ref (singleton);
|
||||
|
||||
return singleton;
|
||||
}
|
||||
|
||||
static void
|
||||
nm_netlink_listener_init (NMNetlinkListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
finalize (GObject *object)
|
||||
{
|
||||
NMNetlinkListenerPrivate *priv = NM_NETLINK_LISTENER_GET_PRIVATE (object);
|
||||
|
||||
if (priv->request_status_id)
|
||||
g_source_remove (priv->request_status_id);
|
||||
|
||||
if (priv->io_channel)
|
||||
close_connection (NM_NETLINK_LISTENER (object));
|
||||
|
||||
if (priv->nlh) {
|
||||
nl_handle_destroy (priv->nlh);
|
||||
priv->nlh = NULL;
|
||||
}
|
||||
|
||||
if (priv->nlh_cb) {
|
||||
nl_cb_put (priv->nlh_cb);
|
||||
priv->nlh_cb = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (nm_netlink_listener_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
nm_netlink_listener_class_init (NMNetlinkListenerClass *listener_class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (listener_class);
|
||||
|
||||
g_type_class_add_private (listener_class, sizeof (NMNetlinkListenerPrivate));
|
||||
|
||||
/* Virtual methods */
|
||||
object_class->finalize = finalize;
|
||||
|
||||
/* Signals */
|
||||
signals[NOTIFICATION] =
|
||||
g_signal_new ("notification",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (NMNetlinkListenerClass, notification),
|
||||
NULL, NULL, g_cclosure_marshal_VOID__POINTER,
|
||||
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||||
|
||||
signals[ERROR] =
|
||||
g_signal_new ("error",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (NMNetlinkListenerClass, error),
|
||||
NULL, NULL, _nm_marshal_VOID__POINTER,
|
||||
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||||
}
|
||||
|
||||
static int
|
||||
netlink_event_input (struct nl_msg *msg, void *listener)
|
||||
{
|
||||
struct nlmsghdr *hdr = nlmsg_hdr (msg);
|
||||
|
||||
if (hdr->nlmsg_pid != 0)
|
||||
return NL_STOP;
|
||||
|
||||
g_signal_emit (listener, signals[NOTIFICATION], 0, msg);
|
||||
|
||||
/* Stop processing messages */
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
open_connection (NMNetlinkListener *listener, GError **error)
|
||||
{
|
||||
NMNetlinkListenerPrivate *priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener);
|
||||
int fd;
|
||||
GError *channel_error = NULL;
|
||||
GIOFlags channel_flags;
|
||||
|
||||
g_return_val_if_fail (priv->io_channel == NULL, FALSE);
|
||||
|
||||
priv->nlh_cb = nl_cb_alloc (NL_CB_DEFAULT);
|
||||
priv->nlh = nl_handle_alloc_cb (priv->nlh_cb);
|
||||
if (!priv->nlh) {
|
||||
g_set_error (error, NM_NETLINK_LISTENER_ERROR,
|
||||
NM_NETLINK_LISTENER_ERROR_NETLINK_ALLOC_HANDLE,
|
||||
_("unable to allocate netlink handle: %s"),
|
||||
nl_geterror ());
|
||||
goto error;
|
||||
}
|
||||
|
||||
nl_disable_sequence_check (priv->nlh);
|
||||
nl_socket_modify_cb (priv->nlh, NL_CB_VALID, NL_CB_CUSTOM, netlink_event_input, listener);
|
||||
if (nl_connect (priv->nlh, NETLINK_ROUTE) < 0) {
|
||||
g_set_error (error, NM_NETLINK_LISTENER_ERROR,
|
||||
NM_NETLINK_LISTENER_ERROR_NETLINK_CONNECT,
|
||||
_("unable to connect to netlink: %s"),
|
||||
nl_geterror ());
|
||||
goto error;
|
||||
}
|
||||
|
||||
fd = nl_socket_get_fd (priv->nlh);
|
||||
priv->io_channel = g_io_channel_unix_new (fd);
|
||||
|
||||
g_io_channel_set_encoding (priv->io_channel, NULL, &channel_error);
|
||||
/* Encoding is NULL, so no conversion error can possibly occur */
|
||||
g_assert (channel_error == NULL);
|
||||
|
||||
g_io_channel_set_close_on_unref (priv->io_channel, TRUE);
|
||||
channel_flags = g_io_channel_get_flags (priv->io_channel);
|
||||
channel_error = NULL;
|
||||
g_io_channel_set_flags (priv->io_channel,
|
||||
channel_flags | G_IO_FLAG_NONBLOCK,
|
||||
&channel_error);
|
||||
if (channel_error != NULL) {
|
||||
g_propagate_error (error, channel_error);
|
||||
goto error;
|
||||
}
|
||||
|
||||
priv->event_id = g_io_add_watch (priv->io_channel,
|
||||
(NM_NETLINK_LISTENER_EVENT_CONDITIONS |
|
||||
NM_NETLINK_LISTENER_ERROR_CONDITIONS |
|
||||
NM_NETLINK_LISTENER_DISCONNECT_CONDITIONS),
|
||||
nm_netlink_listener_event_handler,
|
||||
listener);
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
if (priv->io_channel)
|
||||
close_connection (listener);
|
||||
|
||||
if (priv->nlh) {
|
||||
nl_handle_destroy (priv->nlh);
|
||||
priv->nlh = NULL;
|
||||
}
|
||||
|
||||
if (priv->nlh_cb) {
|
||||
nl_cb_put (priv->nlh_cb);
|
||||
priv->nlh_cb = NULL;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
close_connection (NMNetlinkListener *listener)
|
||||
{
|
||||
NMNetlinkListenerPrivate *priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener);
|
||||
|
||||
g_return_if_fail (priv->io_channel != NULL);
|
||||
|
||||
if (priv->event_id) {
|
||||
g_source_remove (priv->event_id);
|
||||
priv->event_id = 0;
|
||||
}
|
||||
|
||||
g_io_channel_shutdown (priv->io_channel,
|
||||
TRUE /* flush pending data */,
|
||||
NULL);
|
||||
|
||||
g_io_channel_unref (priv->io_channel);
|
||||
priv->io_channel = NULL;
|
||||
}
|
||||
|
||||
GQuark
|
||||
nm_netlink_listener_error_quark (void)
|
||||
{
|
||||
static GQuark error_quark = 0;
|
||||
|
||||
if (error_quark == 0)
|
||||
error_quark = g_quark_from_static_string ("nm-netlink-listener-error-quark");
|
||||
|
||||
return error_quark;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_netlink_listener_subscribe (NMNetlinkListener *listener,
|
||||
int group,
|
||||
GError **error)
|
||||
{
|
||||
NMNetlinkListenerPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (NM_IS_NETLINK_LISTENER (listener), FALSE);
|
||||
|
||||
priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener);
|
||||
|
||||
if (!priv->nlh) {
|
||||
if (!open_connection (listener, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (nl_socket_add_membership (priv->nlh, group) < 0) {
|
||||
g_set_error (error, NM_NETLINK_LISTENER_ERROR,
|
||||
NM_NETLINK_LISTENER_ERROR_NETLINK_JOIN_GROUP,
|
||||
_("unable to join netlink group: %s"),
|
||||
nl_geterror ());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nm_netlink_listener_unsubscribe (NMNetlinkListener *listener, int group)
|
||||
{
|
||||
NMNetlinkListenerPrivate *priv;
|
||||
|
||||
g_return_if_fail (NM_IS_NETLINK_LISTENER (listener));
|
||||
|
||||
priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener);
|
||||
g_return_if_fail (priv->nlh != NULL);
|
||||
|
||||
nl_socket_drop_membership (priv->nlh, group);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nm_netlink_listener_event_handler (GIOChannel *channel,
|
||||
GIOCondition io_condition,
|
||||
gpointer user_data)
|
||||
{
|
||||
NMNetlinkListener *listener = (NMNetlinkListener *) user_data;
|
||||
NMNetlinkListenerPrivate *priv;
|
||||
GError *error = NULL;
|
||||
|
||||
g_return_val_if_fail (NM_IS_NETLINK_LISTENER (listener), TRUE);
|
||||
|
||||
priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener);
|
||||
g_return_val_if_fail (priv->event_id > 0, TRUE);
|
||||
|
||||
if (io_condition & NM_NETLINK_LISTENER_ERROR_CONDITIONS)
|
||||
return nm_netlink_listener_error_handler (channel, io_condition, listener);
|
||||
else if (io_condition & NM_NETLINK_LISTENER_DISCONNECT_CONDITIONS)
|
||||
return nm_netlink_listener_disconnect_handler (channel, io_condition, listener);
|
||||
|
||||
g_return_val_if_fail (!(io_condition & ~(NM_NETLINK_LISTENER_EVENT_CONDITIONS)), FALSE);
|
||||
|
||||
if (nl_recvmsgs_default (priv->nlh) < 0) {
|
||||
error = g_error_new (NM_NETLINK_LISTENER_ERROR,
|
||||
NM_NETLINK_LISTENER_ERROR_PROCESSING_MESSAGE,
|
||||
_("error processing netlink message: %s"),
|
||||
nl_geterror ());
|
||||
|
||||
g_signal_emit (G_OBJECT (listener),
|
||||
signals[ERROR],
|
||||
0, error);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nm_netlink_listener_error_handler (GIOChannel *channel,
|
||||
GIOCondition io_condition,
|
||||
NMNetlinkListener *listener)
|
||||
{
|
||||
GError *socket_error;
|
||||
const char *err_msg;
|
||||
int err_code;
|
||||
socklen_t err_len;
|
||||
|
||||
g_return_val_if_fail (io_condition & NM_NETLINK_LISTENER_ERROR_CONDITIONS, FALSE);
|
||||
|
||||
err_code = 0;
|
||||
err_len = sizeof (err_code);
|
||||
if (getsockopt (g_io_channel_unix_get_fd (channel),
|
||||
SOL_SOCKET, SO_ERROR, (void *) &err_code, &err_len))
|
||||
err_msg = strerror (err_code);
|
||||
else
|
||||
err_msg = _("error occurred while waiting for data on socket");
|
||||
|
||||
socket_error = g_error_new (NM_NETLINK_LISTENER_ERROR,
|
||||
NM_NETLINK_LISTENER_ERROR_WAITING_FOR_SOCKET_DATA,
|
||||
"%s",
|
||||
err_msg);
|
||||
|
||||
g_signal_emit (G_OBJECT (listener),
|
||||
signals[ERROR],
|
||||
0, socket_error);
|
||||
|
||||
g_error_free (socket_error);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nm_netlink_listener_disconnect_handler (GIOChannel *channel,
|
||||
GIOCondition io_condition,
|
||||
NMNetlinkListener *listener)
|
||||
{
|
||||
|
||||
g_return_val_if_fail (!(io_condition & ~(NM_NETLINK_LISTENER_DISCONNECT_CONDITIONS)), FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
78
src/ip6-manager/nm-netlink-listener.h
Normal file
78
src/ip6-manager/nm-netlink-listener.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* NetworkManager -- Netlink socket listener
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright (C) 2005 - 2009 Red Hat, Inc.
|
||||
* Copyright (C) 2005 - 2008 Novell, Inc.
|
||||
* Copyright (C) 2005 Ray Strode
|
||||
*/
|
||||
|
||||
#ifndef NM_NETLINK_LISTENER_H
|
||||
#define NM_NETLINK_LISTENER_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "nm-netlink.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define NM_TYPE_NETLINK_LISTENER (nm_netlink_listener_get_type ())
|
||||
#define NM_NETLINK_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NETLINK_LISTENER, NMNetlinkListener))
|
||||
#define NM_NETLINK_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NETLINK_LISTENER, NMNetlinkListenerClass))
|
||||
#define NM_IS_NETLINK_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_NETLINK_LISTENER))
|
||||
#define NM_IS_NETLINK_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_NETLINK_LISTENER))
|
||||
#define NM_NETLINK_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_NETLINK_LISTENER, NMNetlinkListenerClass))
|
||||
#define NM_NETLINK_LISTENER_ERROR (nm_netlink_listener_error_quark ())
|
||||
|
||||
typedef enum {
|
||||
NM_NETLINK_LISTENER_ERROR_GENERIC = 0,
|
||||
NM_NETLINK_LISTENER_ERROR_NETLINK_ALLOC_HANDLE,
|
||||
NM_NETLINK_LISTENER_ERROR_NETLINK_CONNECT,
|
||||
NM_NETLINK_LISTENER_ERROR_NETLINK_JOIN_GROUP,
|
||||
NM_NETLINK_LISTENER_ERROR_NETLINK_ALLOC_LINK_CACHE,
|
||||
NM_NETLINK_LISTENER_ERROR_PROCESSING_MESSAGE,
|
||||
NM_NETLINK_LISTENER_ERROR_BAD_ALLOC,
|
||||
NM_NETLINK_LISTENER_ERROR_WAITING_FOR_SOCKET_DATA,
|
||||
NM_NETLINK_LISTENER_ERROR_LINK_CACHE_UPDATE
|
||||
} NMNetlinkListenerError;
|
||||
|
||||
typedef struct {
|
||||
GObject parent;
|
||||
} NMNetlinkListener;
|
||||
|
||||
typedef struct {
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* Signals */
|
||||
void (*notification) (NMNetlinkListener *listener, struct nl_msg *msg);
|
||||
void (*error) (NMNetlinkListener *listener, GError *error);
|
||||
} NMNetlinkListenerClass;
|
||||
|
||||
GType nm_netlink_listener_get_type (void) G_GNUC_CONST;
|
||||
GQuark nm_netlink_listener_error_quark (void) G_GNUC_CONST;
|
||||
|
||||
NMNetlinkListener *nm_netlink_listener_get (void);
|
||||
|
||||
gboolean nm_netlink_listener_subscribe (NMNetlinkListener *listener,
|
||||
int group,
|
||||
GError **error);
|
||||
void nm_netlink_listener_unsubscribe (NMNetlinkListener *listener,
|
||||
int group);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* NM_NETLINK_LISTENER_H */
|
||||
191
src/nm-device.c
191
src/nm-device.c
|
|
@ -51,6 +51,7 @@
|
|||
#include "nm-setting-connection.h"
|
||||
#include "nm-dnsmasq-manager.h"
|
||||
#include "nm-dhcp4-config.h"
|
||||
#include "nm-ip6-manager.h"
|
||||
#include "nm-marshal.h"
|
||||
|
||||
#define NM_ACT_REQUEST_IP4_CONFIG "nm-act-request-ip4-config"
|
||||
|
|
@ -120,7 +121,11 @@ typedef struct {
|
|||
guint32 aipd_addr;
|
||||
|
||||
/* IP6 configuration info */
|
||||
NMIP6Config *ip6_config;
|
||||
NMIP6Config * ip6_config;
|
||||
NMIP6Manager * ip6_manager;
|
||||
gulong ip6_addrconf_sigid;
|
||||
gulong ip6_config_changed_sigid;
|
||||
gboolean ip6_waiting_for_config;
|
||||
} NMDevicePrivate;
|
||||
|
||||
static gboolean check_connection_compatible (NMDeviceInterface *device,
|
||||
|
|
@ -509,16 +514,53 @@ activation_source_schedule (NMDevice *self, GSourceFunc func, int family)
|
|||
}
|
||||
|
||||
static void
|
||||
configure_ip6_router_advertisements (NMDevice *dev)
|
||||
ip6_addrconf_complete (NMIP6Manager *ip6_manager,
|
||||
const char *iface,
|
||||
gboolean success,
|
||||
gpointer user_data)
|
||||
{
|
||||
NMDevice *self = NM_DEVICE (user_data);
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
if (strcmp (nm_device_get_iface (self), iface) != 0)
|
||||
return;
|
||||
if (!nm_device_get_act_request (self))
|
||||
return;
|
||||
|
||||
if (priv->ip6_waiting_for_config) {
|
||||
priv->ip6_waiting_for_config = FALSE;
|
||||
if (success)
|
||||
nm_device_activate_schedule_stage4_ip6_config_get (self);
|
||||
else
|
||||
nm_device_activate_schedule_stage4_ip6_config_timeout (self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ip6_config_changed (NMIP6Manager *ip6_manager,
|
||||
const char *iface,
|
||||
gpointer user_data)
|
||||
{
|
||||
NMDevice *self = NM_DEVICE (user_data);
|
||||
|
||||
if (strcmp (nm_device_get_iface (self), iface) != 0)
|
||||
return;
|
||||
if (!nm_device_get_act_request (self))
|
||||
return;
|
||||
|
||||
nm_device_activate_schedule_stage4_ip6_config_get (self);
|
||||
}
|
||||
|
||||
static void
|
||||
nm_device_setup_ip6 (NMDevice *self)
|
||||
{
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
NMActRequest *req;
|
||||
NMConnection *connection;
|
||||
const char *iface, *method = NULL;
|
||||
NMSettingIP6Config *s_ip6;
|
||||
gboolean accept_ra = TRUE;
|
||||
char *sysctl_path;
|
||||
|
||||
req = nm_device_get_act_request (dev);
|
||||
req = nm_device_get_act_request (self);
|
||||
if (!req)
|
||||
return;
|
||||
connection = nm_act_request_get_connection (req);
|
||||
|
|
@ -532,18 +574,44 @@ configure_ip6_router_advertisements (NMDevice *dev)
|
|||
if (!method || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE))
|
||||
return;
|
||||
|
||||
if ( !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)
|
||||
|| !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
|
||||
accept_ra = FALSE;
|
||||
if (!priv->ip6_manager) {
|
||||
priv->ip6_manager = nm_ip6_manager_get ();
|
||||
priv->ip6_addrconf_sigid = g_signal_connect (priv->ip6_manager,
|
||||
"addrconf-complete",
|
||||
G_CALLBACK (ip6_addrconf_complete),
|
||||
self);
|
||||
priv->ip6_config_changed_sigid = g_signal_connect (priv->ip6_manager,
|
||||
"config-changed",
|
||||
G_CALLBACK (ip6_config_changed),
|
||||
self);
|
||||
}
|
||||
|
||||
iface = nm_device_get_iface (dev);
|
||||
g_return_if_fail (strchr (iface, '/') == NULL &&
|
||||
strcmp (iface, "all") != 0 &&
|
||||
strcmp (iface, "default") != 0);
|
||||
priv->ip6_waiting_for_config = FALSE;
|
||||
|
||||
sysctl_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra", iface);
|
||||
nm_utils_do_sysctl (sysctl_path, accept_ra ? "1\n" : "0\n");
|
||||
g_free (sysctl_path);
|
||||
iface = nm_device_get_iface (self);
|
||||
nm_ip6_manager_prepare_interface (priv->ip6_manager, iface, s_ip6);
|
||||
}
|
||||
|
||||
static void
|
||||
nm_device_cleanup_ip6 (NMDevice *self)
|
||||
{
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
if (priv->ip6_manager) {
|
||||
if (priv->ip6_addrconf_sigid) {
|
||||
g_signal_handler_disconnect (priv->ip6_manager,
|
||||
priv->ip6_addrconf_sigid);
|
||||
priv->ip6_addrconf_sigid = 0;
|
||||
}
|
||||
if (priv->ip6_config_changed_sigid) {
|
||||
g_signal_handler_disconnect (priv->ip6_manager,
|
||||
priv->ip6_config_changed_sigid);
|
||||
priv->ip6_config_changed_sigid = 0;
|
||||
}
|
||||
|
||||
g_object_unref (priv->ip6_manager);
|
||||
priv->ip6_manager = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -567,10 +635,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
|
|||
nm_info ("Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface);
|
||||
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
|
||||
|
||||
/* Ensure that IPv6 Router Advertisement handling is properly
|
||||
* enabled/disabled before bringing up the interface.
|
||||
*/
|
||||
configure_ip6_router_advertisements (self);
|
||||
nm_device_setup_ip6 (self);
|
||||
|
||||
ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason);
|
||||
if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
|
||||
|
|
@ -1063,7 +1128,21 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason)
|
|||
static NMActStageReturn
|
||||
real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason)
|
||||
{
|
||||
return NM_ACT_STAGE_RETURN_SUCCESS;
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
const char *iface = nm_device_get_iface (self);
|
||||
|
||||
g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
|
||||
|
||||
/* If we are ignoring IPv6 on this interface then we can go right
|
||||
* to stage 4.
|
||||
*/
|
||||
if (!priv->ip6_manager)
|
||||
return NM_ACT_STAGE_RETURN_SUCCESS;
|
||||
|
||||
priv->ip6_waiting_for_config = TRUE;
|
||||
nm_ip6_manager_begin_addrconf (priv->ip6_manager, iface);
|
||||
|
||||
return NM_ACT_STAGE_RETURN_POSTPONE;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1424,6 +1503,7 @@ real_act_stage4_get_ip6_config (NMDevice *self,
|
|||
NMIP6Config **config,
|
||||
NMDeviceStateReason *reason)
|
||||
{
|
||||
NMDevicePrivate *priv;
|
||||
NMConnection *connection;
|
||||
NMSettingIP6Config *s_ip6;
|
||||
const char *ip_iface;
|
||||
|
|
@ -1448,15 +1528,15 @@ real_act_stage4_get_ip6_config (NMDevice *self,
|
|||
return NM_ACT_STAGE_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
*config = nm_ip6_config_new ();
|
||||
priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
*config = nm_ip6_manager_get_ip6_config (priv->ip6_manager, ip_iface);
|
||||
if (!*config) {
|
||||
*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
|
||||
return NM_ACT_STAGE_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
/* Merge user-defined overrides into the IP6Config to be applied */
|
||||
if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
|
||||
nm_utils_merge_ip6_config (*config, s_ip6);
|
||||
nm_utils_merge_ip6_config (*config, s_ip6);
|
||||
|
||||
return NM_ACT_STAGE_RETURN_SUCCESS;
|
||||
}
|
||||
|
|
@ -1738,6 +1818,7 @@ nm_device_activate_stage5_ip_config_commit (gpointer user_data)
|
|||
{
|
||||
NMDevice *self = NM_DEVICE (user_data);
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
NMActRequest *act_request;
|
||||
NMIP4Config *ip4_config = NULL;
|
||||
NMIP6Config *ip6_config = NULL;
|
||||
const char *iface, *method = NULL;
|
||||
|
|
@ -1746,11 +1827,22 @@ nm_device_activate_stage5_ip_config_commit (gpointer user_data)
|
|||
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
|
||||
gboolean assumed;
|
||||
|
||||
ip4_config = g_object_get_data (G_OBJECT (nm_device_get_act_request (self)),
|
||||
/* Get the new IP4 and IP6 configs; since this stage gets rerun
|
||||
* when automatic configuration changes (DHCP lease renewal, new
|
||||
* IPv6 router advertisement, etc), it's possible that only one of
|
||||
* them will be set.
|
||||
*/
|
||||
act_request = nm_device_get_act_request (self);
|
||||
|
||||
ip4_config = g_object_get_data (G_OBJECT (act_request),
|
||||
NM_ACT_REQUEST_IP4_CONFIG);
|
||||
g_assert (ip4_config);
|
||||
ip6_config = g_object_get_data (G_OBJECT (nm_device_get_act_request (self)),
|
||||
g_object_set_data (G_OBJECT (act_request),
|
||||
NM_ACT_REQUEST_IP4_CONFIG, NULL);
|
||||
|
||||
ip6_config = g_object_get_data (G_OBJECT (act_request),
|
||||
NM_ACT_REQUEST_IP6_CONFIG);
|
||||
g_object_set_data (G_OBJECT (act_request),
|
||||
NM_ACT_REQUEST_IP6_CONFIG, NULL);
|
||||
|
||||
/* Clear the activation source ID now that this stage has run */
|
||||
activation_source_clear (self, FALSE, 0);
|
||||
|
|
@ -1761,7 +1853,7 @@ nm_device_activate_stage5_ip_config_commit (gpointer user_data)
|
|||
|
||||
assumed = nm_act_request_get_assumed (priv->act_request);
|
||||
|
||||
if (!nm_device_set_ip4_config (self, ip4_config, assumed, &reason)) {
|
||||
if (ip4_config && !nm_device_set_ip4_config (self, ip4_config, assumed, &reason)) {
|
||||
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1772,15 +1864,18 @@ nm_device_activate_stage5_ip_config_commit (gpointer user_data)
|
|||
}
|
||||
|
||||
connection = nm_act_request_get_connection (nm_device_get_act_request (self));
|
||||
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
|
||||
if (s_ip4)
|
||||
method = nm_setting_ip4_config_get_method (s_ip4);
|
||||
|
||||
if (s_ip4 && !strcmp (method, "shared")) {
|
||||
if (!start_sharing (self)) {
|
||||
nm_warning ("Activation (%s) Stage 5 of 5 (IP Configure Commit) start sharing failed.", iface);
|
||||
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
|
||||
goto out;
|
||||
if (ip4_config) {
|
||||
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
|
||||
if (s_ip4)
|
||||
method = nm_setting_ip4_config_get_method (s_ip4);
|
||||
|
||||
if (s_ip4 && !strcmp (method, "shared")) {
|
||||
if (!start_sharing (self)) {
|
||||
nm_warning ("Activation (%s) Stage 5 of 5 (IP Configure Commit) start sharing failed.", iface);
|
||||
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1791,7 +1886,8 @@ out:
|
|||
iface);
|
||||
|
||||
/* Balance IP config creation; device takes ownership in set_ip*_config() */
|
||||
g_object_unref (ip4_config);
|
||||
if (ip4_config)
|
||||
g_object_unref (ip4_config);
|
||||
if (ip6_config)
|
||||
g_object_unref (ip6_config);
|
||||
|
||||
|
|
@ -1819,6 +1915,11 @@ nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self, int family)
|
|||
else if (family == AF_INET6)
|
||||
priv->ip6_ready = TRUE;
|
||||
|
||||
/* Note that these are only set FALSE at stage3, so once you've
|
||||
* made it all the way through activation once, you can jump back
|
||||
* into stage4 (eg, for a DHCP lease change) and not worry about
|
||||
* needing both IPv4 and IPv6 to complete.
|
||||
*/
|
||||
if (!priv->ip4_ready || !priv->ip6_ready)
|
||||
return;
|
||||
|
||||
|
|
@ -1905,6 +2006,7 @@ nm_device_deactivate_quickly (NMDevice *self)
|
|||
}
|
||||
|
||||
aipd_cleanup (self);
|
||||
nm_device_cleanup_ip6 (self);
|
||||
|
||||
/* Call device type-specific deactivation */
|
||||
if (NM_DEVICE_GET_CLASS (self)->deactivate_quickly)
|
||||
|
|
@ -1936,15 +2038,15 @@ nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason)
|
|||
|
||||
nm_device_deactivate_quickly (self);
|
||||
|
||||
/* Clean up nameservers and addresses */
|
||||
nm_device_set_ip4_config (self, NULL, FALSE, &ignored);
|
||||
nm_device_set_ip6_config (self, NULL, FALSE, &ignored);
|
||||
|
||||
/* Take out any entries in the routing table and any IP address the device had. */
|
||||
nm_system_device_flush_routes (self);
|
||||
nm_system_device_flush_addresses (self);
|
||||
nm_device_update_ip4_address (self);
|
||||
|
||||
/* Clean up nameservers and addresses */
|
||||
nm_device_set_ip4_config (self, NULL, FALSE, &ignored);
|
||||
nm_device_set_ip6_config (self, NULL, FALSE, &ignored);
|
||||
|
||||
/* Call device type-specific deactivation */
|
||||
if (NM_DEVICE_GET_CLASS (self)->deactivate)
|
||||
NM_DEVICE_GET_CLASS (self)->deactivate (self);
|
||||
|
|
@ -2606,7 +2708,10 @@ dispose (GObject *object)
|
|||
if ( connection
|
||||
&& (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_SYSTEM)) {
|
||||
|
||||
/* Only static or DHCP connections can be left up */
|
||||
/* Only static or DHCP IPv4 connections can be left up.
|
||||
* All IPv6 connections can be left up, so we don't have
|
||||
* to check that.
|
||||
*/
|
||||
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
|
||||
g_assert (s_ip4);
|
||||
|
||||
|
|
@ -2635,8 +2740,10 @@ dispose (GObject *object)
|
|||
activation_source_clear (self, TRUE, AF_INET);
|
||||
activation_source_clear (self, TRUE, AF_INET6);
|
||||
|
||||
if (!take_down)
|
||||
if (!take_down) {
|
||||
nm_device_set_use_dhcp (self, FALSE);
|
||||
nm_device_cleanup_ip6 (self);
|
||||
}
|
||||
|
||||
if (priv->dnsmasq_manager) {
|
||||
if (priv->dnsmasq_state_id) {
|
||||
|
|
|
|||
|
|
@ -227,6 +227,11 @@ out:
|
|||
static int
|
||||
netlink_event_input (struct nl_msg *msg, void *arg)
|
||||
{
|
||||
struct nlmsghdr *hdr = nlmsg_hdr (msg);
|
||||
|
||||
if (hdr->nlmsg_pid != 0)
|
||||
return NL_STOP;
|
||||
|
||||
nl_msg_parse (msg, &netlink_object_message_handler, arg);
|
||||
|
||||
/* Stop processing messages */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue