From c9d763bf41867664a939868b483b3c54bdb8e0a5 Mon Sep 17 00:00:00 2001 From: Qing Li Date: Wed, 20 May 2009 21:07:15 +0000 Subject: [PATCH] When an interface address is removed and the last prefix route is also being deleted, the link-layer address table (arp or nd6) will flush those L2 llinfo entries that match the removed prefix. Reviewed by: kmacy --- sys/net/if_llatbl.c | 17 +++++++++++++++++ sys/net/if_llatbl.h | 5 +++++ sys/netinet/in.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ sys/netinet6/in6.c | 25 +++++++++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c index 7ef1c9a20050..b5da1ea44360 100644 --- a/sys/net/if_llatbl.c +++ b/sys/net/if_llatbl.c @@ -195,6 +195,23 @@ lltable_drain(int af) IFNET_RUNLOCK(); } +void +lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask) +{ + struct lltable *llt; + + IFNET_RLOCK(); + SLIST_FOREACH(llt, &lltables, llt_link) { + if (llt->llt_af != af) + continue; + + llt->llt_prefix_free(llt, prefix, mask); + } + IFNET_RUNLOCK(); +} + + + /* * Create a new lltable. */ diff --git a/sys/net/if_llatbl.h b/sys/net/if_llatbl.h index 3b3086f75f76..4d721efc35f7 100644 --- a/sys/net/if_llatbl.h +++ b/sys/net/if_llatbl.h @@ -147,6 +147,9 @@ struct lltable { struct llentry * (*llt_new)(const struct sockaddr *, u_int); void (*llt_free)(struct lltable *, struct llentry *); + void (*llt_prefix_free)(struct lltable *, + const struct sockaddr *prefix, + const struct sockaddr *mask); struct llentry * (*llt_lookup)(struct lltable *, u_int flags, const struct sockaddr *l3addr); int (*llt_rtcheck)(struct ifnet *, @@ -174,6 +177,8 @@ MALLOC_DECLARE(M_LLTABLE); struct lltable *lltable_init(struct ifnet *, int); void lltable_free(struct lltable *); +void lltable_prefix_free(int, struct sockaddr *, + struct sockaddr *); void lltable_drain(int); int lltable_sysctl_dumparp(int, struct sysctl_req *); diff --git a/sys/netinet/in.c b/sys/netinet/in.c index eaa8c7e87480..6f408eb92860 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -1013,6 +1013,7 @@ in_scrubprefix(struct in_ifaddr *target) struct in_ifaddr *ia; struct in_addr prefix, mask, p; int error; + struct sockaddr_in prefix0, mask0; struct rt_addrinfo info; struct sockaddr_dl null_sdl; @@ -1081,6 +1082,20 @@ 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); + /* * As no-one seem to have this prefix, we can remove the route. */ @@ -1232,6 +1247,34 @@ in_lltable_free(struct lltable *llt, struct llentry *lle) free(lle, M_LLTABLE); } + +#define IN_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ + (((ntohl((d)->sin_addr.s_addr) ^ (a)->sin_addr.s_addr) & (m)->sin_addr.s_addr)) == 0 ) + +static void +in_lltable_prefix_free(struct lltable *llt, + const struct sockaddr *prefix, + const struct sockaddr *mask) +{ + const struct sockaddr_in *pfx = (const struct sockaddr_in *)prefix; + const struct sockaddr_in *msk = (const struct sockaddr_in *)mask; + struct llentry *lle, *next; + register int i; + + for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { + LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { + + if (IN_ARE_MASKED_ADDR_EQUAL((struct sockaddr_in *)L3_ADDR(lle), + pfx, msk)) { + callout_drain(&lle->la_timer); + LLE_WLOCK(lle); + llentry_free(lle); + } + } + } +} + + static int in_lltable_rtcheck(struct ifnet *ifp, const struct sockaddr *l3addr) { @@ -1422,6 +1465,7 @@ in_domifattach(struct ifnet *ifp) if (llt != NULL) { llt->llt_new = in_lltable_new; llt->llt_free = in_lltable_free; + llt->llt_prefix_free = in_lltable_prefix_free; llt->llt_rtcheck = in_lltable_rtcheck; llt->llt_lookup = in_lltable_lookup; llt->llt_dump = in_lltable_dump; diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index a2321ff94c1a..60d56a8ae8e7 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -2284,6 +2284,30 @@ in6_lltable_free(struct lltable *llt, struct llentry *lle) free(lle, M_LLTABLE); } +static void +in6_lltable_prefix_free(struct lltable *llt, + const struct sockaddr *prefix, + const struct sockaddr *mask) +{ + const struct sockaddr_in6 *pfx = (const struct sockaddr_in6 *)prefix; + const struct sockaddr_in6 *msk = (const struct sockaddr_in6 *)mask; + struct llentry *lle, *next; + register int i; + + for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { + LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { + if (IN6_ARE_MASKED_ADDR_EQUAL( + &((struct sockaddr_in6 *)L3_ADDR(lle))->sin6_addr, + &pfx->sin6_addr, + &msk->sin6_addr)) { + callout_drain(&lle->la_timer); + LLE_WLOCK(lle); + llentry_free(lle); + } + } + } +} + static int in6_lltable_rtcheck(struct ifnet *ifp, const struct sockaddr *l3addr) { @@ -2490,6 +2514,7 @@ in6_domifattach(struct ifnet *ifp) if (ext->lltable != NULL) { ext->lltable->llt_new = in6_lltable_new; ext->lltable->llt_free = in6_lltable_free; + ext->lltable->llt_prefix_free = in6_lltable_prefix_free; ext->lltable->llt_rtcheck = in6_lltable_rtcheck; ext->lltable->llt_lookup = in6_lltable_lookup; ext->lltable->llt_dump = in6_lltable_dump;