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>
This commit is contained in:
Thomas Haller 2014-02-14 21:50:13 +01:00
commit 83ff4f819f
9 changed files with 368 additions and 154 deletions

View file

@ -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)
{

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
return nl_cache_search (cache, needle);
_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 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:
case -NLE_OBJ_NOTFOUND:
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,18 +1332,21 @@ event_notification (struct nl_msg *msg, gpointer user_data)
nl_msg_parse (msg, ref_object, &object);
g_return_val_if_fail (object, NL_OK);
cache = choose_cache (platform, object);
cached_object = nl_cache_search (cache, object);
kernel_object = get_kernel_object (priv->nlh, object);
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);
/* 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);
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 = nm_nl_cache_search (cache, object);
kernel_object = get_kernel_object (priv->nlh, object);
hack_empty_master_iff_lower_up (platform, kernel_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);
return delete_object (platform, 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, 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));
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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));

View file

@ -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 ();
}