platform: merge: branch 'lr/ipv6-tokens' (rh #1104689)

This adds support for tokenised identifier use as specified in
draft-chown-6man-tokenised-ipv6-identifiers-02 Internet Draft,
enabled if there's a support for tokenised identifiers in libnl.

https://bugzilla.redhat.com/show_bug.cgi?id=1104689
https://tools.ietf.org/html/draft-chown-6man-tokenised-ipv6-identifiers-02

(cherry picked from commit 5f50c0480a)
This commit is contained in:
Lubomir Rintel 2015-01-26 13:05:35 +01:00 committed by Thomas Haller
commit 8d91bf5769
9 changed files with 150 additions and 10 deletions

View file

@ -428,6 +428,13 @@ else
AC_DEFINE(HAVE_KERNEL_INET6_ADDR_GEN_MODE, 0, [Define if the kernel has IN6_ADDR_GEN_MODE_*])
fi
# IPv6 tokenized identifiers support in libnl
AC_CHECK_LIB([nl-route-3], [rtnl_link_inet6_get_token],
ac_have_ipv6_token="1",
ac_have_ipv6_token="0")
AC_DEFINE_UNQUOTED(HAVE_LIBNL_INET6_TOKEN,
$ac_have_ipv6_token, [Define if libnl has rtnl_link_inet6_get_token()])
# uuid library
PKG_CHECK_MODULES(UUID, uuid)
AC_SUBST(UUID_CFLAGS)

View file

@ -192,12 +192,12 @@ gboolean nm_utils_is_specific_hostname (const char *name);
* and should not normally be treated as a %guint64, but this is done for
* convenience of validity checking and initialization.
*/
typedef struct {
struct _NMUtilsIPv6IfaceId {
union {
guint64 id;
guint8 id_u8[8];
};
} NMUtilsIPv6IfaceId;
};
#define NM_UTILS_IPV6_IFACE_ID_INIT { .id = 0 }

View file

@ -1239,6 +1239,7 @@ device_link_changed (NMDevice *self, NMPlatformLink *info)
{
NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMUtilsIPv6IfaceId token_iid;
gboolean ip_ifname_changed = FALSE;
if (info->udi && g_strcmp0 (info->udi, priv->udi)) {
@ -1285,6 +1286,12 @@ device_link_changed (NMDevice *self, NMPlatformLink *info)
nm_device_enslave_slave (priv->master, self, NULL);
}
if (priv->rdisc && nm_platform_link_get_ipv6_token (priv->ifindex, &token_iid)) {
_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
if (nm_rdisc_set_iid (priv->rdisc, token_iid))
nm_rdisc_start (priv->rdisc);
}
if (klass->link_changed)
klass->link_changed (self, info);
@ -4282,11 +4289,12 @@ addrconf6_start_with_link_ready (NMDevice *self)
g_assert (priv->rdisc);
if (!nm_device_get_ip_iface_identifier (self, &iid)) {
if (nm_platform_link_get_ipv6_token (priv->ifindex, &iid)) {
_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
} else if (!nm_device_get_ip_iface_identifier (self, &iid)) {
_LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 cannot continue");
return FALSE;
}
nm_rdisc_set_iid (priv->rdisc, iid);
/* Apply any manual configuration before starting RA */
if (!ip6_config_merge_and_apply (self, TRUE, NULL))
@ -4305,6 +4313,8 @@ addrconf6_start_with_link_ready (NMDevice *self)
NM_RDISC_RA_TIMEOUT,
G_CALLBACK (rdisc_ra_timeout),
self);
nm_rdisc_set_iid (priv->rdisc, iid);
nm_rdisc_start (priv->rdisc);
return TRUE;
}

View file

@ -113,4 +113,7 @@ typedef struct _NMSecretAgent NMSecretAgent;
typedef struct _NMSettings NMSettings;
typedef struct _NMSettingsConnection NMSettingsConnection;
/* utils */
typedef struct _NMUtilsIPv6IfaceId NMUtilsIPv6IfaceId;
#endif /* NM_TYPES_H */

View file

@ -44,9 +44,9 @@
#include <netlink/route/route.h>
#include <gudev/gudev.h>
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE || HAVE_LIBNL_INET6_TOKEN
#include <netlink/route/link/inet6.h>
#if HAVE_KERNEL_INET6_ADDR_GEN_MODE
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE && HAVE_KERNEL_INET6_ADDR_GEN_MODE
#include <linux/if_link.h>
#else
#define IN6_ADDR_GEN_MODE_EUI64 0
@ -1905,6 +1905,37 @@ nm_nl_object_diff (ObjectType type, struct nl_object *_a, struct nl_object *_b)
return TRUE;
}
#if HAVE_LIBNL_INET6_TOKEN
/* libnl ignores PROTINFO changes in object without AF assigned */
if (type == OBJECT_TYPE_LINK) {
struct rtnl_addr *a = (struct rtnl_addr *) _a;
struct rtnl_addr *b = (struct rtnl_addr *) _b;
auto_nl_addr struct nl_addr *token_a = NULL;
auto_nl_addr struct nl_addr *token_b = NULL;
if (rtnl_link_inet6_get_token ((struct rtnl_link *) a, &token_a) != 0)
token_a = NULL;
if (rtnl_link_inet6_get_token ((struct rtnl_link *) b, &token_b) != 0)
token_b = NULL;
if (token_a && token_b) {
if (nl_addr_get_family (token_a) == AF_INET6 &&
nl_addr_get_family (token_b) == AF_INET6 &&
nl_addr_get_len (token_a) == sizeof (struct in6_addr) &&
nl_addr_get_len (token_b) == sizeof (struct in6_addr) &&
memcmp (nl_addr_get_binary_addr (token_a),
nl_addr_get_binary_addr (token_b),
sizeof (struct in6_addr))) {
/* Token changed */
return TRUE;
}
} else if (token_a != token_b) {
/* Token added or removed (?). */
return TRUE;
}
}
#endif
if (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS) {
struct rtnl_addr *a = (struct rtnl_addr *) _a;
struct rtnl_addr *b = (struct rtnl_addr *) _b;
@ -2488,6 +2519,38 @@ link_set_noarp (NMPlatform *platform, int ifindex)
return link_change_flags (platform, ifindex, IFF_NOARP, TRUE);
}
static gboolean
link_get_ipv6_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId *iid)
{
#if HAVE_LIBNL_INET6_TOKEN
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
struct nl_addr *nladdr;
struct in6_addr *addr;
if (rtnllink &&
(rtnl_link_inet6_get_token (rtnllink, &nladdr)) == 0) {
if (nl_addr_get_family (nladdr) != AF_INET6 ||
nl_addr_get_len (nladdr) != sizeof (struct in6_addr)) {
nl_addr_put (nladdr);
return FALSE;
}
addr = nl_addr_get_binary_addr (nladdr);
iid->id_u8[7] = addr->s6_addr[15];
iid->id_u8[6] = addr->s6_addr[14];
iid->id_u8[5] = addr->s6_addr[13];
iid->id_u8[4] = addr->s6_addr[12];
iid->id_u8[3] = addr->s6_addr[11];
iid->id_u8[2] = addr->s6_addr[10];
iid->id_u8[1] = addr->s6_addr[9];
iid->id_u8[0] = addr->s6_addr[8];
nl_addr_put (nladdr);
return TRUE;
}
#endif
return FALSE;
}
static gboolean
link_get_user_ipv6ll_enabled (NMPlatform *platform, int ifindex)
{
@ -4572,6 +4635,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
platform_class->link_is_connected = link_is_connected;
platform_class->link_uses_arp = link_uses_arp;
platform_class->link_get_ipv6_token = link_get_ipv6_token;
platform_class->link_get_user_ipv6ll_enabled = link_get_user_ipv6ll_enabled;
platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled;

View file

@ -779,6 +779,31 @@ nm_platform_link_uses_arp (int ifindex)
return klass->link_uses_arp (platform, ifindex);
}
/**
* nm_platform_link_get_ipv6_token:
* @ifindex: Interface index
* @iid: Tokenized interface identifier
*
* Returns IPv6 tokenized interface identifier. If the platform or OS doesn't
* support IPv6 tokenized interface identifiers, or the token is not set
* this call will fail and return %FALSE.
*
* Returns: %TRUE a tokenized identifier was available
*/
gboolean
nm_platform_link_get_ipv6_token (int ifindex, NMUtilsIPv6IfaceId *iid)
{
reset_error ();
g_return_val_if_fail (ifindex >= 0, FALSE);
g_return_val_if_fail (iid, FALSE);
if (klass->link_get_ipv6_token)
return klass->link_get_ipv6_token (platform, ifindex, iid);
return FALSE;
}
/**
* nm_platform_link_get_user_ip6vll_enabled:
* @ifindex: Interface index

View file

@ -379,6 +379,8 @@ typedef struct {
gboolean (*link_is_connected) (NMPlatform *, int ifindex);
gboolean (*link_uses_arp) (NMPlatform *, int ifindex);
gboolean (*link_get_ipv6_token) (NMPlatform *, int ifindex, NMUtilsIPv6IfaceId *iid);
gboolean (*link_get_user_ipv6ll_enabled) (NMPlatform *, int ifindex);
gboolean (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled);
@ -528,6 +530,8 @@ gboolean nm_platform_link_is_up (int ifindex);
gboolean nm_platform_link_is_connected (int ifindex);
gboolean nm_platform_link_uses_arp (int ifindex);
gboolean nm_platform_link_get_ipv6_token (int ifindex, NMUtilsIPv6IfaceId *iid);
gboolean nm_platform_link_get_user_ipv6ll_enabled (int ifindex);
gboolean nm_platform_link_set_user_ipv6ll_enabled (int ifindex, gboolean enabled);

View file

@ -241,12 +241,38 @@ clear_rs_timeout (NMRDisc *rdisc)
}
}
void
/**
* nm_rdisc_set_iid:
* @rdisc: the #NMRDisc
* @iid: the new interface ID
*
* Sets the "Modified EUI-64" interface ID to be used when generating
* IPv6 addresses using received prefixes. Identifiers are either generated
* from the hardware addresses or manually set by the operator with
* "ip token" command.
*
* Upon token change (or initial setting) all addresses generated using
* the old identifier are removed. The caller should ensure the addresses
* will be reset by soliciting router advertisements.
*
* Returns: %TRUE if the token was changed, %FALSE otherwise.
**/
gboolean
nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid)
{
g_return_if_fail (NM_IS_RDISC (rdisc));
g_return_val_if_fail (NM_IS_RDISC (rdisc), FALSE);
rdisc->iid = iid;
if (rdisc->iid.id != iid.id) {
rdisc->iid = iid;
if (rdisc->addresses->len) {
debug ("(%s) IPv6 interface identifier changed, flushing addresses", rdisc->ifname);
g_array_remove_range (rdisc->addresses, 0, rdisc->addresses->len);
g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, NM_RDISC_CONFIG_ADDRESSES);
}
return TRUE;
}
return FALSE;
}
static gboolean

View file

@ -141,7 +141,7 @@ typedef struct {
GType nm_rdisc_get_type (void);
void nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid);
gboolean nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid);
void nm_rdisc_start (NMRDisc *rdisc);
#endif /* __NETWORKMANAGER_RDISC_H__ */