summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/net/if_llatbl.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/net/if_llatbl.c')
-rw-r--r--freebsd/sys/net/if_llatbl.c745
1 files changed, 583 insertions, 162 deletions
diff --git a/freebsd/sys/net/if_llatbl.c b/freebsd/sys/net/if_llatbl.c
index 55b816a7..20c0b9d2 100644
--- a/freebsd/sys/net/if_llatbl.c
+++ b/freebsd/sys/net/if_llatbl.c
@@ -64,17 +64,43 @@ __FBSDID("$FreeBSD$");
MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables");
-static VNET_DEFINE(SLIST_HEAD(, lltable), lltables);
+static VNET_DEFINE(SLIST_HEAD(, lltable), lltables) =
+ SLIST_HEAD_INITIALIZER(lltables);
#define V_lltables VNET(lltables)
-extern void arprequest(struct ifnet *, struct in_addr *, struct in_addr *,
- u_char *);
-
-static void vnet_lltable_init(void);
-
struct rwlock lltable_rwlock;
RW_SYSINIT(lltable_rwlock, &lltable_rwlock, "lltable_rwlock");
+static void lltable_unlink(struct lltable *llt);
+static void llentries_unlink(struct lltable *llt, struct llentries *head);
+
+static void htable_unlink_entry(struct llentry *lle);
+static void htable_link_entry(struct lltable *llt, struct llentry *lle);
+static int htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f,
+ void *farg);
+
+/*
+ * Dump lle state for a specific address family.
+ */
+static int
+lltable_dump_af(struct lltable *llt, struct sysctl_req *wr)
+{
+ int error;
+
+ LLTABLE_LOCK_ASSERT();
+
+ if (llt->llt_ifp->if_flags & IFF_LOOPBACK)
+ return (0);
+ error = 0;
+
+ IF_AFDATA_RLOCK(llt->llt_ifp);
+ error = lltable_foreach_lle(llt,
+ (llt_foreach_cb_t *)llt->llt_dump_entry, wr);
+ IF_AFDATA_RUNLOCK(llt->llt_ifp);
+
+ return (error);
+}
+
/*
* Dump arp state for a specific address family.
*/
@@ -87,7 +113,7 @@ lltable_sysctl_dumparp(int af, struct sysctl_req *wr)
LLTABLE_RLOCK();
SLIST_FOREACH(llt, &V_lltables, llt_link) {
if (llt->llt_af == af) {
- error = llt->llt_dump(llt, wr);
+ error = lltable_dump_af(llt, wr);
if (error != 0)
goto done;
}
@@ -98,25 +124,144 @@ done:
}
/*
- * Deletes an address from the address table.
- * This function is called by the timer functions
- * such as arptimer() and nd6_llinfo_timer(), and
- * the caller does the locking.
+ * Common function helpers for chained hash table.
+ */
+
+/*
+ * Runs specified callback for each entry in @llt.
+ * Caller does the locking.
+ *
+ */
+static int
+htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
+{
+ struct llentry *lle, *next;
+ int i, error;
+
+ error = 0;
+
+ for (i = 0; i < llt->llt_hsize; i++) {
+ LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
+ error = f(llt, lle, farg);
+ if (error != 0)
+ break;
+ }
+ }
+
+ return (error);
+}
+
+static void
+htable_link_entry(struct lltable *llt, struct llentry *lle)
+{
+ struct llentries *lleh;
+ uint32_t hashidx;
+
+ if ((lle->la_flags & LLE_LINKED) != 0)
+ return;
+
+ IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
+
+ hashidx = llt->llt_hash(lle, llt->llt_hsize);
+ lleh = &llt->lle_head[hashidx];
+
+ lle->lle_tbl = llt;
+ lle->lle_head = lleh;
+ lle->la_flags |= LLE_LINKED;
+ LIST_INSERT_HEAD(lleh, lle, lle_next);
+}
+
+static void
+htable_unlink_entry(struct llentry *lle)
+{
+
+ if ((lle->la_flags & LLE_LINKED) != 0) {
+ IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
+ LIST_REMOVE(lle, lle_next);
+ lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
+#if 0
+ lle->lle_tbl = NULL;
+ lle->lle_head = NULL;
+#endif
+ }
+}
+
+struct prefix_match_data {
+ const struct sockaddr *addr;
+ const struct sockaddr *mask;
+ struct llentries dchain;
+ u_int flags;
+};
+
+static int
+htable_prefix_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
+{
+ struct prefix_match_data *pmd;
+
+ pmd = (struct prefix_match_data *)farg;
+
+ if (llt->llt_match_prefix(pmd->addr, pmd->mask, pmd->flags, lle)) {
+ LLE_WLOCK(lle);
+ LIST_INSERT_HEAD(&pmd->dchain, lle, lle_chain);
+ }
+
+ return (0);
+}
+
+static void
+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.addr = addr;
+ pmd.mask = mask;
+ pmd.flags = flags;
+ LIST_INIT(&pmd.dchain);
+
+ IF_AFDATA_WLOCK(llt->llt_ifp);
+ /* Push matching lles to chain */
+ lltable_foreach_lle(llt, htable_prefix_free_cb, &pmd);
+
+ llentries_unlink(llt, &pmd.dchain);
+ IF_AFDATA_WUNLOCK(llt->llt_ifp);
+
+ LIST_FOREACH_SAFE(lle, &pmd.dchain, lle_chain, next)
+ lltable_free_entry(llt, lle);
+}
+
+static void
+htable_free_tbl(struct lltable *llt)
+{
+
+ free(llt->lle_head, M_LLTABLE);
+ free(llt, M_LLTABLE);
+}
+
+static void
+llentries_unlink(struct lltable *llt, struct llentries *head)
+{
+ struct llentry *lle, *next;
+
+ LIST_FOREACH_SAFE(lle, head, lle_chain, next)
+ llt->llt_unlink_entry(lle);
+}
+
+/*
+ * Helper function used to drop all mbufs in hold queue.
*
* Returns the number of held packets, if any, that were dropped.
*/
size_t
-llentry_free(struct llentry *lle)
+lltable_drop_entry_queue(struct llentry *lle)
{
size_t pkts_dropped;
struct mbuf *next;
- IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
LLE_WLOCK_ASSERT(lle);
- LIST_REMOVE(lle, lle_next);
- lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
-
pkts_dropped = 0;
while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) {
next = lle->la_hold->m_nextpkt;
@@ -130,6 +275,162 @@ llentry_free(struct llentry *lle)
("%s: la_numheld %d > 0, pkts_droped %zd", __func__,
lle->la_numheld, pkts_dropped));
+ return (pkts_dropped);
+}
+
+void
+lltable_set_entry_addr(struct ifnet *ifp, struct llentry *lle,
+ const char *linkhdr, size_t linkhdrsize, int lladdr_off)
+{
+
+ memcpy(lle->r_linkdata, linkhdr, linkhdrsize);
+ lle->r_hdrlen = linkhdrsize;
+ lle->ll_addr = &lle->r_linkdata[lladdr_off];
+ lle->la_flags |= LLE_VALID;
+ lle->r_flags |= RLLE_VALID;
+}
+
+/*
+ * Tries to update @lle link-level address.
+ * Since update requires AFDATA WLOCK, function
+ * drops @lle lock, acquires AFDATA lock and then acquires
+ * @lle lock to maintain lock order.
+ *
+ * Returns 1 on success.
+ */
+int
+lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle,
+ const char *linkhdr, size_t linkhdrsize, int lladdr_off)
+{
+
+ /* Perform real LLE update */
+ /* use afdata WLOCK to update fields */
+ LLE_WLOCK_ASSERT(lle);
+ LLE_ADDREF(lle);
+ LLE_WUNLOCK(lle);
+ IF_AFDATA_WLOCK(ifp);
+ LLE_WLOCK(lle);
+
+ /*
+ * Since we droppped LLE lock, other thread might have deleted
+ * this lle. Check and return
+ */
+ if ((lle->la_flags & LLE_DELETED) != 0) {
+ IF_AFDATA_WUNLOCK(ifp);
+ LLE_FREE_LOCKED(lle);
+ return (0);
+ }
+
+ /* Update data */
+ lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, lladdr_off);
+
+ IF_AFDATA_WUNLOCK(ifp);
+
+ LLE_REMREF(lle);
+
+ return (1);
+}
+
+ /*
+ * Helper function used to pre-compute full/partial link-layer
+ * header data suitable for feeding into if_output().
+ */
+int
+lltable_calc_llheader(struct ifnet *ifp, int family, char *lladdr,
+ char *buf, size_t *bufsize, int *lladdr_off)
+{
+ struct if_encap_req ereq;
+ int error;
+
+ bzero(buf, *bufsize);
+ bzero(&ereq, sizeof(ereq));
+ ereq.buf = buf;
+ ereq.bufsize = *bufsize;
+ ereq.rtype = IFENCAP_LL;
+ ereq.family = family;
+ ereq.lladdr = lladdr;
+ ereq.lladdr_len = ifp->if_addrlen;
+ error = ifp->if_requestencap(ifp, &ereq);
+ if (error == 0) {
+ *bufsize = ereq.bufsize;
+ *lladdr_off = ereq.lladdr_off;
+ }
+
+ return (error);
+}
+
+/*
+ * Update link-layer header for given @lle after
+ * interface lladdr was changed.
+ */
+static int
+llentry_update_ifaddr(struct lltable *llt, struct llentry *lle, void *farg)
+{
+ struct ifnet *ifp;
+ u_char linkhdr[LLE_MAX_LINKHDR];
+ size_t linkhdrsize;
+ u_char *lladdr;
+ int lladdr_off;
+
+ ifp = (struct ifnet *)farg;
+
+ lladdr = lle->ll_addr;
+
+ LLE_WLOCK(lle);
+ if ((lle->la_flags & LLE_VALID) == 0) {
+ LLE_WUNLOCK(lle);
+ return (0);
+ }
+
+ if ((lle->la_flags & LLE_IFADDR) != 0)
+ lladdr = IF_LLADDR(ifp);
+
+ linkhdrsize = sizeof(linkhdr);
+ lltable_calc_llheader(ifp, llt->llt_af, lladdr, linkhdr, &linkhdrsize,
+ &lladdr_off);
+ memcpy(lle->r_linkdata, linkhdr, linkhdrsize);
+ LLE_WUNLOCK(lle);
+
+ return (0);
+}
+
+/*
+ * Update all calculated headers for given @llt
+ */
+void
+lltable_update_ifaddr(struct lltable *llt)
+{
+
+ if (llt->llt_ifp->if_flags & IFF_LOOPBACK)
+ return;
+
+ IF_AFDATA_WLOCK(llt->llt_ifp);
+ lltable_foreach_lle(llt, llentry_update_ifaddr, llt->llt_ifp);
+ IF_AFDATA_WUNLOCK(llt->llt_ifp);
+}
+
+/*
+ *
+ * Performs generic cleanup routines and frees lle.
+ *
+ * Called for non-linked entries, with callouts and
+ * other AF-specific cleanups performed.
+ *
+ * @lle must be passed WLOCK'ed
+ *
+ * Returns the number of held packets, if any, that were dropped.
+ */
+size_t
+llentry_free(struct llentry *lle)
+{
+ size_t pkts_dropped;
+
+ LLE_WLOCK_ASSERT(lle);
+
+ KASSERT((lle->la_flags & LLE_LINKED) == 0, ("freeing linked lle"));
+
+ pkts_dropped = lltable_drop_entry_queue(lle);
+
LLE_FREE_LOCKED(lle);
return (pkts_dropped);
@@ -144,22 +445,35 @@ struct llentry *
llentry_alloc(struct ifnet *ifp, struct lltable *lt,
struct sockaddr_storage *dst)
{
- struct llentry *la;
+ struct llentry *la, *la_tmp;
IF_AFDATA_RLOCK(ifp);
la = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst);
IF_AFDATA_RUNLOCK(ifp);
- 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);
- IF_AFDATA_WUNLOCK(ifp);
- }
if (la != NULL) {
LLE_ADDREF(la);
LLE_WUNLOCK(la);
+ return (la);
+ }
+
+ if ((ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
+ la = lltable_alloc_entry(lt, 0, (struct sockaddr *)dst);
+ if (la == NULL)
+ return (NULL);
+ IF_AFDATA_WLOCK(ifp);
+ LLE_WLOCK(la);
+ /* Prefer any existing LLE over newly-created one */
+ la_tmp = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst);
+ if (la_tmp == NULL)
+ lltable_link_entry(lt, la);
+ IF_AFDATA_WUNLOCK(ifp);
+ if (la_tmp != NULL) {
+ lltable_free_entry(lt, la);
+ la = la_tmp;
+ }
+ LLE_ADDREF(la);
+ LLE_WUNLOCK(la);
}
return (la);
@@ -168,30 +482,47 @@ llentry_alloc(struct ifnet *ifp, struct lltable *lt,
/*
* Free all entries from given table and free itself.
*/
+
+static int
+lltable_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
+{
+ struct llentries *dchain;
+
+ dchain = (struct llentries *)farg;
+
+ LLE_WLOCK(lle);
+ LIST_INSERT_HEAD(dchain, lle, lle_chain);
+
+ return (0);
+}
+
+/*
+ * Free all entries from given table and free itself.
+ */
void
lltable_free(struct lltable *llt)
{
struct llentry *lle, *next;
- int i;
+ struct llentries dchain;
KASSERT(llt != NULL, ("%s: llt is NULL", __func__));
- LLTABLE_WLOCK();
- SLIST_REMOVE(&V_lltables, llt, lltable, llt_link);
- LLTABLE_WUNLOCK();
+ lltable_unlink(llt);
+ LIST_INIT(&dchain);
IF_AFDATA_WLOCK(llt->llt_ifp);
- for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
- LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
- LLE_WLOCK(lle);
- if (callout_stop(&lle->la_timer))
- LLE_REMREF(lle);
- llentry_free(lle);
- }
- }
+ /* Push all lles to @dchain */
+ lltable_foreach_lle(llt, lltable_free_cb, &dchain);
+ llentries_unlink(llt, &dchain);
IF_AFDATA_WUNLOCK(llt->llt_ifp);
- free(llt, M_LLTABLE);
+ LIST_FOREACH_SAFE(lle, &dchain, lle_chain, next) {
+ if (callout_stop(&lle->lle_timer) > 0)
+ LLE_REMREF(lle);
+ llentry_free(lle);
+ }
+
+ llt->llt_free_tbl(llt);
}
#if 0
@@ -207,7 +538,7 @@ lltable_drain(int af)
if (llt->llt_af != af)
continue;
- for (i=0; i < LLTBL_HASHTBL_SIZE; i++) {
+ for (i=0; i < llt->llt_hsize; i++) {
LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
LLE_WLOCK(lle);
if (lle->la_hold) {
@@ -222,8 +553,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;
@@ -233,38 +598,122 @@ lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask,
if (llt->llt_af != af)
continue;
- llt->llt_prefix_free(llt, prefix, mask, flags);
+ llt->llt_prefix_free(llt, addr, mask, flags);
}
LLTABLE_RUNLOCK();
}
-
-
-/*
- * Create a new lltable.
- */
struct lltable *
-lltable_init(struct ifnet *ifp, int af)
+lltable_allocate_htbl(uint32_t hsize)
{
struct lltable *llt;
- register int i;
+ int i;
- llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK);
+ llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK | M_ZERO);
+ llt->llt_hsize = hsize;
+ llt->lle_head = malloc(sizeof(struct llentries) * hsize,
+ M_LLTABLE, M_WAITOK | M_ZERO);
- llt->llt_af = af;
- llt->llt_ifp = ifp;
- for (i = 0; i < LLTBL_HASHTBL_SIZE; i++)
+ for (i = 0; i < llt->llt_hsize; i++)
LIST_INIT(&llt->lle_head[i]);
+ /* Set some default callbacks */
+ llt->llt_link_entry = htable_link_entry;
+ llt->llt_unlink_entry = htable_unlink_entry;
+ llt->llt_prefix_free = htable_prefix_free;
+ llt->llt_foreach_entry = htable_foreach_lle;
+ llt->llt_free_tbl = htable_free_tbl;
+
+ return (llt);
+}
+
+/*
+ * Links lltable to global llt list.
+ */
+void
+lltable_link(struct lltable *llt)
+{
+
LLTABLE_WLOCK();
SLIST_INSERT_HEAD(&V_lltables, llt, llt_link);
LLTABLE_WUNLOCK();
+}
- return (llt);
+static void
+lltable_unlink(struct lltable *llt)
+{
+
+ LLTABLE_WLOCK();
+ SLIST_REMOVE(&V_lltables, llt, lltable, llt_link);
+ LLTABLE_WUNLOCK();
+
+}
+
+/*
+ * External methods used by lltable consumers
+ */
+
+int
+lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
+{
+
+ return (llt->llt_foreach_entry(llt, f, farg));
+}
+
+struct llentry *
+lltable_alloc_entry(struct lltable *llt, u_int flags,
+ const struct sockaddr *l3addr)
+{
+
+ return (llt->llt_alloc_entry(llt, flags, l3addr));
+}
+
+void
+lltable_free_entry(struct lltable *llt, struct llentry *lle)
+{
+
+ llt->llt_free_entry(llt, lle);
+}
+
+void
+lltable_link_entry(struct lltable *llt, struct llentry *lle)
+{
+
+ llt->llt_link_entry(llt, lle);
+}
+
+void
+lltable_unlink_entry(struct lltable *llt, struct llentry *lle)
+{
+
+ llt->llt_unlink_entry(lle);
+}
+
+void
+lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa)
+{
+ struct lltable *llt;
+
+ llt = lle->lle_tbl;
+ llt->llt_fill_sa_entry(lle, sa);
+}
+
+struct ifnet *
+lltable_get_ifp(const struct lltable *llt)
+{
+
+ return (llt->llt_ifp);
+}
+
+int
+lltable_get_af(const struct lltable *llt)
+{
+
+ return (llt->llt_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)
@@ -274,14 +723,16 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST];
struct ifnet *ifp;
struct lltable *llt;
- struct llentry *lle;
- u_int laflags = 0, flags = 0;
- int error = 0;
+ struct llentry *lle, *lle_tmp;
+ uint8_t linkhdr[LLE_MAX_LINKHDR];
+ size_t linkhdrsize;
+ int lladdr_off;
+ u_int laflags = 0;
+ int error;
+
+ KASSERT(dl != NULL && dl->sdl_family == AF_LINK,
+ ("%s: invalid dl\n", __func__));
- if (dl == NULL || dl->sdl_family != AF_LINK) {
- log(LOG_INFO, "%s: invalid dl\n", __func__);
- return EINVAL;
- }
ifp = ifnet_byindex(dl->sdl_index);
if (ifp == NULL) {
log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n",
@@ -289,44 +740,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;
-#ifdef INET
- if (dst->sa_family == AF_INET &&
- ((struct sockaddr_inarp *)dst)->sin_other != 0) {
- struct rtentry *rt;
- ((struct sockaddr_inarp *)dst)->sin_other = 0;
- rt = rtalloc1(dst, 0, 0);
- if (rt == NULL || !(rt->rt_flags & RTF_HOST)) {
- log(LOG_INFO, "%s: RTM_ADD publish "
- "(proxy only) is invalid\n",
- __func__);
- if (rt)
- RTFREE_LOCKED(rt);
- return EINVAL;
- }
- RTFREE_LOCKED(rt);
-
- flags |= LLE_PROXY;
- }
-#endif
- }
- 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) {
@@ -337,73 +750,82 @@ 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;
-
- 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_PROXY));
- 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,
- ((laflags & LLE_PROXY) ?
- (u_char *)IF_LLADDR(ifp) :
- (u_char *)LLADDR(dl)));
+ error = 0;
+
+ switch (rtm->rtm_type) {
+ case RTM_ADD:
+ /* Add static LLE */
+ laflags = 0;
+ if (rtm->rtm_rmx.rmx_expire == 0)
+ laflags = LLE_STATIC;
+ lle = lltable_alloc_entry(llt, laflags, dst);
+ if (lle == NULL)
+ return (ENOMEM);
+
+ linkhdrsize = sizeof(linkhdr);
+ if (lltable_calc_llheader(ifp, dst->sa_family, LLADDR(dl),
+ linkhdr, &linkhdrsize, &lladdr_off) != 0)
+ return (EINVAL);
+ lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize,
+ lladdr_off);
+ if ((rtm->rtm_flags & RTF_ANNOUNCE))
+ lle->la_flags |= LLE_PUB;
+ lle->la_expire = rtm->rtm_rmx.rmx_expire;
+
+ laflags = lle->la_flags;
+
+ /* Try to link new entry */
+ lle_tmp = NULL;
+ IF_AFDATA_WLOCK(ifp);
+ LLE_WLOCK(lle);
+ lle_tmp = lla_lookup(llt, LLE_EXCLUSIVE, dst);
+ if (lle_tmp != NULL) {
+ /* Check if we are trying to replace immutable entry */
+ if ((lle_tmp->la_flags & LLE_IFADDR) != 0) {
+ IF_AFDATA_WUNLOCK(ifp);
+ LLE_WUNLOCK(lle_tmp);
+ lltable_free_entry(llt, lle);
+ return (EPERM);
}
-#endif
- } else {
- if (flags & LLE_EXCLUSIVE)
- LLE_WUNLOCK(lle);
- else
- LLE_RUNLOCK(lle);
+ /* Unlink existing entry from table */
+ lltable_unlink_entry(llt, lle_tmp);
}
- } else if ((lle == NULL) && (flags & LLE_DELETE))
- error = EINVAL;
+ lltable_link_entry(llt, lle);
+ IF_AFDATA_WUNLOCK(ifp);
+ if (lle_tmp != NULL) {
+ EVENTHANDLER_INVOKE(lle_event, lle_tmp,LLENTRY_EXPIRED);
+ lltable_free_entry(llt, lle_tmp);
+ }
- return (error);
-}
+ /*
+ * By invoking LLE handler here we might get
+ * two events on static LLE entry insertion
+ * in routing socket. However, since we might have
+ * other subscribers we need to generate this event.
+ */
+ EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_RESOLVED);
+ 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
-static void
-vnet_lltable_init()
-{
+ break;
- SLIST_INIT(&V_lltables);
+ case RTM_DELETE:
+ return (lltable_delete_addr(llt, 0, dst));
+
+ default:
+ error = EINVAL;
+ }
+
+ return (error);
}
-VNET_SYSINIT(vnet_lltable_init, SI_SUB_PSEUDO, SI_ORDER_FIRST,
- vnet_lltable_init, NULL);
#ifdef DDB
struct llentry_sa {
@@ -429,15 +851,14 @@ llatbl_lle_show(struct llentry_sa *la)
db_printf(" la_flags=0x%04x\n", lle->la_flags);
db_printf(" la_asked=%u\n", lle->la_asked);
db_printf(" la_preempt=%u\n", lle->la_preempt);
- db_printf(" ln_byhint=%u\n", lle->ln_byhint);
db_printf(" ln_state=%d\n", lle->ln_state);
db_printf(" ln_router=%u\n", lle->ln_router);
db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick);
db_printf(" lle_refcnt=%d\n", lle->lle_refcnt);
- bcopy(&lle->ll_addr.mac16, octet, sizeof(octet));
+ bcopy(lle->ll_addr, octet, sizeof(octet));
db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]);
- db_printf(" la_timer=%p\n", &lle->la_timer);
+ db_printf(" lle_timer=%p\n", &lle->lle_timer);
switch (la->l3_addr.sa_family) {
#ifdef INET
@@ -490,7 +911,7 @@ llatbl_llt_show(struct lltable *llt)
db_printf("llt=%p llt_af=%d llt_ifp=%p\n",
llt, llt->llt_af, llt->llt_ifp);
- for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
+ for (i = 0; i < llt->llt_hsize; i++) {
LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
llatbl_lle_show((struct llentry_sa *)lle);