summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2014-02-14 21:50:13 +0100
committerThomas Haller <thaller@redhat.com>2014-02-14 21:50:13 +0100
commit83ff4f819faf90bdd19dd03a338205c91fc42f6e (patch)
treec1248c7c1e8370f7f1a04b1b165656a85bf58b23
parentaeefde7513b34bfa367d73e8ced29f13e223ee0c (diff)
parent2bc90a5f2d3dabc84a6ce3a8eb6a0e2582c1c9f2 (diff)
downloadNetworkManager-83ff4f819faf90bdd19dd03a338205c91fc42f6e.tar.gz
core: merge branch 'th/bgo719905_platform_caching'
Fix a bug in caching of nm-linux-platform and im do come cleanup. Signed-off-by: Thomas Haller <thaller@redhat.com>
-rw-r--r--src/NetworkManagerUtils.c44
-rw-r--r--src/NetworkManagerUtils.h3
-rw-r--r--src/platform/nm-fake-platform.c24
-rw-r--r--src/platform/nm-linux-platform.c283
-rw-r--r--src/platform/nm-platform.c50
-rw-r--r--src/platform/tests/test-address.c8
-rw-r--r--src/platform/tests/test-route.c8
-rw-r--r--src/rdisc/nm-lndp-rdisc.c27
-rw-r--r--src/tests/test-general.c71
9 files changed, 366 insertions, 152 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index f7f2b986f8..956a6062d6 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -75,6 +75,50 @@ nm_ethernet_address_is_valid (const struct ether_addr *test_addr)
}
+/* nm_utils_ip4_address_clear_host_address:
+ * @addr: source ip6 address
+ * @plen: prefix length of network
+ *
+ * returns: the input address, with the host address set to 0.
+ */
+in_addr_t
+nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen)
+{
+ return addr & nm_utils_ip4_prefix_to_netmask (plen);
+}
+
+ /* nm_utils_ip6_address_clear_host_address:
+ * @dst: destination output buffer, will contain the network part of the @src address
+ * @src: source ip6 address
+ * @plen: prefix length of network
+ *
+ * Note: this function is self assignment save, to update @src inplace, set both
+ * @dst and @src to the same destination.
+ */
+void
+nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen)
+{
+ g_return_if_fail (plen <= 128);
+ g_return_if_fail (src);
+ g_return_if_fail (dst);
+
+ if (plen < 128) {
+ guint nbytes = plen / 8;
+ guint nbits = plen % 8;
+
+ if (nbytes && dst != src)
+ memcpy (dst, src, nbytes);
+ if (nbits) {
+ dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits)));
+ nbytes++;
+ }
+ if (nbytes <= 15)
+ memset (&dst->s6_addr[nbytes], 0, 16 - nbytes);
+ } else if (src != dst)
+ *dst = *src;
+}
+
+
int
nm_spawn_process (const char *args)
{
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index d33ceda507..ee234dda80 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -35,6 +35,9 @@
gboolean nm_ethernet_address_is_valid (const struct ether_addr *test_addr);
+in_addr_t nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen);
+void nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen);
+
int nm_spawn_process (const char *args);
gboolean nm_match_spec_string (const GSList *specs, const char *string);
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index 43944721cd..d0127c4733 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -827,7 +827,7 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen)
}
}
- g_assert_not_reached ();
+ return TRUE;
}
static gboolean
@@ -850,7 +850,7 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int
}
}
- g_assert_not_reached ();
+ return TRUE;
}
static gboolean
@@ -1064,11 +1064,11 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen
NMPlatformIP4Route *route = ip4_route_get (platform, ifindex, network, plen, metric);
NMPlatformIP4Route deleted_route;
- g_assert (route);
-
- memcpy (&deleted_route, route, sizeof (deleted_route));
- memset (route, 0, sizeof (*route));
- g_signal_emit_by_name (platform, NM_PLATFORM_IP4_ROUTE_REMOVED, ifindex, &deleted_route, NM_PLATFORM_REASON_INTERNAL);
+ if (route) {
+ memcpy (&deleted_route, route, sizeof (deleted_route));
+ memset (route, 0, sizeof (*route));
+ g_signal_emit_by_name (platform, NM_PLATFORM_IP4_ROUTE_REMOVED, ifindex, &deleted_route, NM_PLATFORM_REASON_INTERNAL);
+ }
return TRUE;
}
@@ -1079,11 +1079,11 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, in
NMPlatformIP6Route *route = ip6_route_get (platform, ifindex, network, plen, metric);
NMPlatformIP6Route deleted_route;
- g_assert (route);
-
- memcpy (&deleted_route, route, sizeof (deleted_route));
- memset (route, 0, sizeof (*route));
- g_signal_emit_by_name (platform, NM_PLATFORM_IP6_ROUTE_REMOVED, ifindex, &deleted_route, NM_PLATFORM_REASON_INTERNAL);
+ if (route) {
+ memcpy (&deleted_route, route, sizeof (deleted_route));
+ memset (route, 0, sizeof (*route));
+ g_signal_emit_by_name (platform, NM_PLATFORM_IP6_ROUTE_REMOVED, ifindex, &deleted_route, NM_PLATFORM_REASON_INTERNAL);
+ }
return TRUE;
}
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 81630fe60b..dde01c2019 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -141,12 +141,13 @@ nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen)
#define rtnl_addr_set_prefixlen nm_rtnl_addr_set_prefixlen
typedef enum {
+ UNKNOWN_OBJECT_TYPE,
LINK,
IP4_ADDRESS,
IP6_ADDRESS,
IP4_ROUTE,
IP6_ROUTE,
- N_TYPES
+ N_TYPES,
} ObjectType;
typedef enum {
@@ -159,9 +160,10 @@ typedef enum {
static ObjectType
object_type_from_nl_object (const struct nl_object *object)
{
- const char *type_str = nl_object_get_type (object);
+ const char *type_str;
- g_assert (object);
+ if (!object || !(type_str = nl_object_get_type (object)))
+ return UNKNOWN_OBJECT_TYPE;
if (!strcmp (type_str, "route/link"))
return LINK;
@@ -172,7 +174,7 @@ object_type_from_nl_object (const struct nl_object *object)
case AF_INET6:
return IP6_ADDRESS;
default:
- g_assert_not_reached ();
+ return UNKNOWN_OBJECT_TYPE;
}
} else if (!strcmp (type_str, "route/route")) {
switch (rtnl_route_get_family ((struct rtnl_route *) object)) {
@@ -181,72 +183,121 @@ object_type_from_nl_object (const struct nl_object *object)
case AF_INET6:
return IP6_ROUTE;
default:
- g_assert_not_reached ();
+ return UNKNOWN_OBJECT_TYPE;
}
} else
- g_assert_not_reached ();
+ return UNKNOWN_OBJECT_TYPE;
}
-/* libnl inclues LINK_ATTR_FAMILY in oo_id_attrs of link_obj_ops and thus
- * refuses to search for items that lack this attribute. I believe this is a
- * bug or a bad design at the least. Address family is not an identifying
- * attribute of a network interface and IMO is not an attribute of a network
- * interface at all.
- */
+static void
+_nl_link_family_unset (struct nl_object *obj, int *family)
+{
+ if (!obj || object_type_from_nl_object (obj) != LINK)
+ *family = AF_UNSPEC;
+ else {
+ *family = rtnl_link_get_family ((struct rtnl_link *) obj);
+
+ /* Always explicitly set the family to AF_UNSPEC, even if rtnl_link_get_family() might
+ * already return %AF_UNSPEC. The reason is, that %AF_UNSPEC is the default family
+ * and libnl nl_object_identical() function will only succeed, if the family is
+ * explicitly set (which we cannot be sure, unless setting it). */
+ rtnl_link_set_family ((struct rtnl_link *) obj, AF_UNSPEC);
+ }
+}
+
+/* In our link cache, we coerce the family of all link objects to AF_UNSPEC.
+ * Thus, before searching for an object, we fixup @needle to have the right
+ * id (by resetting the family). */
static struct nl_object *
nm_nl_cache_search (struct nl_cache *cache, struct nl_object *needle)
{
- if (object_type_from_nl_object (needle) == LINK)
- rtnl_link_set_family ((struct rtnl_link *) needle, AF_UNSPEC);
+ int family;
+ struct nl_object *obj;
+
+ _nl_link_family_unset (needle, &family);
+ obj = nl_cache_search (cache, needle);
+ if (family != AF_UNSPEC) {
+ /* restore the family of the @needle instance. If the family was
+ * unset before, we cannot make it unset again. Thus, in that case
+ * we cannot undo _nl_link_family_unset() entirely. */
+ rtnl_link_set_family ((struct rtnl_link *) needle, family);
+ }
- return nl_cache_search (cache, needle);
+ return obj;
}
-#define nl_cache_search nm_nl_cache_search
/* Ask the kernel for an object identical (as in nl_cache_identical) to the
* needle argument. This is a kernel counterpart for nl_cache_search.
*
- * libnl 3.2 doesn't seem to provide such functionality.
+ * The returned object must be freed by the caller with nl_object_put().
*/
static struct nl_object *
get_kernel_object (struct nl_sock *sock, struct nl_object *needle)
{
+ struct nl_object *object = NULL;
+ ObjectType type = object_type_from_nl_object (needle);
- switch (object_type_from_nl_object (needle)) {
+ switch (type) {
case LINK:
{
- struct nl_object *kernel_object;
int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) needle);
const char *name = rtnl_link_get_name ((struct rtnl_link *) needle);
int nle;
- nle = rtnl_link_get_kernel (sock, ifindex, name, (struct rtnl_link **) &kernel_object);
+ nle = rtnl_link_get_kernel (sock, ifindex, name, (struct rtnl_link **) &object);
switch (nle) {
case -NLE_SUCCESS:
- return kernel_object;
+ if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+ name = rtnl_link_get_name ((struct rtnl_link *) object);
+ debug ("get_kernel_object for link: %s (%d, family %d)",
+ name ? name : "(unknown)",
+ rtnl_link_get_ifindex ((struct rtnl_link *) object),
+ rtnl_link_get_family ((struct rtnl_link *) object));
+ }
+
+ _nl_link_family_unset (object, &nle);
+ return object;
case -NLE_NODEV:
+ debug ("get_kernel_object for link %s (%d) had no result",
+ name ? name : "(unknown)", ifindex);
return NULL;
default:
- error ("Netlink error: %s", nl_geterror (nle));
+ error ("get_kernel_object for link %s (%d) failed: %s (%d)",
+ name ? name : "(unknown)", ifindex, nl_geterror (nle), nle);
return NULL;
}
}
- default:
+ case IP4_ADDRESS:
+ case IP6_ADDRESS:
+ case IP4_ROUTE:
+ case IP6_ROUTE:
/* Fallback to a one-time cache allocation. */
{
struct nl_cache *cache;
- struct nl_object *object;
int nle;
nle = nl_cache_alloc_and_fill (
nl_cache_ops_lookup (nl_object_get_type (needle)),
sock, &cache);
- g_return_val_if_fail (!nle, NULL);
+ if (nle) {
+ error ("get_kernel_object for type %d failed: %s (%d)",
+ type, nl_geterror (nle), nle);
+ return NULL;
+ }
+
object = nl_cache_search (cache, needle);
nl_cache_free (cache);
+
+ if (object)
+ debug ("get_kernel_object for type %d returned %p", type, object);
+ else
+ debug ("get_kernel_object for type %d had no result", type);
return object;
}
+ default:
+ g_return_val_if_reached (NULL);
+ return NULL;
}
}
@@ -264,25 +315,8 @@ add_kernel_object (struct nl_sock *sock, struct nl_object *object)
case IP6_ROUTE:
return rtnl_route_add (sock, (struct rtnl_route *) object, NLM_F_CREATE | NLM_F_REPLACE);
default:
- g_assert_not_reached ();
- }
-}
-
-/* libnl 3.2 doesn't seem to provide such a generic way to delete libnl-route objects. */
-static int
-delete_kernel_object (struct nl_sock *sock, struct nl_object *object)
-{
- switch (object_type_from_nl_object (object)) {
- case LINK:
- return rtnl_link_delete (sock, (struct rtnl_link *) object);
- case IP4_ADDRESS:
- case IP6_ADDRESS:
- return rtnl_addr_delete (sock, (struct rtnl_addr *) object, 0);
- case IP4_ROUTE:
- case IP6_ROUTE:
- return rtnl_route_delete (sock, (struct rtnl_route *) object, 0);
- default:
- g_assert_not_reached ();
+ g_return_val_if_reached (-NLE_INVAL);
+ return -NLE_INVAL;
}
}
@@ -931,19 +965,19 @@ init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute)
/* Object and cache manipulation */
static const char *signal_by_type_and_status[N_TYPES][N_STATUSES] = {
- { NM_PLATFORM_LINK_ADDED, NM_PLATFORM_LINK_CHANGED, NM_PLATFORM_LINK_REMOVED },
- { NM_PLATFORM_IP4_ADDRESS_ADDED, NM_PLATFORM_IP4_ADDRESS_CHANGED, NM_PLATFORM_IP4_ADDRESS_REMOVED },
- { NM_PLATFORM_IP6_ADDRESS_ADDED, NM_PLATFORM_IP6_ADDRESS_CHANGED, NM_PLATFORM_IP6_ADDRESS_REMOVED },
- { NM_PLATFORM_IP4_ROUTE_ADDED, NM_PLATFORM_IP4_ROUTE_CHANGED, NM_PLATFORM_IP4_ROUTE_REMOVED },
- { NM_PLATFORM_IP6_ROUTE_ADDED, NM_PLATFORM_IP6_ROUTE_CHANGED, NM_PLATFORM_IP6_ROUTE_REMOVED }
+ [LINK] = { NM_PLATFORM_LINK_ADDED, NM_PLATFORM_LINK_CHANGED, NM_PLATFORM_LINK_REMOVED },
+ [IP4_ADDRESS] = { NM_PLATFORM_IP4_ADDRESS_ADDED, NM_PLATFORM_IP4_ADDRESS_CHANGED, NM_PLATFORM_IP4_ADDRESS_REMOVED },
+ [IP6_ADDRESS] = { NM_PLATFORM_IP6_ADDRESS_ADDED, NM_PLATFORM_IP6_ADDRESS_CHANGED, NM_PLATFORM_IP6_ADDRESS_REMOVED },
+ [IP4_ROUTE] = { NM_PLATFORM_IP4_ROUTE_ADDED, NM_PLATFORM_IP4_ROUTE_CHANGED, NM_PLATFORM_IP4_ROUTE_REMOVED },
+ [IP6_ROUTE] = { NM_PLATFORM_IP6_ROUTE_ADDED, NM_PLATFORM_IP6_ROUTE_CHANGED, NM_PLATFORM_IP6_ROUTE_REMOVED }
};
static struct nl_cache *
-choose_cache (NMPlatform *platform, struct nl_object *object)
+choose_cache_by_type (NMPlatform *platform, ObjectType object_type)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- switch (object_type_from_nl_object (object)) {
+ switch (object_type) {
case LINK:
return priv->link_cache;
case IP4_ADDRESS:
@@ -953,10 +987,17 @@ choose_cache (NMPlatform *platform, struct nl_object *object)
case IP6_ROUTE:
return priv->route_cache;
default:
- g_assert_not_reached ();
+ g_return_val_if_reached (NULL);
+ return NULL;
}
}
+static struct nl_cache *
+choose_cache (NMPlatform *platform, struct nl_object *object)
+{
+ return choose_cache_by_type (platform, object_type_from_nl_object (object));
+}
+
static gboolean
object_has_ifindex (struct nl_object *object, int ifindex)
{
@@ -1095,7 +1136,7 @@ announce_object (NMPlatform *platform, const struct nl_object *object, ObjectSta
}
return;
default:
- error ("Announcing object: object type unknown: %d", object_type);
+ g_return_if_reached ();
}
}
@@ -1111,7 +1152,7 @@ refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed
int nle;
cache = choose_cache (platform, object);
- cached_object = nl_cache_search (choose_cache (platform, object), object);
+ cached_object = nm_nl_cache_search (cache, object);
kernel_object = get_kernel_object (priv->nlh, object);
if (removed) {
@@ -1174,6 +1215,8 @@ add_object (NMPlatform *platform, struct nl_object *obj)
.dp_fd = stderr,
};
+ g_return_val_if_fail (object, FALSE);
+
nle = add_kernel_object (priv->nlh, object);
/* NLE_EXIST is considered equivalent to success to avoid race conditions. You
@@ -1198,26 +1241,48 @@ static gboolean
delete_object (NMPlatform *platform, struct nl_object *obj)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- auto_nl_object struct nl_object *object = obj;
+ auto_nl_object struct nl_object *obj_cleanup = obj;
auto_nl_object struct nl_object *cached_object = NULL;
+ struct nl_object *object;
+ int object_type;
int nle;
- /* FIXME: For some reason the result of build_rtnl_route() is not suitable
- * for delete_kernel_object() and we need to search the cache first. If
- * that problem is fixed, we can use 'object' directly.
- */
- cached_object = nl_cache_search (choose_cache (platform, object), object);
- g_return_val_if_fail (cached_object, FALSE);
+ object_type = object_type_from_nl_object (obj);
+ g_return_val_if_fail (object_type != UNKNOWN_OBJECT_TYPE, FALSE);
- nle = delete_kernel_object (priv->nlh, cached_object);
+ cached_object = nm_nl_cache_search (choose_cache_by_type (platform, object_type), obj);
+ object = cached_object ? cached_object : obj;
+
+ switch (object_type) {
+ case LINK:
+ nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object);
+ break;
+ case IP4_ADDRESS:
+ case IP6_ADDRESS:
+ nle = rtnl_addr_delete (priv->nlh, (struct rtnl_addr *) object, 0);
+ break;
+ case IP4_ROUTE:
+ case IP6_ROUTE:
+ nle = rtnl_route_delete (priv->nlh, (struct rtnl_route *) object, 0);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
- /* NLE_OBJ_NOTFOUND is considered equivalent to success to avoid race conditions. You
- * never know when something deletes the same object just before NetworkManager.
- */
switch (nle) {
case -NLE_SUCCESS:
+ break;
case -NLE_OBJ_NOTFOUND:
+ debug("delete_object failed with \"%s\" (%d), meaning the object was already removed",
+ nl_geterror (nle), nle);
break;
+ case -NLE_NOADDR:
+ if (object_type == IP4_ADDRESS || object_type == IP6_ADDRESS) {
+ debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed",
+ nl_geterror (nle), nle);
+ break;
+ }
+ /* fall-through to error, because we only expect this for addresses. */
default:
error ("Netlink error: %s", nl_geterror (nle));
return FALSE;
@@ -1267,19 +1332,22 @@ event_notification (struct nl_msg *msg, gpointer user_data)
nl_msg_parse (msg, ref_object, &object);
g_return_val_if_fail (object, NL_OK);
+ if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+ if (object_type_from_nl_object (object) == LINK) {
+ const char *name = rtnl_link_get_name ((struct rtnl_link *) object);
+
+ debug ("netlink event (type %d) for link: %s (%d, family %d)",
+ event, name ? name : "(unknown)",
+ rtnl_link_get_ifindex ((struct rtnl_link *) object),
+ rtnl_link_get_family ((struct rtnl_link *) object));
+ } else
+ debug ("netlink event (type %d)", event);
+ }
+
cache = choose_cache (platform, object);
- cached_object = nl_cache_search (cache, object);
+ cached_object = nm_nl_cache_search (cache, object);
kernel_object = get_kernel_object (priv->nlh, object);
- /* Just for debugging */
- if (object_type_from_nl_object (object) == LINK) {
- int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) object);
- const char *name = rtnl_link_get_name ((struct rtnl_link *) object);
- debug ("netlink event (type %d) for link: %s (%d)",
- event, name ? name : "(unknown)", ifindex);
- } else
- debug ("netlink event (type %d)", event);
-
hack_empty_master_iff_lower_up (platform, kernel_object);
/* Removed object */
@@ -1304,7 +1372,7 @@ event_notification (struct nl_msg *msg, gpointer user_data)
* already removed and announced.
*/
if (event == RTM_DELLINK) {
- if (!link_is_announceable (platform, (struct rtnl_link *) object))
+ if (!link_is_announceable (platform, (struct rtnl_link *) cached_object))
return NL_OK;
}
announce_object (platform, cached_object, REMOVED, NM_PLATFORM_REASON_EXTERNAL);
@@ -1487,7 +1555,7 @@ static struct rtnl_link *
link_get (NMPlatform *platform, int ifindex)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- auto_nl_object struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex);
+ struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex);
if (!rtnllink) {
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
@@ -1497,10 +1565,10 @@ link_get (NMPlatform *platform, int ifindex)
/* physical interfaces must be found by udev before they can be used */
if (!link_is_announceable (platform, rtnllink)) {
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
+ rtnl_link_put (rtnllink);
return NULL;
}
- nl_object_get ((struct nl_object *) rtnllink);
return rtnllink;
}
@@ -1951,7 +2019,8 @@ master_category (NMPlatform *platform, int master)
case NM_LINK_TYPE_BOND:
return "bonding";
default:
- g_assert_not_reached ();
+ g_return_val_if_reached (NULL);
+ return NULL;
}
}
@@ -1969,7 +2038,8 @@ slave_category (NMPlatform *platform, int slave)
case NM_LINK_TYPE_BRIDGE:
return "brport";
default:
- g_assert_not_reached ();
+ g_return_val_if_reached (NULL);
+ return NULL;
}
}
@@ -2517,16 +2587,42 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, gboolean include_default)
return routes;
}
+static void
+clear_host_address (int family, const void *network, int plen, void *dst)
+{
+ g_return_if_fail (plen == (guint8)plen);
+ g_return_if_fail (network);
+
+ switch (family) {
+ case AF_INET:
+ *((in_addr_t *) dst) = nm_utils_ip4_address_clear_host_address (*((in_addr_t *) network), plen);
+ break;
+ case AF_INET6:
+ nm_utils_ip6_address_clear_host_address ((struct in6_addr *) dst, (const struct in6_addr *) network, plen);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
static struct nl_object *
build_rtnl_route (int family, int ifindex, gconstpointer network, int plen, gconstpointer gateway, int metric, int mss)
{
+ guint32 network_clean[4];
struct rtnl_route *rtnlroute = rtnl_route_alloc ();
struct rtnl_nexthop *nexthop = rtnl_route_nh_alloc ();
int addrlen = (family == AF_INET) ? sizeof (in_addr_t) : sizeof (struct in6_addr);
/* Workaround a libnl bug by using zero destination address length for default routes */
- auto_nl_addr struct nl_addr *dst = nl_addr_build (family, network, plen ? addrlen : 0);
+ auto_nl_addr struct nl_addr *dst = NULL;
auto_nl_addr struct nl_addr *gw = gateway ? nl_addr_build (family, gateway, addrlen) : NULL;
+ /* There seem to be problems adding a route with non-zero host identifier.
+ * Adding IPv6 routes is simply ignored, without error message.
+ * In the IPv4 case, we got an error. Thus, we have to make sure, that
+ * the address is sane. */
+ clear_host_address (family, network, plen, network_clean);
+ dst = nl_addr_build (family, network_clean, plen ? addrlen : 0);
+
g_assert (rtnlroute && dst && nexthop);
nl_addr_set_prefixlen (dst, plen);
@@ -2535,6 +2631,7 @@ build_rtnl_route (int family, int ifindex, gconstpointer network, int plen, gcon
rtnl_route_set_tos (rtnlroute, 0);
rtnl_route_set_dst (rtnlroute, dst);
rtnl_route_set_priority (rtnlroute, metric);
+ rtnl_route_set_family (rtnlroute, family);
rtnl_route_nh_set_ifindex (nexthop, ifindex);
if (gw && !nl_addr_iszero (gw))
@@ -2563,14 +2660,34 @@ static gboolean
ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, int metric)
{
in_addr_t gateway = 0;
+ struct nl_object *route = build_rtnl_route (AF_INET, ifindex, &network, plen, &gateway, metric, 0);
+
+ g_return_val_if_fail (route, FALSE);
+
+ /* When searching for a matching IPv4 route to delete, the kernel
+ * searches for a matching scope, unless the RTM_DELROUTE message
+ * specifies RT_SCOPE_NOWHERE (see fib_table_delete()).
+ *
+ * However, if we set the scope of @rtnlroute to RT_SCOPE_NOWHERE (or
+ * leave it unset), rtnl_route_build_msg() will reset the scope to
+ * rtnl_route_guess_scope() -- which might be the wrong scope.
+ *
+ * As a workaround, we set the scope to RT_SCOPE_UNIVERSE, so libnl
+ * will not overwrite it. But this only works if we guess correctly.
+ *
+ * As a better workaround, we don't use @rtnlroute as argument for
+ * rtnl_route_delete(), but we look into our cache, if we already have
+ * this route ready.
+ **/
+ rtnl_route_set_scope ((struct rtnl_route *) route, RT_SCOPE_UNIVERSE);
- return delete_object (platform, build_rtnl_route (AF_INET, ifindex, &network, plen, &gateway, metric, 0));
+ return delete_object (platform, route);
}
static gboolean
ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, int metric)
{
- struct in6_addr gateway = in6addr_any;
+ struct in6_addr gateway = IN6ADDR_ANY_INIT;
return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, &network, plen, &gateway, metric, 0));
}
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index bc10d7c49b..8485e74ebe 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -1281,12 +1281,6 @@ nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen)
g_return_val_if_fail (plen > 0, FALSE);
g_return_val_if_fail (klass->ip4_address_delete, FALSE);
- if (!nm_platform_ip4_address_exists (ifindex, address, plen)) {
- debug ("address doesn't exists");
- platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
- return FALSE;
- }
-
debug ("address: deleting IPv4 address %s/%d", nm_utils_inet4_ntop (address, NULL), plen);
return klass->ip4_address_delete (platform, ifindex, address, plen);
}
@@ -1300,12 +1294,6 @@ nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen)
g_return_val_if_fail (plen > 0, FALSE);
g_return_val_if_fail (klass->ip6_address_delete, FALSE);
- if (!nm_platform_ip6_address_exists (ifindex, address, plen)) {
- debug ("address doesn't exists");
- platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
- return FALSE;
- }
-
debug ("address: deleting IPv6 address %s/%d", nm_utils_inet6_ntop (&address, NULL), plen);
return klass->ip6_address_delete (platform, ifindex, address, plen);
}
@@ -1530,6 +1518,18 @@ nm_platform_ip4_route_add (int ifindex,
g_return_val_if_fail (mss >= 0, FALSE);
g_return_val_if_fail (klass->ip4_route_add, FALSE);
+ if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+ NMPlatformIP4Route route = { 0 };
+
+ route.ifindex = ifindex;
+ route.network = network;
+ route.plen = plen;
+ route.gateway = gateway;
+ route.metric = metric;
+ route.mss = mss;
+
+ debug ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route));
+ }
return klass->ip4_route_add (platform, ifindex, network, plen, gateway, metric, mss);
}
@@ -1543,6 +1543,18 @@ nm_platform_ip6_route_add (int ifindex,
g_return_val_if_fail (mss >= 0, FALSE);
g_return_val_if_fail (klass->ip6_route_add, FALSE);
+ if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+ NMPlatformIP6Route route = { 0 };
+
+ route.ifindex = ifindex;
+ route.network = network;
+ route.plen = plen;
+ route.gateway = gateway;
+ route.metric = metric;
+ route.mss = mss;
+
+ debug ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route));
+ }
return klass->ip6_route_add (platform, ifindex, network, plen, gateway, metric, mss);
}
@@ -1554,12 +1566,7 @@ nm_platform_ip4_route_delete (int ifindex, in_addr_t network, int plen, int metr
g_return_val_if_fail (platform, FALSE);
g_return_val_if_fail (klass->ip4_route_delete, FALSE);
- if (!nm_platform_ip4_route_exists (ifindex, network, plen, metric)) {
- debug ("route not found");
- platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
- return FALSE;
- }
-
+ debug ("route: deleting IPv4 route %s/%d, metric=%d", nm_utils_inet4_ntop (network, NULL), plen, metric);
return klass->ip4_route_delete (platform, ifindex, network, plen, metric);
}
@@ -1572,12 +1579,7 @@ nm_platform_ip6_route_delete (int ifindex,
g_return_val_if_fail (platform, FALSE);
g_return_val_if_fail (klass->ip6_route_delete, FALSE);
- if (!nm_platform_ip6_route_exists (ifindex, network, plen, metric)) {
- debug ("route not found");
- platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
- return FALSE;
- }
-
+ debug ("route: deleting IPv6 route %s/%d, metric=%d", nm_utils_inet6_ntop (&network, NULL), plen, metric);
return klass->ip6_route_delete (platform, ifindex, network, plen, metric);
}
diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c
index 0f0ccc81eb..20f1bd9ed6 100644
--- a/src/platform/tests/test-address.c
+++ b/src/platform/tests/test-address.c
@@ -89,8 +89,8 @@ test_ip4_address (void)
accept_signal (address_removed);
/* Remove address again */
- g_assert (!nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN));
- error (NM_PLATFORM_ERROR_NOT_FOUND);
+ g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN));
+ no_error ();
free_signal (address_added);
free_signal (address_changed);
@@ -145,8 +145,8 @@ test_ip6_address (void)
accept_signal (address_removed);
/* Remove address again */
- g_assert (!nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN));
- error (NM_PLATFORM_ERROR_NOT_FOUND);
+ g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN));
+ no_error ();
free_signal (address_added);
free_signal (address_changed);
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index 6c764ad3c3..f333ffc3c1 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -113,8 +113,8 @@ test_ip4_route ()
accept_signal (route_removed);
/* Remove route again */
- g_assert (!nm_platform_ip4_route_delete (ifindex, network, plen, metric));
- error (NM_PLATFORM_ERROR_NOT_FOUND);
+ g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric));
+ no_error ();
free_signal (route_added);
free_signal (route_changed);
@@ -196,8 +196,8 @@ test_ip6_route ()
accept_signal (route_removed);
/* Remove route again */
- g_assert (!nm_platform_ip6_route_delete (ifindex, network, plen, metric));
- error (NM_PLATFORM_ERROR_NOT_FOUND);
+ g_assert (nm_platform_ip6_route_delete (ifindex, network, plen, metric));
+ no_error ();
free_signal (route_added);
free_signal (route_changed);
diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c
index 5fb28778fc..3b0b0365b4 100644
--- a/src/rdisc/nm-lndp-rdisc.c
+++ b/src/rdisc/nm-lndp-rdisc.c
@@ -428,29 +428,6 @@ fill_address_from_mac (struct in6_addr *address, const char *mac)
memcpy (identifier + 5, mac + 3, 3);
}
-/* Ensure the given address is masked with its prefix and that all host
- * bits are set to zero. Some IPv6 router advertisement daemons (eg, radvd)
- * don't enforce this in their configuration.
- */
-static void
-set_address_masked (struct in6_addr *dst, struct in6_addr *src, guint8 plen)
-{
- guint nbytes = plen / 8;
- guint nbits = plen % 8;
-
- g_return_if_fail (plen <= 128);
- g_assert (src);
- g_assert (dst);
-
- if (plen >= 128)
- *dst = *src;
- else {
- memset (dst, 0, sizeof (*dst));
- memcpy (dst, src, nbytes);
- dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits)));
- }
-}
-
static int
receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
{
@@ -529,7 +506,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
/* Device route */
memset (&route, 0, sizeof (route));
route.plen = ndp_msg_opt_prefix_len (msg, offset);
- set_address_masked (&route.network, ndp_msg_opt_prefix (msg, offset), route.plen);
+ nm_utils_ip6_address_clear_host_address (&route.network, ndp_msg_opt_prefix (msg, offset), route.plen);
route.timestamp = now;
if (ndp_msg_opt_prefix_flag_on_link (msg, offset)) {
route.lifetime = ndp_msg_opt_prefix_valid_time (msg, offset);
@@ -560,7 +537,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
memset (&route, 0, sizeof (route));
route.gateway = gateway.address;
route.plen = ndp_msg_opt_route_prefix_len (msg, offset);
- set_address_masked (&route.network, ndp_msg_opt_route_prefix (msg, offset), route.plen);
+ nm_utils_ip6_address_clear_host_address (&route.network, ndp_msg_opt_route_prefix (msg, offset), route.plen);
route.timestamp = now;
route.lifetime = ndp_msg_opt_route_lifetime (msg, offset);
route.preference = translate_preference (ndp_msg_opt_route_preference (msg, offset));
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index 649653c75e..3fd7115cb0 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -60,6 +60,76 @@ test_nm_utils_ascii_str_to_int64 (void)
test_nm_utils_ascii_str_to_int64_do ("\r\n\t10000\t\n\t\n", 10, 0, 10000, -1, 0, 10000);
}
+/* Reference implementation for nm_utils_ip6_address_clear_host_address.
+ * Taken originally from set_address_masked(), src/rdisc/nm-lndp-rdisc.c
+ **/
+static void
+ip6_address_clear_host_address_reference (struct in6_addr *dst, struct in6_addr *src, guint8 plen)
+{
+ guint nbytes = plen / 8;
+ guint nbits = plen % 8;
+
+ g_return_if_fail (plen <= 128);
+ g_assert (src);
+ g_assert (dst);
+
+ if (plen >= 128)
+ *dst = *src;
+ else {
+ memset (dst, 0, sizeof (*dst));
+ memcpy (dst, src, nbytes);
+ dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits)));
+ }
+}
+
+static void
+_randomize_in6_addr (struct in6_addr *addr, GRand *rand)
+{
+ int i;
+
+ for (i=0; i < 4; i++)
+ ((guint32 *)addr)[i] = g_rand_int (rand);
+}
+
+static void
+test_nm_utils_ip6_address_clear_host_address (void)
+{
+ GRand *rand = g_rand_new ();
+ int plen, i;
+
+ g_rand_set_seed (rand, 0);
+
+ for (plen = 0; plen <= 128; plen++) {
+ for (i =0; i<50; i++) {
+ struct in6_addr addr_src, addr_ref;
+ struct in6_addr addr1, addr2;
+
+ _randomize_in6_addr (&addr_src, rand);
+ _randomize_in6_addr (&addr_ref, rand);
+ _randomize_in6_addr (&addr1, rand);
+ _randomize_in6_addr (&addr2, rand);
+
+ addr1 = addr_src;
+ ip6_address_clear_host_address_reference (&addr_ref, &addr1, plen);
+
+ _randomize_in6_addr (&addr1, rand);
+ _randomize_in6_addr (&addr2, rand);
+ addr1 = addr_src;
+ nm_utils_ip6_address_clear_host_address (&addr2, &addr1, plen);
+ g_assert_cmpint (memcmp (&addr1, &addr_src, sizeof (struct in6_addr)), ==, 0);
+ g_assert_cmpint (memcmp (&addr2, &addr_ref, sizeof (struct in6_addr)), ==, 0);
+
+ /* test for self assignment/inplace update. */
+ _randomize_in6_addr (&addr1, rand);
+ addr1 = addr_src;
+ nm_utils_ip6_address_clear_host_address (&addr1, &addr1, plen);
+ g_assert_cmpint (memcmp (&addr1, &addr_ref, sizeof (struct in6_addr)), ==, 0);
+ }
+ }
+
+ g_rand_free (rand);
+}
+
/*******************************************/
int
@@ -70,6 +140,7 @@ main (int argc, char **argv)
g_type_init ();
g_test_add_func ("/general/nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
+ g_test_add_func ("/general/nm_utils_ip6_address_clear_host_address", test_nm_utils_ip6_address_clear_host_address);
return g_test_run ();
}