From d4b67b9441a61f03e8796972f2e9741be77799bb Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 11 Oct 2018 13:40:58 +0200 Subject: [PATCH] ndisc: only generate address interface identifer after checking existing prefix RFC4862 5.5.3, points d) and e) make it clear, that the list of addresses should be compared based on the prefix. d) If the prefix advertised is not equal to the prefix of an address configured by stateless autoconfiguration already in the list of addresses associated with the interface (where "equal" means the two prefix lengths are the same and the first prefix- length bits of the prefixes are identical), and if the Valid Lifetime is not 0, form an address (and add it to the list) by combining the advertised prefix with an interface identifier of the link as follows: That means, we should not initialize the interface identifier first (via complete_address()) and then search for the full address. See-also: https://tools.ietf.org/search/rfc4862#section-5.5.3 (cherry picked from commit 23c417854a943eb796a8c06e7d94b02d18f19691) (cherry picked from commit ac5669633c44f6da3ec417282d59d71f229c2b7a) (cherry picked from commit b2f084a8ae927d058818d7126e4536d3b36a23bd) --- src/ndisc/nm-ndisc-private.h | 2 +- src/ndisc/nm-ndisc.c | 63 ++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/ndisc/nm-ndisc-private.h b/src/ndisc/nm-ndisc-private.h index bbecb01a43..cdf8dc282a 100644 --- a/src/ndisc/nm-ndisc-private.h +++ b/src/ndisc/nm-ndisc-private.h @@ -40,7 +40,7 @@ void nm_ndisc_ra_received (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed) void nm_ndisc_rs_received (NMNDisc *ndisc); gboolean nm_ndisc_add_gateway (NMNDisc *ndisc, const NMNDiscGateway *new); -gboolean nm_ndisc_complete_and_add_address (NMNDisc *ndisc, NMNDiscAddress *new); +gboolean nm_ndisc_complete_and_add_address (NMNDisc *ndisc, const NMNDiscAddress *new); gboolean nm_ndisc_add_route (NMNDisc *ndisc, const NMNDiscRoute *new); gboolean nm_ndisc_add_dns_server (NMNDisc *ndisc, const NMNDiscDNSServer *new); gboolean nm_ndisc_add_dns_domain (NMNDisc *ndisc, const NMNDiscDNSDomain *new); diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c index 87e5e0733a..6b580ccc98 100644 --- a/src/ndisc/nm-ndisc.c +++ b/src/ndisc/nm-ndisc.c @@ -406,10 +406,11 @@ complete_address (NMNDisc *ndisc, NMNDiscAddress *addr) } static gboolean -nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new) +nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new, gboolean from_ra) { NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc); NMNDiscDataInternal *rdata = &priv->rdata; + NMNDiscAddress new2; guint i; nm_assert (new); @@ -421,20 +422,33 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new) for (i = 0; i < rdata->addresses->len; i++) { NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); - if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { - if (new->lifetime == 0) { - g_array_remove_index (rdata->addresses, i); - return TRUE; - } + if (from_ra) { + /* RFC4862 5.5.3.d, we find an existing address with the same prefix. + * (note that all prefixes at this point have implicity length /64). */ + if (memcmp (&item->address, &new->address, 8) != 0) + continue; + } else { + if (!IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) + continue; + } - if ( item->dad_counter == new->dad_counter - && get_expiry (item) == get_expiry (new) - && get_expiry_preferred (item) == get_expiry_preferred (new)) - return FALSE; - - *item = *new; + if (new->lifetime == 0) { + g_array_remove_index (rdata->addresses, i); return TRUE; } + + if ( get_expiry (item) == get_expiry (new) + && get_expiry_preferred (item) == get_expiry_preferred (new) + && ( from_ra + || item->dad_counter == new->dad_counter)) + return FALSE; + + if (!from_ra) + item->dad_counter = new->dad_counter; + item->timestamp = new->timestamp; + item->lifetime = new->lifetime; + item->preferred = new->preferred; + return TRUE; } /* we create at most max_addresses autoconf addresses. This is different from @@ -445,18 +459,25 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new) && rdata->addresses->len >= priv->max_addresses) return FALSE; - if (new->lifetime) - g_array_append_val (rdata->addresses, *new); - return !!new->lifetime; + if (new->lifetime == 0) + return FALSE; + + if (from_ra) { + new2 = *new; + new2.dad_counter = 0; + if (!complete_address (ndisc, &new2)) + return FALSE; + new = &new2; + } + + g_array_append_val (rdata->addresses, *new); + return TRUE; } gboolean -nm_ndisc_complete_and_add_address (NMNDisc *ndisc, NMNDiscAddress *new) +nm_ndisc_complete_and_add_address (NMNDisc *ndisc, const NMNDiscAddress *new) { - if (!complete_address (ndisc, new)) - return FALSE; - - return nm_ndisc_add_address (ndisc, new); + return nm_ndisc_add_address (ndisc, new, TRUE); } gboolean @@ -754,7 +775,7 @@ nm_ndisc_set_config (NMNDisc *ndisc, guint i; for (i = 0; i < addresses->len; i++) { - if (nm_ndisc_add_address (ndisc, &g_array_index (addresses, NMNDiscAddress, i))) + if (nm_ndisc_add_address (ndisc, &g_array_index (addresses, NMNDiscAddress, i), FALSE)) changed = TRUE; }