ppp: add IPv6 support
authorDan Williams <dcbw@redhat.com>
Thu, 12 Jun 2014 14:59:38 +0000 (09:59 -0500)
committerDan Williams <dcbw@redhat.com>
Wed, 23 Jul 2014 19:26:39 +0000 (14:26 -0500)
Add support for IPv6 to the pppd plugin and return the interface identifiers
to NetworkManager.  Use those to construct the IPv6LL addresses for the
PPP interface and the peer.

introspection/nm-ppp-manager.xml
src/ppp-manager/Makefile.am
src/ppp-manager/nm-ppp-manager.c
src/ppp-manager/nm-ppp-manager.h
src/ppp-manager/nm-pppd-plugin.c
src/ppp-manager/nm-pppd-plugin.h

index 9e2dfdb..2867daf 100644 (file)
       <arg name="config" type="a{sv}" direction="in"/>
     </method>
 
+    <method name="SetIp6Config">
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_ppp_manager_set_ip6_config"/>
+      <arg name="config" type="a{sv}" direction="in"/>
+    </method>
+
     <method name="SetState">
       <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_ppp_manager_set_state"/>
       <arg name="state" type="u" direction="in"/>
index 2d740f1..ba8c2d7 100644 (file)
@@ -20,8 +20,8 @@ nm_pppd_plugin_la_SOURCES = \
 nm_pppd_plugin_la_LDFLAGS = -module -avoid-version
 
 nm_pppd_plugin_la_LIBADD = \
-       $(top_builddir)/libnm-util/libnm-util.la \
        $(DBUS_LIBS) \
-       $(GLIB_LIBS)
+       $(GLIB_LIBS) \
+       -ldl
 
 endif
index 7a837fb..9e7669d 100644 (file)
@@ -67,6 +67,10 @@ static gboolean impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
                                                  GHashTable *config,
                                                  GError **err);
 
+static gboolean impl_ppp_manager_set_ip6_config (NMPPPManager *manager,
+                                                 GHashTable *config,
+                                                 GError **err);
+
 #include "nm-ppp-manager-glue.h"
 
 static void _ppp_cleanup  (NMPPPManager *manager);
@@ -101,6 +105,7 @@ G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, G_TYPE_OBJECT)
 enum {
        STATE_CHANGED,
        IP4_CONFIG,
+       IP6_CONFIG,
        STATS,
 
        LAST_SIGNAL
@@ -132,6 +137,7 @@ nm_ppp_manager_error_quark (void)
 static void
 nm_ppp_manager_init (NMPPPManager *manager)
 {
+       NM_PPP_MANAGER_GET_PRIVATE (manager)->monitor_fd = -1;
 }
 
 static void
@@ -245,6 +251,14 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class)
                              G_TYPE_STRING,
                              G_TYPE_OBJECT);
 
+       signals[IP6_CONFIG] =
+               g_signal_new ("ip6-config",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (NMPPPManagerClass, ip6_config),
+                             NULL, NULL, NULL,
+                             G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_OBJECT);
+
        signals[STATS] =
                g_signal_new ("stats",
                              G_OBJECT_CLASS_TYPE (object_class),
@@ -300,8 +314,12 @@ monitor_stats (NMPPPManager *manager)
 {
        NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
 
+       /* already monitoring */
+       if (priv->monitor_fd >= 0)
+               return;
+
        priv->monitor_fd = socket (AF_INET, SOCK_DGRAM, 0);
-       if (priv->monitor_fd > 0) {
+       if (priv->monitor_fd >= 0) {
                g_warn_if_fail (priv->monitor_id == 0);
                if (priv->monitor_id)
                        g_source_remove (priv->monitor_id);
@@ -498,20 +516,52 @@ static gboolean impl_ppp_manager_set_state (NMPPPManager *manager,
        return TRUE;
 }
 
+static gboolean
+set_ip_config_common (NMPPPManager *self,
+                      GHashTable *hash,
+                      const char *iface_prop,
+                      guint32 *out_mtu)
+{
+       NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self);
+       NMConnection *connection;
+       NMSettingPPP *s_ppp;
+       GValue *val;
+
+       val = g_hash_table_lookup (hash, iface_prop);
+       if (!val || !G_VALUE_HOLDS_STRING (val)) {
+               nm_log_err (LOGD_PPP, "no interface received!");
+               return FALSE;
+       }
+       if (priv->ip_iface == NULL)
+               priv->ip_iface = g_value_dup_string (val);
+
+       /* Got successful IP config; obviously the secrets worked */
+       connection = nm_act_request_get_connection (priv->act_req);
+       g_assert (connection);
+       g_object_set_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES, NULL);
+
+       /* Get any custom MTU */
+       s_ppp = nm_connection_get_setting_ppp (connection);
+       if (s_ppp && out_mtu)
+               *out_mtu = nm_setting_ppp_get_mtu (s_ppp);
+
+       monitor_stats (self);
+       return TRUE;
+}
+
 static gboolean
 impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
                                  GHashTable *config_hash,
                                  GError **err)
 {
        NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
-       NMConnection *connection;
-       NMSettingPPP *s_ppp;
        NMIP4Config *config;
        NMPlatformIP4Address address;
        GValue *val;
        int i;
+       guint32 mtu = 0;
 
-       nm_log_info (LOGD_PPP, "PPP manager(IP Config Get) reply received.");
+       nm_log_info (LOGD_PPP, "PPP manager (IPv4 Config Get) reply received.");
 
        remove_timeout_handler (manager);
 
@@ -557,35 +607,89 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
                        nm_ip4_config_add_wins (config, g_array_index (wins, guint, i));
        }
 
-       val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_INTERFACE);
-       if (!val || !G_VALUE_HOLDS_STRING (val)) {
-               nm_log_err (LOGD_PPP, "no interface received!");
+       if (!set_ip_config_common (manager, config_hash, NM_PPP_IP4_CONFIG_INTERFACE, &mtu))
                goto out;
+
+       if (mtu)
+               nm_ip4_config_set_mtu (config, mtu);
+
+       /* Push the IP4 config up to the device */
+       g_signal_emit (manager, signals[IP4_CONFIG], 0, priv->ip_iface, config);
+
+out:
+       g_object_unref (config);
+       return TRUE;
+}
+
+/* Converts the named Interface Identifier item to an IPv6 LL address and
+ * returns the IID.
+ */
+static gboolean
+iid_value_to_ll6_addr (GHashTable *hash,
+                       const char *prop,
+                       struct in6_addr *out_addr,
+                       NMUtilsIPv6IfaceId *out_iid)
+{
+       GValue *val;
+       guint64 iid;
+
+       val = g_hash_table_lookup (hash, prop);
+       if (!val || !G_VALUE_HOLDS (val, G_TYPE_UINT64)) {
+               nm_log_dbg (LOGD_PPP, "pppd plugin property '%s' missing or not a uint64", prop);
+               return FALSE;
        }
-       priv->ip_iface = g_value_dup_string (val);
 
-       /* Got successful IP4 config; obviously the secrets worked */
-       connection = nm_act_request_get_connection (priv->act_req);
-       g_assert (connection);
-       g_object_set_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES, NULL);
+       iid = g_value_get_uint64 (val);
+       g_return_val_if_fail (iid != 0, FALSE);
 
-       /* Merge in custom MTU */
-       s_ppp = nm_connection_get_setting_ppp (connection);
-       if (s_ppp) {
-               guint32 mtu = nm_setting_ppp_get_mtu (s_ppp);
+       /* Construct an IPv6 LL address from the interface identifier.  See
+        * http://tools.ietf.org/html/rfc4291#section-2.5.1 (IPv6) and
+        * http://tools.ietf.org/html/rfc5072#section-4.1 (IPv6 over PPP).
+        */
+       memset (out_addr->s6_addr, 0, sizeof (out_addr->s6_addr));
+       out_addr->s6_addr16[0] = htons (0xfe80);
+       memcpy (out_addr->s6_addr + 8, &iid, sizeof (iid));
+       if (out_iid)
+               nm_utils_ipv6_interface_identfier_get_from_addr (out_iid, out_addr);
+       return TRUE;
+}
+
+static gboolean
+impl_ppp_manager_set_ip6_config (NMPPPManager *manager,
+                                 GHashTable *hash,
+                                 GError **err)
+{
+       NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+       NMIP6Config *config;
+       NMPlatformIP6Address addr;
+       struct in6_addr a;
+       NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT;
+
+       nm_log_info (LOGD_PPP, "PPP manager (IPv6 Config Get) reply received.");
+
+       remove_timeout_handler (manager);
+
+       config = nm_ip6_config_new ();
+
+       memset (&addr, 0, sizeof (addr));
+       addr.plen = 64;
 
-               if (mtu)
-                       nm_ip4_config_set_mtu (config, mtu);
+       if (iid_value_to_ll6_addr (hash, NM_PPP_IP6_CONFIG_PEER_IID, &a, NULL)) {
+               nm_ip6_config_set_gateway (config, &a);
+               addr.peer_address = a;
        }
 
-       /* Push the IP4 config up to the device */
-       g_signal_emit (manager, signals[IP4_CONFIG], 0, priv->ip_iface, config);
+       if (iid_value_to_ll6_addr (hash, NM_PPP_IP6_CONFIG_OUR_IID, &addr.address, &iid)) {
+               nm_ip6_config_add_address (config, &addr);
 
-       monitor_stats (manager);
+               if (set_ip_config_common (manager, hash, NM_PPP_IP6_CONFIG_INTERFACE, NULL)) {
+                       /* Push the IPv6 config and interface identifier up to the device */
+                       g_signal_emit (manager, signals[IP6_CONFIG], 0, priv->ip_iface, &iid, config);
+               }
+       } else
+               nm_log_err (LOGD_PPP, "invalid IPv6 address received!");
 
- out:
        g_object_unref (config);
-
        return TRUE;
 }
 
@@ -1127,11 +1231,11 @@ _ppp_cleanup (NMPPPManager *manager)
                priv->monitor_id = 0;
        }
 
-       if (priv->monitor_fd) {
+       if (priv->monitor_fd >= 0) {
                /* Get the stats one last time */
                monitor_cb (manager);
                close (priv->monitor_fd);
-               priv->monitor_fd = 0;
+               priv->monitor_fd = -1;
        }
 
        if (priv->ppp_timeout_handler) {
index 1b4bc18..7b0125b 100644 (file)
@@ -29,7 +29,9 @@
 #include "nm-activation-request.h"
 #include "nm-connection.h"
 #include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
 #include "nm-pppd-plugin.h"
+#include "NetworkManagerUtils.h"
 
 #define NM_TYPE_PPP_MANAGER            (nm_ppp_manager_get_type ())
 #define NM_PPP_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PPP_MANAGER, NMPPPManager))
@@ -50,6 +52,10 @@ typedef struct {
        /* Signals */
        void (*state_changed) (NMPPPManager *manager, NMPPPStatus status);
        void (*ip4_config) (NMPPPManager *manager, const char *iface, NMIP4Config *config);
+       void (*ip6_config) (NMPPPManager *manager,
+                           const char *iface,
+                           const NMUtilsIPv6IfaceId *iid,
+                           NMIP6Config *config);
        void (*stats) (NMPPPManager *manager, guint32 in_bytes, guint32 out_bytes);
 } NMPPPManagerClass;
 
index e6ee86f..0cea4e1 100644 (file)
@@ -19,6 +19,7 @@
  * Copyright (C) 2008 Red Hat, Inc.
  */
 
+#include <config.h>
 #include <string.h>
 #include <pppd/pppd.h>
 #include <pppd/fsm.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <dlfcn.h>
 #include <glib.h>
 #include <glib-object.h>
 #include <dbus/dbus-glib.h>
 
+#define INET6
+#include <pppd/eui64.h>
+#include <pppd/ipv6cp.h>
+
 #include "NetworkManager.h"
 #include "nm-pppd-plugin.h"
 #include "nm-ppp-status.h"
@@ -128,7 +134,6 @@ str_to_gvalue (const char *str)
        val = g_slice_new0 (GValue);
        g_value_init (val, G_TYPE_STRING);
        g_value_set_string (val, str);
-
        return val;
 }
 
@@ -140,7 +145,6 @@ uint_to_gvalue (guint32 i)
        val = g_slice_new0 (GValue);
        g_value_init (val, G_TYPE_UINT);
        g_value_set_uint (val, i);
-
        return val;
 }
 
@@ -230,7 +234,7 @@ nm_ip_up (void *data, int arg)
                g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_WINS, val);
        }
 
-       g_message ("nm-ppp-plugin: (%s): sending Ip4Config to NetworkManager...", __func__);
+       g_message ("nm-ppp-plugin: (%s): sending IPv4 config to NetworkManager...", __func__);
 
        dbus_g_proxy_call_no_reply (proxy, "SetIp4Config",
                                    DBUS_TYPE_G_MAP_OF_VARIANT, hash, G_TYPE_INVALID,
@@ -239,6 +243,48 @@ nm_ip_up (void *data, int arg)
        g_hash_table_destroy (hash);
 }
 
+static GValue *
+eui64_to_gvalue (eui64_t eui)
+{
+       GValue *val;
+       guint64 iid;
+
+       G_STATIC_ASSERT (sizeof (iid) == sizeof (eui));
+
+       val = g_slice_new0 (GValue);
+       g_value_init (val, G_TYPE_UINT64);
+       memcpy (&iid, &eui, sizeof (eui));
+       g_value_set_uint64 (val, iid);
+       return val;
+}
+
+static void
+nm_ip6_up (void *data, int arg)
+{
+       ipv6cp_options *ho = &ipv6cp_hisoptions[0];
+       ipv6cp_options *go = &ipv6cp_gotoptions[0];
+       GHashTable *hash;
+
+       g_return_if_fail (DBUS_IS_G_PROXY (proxy));
+
+       g_message ("nm-ppp-plugin: (%s): ip6-up event", __func__);
+
+       hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy);
+       g_hash_table_insert (hash, NM_PPP_IP6_CONFIG_INTERFACE, str_to_gvalue (ifname));
+       g_hash_table_insert (hash, NM_PPP_IP6_CONFIG_OUR_IID, eui64_to_gvalue (go->ourid));
+       g_hash_table_insert (hash, NM_PPP_IP6_CONFIG_PEER_IID, eui64_to_gvalue (ho->hisid));
+
+       /* DNS is done via DHCPv6 or router advertisements */
+
+       g_message ("nm-ppp-plugin: (%s): sending IPv6 config to NetworkManager...", __func__);
+
+       dbus_g_proxy_call_no_reply (proxy, "SetIp6Config",
+                                   DBUS_TYPE_G_MAP_OF_VARIANT, hash, G_TYPE_INVALID,
+                                   G_TYPE_INVALID);
+
+       g_hash_table_destroy (hash);
+}
+
 static int
 get_chap_check (void)
 {
@@ -319,6 +365,27 @@ nm_exit_notify (void *data, int arg)
        proxy = NULL;
 }
 
+static void
+add_ip6_notifier (void)
+{
+       static struct notifier **notifier = NULL;
+       static gsize load_once = 0;
+
+       if (g_once_init_enter (&load_once)) {
+               void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
+
+               if (handle) {
+                       notifier = dlsym (handle, "ipv6_up_notifier");
+                       dlclose (handle);
+               }
+               g_once_init_leave (&load_once, 1);
+       }
+       if (notifier)
+               add_notifier (notifier, nm_ip6_up, NULL);
+       else
+               g_message ("nm-ppp-plugin: no IPV6CP notifier support; IPv6 not available");
+}
+
 int
 plugin_init (void)
 {
@@ -356,6 +423,7 @@ plugin_init (void)
        add_notifier (&phasechange, nm_phasechange, NULL);
        add_notifier (&ip_up_notifier, nm_ip_up, NULL);
        add_notifier (&exitnotify, nm_exit_notify, proxy);
+       add_ip6_notifier ();
 
        return 0;
 }
index 95a2a18..2c3073e 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2008 - 2014 Red Hat, Inc.
  */
 
 #define NM_DBUS_INTERFACE_PPP  "org.freedesktop.NetworkManager.PPP"
 
 #define NM_PPP_IP4_CONFIG_INTERFACE "interface"
-#define NM_PPP_IP4_CONFIG_ADDRESS "address"
-#define NM_PPP_IP4_CONFIG_PREFIX "prefix"
-#define NM_PPP_IP4_CONFIG_GATEWAY "gateway"
-#define NM_PPP_IP4_CONFIG_DNS "dns"
-#define NM_PPP_IP4_CONFIG_WINS "wins"
+#define NM_PPP_IP4_CONFIG_ADDRESS   "address"
+#define NM_PPP_IP4_CONFIG_PREFIX    "prefix"
+#define NM_PPP_IP4_CONFIG_GATEWAY   "gateway"
+#define NM_PPP_IP4_CONFIG_DNS       "dns"
+#define NM_PPP_IP4_CONFIG_WINS      "wins"
+
+#define NM_PPP_IP6_CONFIG_INTERFACE "interface"
+#define NM_PPP_IP6_CONFIG_OUR_IID   "our-iid"
+#define NM_PPP_IP6_CONFIG_PEER_IID  "peer-iid"
+
+#define DBUS_TYPE_EUI64 (dbus_g_type_get_collection ("GByteArray", G_TYPE_UINT8))
+