Page MenuHomeFreeBSD

D55015.diff
No OneTemporary

D55015.diff

diff --git a/share/man/man4/inet6.4 b/share/man/man4/inet6.4
--- a/share/man/man4/inet6.4
+++ b/share/man/man4/inet6.4
@@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd February 22, 2023
+.Dd January 31, 2026
.Dt INET6 4
.Os
.Sh NAME
@@ -295,6 +295,17 @@
The packets will be generated when
.Tn IPv6
interface addresses are configured.
+.It Dv IPV6CTL_GRAND_COUNT
+.Pq ip6.grand_count
+Integer: default number of
+.Tn IPv6
+GRAND
+.Pq gratuitous neighbor discovery
+unsolicited NA packets.
+The packets will be generated when
+.Tn IPv6
+interface addresses are configured or when there are changes to
+link-layer interface addresses.
.It Dv IPV6CTL_AUTO_FLOWLABEL
.Pq ip6.auto_flowlabel
Boolean: enable/disable automatic filling of
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -643,7 +643,8 @@
* queue */
#define IPV6CTL_MAXFRAGSPERPACKET 53 /* Max fragments per packet */
#define IPV6CTL_MAXFRAGBUCKETSIZE 54 /* Max reassembly queues per bucket */
-#define IPV6CTL_MAXID 55
+#define IPV6CTL_GRAND_COUNT 55
+#define IPV6CTL_MAXID 56
#endif /* __BSD_VISIBLE */
/*
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1432,6 +1432,9 @@
ia->ia_flags &= ~IFA_RTSELF;
}
+ /* make sure there are no queued ND6 */
+ nd6_queue_stop(ifa);
+
/* stop DAD processing */
nd6_dad_stop(ifa);
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -156,6 +156,7 @@
VNET_DEFINE(int, ip6_hdrnestlimit) = 15;/* How many header options will we
* process? */
VNET_DEFINE(int, ip6_dad_count) = 1; /* DupAddrDetectionTransmits */
+VNET_DEFINE(int, ip6_grand_count) = MAX_NEIGHBOR_ADVERTISEMENT;
VNET_DEFINE(int, ip6_auto_flowlabel) = 1;
VNET_DEFINE(int, ip6_use_deprecated) = 1;/* allow deprecated addr
* (RFC2462 5.5.4) */
@@ -291,6 +292,9 @@
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DAD_COUNT, dad_count,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_dad_count), 0,
"Number of ICMPv6 NS messages sent during duplicate address detection");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_GRAND_COUNT, grand_count,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_grand_count), 0,
+ "Number of ICMPv6 NA messages sent by gratuitous ND per interface");
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_FLOWLABEL, auto_flowlabel,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_auto_flowlabel), 0,
"Provide an IPv6 flowlabel in outbound packets");
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -505,6 +505,7 @@
int nd_recalc_timer;
u_int nd_dad_failures;
uint8_t nd_curhoplimit;
+ TAILQ_HEAD(, nd_queue) nd_queue;
struct mld_ifsoftc {
/* Timers and invervals measured in seconds. */
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -319,6 +319,7 @@
VNET_DECLARE(int, ip6_hdrnestlimit); /* upper limit of # of extension
* headers */
VNET_DECLARE(int, ip6_dad_count); /* DupAddrDetectionTransmits */
+VNET_DECLARE(int, ip6_grand_count); /* Gratuitous ND Transmits */
#define V_ip6_sendredirects VNET(ip6_sendredirects)
#define V_ip6_accept_rtadv VNET(ip6_accept_rtadv)
#define V_ip6_no_radr VNET(ip6_no_radr)
@@ -326,6 +327,7 @@
#define V_ip6_rfc6204w3 VNET(ip6_rfc6204w3)
#define V_ip6_hdrnestlimit VNET(ip6_hdrnestlimit)
#define V_ip6_dad_count VNET(ip6_dad_count)
+#define V_ip6_grand_count VNET(ip6_grand_count)
VNET_DECLARE(int, ip6_auto_flowlabel);
VNET_DECLARE(int, ip6_auto_linklocal);
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -152,6 +152,10 @@
#define ND6_NA_OPT_LLA 0x01
#define ND6_NA_CARP_MASTER 0x02
+/* ND6 queue flags */
+#define ND6_QUEUE_FLAG_NEWGUA 0x01 /* new global unicast address event */
+#define ND6_QUEUE_FLAG_LLADDR 0x02 /* link-layer address change event */
+
/* protocol constants */
#define MAX_RTR_SOLICITATION_DELAY 1 /* 1sec */
#define RTR_SOLICITATION_INTERVAL 4 /* 4sec */
@@ -173,6 +177,7 @@
#define ND_COMPUTE_RTIME(x) \
(((MIN_RANDOM_FACTOR * (x >> 10)) + (arc4random() & \
((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000)
+#define MAX_NEIGHBOR_ADVERTISEMENT 3 /* RFC4891 Section 10 */
struct nd_defrouter {
TAILQ_ENTRY(nd_defrouter) dr_entry;
@@ -366,6 +371,8 @@
void nd6_dad_init(void);
void nd6_dad_start(struct ifaddr *, int);
void nd6_dad_stop(struct ifaddr *);
+void nd6_grand_start(struct ifaddr *, uint32_t);
+void nd6_queue_stop(struct ifaddr *);
/* nd6_rtr.c */
void nd6_rs_input(struct mbuf *, int, int);
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -221,11 +221,28 @@
static void
nd6_iflladdr(void *arg __unused, struct ifnet *ifp)
{
+ struct ifaddr *ifa;
+ struct epoch_tracker et;
+
/* XXXGL: ??? */
if (ifp->if_inet6 == NULL)
return;
lltable_update_ifaddr(LLTABLE6(ifp));
+
+ if ((ifp->if_flags & IFF_UP) == 0)
+ return;
+
+ /*
+ * Sends gratuitous NAs for each ifaddr to notify other
+ * nodes about the address change.
+ */
+ NET_EPOCH_ENTER(et);
+ CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ if (ifa->ifa_addr->sa_family == AF_INET6)
+ nd6_grand_start(ifa, ND6_QUEUE_FLAG_LLADDR);
+ }
+ NET_EPOCH_EXIT(et);
}
void
@@ -321,6 +338,9 @@
/* If we globally accept rtadv, assume IPv6 on. */
nd->nd_flags &= ~ND6_IFF_IFDISABLED;
}
+
+ /* nd6 queue initialization */
+ TAILQ_INIT(&nd->nd_queue);
}
void
@@ -334,6 +354,9 @@
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
+ /* make sure there are no queued ND6 */
+ nd6_queue_stop(ifa);
+
/* stop DAD processing */
nd6_dad_stop(ifa);
}
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -121,6 +121,13 @@
&VNET_NAME(nd6_onlink_ns_rfc4861), 0,
"Accept 'on-link' ICMPv6 NS messages in compliance with RFC 4861");
+struct nd_queue {
+ TAILQ_ENTRY(nd_queue) ndq_list;
+ struct ifaddr *ndq_ifa;
+ uint32_t ndq_flags;
+ struct callout ndq_callout;
+};
+
/*
* Input a Neighbor Solicitation Message.
*
@@ -1464,6 +1471,12 @@
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
if ((ifp->if_inet6->nd_flags & ND6_IFF_STABLEADDR) && !(ia->ia6_flags & IN6_IFF_TEMPORARY))
atomic_store_int(&DAD_FAILURES(ifp), 0);
+ /*
+ * RFC 9131 Section 6.1.2: The first advertisement
+ * SHOULD be sent as soon as an address changes the
+ * state from tentative to preferred.
+ */
+ nd6_grand_start(ifa, ND6_QUEUE_FLAG_NEWGUA);
}
nd6log((LOG_DEBUG,
@@ -1632,3 +1645,175 @@
dp->dad_na_icount++;
DADQ_RUNLOCK();
}
+
+static void
+nd6_queue_rel(void *arg)
+{
+ struct nd_queue *ndq = arg;
+ struct ifnet *ifp = ndq->ndq_ifa->ifa_ifp;
+
+ IF_ADDR_WLOCK_ASSERT(ifp);
+
+ /* Remove ndq from the nd_queue and release its reference */
+ TAILQ_REMOVE(&ifp->if_inet6->nd_queue, ndq, ndq_list);
+ IF_ADDR_WUNLOCK(ifp);
+
+ ifa_free(ndq->ndq_ifa);
+ free(ndq, M_IP6NDP);
+}
+
+static void
+nd6_queue_timer(void *arg)
+{
+ struct nd_queue *ndq = arg;
+ struct ifaddr *ifa = ndq->ndq_ifa;
+ struct ifnet *ifp = ifa->ifa_ifp;
+ struct in6_ifextra *ext = ifp->if_inet6;
+ struct in6_addr taddr6 = IN6ADDR_ANY_INIT;
+ struct epoch_tracker et;
+ int delay, tlladdr;
+ u_long flags;
+
+ KASSERT(ifa != NULL, ("ND6 queue entry %p with no address", ndq));
+
+ CURVNET_SET(ifp->if_vnet);
+ NET_EPOCH_ENTER(et);
+
+ tlladdr = ND6_NA_OPT_LLA;
+ flags = (V_ip6_forwarding) ? ND_NA_FLAG_ROUTER : 0;
+ if ((ext->nd_flags & ND6_IFF_ACCEPT_RTADV) != 0 && V_ip6_norbit_raif)
+ flags &= ~ND_NA_FLAG_ROUTER;
+
+ /*
+ * RFC 9131 Section 6.1.2: if new global address added,
+ * use the all-routers multicast address.
+ * If the address is preferred, then the Override flag SHOULD NOT be set.
+ */
+ if ((ndq->ndq_flags & ND6_QUEUE_FLAG_NEWGUA) != 0) {
+ taddr6 = in6addr_linklocal_allrouters;
+ /*
+ * XXX: If the address is in the Optimistic state,
+ * then the Override flag MUST NOT be set.
+ * We don't support RFC 4429 yet.
+ */
+ if ((ext->nd_flags & ND6_IFF_PREFER_SOURCE) == 0)
+ flags |= ND_NA_FLAG_OVERRIDE;
+ }
+ /*
+ * RFC 4891 Section 7.2.6: if link-layer address changed,
+ * use the all-nodes multicast address.
+ * The Override flag MAY be set to either zero or one.
+ */
+ if ((ndq->ndq_flags & ND6_QUEUE_FLAG_LLADDR) != 0) {
+ taddr6 = in6addr_linklocal_allnodes;
+ flags |= ND_NA_FLAG_OVERRIDE;
+ }
+
+ /* Wait at least a RetransTimer before removing from queue */
+ delay = ext->nd_retrans * hz / 1000;
+ callout_reset(&ndq->ndq_callout, delay, nd6_queue_rel, ndq);
+ IF_ADDR_WUNLOCK(ifp);
+
+ if (__predict_true(in6_setscope(&taddr6, ifp, NULL) == 0))
+ nd6_na_output_fib(ifp, &taddr6, IFA_IN6(ifa), flags, tlladdr, NULL, ifp->if_fib);
+
+ NET_EPOCH_EXIT(et);
+ CURVNET_RESTORE();
+}
+
+static void
+nd6_queue_add(struct ifaddr *ifa, int delay, uint32_t flags)
+{
+ struct nd_queue *ndq;
+ struct ifnet *ifp = ifa->ifa_ifp;
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct in6_ifextra *ext = ifp->if_inet6;
+ char ip6buf[INET6_ADDRSTRLEN];
+
+ NET_EPOCH_ASSERT();
+
+ ndq = malloc(sizeof(*ndq), M_IP6NDP, M_NOWAIT | M_ZERO);
+ if (ndq == NULL) {
+ log(LOG_ERR, "nd6_queue_add: memory allocation failed for "
+ "%s(%s)\n",
+ ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr),
+ ifp ? if_name(ifp) : "???");
+ return;
+ }
+
+ IF_ADDR_WLOCK(ifa->ifa_ifp);
+ callout_init_mtx(&ndq->ndq_callout, &ifp->if_addr_lock,
+ CALLOUT_TRYLOCK | CALLOUT_RETURNUNLOCKED);
+ nd6log((LOG_DEBUG, "%s: send delayed IPv6 ND for %s\n", if_name(ifp),
+ ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
+
+ ndq->ndq_ifa = ifa;
+ ifa_ref(ndq->ndq_ifa);
+ ndq->ndq_flags = flags;
+
+ TAILQ_INSERT_TAIL(&ext->nd_queue, ndq, ndq_list);
+ callout_reset(&ndq->ndq_callout, delay, nd6_queue_timer, ndq);
+ IF_ADDR_WUNLOCK(ifa->ifa_ifp);
+}
+
+/*
+ * Start Gratuitous Neighbor Discovery (GRAND) for specified address.
+ * Called after DAD completes and by interface link layer change event.
+ */
+void
+nd6_grand_start(struct ifaddr *ifa, uint32_t flags)
+{
+ struct nd_queue *ndq;
+ struct in6_ifextra *ext = ifa->ifa_ifp->if_inet6;
+ int delay, count = 0;
+
+ NET_EPOCH_ASSERT();
+ /* If we don't need GRAND, don't do it. */
+ if (V_ip6_grand_count == 0 ||
+ ifa->ifa_carp != NULL)
+ return;
+
+ /* Check if new address is global */
+ if ((flags & ND6_QUEUE_FLAG_NEWGUA) != 0 &&
+ in6_addrscope(IFA_IN6(ifa)) != IPV6_ADDR_SCOPE_GLOBAL)
+ return;
+
+ /*
+ * RFC 9131 Section 6.1.2: These advertisements MUST be
+ * separated by at least RetransTimer seconds.
+ */
+ TAILQ_FOREACH(ndq, &ext->nd_queue, ndq_list) {
+ /*
+ * RFC 9131 Section 6.1.2: a node SHOULD send
+ * up to MAX_NEIGHBOR_ADVERTISEMENT Neighbor Advertisement messages.
+ * Make sure we don't queue GRAND more than V_ip6_grand_count
+ * per interface.
+ */
+ count++;
+ if (count >= V_ip6_grand_count)
+ return;
+ }
+
+ delay = ext->nd_retrans * hz / 1000;
+ nd6_queue_add(ifa, count * delay, flags);
+}
+
+/*
+ * drain nd6 queue. used for address removals.
+ */
+void
+nd6_queue_stop(struct ifaddr *ifa)
+{
+ struct nd_queue *ndq;
+
+ IF_ADDR_WLOCK(ifa->ifa_ifp);
+ TAILQ_FOREACH(ndq, &ifa->ifa_ifp->if_inet6->nd_queue, ndq_list) {
+ if (ndq->ndq_ifa != ifa)
+ continue;
+
+ callout_stop(&ndq->ndq_callout);
+ nd6_queue_rel(ndq);
+ return;
+ }
+ IF_ADDR_WUNLOCK(ifa->ifa_ifp);
+}

File Metadata

Mime Type
text/plain
Expires
Fri, Feb 20, 5:34 PM (11 h, 49 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28902210
Default Alt Text
D55015.diff (11 KB)

Event Timeline