diff options
Diffstat (limited to 'freebsd/sys/net/if_llatbl.c')
-rw-r--r-- | freebsd/sys/net/if_llatbl.c | 745 |
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); |