* 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:
parent
f7bab8d0dd
commit
b4b1367ae4
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
172
sys/netinet/in.c
172
sys/netinet/in.c
@ -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;
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user