diff --git a/sys/netinet/if_arp.c b/sys/netinet/if_arp.c index 6231852e919..066b732596a 100644 --- a/sys/netinet/if_arp.c +++ b/sys/netinet/if_arp.c @@ -173,7 +173,8 @@ static void revarprequest(struct ifnet *); static void arp_drainstub(void); -static void arp_dad_timer(struct ifaddr *); +struct dadq; +static void arp_dad_timer(struct dadq *); static void arp_dad_start(struct ifaddr *); static void arp_dad_stop(struct ifaddr *); static void arp_dad_duplicated(struct ifaddr *, const char *); @@ -1534,18 +1535,18 @@ arp_dad_starttimer(struct dadq *dp, int ticks) { callout_reset(&dp->dad_timer_ch, ticks, - (void (*)(void *))arp_dad_timer, (void *)dp->dad_ifa); + (void (*)(void *))arp_dad_timer, dp); } static void -arp_dad_stoptimer(struct dadq *dp) +arp_dad_destroytimer(struct dadq *dp) { -#ifdef NET_MPSAFE - callout_halt(&dp->dad_timer_ch, NULL); -#else - callout_halt(&dp->dad_timer_ch, softnet_lock); -#endif + TAILQ_REMOVE(&dadq, dp, dad_list); + /* Request the timer to destroy dp. */ + dp->dad_ifa = NULL; + callout_reset(&dp->dad_timer_ch, 0, + (void (*)(void *))arp_dad_timer, dp); } static void @@ -1665,36 +1666,39 @@ arp_dad_stop(struct ifaddr *ifa) } /* Prevent the timer from running anymore. */ - TAILQ_REMOVE(&dadq, dp, dad_list); + arp_dad_destroytimer(dp); + mutex_exit(&arp_dad_lock); - arp_dad_stoptimer(dp); - - kmem_intr_free(dp, sizeof(*dp)); ifafree(ifa); } static void -arp_dad_timer(struct ifaddr *ifa) +arp_dad_timer(struct dadq *dp) { - struct in_ifaddr *ia = (struct in_ifaddr *)ifa; - struct dadq *dp; + struct ifaddr *ifa; + struct in_ifaddr *ia; char ipbuf[INET_ADDRSTRLEN]; bool need_free = false; SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); mutex_enter(&arp_dad_lock); - /* Sanity check */ - if (ia == NULL) { - log(LOG_ERR, "%s: called with null parameter\n", __func__); - goto done; - } - dp = arp_dad_find(ifa); - if (dp == NULL) { - /* DAD seems to be stopping, so do nothing. */ + ifa = dp->dad_ifa; + if (ifa == NULL) { + /* ifa is being deleted. DAD should be freed too. */ + if (callout_pending(&dp->dad_timer_ch)) { + /* + * The callout is scheduled again, so we cannot destroy + * dp in this run. + */ + goto done; + } + need_free = true; goto done; } + + ia = (struct in_ifaddr *)ifa; if (ia->ia4_flags & IN_IFF_DUPLICATED) { log(LOG_ERR, "%s: called with duplicate address %s(%s)\n", __func__, IN_PRINT(ipbuf, &ia->ia_addr.sin_addr), @@ -1770,7 +1774,8 @@ done: if (need_free) { kmem_intr_free(dp, sizeof(*dp)); - ifafree(ifa); + if (ifa != NULL) + ifafree(ifa); } SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index b5d3c1b57fb..26d647d9de7 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -79,8 +79,8 @@ __KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.142 2018/01/10 07:34:31 ozaki-r Exp $" struct dadq; static struct dadq *nd6_dad_find(struct ifaddr *); static void nd6_dad_starttimer(struct dadq *, int); -static void nd6_dad_stoptimer(struct dadq *); -static void nd6_dad_timer(struct ifaddr *); +static void nd6_dad_destroytimer(struct dadq *); +static void nd6_dad_timer(struct dadq *); static void nd6_dad_ns_output(struct dadq *, struct ifaddr *); static void nd6_dad_ns_input(struct ifaddr *); static void nd6_dad_na_input(struct ifaddr *); @@ -1087,18 +1087,18 @@ nd6_dad_starttimer(struct dadq *dp, int ticks) { callout_reset(&dp->dad_timer_ch, ticks, - (void (*)(void *))nd6_dad_timer, (void *)dp->dad_ifa); + (void (*)(void *))nd6_dad_timer, dp); } static void -nd6_dad_stoptimer(struct dadq *dp) +nd6_dad_destroytimer(struct dadq *dp) { -#ifdef NET_MPSAFE - callout_halt(&dp->dad_timer_ch, NULL); -#else - callout_halt(&dp->dad_timer_ch, softnet_lock); -#endif + TAILQ_REMOVE(&dadq, dp, dad_list); + /* Request the timer to destroy dp. */ + dp->dad_ifa = NULL; + callout_reset(&dp->dad_timer_ch, 0, + (void (*)(void *))nd6_dad_timer, dp); } /* @@ -1210,20 +1210,18 @@ nd6_dad_stop(struct ifaddr *ifa) } /* Prevent the timer from running anymore. */ - TAILQ_REMOVE(&dadq, dp, dad_list); + nd6_dad_destroytimer(dp); + mutex_exit(&nd6_dad_lock); - nd6_dad_stoptimer(dp); - - kmem_intr_free(dp, sizeof(*dp)); ifafree(ifa); } static void -nd6_dad_timer(struct ifaddr *ifa) +nd6_dad_timer(struct dadq *dp) { - struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; - struct dadq *dp; + struct ifaddr *ifa; + struct in6_ifaddr *ia; int duplicate = 0; char ip6buf[INET6_ADDRSTRLEN]; bool need_free = false; @@ -1231,16 +1229,21 @@ nd6_dad_timer(struct ifaddr *ifa) SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); mutex_enter(&nd6_dad_lock); - /* Sanity check */ - if (ia == NULL) { - log(LOG_ERR, "nd6_dad_timer: called with null parameter\n"); - goto done; - } - dp = nd6_dad_find(ifa); - if (dp == NULL) { - /* DAD seems to be stopping, so do nothing. */ + ifa = dp->dad_ifa; + if (ifa == NULL) { + /* ifa is being deleted. DAD should be freed too. */ + if (callout_pending(&dp->dad_timer_ch)) { + /* + * The callout is scheduled again, so we cannot destroy + * dp in this run. + */ + goto done; + } + need_free = true; goto done; } + + ia = (struct in6_ifaddr *)ifa; if (ia->ia6_flags & IN6_IFF_DUPLICATED) { log(LOG_ERR, "nd6_dad_timer: called with duplicate address " "%s(%s)\n", @@ -1317,8 +1320,8 @@ done: if (need_free) { kmem_intr_free(dp, sizeof(*dp)); - ifafree(ifa); - ifa = NULL; + if (ifa != NULL) + ifafree(ifa); } if (duplicate) @@ -1392,13 +1395,11 @@ nd6_dad_duplicated(struct ifaddr *ifa) } } - TAILQ_REMOVE(&dadq, dp, dad_list); - mutex_exit(&nd6_dad_lock); - /* We are done with DAD, with duplicated address found. (failure) */ - nd6_dad_stoptimer(dp); + nd6_dad_destroytimer(dp); + + mutex_exit(&nd6_dad_lock); - kmem_intr_free(dp, sizeof(*dp)); ifafree(ifa); }