diff options
author | Dan Williams <dcbw@redhat.com> | 2013-10-02 13:07:16 -0500 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2013-10-03 13:59:18 -0500 |
commit | 72ac1e38f9bc9aa972f03ed0c85d9d7039d0583f (patch) | |
tree | 49ac6d67329af3e440442fb82969f7914ebb6bf8 | |
parent | b2ff06fc12dd755002216d7e70b98c14d8b6bfed (diff) | |
download | NetworkManager-72ac1e38f9bc9aa972f03ed0c85d9d7039d0583f.tar.gz |
rdisc: mask host bits off RA prefix (rh #1008104) (bgo #709230)
Some RA implementations (like radvd) dump whatever the user configures
onto the wire, accepting a prefix of "2001:db8:1:0::1/64" without
masking the host bits off.
This causes NetworkManager to send that route down to the kernel, which
*does* mask the host bits off. This causes a mismatch between the
route NetworkManager expects the kernel to create, and what the kernel
actually created, when searching for the kernel object in the platform's
refresh_object() function:
cache = choose_cache (platform, object);
cached_object = nl_cache_search (choose_cache (platform, object), object);
kernel_object = get_kernel_object (priv->nlh, object);
kernel_object is NULL since 'object' (a route which came from the RA
prefix) is not the same as the object the kernel actually did create.
Ensure we match kernel behavior by fixing up prefixes for dumb router
advertisement services.
-rw-r--r-- | src/rdisc/nm-lndp-rdisc.c | 23 |
1 files changed, 21 insertions, 2 deletions
diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c index cc228fe0ed..3dbabbf6b8 100644 --- a/src/rdisc/nm-lndp-rdisc.c +++ b/src/rdisc/nm-lndp-rdisc.c @@ -401,6 +401,25 @@ 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); + + 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) { @@ -475,8 +494,8 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) /* Device route */ memset (&route, 0, sizeof (route)); - route.network = *ndp_msg_opt_prefix (msg, offset); route.plen = ndp_msg_opt_prefix_len (msg, offset); + set_address_masked (&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); @@ -506,8 +525,8 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) /* Routers through this particular gateway */ memset (&route, 0, sizeof (route)); route.gateway = gateway.address; - route.network = *ndp_msg_opt_route_prefix (msg, offset); route.plen = ndp_msg_opt_route_prefix_len (msg, offset); + set_address_masked (&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)); |