diff options
author | Thomas Haller <thaller@redhat.com> | 2014-02-14 21:50:13 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2014-02-14 21:50:13 +0100 |
commit | 83ff4f819faf90bdd19dd03a338205c91fc42f6e (patch) | |
tree | c1248c7c1e8370f7f1a04b1b165656a85bf58b23 | |
parent | aeefde7513b34bfa367d73e8ced29f13e223ee0c (diff) | |
parent | 2bc90a5f2d3dabc84a6ce3a8eb6a0e2582c1c9f2 (diff) | |
download | NetworkManager-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.c | 44 | ||||
-rw-r--r-- | src/NetworkManagerUtils.h | 3 | ||||
-rw-r--r-- | src/platform/nm-fake-platform.c | 24 | ||||
-rw-r--r-- | src/platform/nm-linux-platform.c | 283 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 50 | ||||
-rw-r--r-- | src/platform/tests/test-address.c | 8 | ||||
-rw-r--r-- | src/platform/tests/test-route.c | 8 | ||||
-rw-r--r-- | src/rdisc/nm-lndp-rdisc.c | 27 | ||||
-rw-r--r-- | src/tests/test-general.c | 71 |
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 (); } |