Index: in6.c =================================================================== --- in6.c (revision 260904) +++ in6.c (working copy) @@ -1530,6 +1530,9 @@ in6_purgeaddr(struct ifaddr *ifa) /* Remove local address entry from lltable. */ nd6_rem_ifa_lle(ia); + /* stop NS deferred processing */ + nd6_ns_drain(ifp); + /* Leave multicast groups. */ error = in6_purgeaddr_mc(ifp, ia, ifa0); Index: nd6_nbr.c =================================================================== --- nd6_nbr.c (revision 260904) +++ nd6_nbr.c (working copy) @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -93,6 +94,21 @@ VNET_DEFINE(int, dad_maxtry) = 15; /* max # of *tr #define V_dad_ignore_ns VNET(dad_ignore_ns) #define V_dad_maxtry VNET(dad_maxtry) +struct nd6_ns { + STAILQ_ENTRY(nd6_ns) entry; + struct ifnet *ifp; + struct in6_addr src; + struct in6_addr dst; + struct in6_addr tgt; +}; + +static STAILQ_HEAD(, nd6_ns) nd6_tasks; +static struct task nd6_ns_task; +static struct mtx nd6_ns_lock; +#define ND6_NS_LOCK_INIT() mtx_init(&nd6_ns_lock, \ + "nd6_ns_lock", NULL, MTX_DEF) +#define ND6_NS_LOCK() mtx_lock(&nd6_ns_lock) +#define ND6_NS_UNLOCK() mtx_unlock(&nd6_ns_lock) /* * Input a Neighbor Solicitation Message. * @@ -378,7 +394,67 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len * ln - for source address determination * dad - duplicate address detection */ + void +nd6_ns_init(void) +{ + +#ifdef VIMAGE + if (!IS_DEFAULT_VNET(curvnet)) + return; +#endif + STAILQ_INIT(&nd6_tasks); + TASK_INIT(&nd6_ns_task, 0, nd6_process_ns_tasks, NULL); + ND6_NS_LOCK_INIT(); +} + +static void +nd6_process_ns_tasks(void *arg __unused, int pending) +{ + struct nd6_ns *ns; + STAILQ_HEAD(, nd6_ns) tasks; + + STAILQ_INIT(&tasks); + for (;;) { + ND6_NS_LOCK(); + if (STAILQ_FIRST(&nd6_tasks) == NULL) { + ND6_NS_UNLOCK(); + break; + } + STAILQ_SWAP(&nd6_tasks, &tasks, entry); + ND6_NS_UNLOCK(); + while ((ns = STAILQ_FIRST(&tasks)) != NULL) { + STAILQ_REMOVE_HEAD(&tasks, entry); + nd6_ns_output(ns->ifp, &ns->src, &ns->dst, &ns->tgt); + free(ns, M_TMP); + } + } +} + +int +nd6_ns_output_sched(struct ifnet *ifp, const struct in6_addr *src, + const struct in6_addr *dst, const struct in6_addr *tgt) +{ + struct nd6_ns *ns; + + ns = malloc(sizeof(*ns), M_TEMP, M_NOWAIT | M_ZERO); + if (ns == NULL) + return (ENOMEM); + ns->ifp = ifp; + if (src != NULL) + ns->src = *src; + if (dst != NULL) + ns->dst = *dst; + if (tgt != NULL) + ns->tgt = *tgt; + ND6_NS_LOCK(); + SLIST_INSERT_TAIL(&nd6_tasks, ns, entry); + ND6_NS_UNLOCK(); + taskqueue_enqueue(taskqueue_thread, &nd6_ns_task); + return (0); +} + +void nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, const struct in6_addr *taddr6, struct llentry *ln, int dad) {