diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c index 43e7aa19069..e11f9600c27 100644 --- a/sys/net/if_llatbl.c +++ b/sys/net/if_llatbl.c @@ -440,7 +440,7 @@ 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; + struct llentry *lle, *lle_tmp; u_int laflags = 0; int error; @@ -469,36 +469,37 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) switch (rtm->rtm_type) { case RTM_ADD: /* Add static LLE */ - IF_AFDATA_CFG_WLOCK(ifp); lle = llt->llt_create(llt, 0, dst); - if (lle == NULL) { - IF_AFDATA_CFG_WUNLOCK(ifp); + if (lle == NULL) return (ENOMEM); - } - - IF_AFDATA_RUN_WLOCK(ifp); + /* Save initial info to provide to _prepare hook */ 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) - */ + lle->la_expire = rtm->rtm_rmx.rmx_expire; - if (rtm->rtm_rmx.rmx_expire == 0) { - lle->la_flags |= LLE_STATIC; - lle->r_flags |= RLLE_VALID; - lle->la_expire = 0; - } else - lle->la_expire = rtm->rtm_rmx.rmx_expire; + error = llt->llt_prepare_static_entry(llt, lle, info); + + if (error != 0) { + LLE_FREE(lle); + return (error); + } + + /* Let's try to link new lle to the list */ + IF_AFDATA_CFG_WLOCK(ifp); + LLE_WLOCK(lle); + /* Check if we already have this lle */ + /* XXX: Use LLE_UNLOCKED */ + lle_tmp = llt->llt_lookup(llt, LLE_EXCLUSIVE, dst); + if (lle_tmp != NULL) { + IF_AFDATA_CFG_WUNLOCK(ifp); + LLE_WUNLOCK(lle_tmp); + LLE_FREE_LOCKED(lle); + return (EEXIST); + } + + IF_AFDATA_RUN_WLOCK(ifp); llentry_link(llt, lle); IF_AFDATA_RUN_WUNLOCK(ifp); laflags = lle->la_flags; diff --git a/sys/net/if_llatbl.h b/sys/net/if_llatbl.h index bff24977cec..e451319ba53 100644 --- a/sys/net/if_llatbl.h +++ b/sys/net/if_llatbl.h @@ -162,6 +162,8 @@ typedef void (llt_clear_entry_t)(struct lltable *, struct llentry *); typedef void (llt_free_tbl_t)(struct lltable *); typedef void (llt_link_entry_t)(struct lltable *, struct llentry *); typedef void (llt_unlink_entry_t)(struct llentry *); +typedef int (llt_prepare_sentry_t)(struct lltable *, struct llentry *, + struct rt_addrinfo *); typedef int (llt_foreach_cb_t)(struct lltable *, struct llentry *, void *); typedef int (llt_foreach_entry_t)(struct lltable *, llt_foreach_cb_t *, void *); @@ -183,6 +185,7 @@ struct lltable { llt_foreach_entry_t *llt_foreach_entry; llt_link_entry_t *llt_link_entry; llt_unlink_entry_t *llt_unlink_entry; + llt_prepare_sentry_t *llt_prepare_static_entry; llt_free_tbl_t *llt_free_tbl; }; diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index 84b19c643e9..a3f9e015db6 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -268,6 +268,20 @@ arptimer(void *arg) CURVNET_RESTORE(); } +int +arp_lltable_prepare_static_entry(struct lltable *llt, struct llentry *lle, + struct rt_addrinfo *info) +{ + + lle->la_flags |= LLE_VALID; + lle->r_flags |= RLLE_VALID; + + if (lle->la_expire == 0) + lle->la_flags |= LLE_STATIC; + + return (0); +} + /* * Calback for lltable. */ diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h index 82aafee61aa..6df8a2ae263 100644 --- a/sys/netinet/if_ether.h +++ b/sys/netinet/if_ether.h @@ -115,6 +115,7 @@ extern u_char ether_ipmulticast_max[ETHER_ADDR_LEN]; struct lltable; struct llentry; struct ifaddr; +struct rt_addrinfo; int arpresolve(struct ifnet *ifp, struct rtentry *rt, struct mbuf *m, const struct sockaddr *dst, u_char *desten, struct llentry **lle); @@ -126,6 +127,8 @@ void arp_ifinit(struct ifnet *, struct ifaddr *); void arp_ifinit2(struct ifnet *, struct ifaddr *, u_char *); void arp_ifscrub(struct ifnet *, uint32_t); void arp_lltable_clear_entry(struct lltable *, struct llentry *); +int arp_lltable_prepare_static_entry(struct lltable *, struct llentry *, + struct rt_addrinfo *); #endif #endif diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 4bed99008d0..4298eeb995a 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -1314,6 +1314,7 @@ in_domifattach(struct ifnet *ifp) llt->llt_hash = in_lltable_hash; llt->llt_clear_entry = arp_lltable_clear_entry; llt->llt_match_prefix = in_lltable_match_prefix; + llt->llt_prepare_static_entry = arp_lltable_prepare_static_entry; lltable_link(llt); ii = malloc(sizeof(struct in_ifinfo), M_IFADDR, M_WAITOK|M_ZERO); diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index bbb6f28275d..40d3a4cefef 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -2379,6 +2379,7 @@ in6_domifattach(struct ifnet *ifp) llt->llt_hash = in6_lltable_hash; llt->llt_clear_entry = nd6_lltable_clear_entry; llt->llt_match_prefix = in6_lltable_match_prefix; + llt->llt_prepare_static_entry = nd6_lltable_prepare_static_entry; lltable_link(llt); ext->lltable = llt; diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 8a59f2e8f59..a92ed1b3f96 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1095,6 +1095,22 @@ nd6_free(struct llentry *ln, int gc) llt->llt_clear_entry(ln->lle_tbl, ln); } +int +nd6_lltable_prepare_static_entry(struct lltable *llt, struct llentry *lle, + struct rt_addrinfo *info) +{ + + lle->la_flags |= LLE_VALID; + lle->r_flags |= RLLE_VALID; + + lle->ln_state = ND6_LLINFO_REACHABLE; + + if (lle->la_expire == 0) + lle->la_flags |= LLE_STATIC; + + return (0); +} + /* * Calback for lltable. */ diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 1a1ebf04276..8d105686abd 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -421,6 +421,8 @@ void nd6_rem_ifa_lle(struct in6_ifaddr *); int nd6_storelladdr(struct ifnet *, struct mbuf *, const struct sockaddr *, u_char *, struct llentry **); void nd6_lltable_clear_entry(struct lltable *, struct llentry *); +int nd6_lltable_prepare_static_entry(struct lltable *, struct llentry *, + struct rt_addrinfo *); /* nd6_nbr.c */ void nd6_na_input(struct mbuf *, int, int);