platform: create netlink messages directly without libnl-route-3

Instead of using libnl-route-3 library to serialize netlink messages,
construct the netlink messages ourselves.

This has several advantages:

- Creating the netlink message ourself is actually more straight
  forward then having an intermediate layer between NM and the kernel.
  Now it is immediately clear, how a platform request translates to
  a netlink/kernel request.
  You can look at the kernel sources how a certain netlink attribute
  behaves, and then it's immediately clear how to set that (and vice
  versa).

- Older libnl versions might have bugs or missing features for which
  we needed to workaround (often by offering a reduced/broken/untested
  functionality). Now we can get rid or workaround like _nl_has_capability(),
  check_support_libnl_extended_ifa_flags(), HAVE_LIBNL_INET6_TOKEN.
  Another example is a libnl bug when setting vlan ingress map which
  isn't even yet fixed in libnl upstream.

- We no longer need libnl-route-3 at all and can drop that runtime
  requirement, saving some 400k.
  Constructing the messages ourselves also gives better performance
  because we don't have to create the intermediate libnl object.

- In the future we will add more link-type support which is easier
  to support by basing directly on the plain kernel/netlink API,
  instead of requiring also libnl3 to expose this functionality.
  E.g. adding macvtap support: we already parsed macvtap properties
  ourselves because of missing libnl support. To *add* macvtap
  support, we also would have to do it ourself (or extend libnl).
This commit is contained in:
Thomas Haller
2015-10-20 09:27:16 +02:00
parent b296325cca
commit 9ecdba316c
10 changed files with 938 additions and 1008 deletions

View File

@@ -528,19 +528,6 @@ AC_SUBST(NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT)
# libnl support for the linux platform
PKG_CHECK_MODULES(LIBNL, libnl-3.0 >= 3.2.8 libnl-route-3.0)
AC_CHECK_LIB([nl-route-3], [rtnl_link_inet6_get_addr_gen_mode],
ac_have_addr_gen_mode="1",
ac_have_addr_gen_mode="0")
AC_DEFINE_UNQUOTED(HAVE_LIBNL_INET6_ADDR_GEN_MODE,
$ac_have_addr_gen_mode, [Define if libnl has rtnl_link_inet6_get_addr_gen_mode()])
# 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)

View File

@@ -4901,53 +4901,6 @@ linklocal6_start (NMDevice *self)
/******************************************/
static void
print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr)
{
static gint8 warn = 0;
static gint8 s_libnl = -1, s_kernel;
if (warn >= 2)
return;
if (s_libnl == -1) {
s_libnl = !!nm_platform_check_support_libnl_extended_ifa_flags ();
s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
if (s_libnl && s_kernel) {
nm_log_dbg (LOGD_IP6, "kernel and libnl support extended IFA_FLAGS (needed by NM for IPv6 private addresses)");
warn = 2;
return;
}
}
if ( use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
&& use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) {
if (warn == 0) {
nm_log_dbg (LOGD_IP6, "%s%s%s %s not support extended IFA_FLAGS (needed by NM for IPv6 private addresses)",
!s_kernel ? "kernel" : "",
!s_kernel && !s_libnl ? " and " : "",
!s_libnl ? "libnl" : "",
!s_kernel && !s_libnl ? "do" : "does");
warn = 1;
}
return;
}
if (!s_libnl && !s_kernel) {
nm_log_warn (LOGD_IP6, "libnl and the kernel do not support extended IFA_FLAGS needed by NM for "
"IPv6 private addresses. This feature is not available");
} else if (!s_libnl) {
nm_log_warn (LOGD_IP6, "libnl does not support extended IFA_FLAGS needed by NM for "
"IPv6 private addresses. This feature is not available");
} else if (!s_kernel) {
nm_log_warn (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for "
"IPv6 private addresses. This feature is not available");
}
warn = 2;
}
static void nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu);
static void
@@ -5005,24 +4958,20 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
int i;
static int system_support = -1;
int system_support;
guint ifa_flags = 0x00;
if (system_support == -1) {
/*
* Check, if both libnl and the kernel are recent enough,
* to help user space handling RA. If it's not supported,
* we have no ipv6-privacy and must add autoconf addresses
* as /128. The reason for the /128 is to prevent the kernel
* from adding a prefix route for this address.
**/
system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
}
/*
* Check, whether kernel is recent enough to help user space handling RA.
* If it's not supported, we have no ipv6-privacy and must add autoconf
* addresses as /128. The reason for the /128 is to prevent the kernel
* from adding a prefix route for this address.
**/
system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
if (system_support)
ifa_flags = IFA_F_NOPREFIXROUTE;
if (priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
if ( priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
|| priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
{
/* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */
@@ -5232,7 +5181,12 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
}
priv->rdisc_use_tempaddr = use_tempaddr;
print_support_extended_ifa_flags (use_tempaddr);
if ( NM_IN_SET (use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
&& !nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET)) {
_LOGW (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for "
"IPv6 private addresses. This feature is not available");
}
if (!nm_setting_ip_config_get_may_fail (nm_connection_get_setting_ip6_config (connection)))
nm_device_add_pending_action (self, PENDING_ACTION_AUTOCONF6, TRUE);

View File

@@ -137,14 +137,13 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
if (system_support == -1) {
/*
* Check, if both libnl and the kernel are recent enough,
* to help user space handling RA. If it's not supported,
* we have no ipv6-privacy and must add autoconf addresses
* as /128. The reason for the /128 is to prevent the kernel
* Check, whether kernel is recent enough, to help user space handling RA.
* If it's not supported, we have no ipv6-privacy and must add autoconf
* addresses as /128.
* The reason for the /128 is to prevent the kernel
* from adding a prefix route for this address.
**/
system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
}
if (system_support)

File diff suppressed because it is too large Load Diff

View File

@@ -212,22 +212,6 @@ nm_platform_error_to_string (NMPlatformError error)
/******************************************************************/
#define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr"
#define IFA_F_NOPREFIXROUTE_STR "noprefixroute"
gboolean
nm_platform_check_support_libnl_extended_ifa_flags ()
{
static int supported = -1;
/* support for extended ifa-flags was added together
* with the IFA_F_MANAGETEMPADDR flag. So, check if libnl
* is able to parse this flag. */
if (supported == -1)
supported = rtnl_addr_str2flags (IFA_F_MANAGETEMPADDR_STR) == IFA_F_MANAGETEMPADDR;
return supported;
}
gboolean
nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self)
{
@@ -897,22 +881,19 @@ nm_platform_link_uses_arp (NMPlatform *self, int ifindex)
gboolean
nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid)
{
const NMPlatformLink *pllink;
_CHECK_SELF (self, klass, FALSE);
g_return_val_if_fail (ifindex >= 0, FALSE);
g_return_val_if_fail (iid, FALSE);
#if HAVE_LIBNL_INET6_TOKEN
{
const NMPlatformLink *pllink;
pllink = nm_platform_link_get (self, ifindex);
if (pllink && pllink->inet6_token.is_valid) {
*iid = pllink->inet6_token.iid;
return TRUE;
}
pllink = nm_platform_link_get (self, ifindex);
if (pllink && pllink->inet6_token.is_valid) {
*iid = pllink->inet6_token.iid;
return TRUE;
}
#endif
return FALSE;
}

View File

@@ -101,6 +101,8 @@ typedef struct {
};
} NMIPAddr;
extern const NMIPAddr nm_ip_addr_zero;
#define NMIPAddrInit { .addr6 = IN6ADDR_ANY_INIT }
@@ -792,7 +794,6 @@ int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatform
int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b);
int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b);
gboolean nm_platform_check_support_libnl_extended_ifa_flags (void);
gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self);
gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self);

View File

@@ -828,14 +828,6 @@ _vt_cmd_obj_is_visible_ipx_route (const NMPObject *obj)
/******************************************************************/
struct nl_object *
nmp_object_to_nl (NMPlatform *platform, const NMPObject *obj, gboolean id_only)
{
return NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_nl (platform, &obj->object, id_only);
}
/******************************************************************/
gboolean
nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b)
{
@@ -1809,7 +1801,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_obj_dispose = _vt_cmd_obj_dispose_link,
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_link,
.cmd_obj_is_visible = _vt_cmd_obj_is_visible_link,
.cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_link,
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link,
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_link,
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_link,
@@ -1829,7 +1820,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_address,
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address,
.cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_address,
.cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip4_address,
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_address,
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_address,
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_address,
@@ -1849,7 +1839,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_address,
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address,
.cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_address,
.cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip6_address,
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address,
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_address,
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_address,
@@ -1869,7 +1858,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_route,
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route,
.cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_route,
.cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip4_route,
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route,
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_route,
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_route,
@@ -1889,7 +1877,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_route,
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route,
.cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_route,
.cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip6_route,
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route,
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_route,
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_route,

View File

@@ -131,7 +131,6 @@ typedef struct {
gboolean (*cmd_obj_is_visible) (const NMPObject *obj);
/* functions that operate on NMPlatformObject */
struct nl_object *(*cmd_plobj_to_nl) (NMPlatform *platform, const NMPlatformObject *obj, gboolean id_only);
void (*cmd_plobj_id_copy) (NMPlatformObject *dst, const NMPlatformObject *src);
gboolean (*cmd_plobj_id_equal) (const NMPlatformObject *obj1, const NMPlatformObject *obj2);
guint (*cmd_plobj_id_hash) (const NMPlatformObject *obj);
@@ -377,14 +376,4 @@ NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifi
NMPCache *nmp_cache_new (void);
void nmp_cache_free (NMPCache *cache);
struct nl_object *nmp_object_to_nl (NMPlatform *platform, const NMPObject *obj, gboolean id_only);
/* the following functions are currently implemented inside nm-linux-platform, because
* they depend on utility functions there. */
struct nl_object *_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
#endif /* __NMP_OBJECT_H__ */

View File

@@ -593,7 +593,7 @@ do_ip6_address_add (char **argv)
if (ifindex && parse_ip6_address (*argv++, &address, &plen)) {
guint32 lifetime = strtol (*argv++, NULL, 10);
guint32 preferred = strtol (*argv++, NULL, 10);
guint flags = (*argv) ? rtnl_addr_str2flags (*argv++) : 0;
guint flags = 0; /* don't support flags */
gboolean value = nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, address, plen, in6addr_any, lifetime, preferred, flags);
return value;

View File

@@ -834,10 +834,8 @@ test_vlan_set_xgress (void)
g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 7, 0));
g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 0));
/* FIXME: with libnl3 there is a bug that we cannot actually clear the ingress
* map. See http://lists.infradead.org/pipermail/libnl/2015-October/001988.html */
//g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 0));
//g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 0));
g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 0));
g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 0));
if (nmtst_is_debug ())
nmtstp_run_command_check ("ip -d link show %s", DEVICE_NAME);