ppp: add IPv6 support

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.
This commit is contained in:
Dan Williams 2014-06-12 09:59:38 -05:00
parent af1f183b3d
commit 75fa46bd19
6 changed files with 228 additions and 38 deletions

View file

@ -14,6 +14,11 @@
<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"/>

View 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

View 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;
}
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);
/* Merge in custom MTU */
s_ppp = nm_connection_get_setting_ppp (connection);
if (s_ppp) {
guint32 mtu = nm_setting_ppp_get_mtu (s_ppp);
if (mtu)
nm_ip4_config_set_mtu (config, mtu);
}
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);
monitor_stats (manager);
out:
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;
}
iid = g_value_get_uint64 (val);
g_return_val_if_fail (iid != 0, FALSE);
/* 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 (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;
}
if (iid_value_to_ll6_addr (hash, NM_PPP_IP6_CONFIG_OUR_IID, &addr.address, &iid)) {
nm_ip6_config_add_address (config, &addr);
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!");
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) {

View 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;

View 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>
@ -26,10 +27,15 @@
#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;
}

View file

@ -16,14 +16,21 @@
* 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))