From owner-svn-src-head@freebsd.org Mon Sep 14 16:48:23 2015 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 31095A0485B; Mon, 14 Sep 2015 16:48:23 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 185E51F9E; Mon, 14 Sep 2015 16:48:23 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.70]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id t8EGmNRY062503; Mon, 14 Sep 2015 16:48:23 GMT (envelope-from melifaro@FreeBSD.org) Received: (from melifaro@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id t8EGmK0G062494; Mon, 14 Sep 2015 16:48:20 GMT (envelope-from melifaro@FreeBSD.org) Message-Id: <201509141648.t8EGmK0G062494@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: melifaro set sender to melifaro@FreeBSD.org using -f From: "Alexander V. Chernikov" Date: Mon, 14 Sep 2015 16:48:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r287789 - in head/sys: net netinet netinet6 X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 14 Sep 2015 16:48:23 -0000 Author: melifaro Date: Mon Sep 14 16:48:19 2015 New Revision: 287789 URL: https://svnweb.freebsd.org/changeset/base/287789 Log: * Do more fine-grained locking: call eventhandlers/free_entry without holding afdata wlock * convert per-af delete_address callback to global lltable_delete_entry() and more low-level "delete this lle" per-af callback * fix some bugs/inconsistencies in IPv4/IPv6 ifscrub procedures Sponsored by: Yandex LLC Differential Revision: https://reviews.freebsd.org/D3573 Modified: head/sys/net/if_llatbl.c head/sys/net/if_llatbl.h head/sys/netinet/if_ether.c head/sys/netinet/if_ether.h head/sys/netinet/in.c head/sys/netinet/ip_carp.c head/sys/netinet6/in6.c head/sys/netinet6/nd6.c head/sys/netinet6/nd6.h Modified: head/sys/net/if_llatbl.c ============================================================================== --- head/sys/net/if_llatbl.c Mon Sep 14 15:47:25 2015 (r287788) +++ head/sys/net/if_llatbl.c Mon Sep 14 16:48:19 2015 (r287789) @@ -186,7 +186,7 @@ htable_unlink_entry(struct llentry *lle) } struct prefix_match_data { - const struct sockaddr *prefix; + const struct sockaddr *addr; const struct sockaddr *mask; struct llentries dchain; u_int flags; @@ -199,7 +199,7 @@ htable_prefix_free_cb(struct lltable *ll pmd = (struct prefix_match_data *)farg; - if (llt->llt_match_prefix(pmd->prefix, pmd->mask, pmd->flags, lle)) { + if (llt->llt_match_prefix(pmd->addr, pmd->mask, pmd->flags, lle)) { LLE_WLOCK(lle); LIST_INSERT_HEAD(&pmd->dchain, lle, lle_chain); } @@ -208,14 +208,14 @@ htable_prefix_free_cb(struct lltable *ll } static void -htable_prefix_free(struct lltable *llt, const struct sockaddr *prefix, +htable_prefix_free(struct lltable *llt, const struct sockaddr *addr, const struct sockaddr *mask, u_int flags) { struct llentry *lle, *next; struct prefix_match_data pmd; bzero(&pmd, sizeof(pmd)); - pmd.prefix = prefix; + pmd.addr = addr; pmd.mask = mask; pmd.flags = flags; LIST_INIT(&pmd.dchain); @@ -427,8 +427,42 @@ lltable_drain(int af) } #endif +/* + * Deletes an address from given lltable. + * Used for userland interaction to remove + * individual entries. Skips entries added by OS. + */ +int +lltable_delete_addr(struct lltable *llt, u_int flags, + const struct sockaddr *l3addr) +{ + struct llentry *lle; + struct ifnet *ifp; + + ifp = llt->llt_ifp; + IF_AFDATA_WLOCK(ifp); + lle = lla_lookup(llt, LLE_EXCLUSIVE, l3addr); + + if (lle == NULL) { + IF_AFDATA_WUNLOCK(ifp); + return (ENOENT); + } + if ((lle->la_flags & LLE_IFADDR) != 0 && (flags & LLE_IFADDR) == 0) { + IF_AFDATA_WUNLOCK(ifp); + LLE_WUNLOCK(lle); + return (EPERM); + } + + lltable_unlink_entry(llt, lle); + IF_AFDATA_WUNLOCK(ifp); + + llt->llt_delete_entry(llt, lle); + + return (0); +} + void -lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask, +lltable_prefix_free(int af, struct sockaddr *addr, struct sockaddr *mask, u_int flags) { struct lltable *llt; @@ -438,7 +472,7 @@ lltable_prefix_free(int af, struct socka if (llt->llt_af != af) continue; - llt->llt_prefix_free(llt, prefix, mask, flags); + llt->llt_prefix_free(llt, addr, mask, flags); } LLTABLE_RUNLOCK(); } @@ -651,10 +685,7 @@ lla_rt_output(struct rt_msghdr *rtm, str break; case RTM_DELETE: - IF_AFDATA_WLOCK(ifp); - error = lla_delete(llt, 0, dst); - IF_AFDATA_WUNLOCK(ifp); - return (error == 0 ? 0 : ENOENT); + return (lltable_delete_addr(llt, 0, dst)); default: error = EINVAL; Modified: head/sys/net/if_llatbl.h ============================================================================== --- head/sys/net/if_llatbl.h Mon Sep 14 15:47:25 2015 (r287788) +++ head/sys/net/if_llatbl.h Mon Sep 14 16:48:19 2015 (r287789) @@ -135,10 +135,9 @@ typedef struct llentry *(llt_lookup_t)(s const struct sockaddr *l3addr); typedef struct llentry *(llt_alloc_t)(struct lltable *, u_int flags, const struct sockaddr *l3addr); -typedef int (llt_delete_t)(struct lltable *, u_int flags, - const struct sockaddr *l3addr); +typedef void (llt_delete_t)(struct lltable *, struct llentry *); typedef void (llt_prefix_free_t)(struct lltable *, - const struct sockaddr *prefix, const struct sockaddr *mask, u_int flags); + const struct sockaddr *addr, const struct sockaddr *mask, u_int flags); typedef int (llt_dump_entry_t)(struct lltable *, struct llentry *, struct sysctl_req *); typedef uint32_t (llt_hash_t)(const struct llentry *, uint32_t); @@ -162,7 +161,7 @@ struct lltable { llt_lookup_t *llt_lookup; llt_alloc_t *llt_alloc_entry; - llt_delete_t *llt_delete; + llt_delete_t *llt_delete_entry; llt_prefix_free_t *llt_prefix_free; llt_dump_entry_t *llt_dump_entry; llt_hash_t *llt_hash; @@ -212,6 +211,8 @@ size_t lltable_drop_entry_queue(struct l struct llentry *lltable_alloc_entry(struct lltable *llt, u_int flags, const struct sockaddr *l4addr); void lltable_free_entry(struct lltable *llt, struct llentry *lle); +int lltable_delete_addr(struct lltable *llt, u_int flags, + const struct sockaddr *l3addr); void lltable_link_entry(struct lltable *llt, struct llentry *lle); void lltable_unlink_entry(struct lltable *llt, struct llentry *lle); void lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa); @@ -230,14 +231,6 @@ lla_lookup(struct lltable *llt, u_int fl return (llt->llt_lookup(llt, flags, l3addr)); } -static __inline int -lla_delete(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) -{ - - return (llt->llt_delete(llt, flags, l3addr)); -} - - int lla_rt_output(struct rt_msghdr *, struct rt_addrinfo *); #include Modified: head/sys/netinet/if_ether.c ============================================================================== --- head/sys/netinet/if_ether.c Mon Sep 14 15:47:25 2015 (r287788) +++ head/sys/netinet/if_ether.c Mon Sep 14 16:48:19 2015 (r287789) @@ -140,26 +140,6 @@ static const struct netisr_handler arp_n .nh_policy = NETISR_POLICY_SOURCE, }; -#ifdef AF_INET -/* - * called by in_scrubprefix() to remove entry from the table when - * the interface goes away - */ -void -arp_ifscrub(struct ifnet *ifp, uint32_t addr) -{ - struct sockaddr_in addr4; - - bzero((void *)&addr4, sizeof(addr4)); - addr4.sin_len = sizeof(addr4); - addr4.sin_family = AF_INET; - addr4.sin_addr.s_addr = addr; - IF_AFDATA_WLOCK(ifp); - lla_delete(LLTABLE(ifp), LLE_IFADDR, (struct sockaddr *)&addr4); - IF_AFDATA_WUNLOCK(ifp); -} -#endif - /* * Timeout routine. Age arp_tab entries periodically. */ Modified: head/sys/netinet/if_ether.h ============================================================================== --- head/sys/netinet/if_ether.h Mon Sep 14 15:47:25 2015 (r287788) +++ head/sys/netinet/if_ether.h Mon Sep 14 16:48:19 2015 (r287789) @@ -120,7 +120,6 @@ void arprequest(struct ifnet *, const st const struct in_addr *, u_char *); void arp_ifinit(struct ifnet *, struct ifaddr *); void arp_ifinit2(struct ifnet *, struct ifaddr *, u_char *); -void arp_ifscrub(struct ifnet *, uint32_t); #endif #endif Modified: head/sys/netinet/in.c ============================================================================== --- head/sys/netinet/in.c Mon Sep 14 15:47:25 2015 (r287788) +++ head/sys/netinet/in.c Mon Sep 14 16:48:19 2015 (r287789) @@ -724,6 +724,38 @@ in_addprefix(struct in_ifaddr *target, i } /* + * Removes either all lle entries for given @ia, or lle + * corresponding to @ia address. + */ +static void +in_scrubprefixlle(struct in_ifaddr *ia, int all, u_int flags) +{ + struct sockaddr_in addr, mask; + struct sockaddr *saddr, *smask; + struct ifnet *ifp; + + /* + * remove all L2 entries on the given prefix + */ + saddr = (struct sockaddr *)&addr; + bzero(&addr, sizeof(addr)); + addr.sin_len = sizeof(addr); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ntohl(ia->ia_addr.sin_addr.s_addr); + smask = (struct sockaddr *)&mask; + bzero(&mask, sizeof(mask)); + mask.sin_len = sizeof(mask); + mask.sin_family = AF_INET; + mask.sin_addr.s_addr = ia->ia_subnetmask; + ifp = ia->ia_ifp; + + if (all) + lltable_prefix_free(AF_INET, saddr, smask, flags); + else + lltable_delete_addr(LLTABLE(ifp), LLE_IFADDR, saddr); +} + +/* * If there is no other address in the system that can serve a route to the * same prefix, remove the route. Hand over the route to the new address * otherwise. @@ -735,7 +767,6 @@ in_scrubprefix(struct in_ifaddr *target, struct in_ifaddr *ia; struct in_addr prefix, mask, p, m; int error = 0; - struct sockaddr_in prefix0, mask0; /* * Remove the loopback route to the interface address. @@ -757,11 +788,6 @@ in_scrubprefix(struct in_ifaddr *target, error = ifa_del_loopback_route((struct ifaddr *)target, (struct sockaddr *)&target->ia_addr); } - - if (!(target->ia_ifp->if_flags & IFF_NOARP)) - /* remove arp cache */ - arp_ifscrub(target->ia_ifp, - IA_SIN(target)->sin_addr.s_addr); } if (rtinitflags(target)) { @@ -817,6 +843,9 @@ in_scrubprefix(struct in_ifaddr *target, else log(LOG_INFO, "in_scrubprefix: err=%d, old prefix delete failed\n", error); + /* Scrub all entries IFF interface is different */ + in_scrubprefixlle(target, target->ia_ifp != ia->ia_ifp, + flags); error = rtinit(&ia->ia_ifa, (int)RTM_ADD, rtinitflags(ia) | RTF_UP); if (error == 0) @@ -833,16 +862,7 @@ in_scrubprefix(struct in_ifaddr *target, /* * remove all L2 entries on the given prefix */ - bzero(&prefix0, sizeof(prefix0)); - prefix0.sin_len = sizeof(prefix0); - prefix0.sin_family = AF_INET; - prefix0.sin_addr.s_addr = target->ia_subnet; - bzero(&mask0, sizeof(mask0)); - mask0.sin_len = sizeof(mask0); - mask0.sin_family = AF_INET; - mask0.sin_addr.s_addr = target->ia_subnetmask; - lltable_prefix_free(AF_INET, (struct sockaddr *)&prefix0, - (struct sockaddr *)&mask0, flags); + in_scrubprefixlle(target, 1, flags); /* * As no-one seem to have this prefix, we can remove the route. @@ -1001,22 +1021,38 @@ in_lltable_new(struct in_addr addr4, u_i return (&lle->base); } -#define IN_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ - (((ntohl((d).s_addr) ^ (a)->sin_addr.s_addr) & (m)->sin_addr.s_addr)) == 0 ) +#define IN_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ + ((((d).s_addr ^ (a).s_addr) & (m).s_addr)) == 0 ) static int -in_lltable_match_prefix(const struct sockaddr *prefix, - const struct sockaddr *mask, u_int flags, struct llentry *lle) +in_lltable_match_prefix(const struct sockaddr *saddr, + const struct sockaddr *smask, u_int flags, struct llentry *lle) { - const struct sockaddr_in *pfx = (const struct sockaddr_in *)prefix; - const struct sockaddr_in *msk = (const struct sockaddr_in *)mask; + struct in_addr addr, mask, lle_addr; - /* - * (flags & LLE_STATIC) means deleting all entries - * including static ARP entries. - */ - if (IN_ARE_MASKED_ADDR_EQUAL(lle->r_l3addr.addr4, pfx, msk) && - ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC))) + addr = ((const struct sockaddr_in *)saddr)->sin_addr; + mask = ((const struct sockaddr_in *)smask)->sin_addr; + lle_addr.s_addr = ntohl(lle->r_l3addr.addr4.s_addr); + + if (IN_ARE_MASKED_ADDR_EQUAL(lle_addr, addr, mask) == 0) + return (0); + + if (lle->la_flags & LLE_IFADDR) { + + /* + * Delete LLE_IFADDR records IFF address & flag matches. + * Note that addr is the interface address within prefix + * being matched. + * Note also we should handle 'ifdown' cases without removing + * ifaddr macs. + */ + if (addr.s_addr == lle_addr.s_addr && (flags & LLE_STATIC) != 0) + return (1); + return (0); + } + + /* flags & LLE_STATIC means deleting both dynamic and static entries */ + if ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)) return (1); return (0); @@ -1166,39 +1202,16 @@ in_lltable_find_dst(struct lltable *llt, return (lle); } -static int -in_lltable_delete(struct lltable *llt, u_int flags, - const struct sockaddr *l3addr) +static void +in_lltable_delete_entry(struct lltable *llt, struct llentry *lle) { - const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr; - struct llentry *lle; - - IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp); - KASSERT(l3addr->sa_family == AF_INET, - ("sin_family %d", l3addr->sa_family)); - lle = in_lltable_find_dst(llt, sin->sin_addr); - if (lle == NULL) { -#ifdef DIAGNOSTIC - log(LOG_INFO, "interface address is missing from cache = %p in delete\n", lle); -#endif - return (ENOENT); - } - - if (!(lle->la_flags & LLE_IFADDR) || (flags & LLE_IFADDR)) { - LLE_WLOCK(lle); - lle->la_flags |= LLE_DELETED; - EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED); + lle->la_flags |= LLE_DELETED; + EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED); #ifdef DIAGNOSTIC - log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); + log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); #endif - if ((lle->la_flags & (LLE_STATIC | LLE_IFADDR)) == LLE_STATIC) - llentry_free(lle); - else - LLE_WUNLOCK(lle); - } - - return (0); + llentry_free(lle); } static struct llentry * @@ -1334,7 +1347,7 @@ in_lltattach(struct ifnet *ifp) llt->llt_lookup = in_lltable_lookup; llt->llt_alloc_entry = in_lltable_alloc; - llt->llt_delete = in_lltable_delete; + llt->llt_delete_entry = in_lltable_delete_entry; llt->llt_dump_entry = in_lltable_dump_entry; llt->llt_hash = in_lltable_hash; llt->llt_fill_sa_entry = in_lltable_fill_sa_entry; Modified: head/sys/netinet/ip_carp.c ============================================================================== --- head/sys/netinet/ip_carp.c Mon Sep 14 15:47:25 2015 (r287788) +++ head/sys/netinet/ip_carp.c Mon Sep 14 16:48:19 2015 (r287789) @@ -985,7 +985,7 @@ carp_ifa_delroute(struct ifaddr *ifa) case AF_INET6: ifa_del_loopback_route(ifa, (struct sockaddr *)&ifatoia6(ifa)->ia_addr); - nd6_rem_ifa_lle(ifatoia6(ifa)); + nd6_rem_ifa_lle(ifatoia6(ifa), 1); break; #endif } Modified: head/sys/netinet6/in6.c ============================================================================== --- head/sys/netinet6/in6.c Mon Sep 14 15:47:25 2015 (r287788) +++ head/sys/netinet6/in6.c Mon Sep 14 16:48:19 2015 (r287789) @@ -1307,9 +1307,6 @@ in6_purgeaddr(struct ifaddr *ifa) /* stop DAD processing */ nd6_dad_stop(ifa); - /* Remove local address entry from lltable. */ - nd6_rem_ifa_lle(ia); - /* Leave multicast groups. */ while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) { LIST_REMOVE(imm, i6mm_chain); @@ -1333,6 +1330,7 @@ static void in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { char ip6buf[INET6_ADDRSTRLEN]; + int remove_lle; IF_ADDR_WLOCK(ifp); TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); @@ -1353,15 +1351,21 @@ in6_unlink_ifa(struct in6_ifaddr *ia, st * Release the reference to the base prefix. There should be a * positive reference. */ + remove_lle = 0; if (ia->ia6_ndpr == NULL) { nd6log((LOG_NOTICE, "in6_unlink_ifa: autoconf'ed address " "%s has no prefix\n", ip6_sprintf(ip6buf, IA6_IN6(ia)))); } else { ia->ia6_ndpr->ndpr_refcnt--; + /* Do not delete lles within prefix if refcont != 0 */ + if (ia->ia6_ndpr->ndpr_refcnt == 0) + remove_lle = 1; ia->ia6_ndpr = NULL; } + nd6_rem_ifa_lle(ia, remove_lle); + /* * Also, if the address being removed is autoconf'ed, call * pfxlist_onlink_check() since the release might affect the status of @@ -2081,15 +2085,33 @@ in6_lltable_new(const struct in6_addr *a } static int -in6_lltable_match_prefix(const struct sockaddr *prefix, - const struct sockaddr *mask, u_int flags, struct llentry *lle) +in6_lltable_match_prefix(const struct sockaddr *saddr, + const struct sockaddr *smask, u_int flags, struct llentry *lle) { - const struct sockaddr_in6 *pfx = (const struct sockaddr_in6 *)prefix; - const struct sockaddr_in6 *msk = (const struct sockaddr_in6 *)mask; + const struct in6_addr *addr, *mask, *lle_addr; + + addr = &((const struct sockaddr_in6 *)saddr)->sin6_addr; + mask = &((const struct sockaddr_in6 *)smask)->sin6_addr; + lle_addr = &lle->r_l3addr.addr6; + + if (IN6_ARE_MASKED_ADDR_EQUAL(lle_addr, addr, mask) == 0) + return (0); + + if (lle->la_flags & LLE_IFADDR) { - if (IN6_ARE_MASKED_ADDR_EQUAL(&lle->r_l3addr.addr6, - &pfx->sin6_addr, &msk->sin6_addr) && - ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC))) + /* + * Delete LLE_IFADDR records IFF address & flag matches. + * Note that addr is the interface address within prefix + * being matched. + */ + if (IN6_ARE_ADDR_EQUAL(addr, lle_addr) && + (flags & LLE_STATIC) != 0) + return (1); + return (0); + } + + /* flags & LLE_STATIC means deleting both dynamic and static entries */ + if ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)) return (1); return (0); @@ -2200,36 +2222,16 @@ in6_lltable_find_dst(struct lltable *llt return (lle); } -static int -in6_lltable_delete(struct lltable *llt, u_int flags, - const struct sockaddr *l3addr) +static void +in6_lltable_delete_entry(struct lltable *llt, struct llentry *lle) { - const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr; - struct llentry *lle; - - IF_AFDATA_LOCK_ASSERT(llt->llt_ifp); - KASSERT(l3addr->sa_family == AF_INET6, - ("sin_family %d", l3addr->sa_family)); - - lle = in6_lltable_find_dst(llt, &sin6->sin6_addr); - if (lle == NULL) - return (ENOENT); - - if (!(lle->la_flags & LLE_IFADDR) || (flags & LLE_IFADDR)) { - LLE_WLOCK(lle); - lle->la_flags |= LLE_DELETED; - EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED); + lle->la_flags |= LLE_DELETED; + EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED); #ifdef DIAGNOSTIC - log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); + log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); #endif - if ((lle->la_flags & (LLE_STATIC | LLE_IFADDR)) == LLE_STATIC) - llentry_free(lle); - else - LLE_WUNLOCK(lle); - } - - return (0); + llentry_free(lle); } static struct llentry * @@ -2369,7 +2371,7 @@ in6_lltattach(struct ifnet *ifp) llt->llt_lookup = in6_lltable_lookup; llt->llt_alloc_entry = in6_lltable_alloc; - llt->llt_delete = in6_lltable_delete; + llt->llt_delete_entry = in6_lltable_delete_entry; llt->llt_dump_entry = in6_lltable_dump_entry; llt->llt_hash = in6_lltable_hash; llt->llt_fill_sa_entry = in6_lltable_fill_sa_entry; Modified: head/sys/netinet6/nd6.c ============================================================================== --- head/sys/netinet6/nd6.c Mon Sep 14 15:47:25 2015 (r287788) +++ head/sys/netinet6/nd6.c Mon Sep 14 16:48:19 2015 (r287789) @@ -2245,23 +2245,26 @@ nd6_add_ifa_lle(struct in6_ifaddr *ia) } /* - * Removes ALL lle records for interface address prefix. - * XXXME: That's probably not we really want to do, we need - * to remove address record only and keep other records - * until we determine if given prefix is really going - * to be removed. + * Removes either all lle entries for given @ia, or lle + * corresponding to @ia address. */ void -nd6_rem_ifa_lle(struct in6_ifaddr *ia) +nd6_rem_ifa_lle(struct in6_ifaddr *ia, int all) { struct sockaddr_in6 mask, addr; + struct sockaddr *saddr, *smask; struct ifnet *ifp; ifp = ia->ia_ifa.ifa_ifp; memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); - lltable_prefix_free(AF_INET6, (struct sockaddr *)&addr, - (struct sockaddr *)&mask, LLE_STATIC); + saddr = (struct sockaddr *)&addr; + smask = (struct sockaddr *)&mask; + + if (all != 0) + lltable_prefix_free(AF_INET6, saddr, smask, LLE_STATIC); + else + lltable_delete_addr(LLTABLE6(ifp), LLE_IFADDR, saddr); } /* Modified: head/sys/netinet6/nd6.h ============================================================================== --- head/sys/netinet6/nd6.h Mon Sep 14 15:47:25 2015 (r287788) +++ head/sys/netinet6/nd6.h Mon Sep 14 16:48:19 2015 (r287789) @@ -427,7 +427,7 @@ int nd6_flush_holdchain(struct ifnet *, struct sockaddr_in6 *); int nd6_need_cache(struct ifnet *); int nd6_add_ifa_lle(struct in6_ifaddr *); -void nd6_rem_ifa_lle(struct in6_ifaddr *); +void nd6_rem_ifa_lle(struct in6_ifaddr *, int); int nd6_storelladdr(struct ifnet *, struct mbuf *, const struct sockaddr *, u_char *, uint32_t *);