* Move lle creation/deletion from lla_lookup to separate functions:

lla_lookup(LLE_CREATE) -> lla_create
  lla_lookup(LLE_DELETE) -> lla_delete
  Assume lla_create to return LLE_EXCLUSIVE lock for lle.
* Rework lla_rt_output to perform all lle changes under afdata WLOCK.
* change arp_ifscrub() ackquire afdata WLOCK, the same as arp_ifinit().
This commit is contained in:
Alexander V. Chernikov 2014-11-15 18:54:07 +00:00
parent f7bab8d0dd
commit b4b1367ae4
8 changed files with 372 additions and 226 deletions

View File

@ -147,8 +147,7 @@ llentry_alloc(struct ifnet *ifp, struct lltable *lt,
if ((la == NULL) &&
(ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
IF_AFDATA_WLOCK(ifp);
la = lla_lookup(lt, (LLE_CREATE | LLE_EXCLUSIVE),
(struct sockaddr *)dst);
la = lla_create(lt, 0, (struct sockaddr *)dst);
IF_AFDATA_WUNLOCK(ifp);
}
@ -259,7 +258,7 @@ lltable_init(struct ifnet *ifp, int af)
}
/*
* Called in route_output when adding/deleting a route to an interface.
* Called in route_output when rtm_flags contains RTF_LLDATA.
*/
int
lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
@ -270,8 +269,8 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
struct ifnet *ifp;
struct lltable *llt;
struct llentry *lle;
u_int laflags = 0, flags = 0;
int error = 0;
u_int laflags = 0;
int error;
KASSERT(dl != NULL && dl->sdl_family == AF_LINK,
("%s: invalid dl\n", __func__));
@ -283,24 +282,6 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
return EINVAL;
}
switch (rtm->rtm_type) {
case RTM_ADD:
if (rtm->rtm_flags & RTF_ANNOUNCE)
flags |= LLE_PUB;
flags |= LLE_CREATE;
break;
case RTM_DELETE:
flags |= LLE_DELETE;
break;
case RTM_CHANGE:
break;
default:
return EINVAL; /* XXX not implemented yet */
}
/* XXX linked list may be too expensive */
LLTABLE_RLOCK();
SLIST_FOREACH(llt, &V_lltables, llt_link) {
@ -311,59 +292,63 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
LLTABLE_RUNLOCK();
KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n"));
if (flags & LLE_CREATE)
flags |= LLE_EXCLUSIVE;
error = 0;
IF_AFDATA_LOCK(ifp);
lle = lla_lookup(llt, flags, dst);
IF_AFDATA_UNLOCK(ifp);
if (LLE_IS_VALID(lle)) {
if (flags & LLE_CREATE) {
/*
* If we delay the delete, then a subsequent
* "arp add" should look up this entry, reset the
* LLE_DELETED flag, and reset the expiration timer
*/
bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen);
lle->la_flags |= (flags & LLE_PUB);
lle->la_flags |= LLE_VALID;
lle->la_flags &= ~LLE_DELETED;
#ifdef INET6
/*
* ND6
*/
if (dst->sa_family == AF_INET6)
lle->ln_state = ND6_LLINFO_REACHABLE;
#endif
/*
* NB: arp and ndp always set (RTF_STATIC | RTF_HOST)
*/
if (rtm->rtm_rmx.rmx_expire == 0) {
lle->la_flags |= LLE_STATIC;
lle->la_expire = 0;
} else
lle->la_expire = rtm->rtm_rmx.rmx_expire;
laflags = lle->la_flags;
LLE_WUNLOCK(lle);
#ifdef INET
/* gratuitous ARP */
if ((laflags & LLE_PUB) && dst->sa_family == AF_INET)
arprequest(ifp,
&((struct sockaddr_in *)dst)->sin_addr,
&((struct sockaddr_in *)dst)->sin_addr,
(u_char *)LLADDR(dl));
#endif
} else {
if (flags & LLE_EXCLUSIVE)
LLE_WUNLOCK(lle);
else
LLE_RUNLOCK(lle);
switch (rtm->rtm_type) {
case RTM_ADD:
/* Add static LLE */
IF_AFDATA_WLOCK(ifp);
lle = lla_create(llt, 0, dst);
if (lle == NULL) {
IF_AFDATA_WUNLOCK(ifp);
return (ENOMEM);
}
} else if ((lle == NULL) && (flags & LLE_DELETE))
error = EINVAL;
bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen);
if ((rtm->rtm_flags & RTF_ANNOUNCE))
lle->la_flags |= LLE_PUB;
lle->la_flags |= LLE_VALID;
#ifdef INET6
/*
* ND6
*/
if (dst->sa_family == AF_INET6)
lle->ln_state = ND6_LLINFO_REACHABLE;
#endif
/*
* NB: arp and ndp always set (RTF_STATIC | RTF_HOST)
*/
if (rtm->rtm_rmx.rmx_expire == 0) {
lle->la_flags |= LLE_STATIC;
lle->la_expire = 0;
} else
lle->la_expire = rtm->rtm_rmx.rmx_expire;
laflags = lle->la_flags;
LLE_WUNLOCK(lle);
IF_AFDATA_WUNLOCK(ifp);
#ifdef INET
/* gratuitous ARP */
if ((laflags & LLE_PUB) && dst->sa_family == AF_INET)
arprequest(ifp,
&((struct sockaddr_in *)dst)->sin_addr,
&((struct sockaddr_in *)dst)->sin_addr,
(u_char *)LLADDR(dl));
#endif
break;
case RTM_DELETE:
IF_AFDATA_WLOCK(ifp);
error = lla_delete(llt, 0, dst);
IF_AFDATA_WUNLOCK(ifp);
return (error == 0 ? 0 : ENOENT);
default:
error = EINVAL;
}
return (error);
}

View File

@ -144,25 +144,33 @@ struct llentry {
#define LLTBL_HASHMASK (LLTBL_HASHTBL_SIZE - 1)
#endif
typedef struct llentry *(llt_lookup_t)(struct lltable *, u_int flags,
const struct sockaddr *l3addr);
typedef struct llentry *(llt_create_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_prefix_free_t)(struct lltable *,
const struct sockaddr *prefix, const struct sockaddr *mask, u_int flags);
typedef int (llt_dump_t)(struct lltable *, struct sysctl_req *);
struct lltable {
SLIST_ENTRY(lltable) llt_link;
struct llentries lle_head[LLTBL_HASHTBL_SIZE];
int llt_af;
struct ifnet *llt_ifp;
void (*llt_prefix_free)(struct lltable *,
const struct sockaddr *prefix,
const struct sockaddr *mask,
u_int flags);
struct llentry * (*llt_lookup)(struct lltable *, u_int flags,
const struct sockaddr *l3addr);
int (*llt_dump)(struct lltable *,
struct sysctl_req *);
llt_lookup_t *llt_lookup;
llt_create_t *llt_create;
llt_delete_t *llt_delete;
llt_prefix_free_t *llt_prefix_free;
llt_dump_t *llt_dump;
};
MALLOC_DECLARE(M_LLTABLE);
/*
* flags to be passed to arplookup.
* Various LLE flags
*/
#define LLE_DELETED 0x0001 /* entry must be deleted */
#define LLE_STATIC 0x0002 /* entry is static */
@ -170,9 +178,8 @@ MALLOC_DECLARE(M_LLTABLE);
#define LLE_VALID 0x0008 /* ll_addr is valid */
#define LLE_PUB 0x0020 /* publish entry ??? */
#define LLE_LINKED 0x0040 /* linked to lookup structure */
/* LLE request flags */
#define LLE_EXCLUSIVE 0x2000 /* return lle xlocked */
#define LLE_DELETE 0x4000 /* delete on a lookup - match LLE_IFADDR */
#define LLE_CREATE 0x8000 /* create on a lookup miss */
#define LLATBL_HASH(key, mask) \
(((((((key >> 8) ^ key) >> 8) ^ key) >> 8) ^ key) & mask)
@ -196,9 +203,25 @@ struct llentry *llentry_alloc(struct ifnet *, struct lltable *,
static __inline struct llentry *
lla_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
{
return llt->llt_lookup(llt, flags, l3addr);
}
static __inline struct llentry *
lla_create(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
{
return llt->llt_create(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 <sys/eventhandler.h>

View File

@ -152,10 +152,9 @@ arp_ifscrub(struct ifnet *ifp, uint32_t addr)
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_addr.s_addr = addr;
IF_AFDATA_RLOCK(ifp);
lla_lookup(LLTABLE(ifp), (LLE_DELETE | LLE_IFADDR),
(struct sockaddr *)&addr4);
IF_AFDATA_RUNLOCK(ifp);
IF_AFDATA_WLOCK(ifp);
lla_delete(LLTABLE(ifp), LLE_IFADDR, (struct sockaddr *)&addr4);
IF_AFDATA_WUNLOCK(ifp);
}
#endif
@ -373,8 +372,9 @@ arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m,
u_int flags = 0;
struct mbuf *curr = NULL;
struct mbuf *next = NULL;
int error, renew;
int create, error, renew;
create = 0;
*lle = NULL;
if (m != NULL) {
if (m->m_flags & M_BCAST) {
@ -395,13 +395,14 @@ arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m,
IF_AFDATA_RUNLOCK(ifp);
if ((la == NULL) && ((flags & LLE_EXCLUSIVE) == 0)
&& ((ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0)) {
flags |= (LLE_CREATE | LLE_EXCLUSIVE);
create = 1;
flags |= LLE_EXCLUSIVE;
IF_AFDATA_WLOCK(ifp);
la = lla_lookup(LLTABLE(ifp), flags, dst);
la = lla_create(LLTABLE(ifp), flags, dst);
IF_AFDATA_WUNLOCK(ifp);
}
if (la == NULL) {
if (flags & LLE_CREATE)
if (create != 0)
log(LOG_DEBUG,
"arpresolve: can't allocate llinfo for %s on %s\n",
inet_ntoa(SIN(dst)->sin_addr), ifp->if_xname);
@ -613,7 +614,7 @@ in_arpinput(struct mbuf *m)
int op, flags;
int req_len;
int bridged = 0, is_bridge = 0;
int carped;
int carped, create;
struct nhop4_extended nh_ext;
struct sockaddr_in sin;
sin.sin_len = sizeof(struct sockaddr_in);
@ -765,10 +766,13 @@ in_arpinput(struct mbuf *m)
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.sin_addr = isaddr;
flags = (itaddr.s_addr == myaddr.s_addr) ? LLE_CREATE : 0;
flags |= LLE_EXCLUSIVE;
create = (itaddr.s_addr == myaddr.s_addr) ? 1 : 0;
flags = LLE_EXCLUSIVE;
IF_AFDATA_LOCK(ifp);
la = lla_lookup(LLTABLE(ifp), flags, (struct sockaddr *)&sin);
if (create != 0)
la = lla_create(LLTABLE(ifp), 0, (struct sockaddr *)&sin);
else
la = lla_lookup(LLTABLE(ifp), flags, (struct sockaddr *)&sin);
IF_AFDATA_UNLOCK(ifp);
if (la != NULL) {
/* the following is not an error when doing bridging */
@ -975,14 +979,14 @@ arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa)
* that L2 entry as permanent
*/
IF_AFDATA_LOCK(ifp);
lle = lla_lookup(LLTABLE(ifp), (LLE_CREATE | LLE_IFADDR | LLE_STATIC),
lle = lla_create(LLTABLE(ifp), LLE_IFADDR | LLE_STATIC,
(struct sockaddr *)IA_SIN(ifa));
IF_AFDATA_UNLOCK(ifp);
if (lle == NULL)
log(LOG_INFO, "arp_ifinit: cannot create arp "
"entry for interface address\n");
else
LLE_RUNLOCK(lle);
LLE_WUNLOCK(lle);
}
ifa->ifa_rtrequest = NULL;
}

View File

@ -1046,6 +1046,117 @@ in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr
return (0);
}
static inline struct llentry *
in_lltable_find_dst(struct lltable *llt, struct in_addr dst)
{
struct llentry *lle;
struct llentries *lleh;
struct sockaddr_in *sa2;
u_int hashkey;
hashkey = dst.s_addr;
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
LIST_FOREACH(lle, lleh, lle_next) {
sa2 = satosin(L3_ADDR(lle)); /* XXX: Change to proper L3 */
if (lle->la_flags & LLE_DELETED)
continue;
if (sa2->sin_addr.s_addr == dst.s_addr)
break;
}
return (lle);
}
static int
in_lltable_delete(struct lltable *llt, u_int flags,
const struct sockaddr *l3addr)
{
const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr;
struct ifnet *ifp = llt->llt_ifp;
struct llentry *lle;
IF_AFDATA_WLOCK_ASSERT(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);
#ifdef DIAGNOSTIC
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);
}
static struct llentry *
in_lltable_create(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
{
const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr;
struct ifnet *ifp = llt->llt_ifp;
struct llentry *lle;
struct llentries *lleh;
u_int hashkey;
IF_AFDATA_WLOCK_ASSERT(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) {
LLE_WLOCK(lle);
return (lle);
}
/* no existing record, we need to create new one */
/*
* A route that covers the given address must have
* been installed 1st because we are doing a resolution,
* verify this.
*/
if (!(flags & LLE_IFADDR) &&
in_lltable_rtcheck(ifp, flags, l3addr) != 0)
return (NULL);
lle = in_lltable_new(l3addr, flags);
if (lle == NULL) {
log(LOG_INFO, "lla_lookup: new lle malloc failed\n");
return (NULL);
}
lle->la_flags = flags;
if ((flags & LLE_IFADDR) == LLE_IFADDR) {
bcopy(IF_LLADDR(ifp), &lle->ll_addr, ifp->if_addrlen);
lle->la_flags |= (LLE_VALID | LLE_STATIC);
}
hashkey = sin->sin_addr.s_addr;
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
lle->lle_tbl = llt;
lle->lle_head = lleh;
lle->la_flags |= LLE_LINKED;
LIST_INSERT_HEAD(lleh, lle, lle_next);
LLE_WLOCK(lle);
return (lle);
}
/*
* Return NULL if not found or marked for deletion.
* If found return lle read locked.
@ -1072,62 +1183,15 @@ in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3add
if (sa2->sin_addr.s_addr == sin->sin_addr.s_addr)
break;
}
if (lle == NULL) {
#ifdef DIAGNOSTIC
if (flags & LLE_DELETE)
log(LOG_INFO, "interface address is missing from cache = %p in delete\n", lle);
#endif
if (!(flags & LLE_CREATE))
return (NULL);
IF_AFDATA_WLOCK_ASSERT(ifp);
/*
* A route that covers the given address must have
* been installed 1st because we are doing a resolution,
* verify this.
*/
if (!(flags & LLE_IFADDR) &&
in_lltable_rtcheck(ifp, flags, l3addr) != 0)
goto done;
lle = in_lltable_new(l3addr, flags);
if (lle == NULL) {
log(LOG_INFO, "lla_lookup: new lle malloc failed\n");
goto done;
}
lle->la_flags = flags & ~LLE_CREATE;
if ((flags & (LLE_CREATE | LLE_IFADDR)) == (LLE_CREATE | LLE_IFADDR)) {
bcopy(IF_LLADDR(ifp), &lle->ll_addr, ifp->if_addrlen);
lle->la_flags |= (LLE_VALID | LLE_STATIC);
}
if (lle == NULL)
return (NULL);
lle->lle_tbl = llt;
lle->lle_head = lleh;
lle->la_flags |= LLE_LINKED;
LIST_INSERT_HEAD(lleh, lle, lle_next);
} else if (flags & LLE_DELETE) {
if (!(lle->la_flags & LLE_IFADDR) || (flags & LLE_IFADDR)) {
LLE_WLOCK(lle);
lle->la_flags |= LLE_DELETED;
EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED);
#ifdef DIAGNOSTIC
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);
}
lle = (void *)-1;
if (flags & LLE_EXCLUSIVE)
LLE_WLOCK(lle);
else
LLE_RLOCK(lle);
}
if (LLE_IS_VALID(lle)) {
if (flags & LLE_EXCLUSIVE)
LLE_WLOCK(lle);
else
LLE_RLOCK(lle);
}
done:
return (lle);
}
@ -1218,6 +1282,8 @@ in_domifattach(struct ifnet *ifp)
if (llt != NULL) {
llt->llt_prefix_free = in_lltable_prefix_free;
llt->llt_lookup = in_lltable_lookup;
llt->llt_create = in_lltable_create;
llt->llt_delete = in_lltable_delete;
llt->llt_dump = in_lltable_dump;
}
ii->ii_llt = llt;

View File

@ -463,8 +463,7 @@ toe_nd6_resolve(struct ifnet *ifp, struct sockaddr *sa, uint8_t *lladdr)
IF_AFDATA_RUNLOCK(ifp);
if (lle == NULL) {
IF_AFDATA_LOCK(ifp);
lle = nd6_lookup(&sin6->sin6_addr, ND6_CREATE | ND6_EXCLUSIVE,
ifp);
lle = nd6_create(&sin6->sin6_addr, 0, ifp);
IF_AFDATA_UNLOCK(ifp);
if (lle == NULL)
return (ENOMEM); /* Couldn't create entry in cache. */

View File

@ -2200,8 +2200,60 @@ in6_lltable_rtcheck(struct ifnet *ifp,
return 0;
}
static inline struct llentry *
in6_lltable_find_dst(struct lltable *llt, const struct in6_addr *dst)
{
struct llentry *lle;
struct llentries *lleh;
u_int hashkey;
hashkey = dst->s6_addr32[3];
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
LIST_FOREACH(lle, lleh, lle_next) {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)L3_ADDR(lle);
if (lle->la_flags & LLE_DELETED)
continue;
if (bcmp(&sa6->sin6_addr, dst, sizeof(struct in6_addr)) == 0)
break;
}
return (lle);
}
static int
in6_lltable_delete(struct lltable *llt, u_int flags,
const struct sockaddr *l3addr)
{
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr;
struct ifnet *ifp = llt->llt_ifp;
struct llentry *lle;
IF_AFDATA_LOCK_ASSERT(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;
#ifdef DIAGNOSTIC
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);
}
static struct llentry *
in6_lltable_lookup(struct lltable *llt, u_int flags,
in6_lltable_create(struct lltable *llt, u_int flags,
const struct sockaddr *l3addr)
{
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr;
@ -2210,70 +2262,70 @@ in6_lltable_lookup(struct lltable *llt, u_int flags,
struct llentries *lleh;
u_int hashkey;
IF_AFDATA_WLOCK_ASSERT(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) {
LLE_WLOCK(lle);
return (lle);
}
/*
* A route that covers the given address must have
* been installed 1st because we are doing a resolution,
* verify this.
*/
if (!(flags & LLE_IFADDR) &&
in6_lltable_rtcheck(ifp, flags, l3addr) != 0)
return NULL;
lle = in6_lltable_new(l3addr, flags);
if (lle == NULL) {
log(LOG_INFO, "lla_lookup: new lle malloc failed\n");
return NULL;
}
lle->la_flags = flags;
if ((flags & LLE_IFADDR) == LLE_IFADDR) {
bcopy(IF_LLADDR(ifp), &lle->ll_addr, ifp->if_addrlen);
lle->la_flags |= (LLE_VALID | LLE_STATIC);
}
hashkey = sin6->sin6_addr.s6_addr32[3];
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
lle->lle_tbl = llt;
lle->lle_head = lleh;
lle->la_flags |= LLE_LINKED;
LIST_INSERT_HEAD(lleh, lle, lle_next);
LLE_WLOCK(lle);
return (lle);
}
static struct llentry *
in6_lltable_lookup(struct lltable *llt, u_int flags,
const struct sockaddr *l3addr)
{
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr;
struct ifnet *ifp = llt->llt_ifp;
struct llentry *lle;
IF_AFDATA_LOCK_ASSERT(ifp);
KASSERT(l3addr->sa_family == AF_INET6,
("sin_family %d", l3addr->sa_family));
hashkey = sin6->sin6_addr.s6_addr32[3];
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
LIST_FOREACH(lle, lleh, lle_next) {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)L3_ADDR(lle);
if (lle->la_flags & LLE_DELETED)
continue;
if (bcmp(&sa6->sin6_addr, &sin6->sin6_addr,
sizeof(struct in6_addr)) == 0)
break;
}
lle = in6_lltable_find_dst(llt, &sin6->sin6_addr);
if (lle == NULL) {
if (!(flags & LLE_CREATE))
return (NULL);
IF_AFDATA_WLOCK_ASSERT(ifp);
/*
* A route that covers the given address must have
* been installed 1st because we are doing a resolution,
* verify this.
*/
if (!(flags & LLE_IFADDR) &&
in6_lltable_rtcheck(ifp, flags, l3addr) != 0)
return NULL;
if (lle == NULL)
return (NULL);
lle = in6_lltable_new(l3addr, flags);
if (lle == NULL) {
log(LOG_INFO, "lla_lookup: new lle malloc failed\n");
return NULL;
}
lle->la_flags = flags & ~LLE_CREATE;
if ((flags & (LLE_CREATE | LLE_IFADDR)) == (LLE_CREATE | LLE_IFADDR)) {
bcopy(IF_LLADDR(ifp), &lle->ll_addr, ifp->if_addrlen);
lle->la_flags |= (LLE_VALID | LLE_STATIC);
}
lle->lle_tbl = llt;
lle->lle_head = lleh;
lle->la_flags |= LLE_LINKED;
LIST_INSERT_HEAD(lleh, lle, lle_next);
} else if (flags & LLE_DELETE) {
if (!(lle->la_flags & LLE_IFADDR) || (flags & LLE_IFADDR)) {
LLE_WLOCK(lle);
lle->la_flags |= LLE_DELETED;
#ifdef DIAGNOSTIC
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);
}
lle = (void *)-1;
}
if (LLE_IS_VALID(lle)) {
if (flags & LLE_EXCLUSIVE)
LLE_WLOCK(lle);
else
LLE_RLOCK(lle);
}
if (flags & LLE_EXCLUSIVE)
LLE_WLOCK(lle);
else
LLE_RLOCK(lle);
return (lle);
}
@ -2387,6 +2439,8 @@ in6_domifattach(struct ifnet *ifp)
if (ext->lltable != NULL) {
ext->lltable->llt_prefix_free = in6_lltable_prefix_free;
ext->lltable->llt_lookup = in6_lltable_lookup;
ext->lltable->llt_create = in6_lltable_create;
ext->lltable->llt_delete = in6_lltable_delete;
ext->lltable->llt_dump = in6_lltable_dump;
}

View File

@ -854,14 +854,31 @@ nd6_lookup(struct in6_addr *addr6, int flags, struct ifnet *ifp)
IF_AFDATA_LOCK_ASSERT(ifp);
llflags = 0;
if (flags & ND6_CREATE)
llflags |= LLE_CREATE;
if (flags & ND6_EXCLUSIVE)
llflags |= LLE_EXCLUSIVE;
llflags = (flags & ND6_EXCLUSIVE) ? LLE_EXCLUSIVE : 0;
ln = lla_lookup(LLTABLE6(ifp), llflags, (struct sockaddr *)&sin6);
if ((ln != NULL) && (llflags & LLE_CREATE))
return (ln);
}
/*
* the caller acquires and releases the lock on the lltbls
* Returns the llentry wlocked
*/
struct llentry *
nd6_create(struct in6_addr *addr6, int flags, struct ifnet *ifp)
{
struct sockaddr_in6 sin6;
struct llentry *ln;
bzero(&sin6, sizeof(sin6));
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = *addr6;
IF_AFDATA_WLOCK_ASSERT(ifp);
ln = lla_create(LLTABLE6(ifp), 0, (struct sockaddr *)&sin6);
if (ln != NULL)
ln->ln_state = ND6_LLINFO_NOSTATE;
return (ln);
@ -1581,7 +1598,7 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
if (ln == NULL) {
flags |= ND6_EXCLUSIVE;
IF_AFDATA_LOCK(ifp);
ln = nd6_lookup(from, flags | ND6_CREATE, ifp);
ln = nd6_create(from, 0, ifp);
IF_AFDATA_UNLOCK(ifp);
is_newentry = 1;
} else {
@ -1938,7 +1955,6 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
struct m_tag *mtag;
struct ip6_hdr *ip6;
int error = 0;
int flags = 0;
int has_lle = 0;
int ip6len;
@ -1982,9 +1998,8 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
* the condition below is not very efficient. But we believe
* it is tolerable, because this should be a rare case.
*/
flags = ND6_CREATE | ND6_EXCLUSIVE;
IF_AFDATA_LOCK(ifp);
lle = nd6_lookup(&dst->sin6_addr, flags, ifp);
lle = nd6_create(&dst->sin6_addr, 0, ifp);
IF_AFDATA_UNLOCK(ifp);
}
}
@ -2238,8 +2253,8 @@ nd6_add_ifa_lle(struct in6_ifaddr *ia)
ifp = ia->ia_ifa.ifa_ifp;
IF_AFDATA_LOCK(ifp);
ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
ln = lla_lookup(LLTABLE6(ifp), (LLE_CREATE | LLE_IFADDR |
LLE_EXCLUSIVE), (struct sockaddr *)&ia->ia_addr);
ln = lla_create(LLTABLE6(ifp), LLE_IFADDR,
(struct sockaddr *)&ia->ia_addr);
IF_AFDATA_UNLOCK(ifp);
if (ln != NULL) {
ln->la_expire = 0; /* for IPv6 this means permanent */

View File

@ -88,7 +88,6 @@ struct nd_ifinfo {
#define ND6_IFF_NO_RADR 0x40
#define ND6_IFF_NO_PREFER_IFACE 0x80 /* XXX: not related to ND. */
#define ND6_CREATE LLE_CREATE
#define ND6_EXCLUSIVE LLE_EXCLUSIVE
#ifdef _KERNEL
@ -395,7 +394,8 @@ int nd6_is_addr_neighbor(struct sockaddr_in6 *, struct ifnet *);
void nd6_option_init(void *, int, union nd_opts *);
struct nd_opt_hdr *nd6_option(union nd_opts *);
int nd6_options(union nd_opts *);
struct llentry *nd6_lookup(struct in6_addr *, int, struct ifnet *);
struct llentry *nd6_lookup(struct in6_addr *, int, struct ifnet *);
struct llentry *nd6_create(struct in6_addr *, int, struct ifnet *);
void nd6_setmtu(struct ifnet *);
void nd6_llinfo_settimer(struct llentry *, long);
void nd6_llinfo_settimer_locked(struct llentry *, long);