xref: /openbsd-src/sys/net/if_etherbridge.c (revision dd76c598e3c4115465b3230dcbe7a412fc2a9b5b)
1*dd76c598Sdlg /*	$OpenBSD: if_etherbridge.c,v 1.7 2021/07/05 04:17:41 dlg Exp $ */
25a88a734Sdlg 
35a88a734Sdlg /*
45a88a734Sdlg  * Copyright (c) 2018, 2021 David Gwynne <dlg@openbsd.org>
55a88a734Sdlg  *
65a88a734Sdlg  * Permission to use, copy, modify, and distribute this software for any
75a88a734Sdlg  * purpose with or without fee is hereby granted, provided that the above
85a88a734Sdlg  * copyright notice and this permission notice appear in all copies.
95a88a734Sdlg  *
105a88a734Sdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115a88a734Sdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125a88a734Sdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135a88a734Sdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145a88a734Sdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
155a88a734Sdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
165a88a734Sdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175a88a734Sdlg  */
185a88a734Sdlg 
195a88a734Sdlg #include "bpfilter.h"
205a88a734Sdlg 
215a88a734Sdlg #include <sys/param.h>
225a88a734Sdlg #include <sys/systm.h>
235a88a734Sdlg #include <sys/kernel.h>
245a88a734Sdlg #include <sys/mbuf.h>
255a88a734Sdlg #include <sys/socket.h>
265a88a734Sdlg #include <sys/ioctl.h>
275a88a734Sdlg #include <sys/timeout.h>
285a88a734Sdlg #include <sys/pool.h>
295a88a734Sdlg #include <sys/tree.h>
305a88a734Sdlg 
315a88a734Sdlg #include <net/if.h>
325a88a734Sdlg #include <net/if_var.h>
335a88a734Sdlg #include <net/if_dl.h>
345a88a734Sdlg #include <net/if_media.h>
355a88a734Sdlg #include <net/if_types.h>
365a88a734Sdlg #include <net/rtable.h>
375a88a734Sdlg #include <net/toeplitz.h>
385a88a734Sdlg 
395a88a734Sdlg #include <netinet/in.h>
405a88a734Sdlg #include <netinet/if_ether.h>
415a88a734Sdlg 
425a88a734Sdlg /* for bridge stuff */
435a88a734Sdlg #include <net/if_bridge.h>
445a88a734Sdlg 
455a88a734Sdlg #include <net/if_etherbridge.h>
465a88a734Sdlg 
475a88a734Sdlg static inline void	ebe_rele(struct eb_entry *);
485a88a734Sdlg static void		ebe_free(void *);
495a88a734Sdlg 
505a88a734Sdlg static void		etherbridge_age(void *);
515a88a734Sdlg 
525a88a734Sdlg RBT_PROTOTYPE(eb_tree, eb_entry, ebe_tentry, ebt_cmp);
535a88a734Sdlg 
545a88a734Sdlg static struct pool	eb_entry_pool;
555a88a734Sdlg 
565a88a734Sdlg static inline int
eb_port_eq(struct etherbridge * eb,void * a,void * b)575a88a734Sdlg eb_port_eq(struct etherbridge *eb, void *a, void *b)
585a88a734Sdlg {
595a88a734Sdlg 	return ((*eb->eb_ops->eb_op_port_eq)(eb->eb_cookie, a, b));
605a88a734Sdlg }
615a88a734Sdlg 
625a88a734Sdlg static inline void *
eb_port_take(struct etherbridge * eb,void * port)635a88a734Sdlg eb_port_take(struct etherbridge *eb, void *port)
645a88a734Sdlg {
655a88a734Sdlg 	return ((*eb->eb_ops->eb_op_port_take)(eb->eb_cookie, port));
665a88a734Sdlg }
675a88a734Sdlg 
685a88a734Sdlg static inline void
eb_port_rele(struct etherbridge * eb,void * port)695a88a734Sdlg eb_port_rele(struct etherbridge *eb, void *port)
705a88a734Sdlg {
715a88a734Sdlg 	return ((*eb->eb_ops->eb_op_port_rele)(eb->eb_cookie, port));
725a88a734Sdlg }
735a88a734Sdlg 
745a88a734Sdlg static inline size_t
eb_port_ifname(struct etherbridge * eb,char * dst,size_t len,void * port)755a88a734Sdlg eb_port_ifname(struct etherbridge *eb, char *dst, size_t len, void *port)
765a88a734Sdlg {
775a88a734Sdlg 	return ((*eb->eb_ops->eb_op_port_ifname)(eb->eb_cookie, dst, len,
785a88a734Sdlg 	    port));
795a88a734Sdlg }
805a88a734Sdlg 
815a88a734Sdlg static inline void
eb_port_sa(struct etherbridge * eb,struct sockaddr_storage * ss,void * port)825a88a734Sdlg eb_port_sa(struct etherbridge *eb, struct sockaddr_storage *ss, void *port)
835a88a734Sdlg {
845a88a734Sdlg 	(*eb->eb_ops->eb_op_port_sa)(eb->eb_cookie, ss, port);
855a88a734Sdlg }
865a88a734Sdlg 
875a88a734Sdlg int
etherbridge_init(struct etherbridge * eb,const char * name,const struct etherbridge_ops * ops,void * cookie)885a88a734Sdlg etherbridge_init(struct etherbridge *eb, const char *name,
895a88a734Sdlg     const struct etherbridge_ops *ops, void *cookie)
905a88a734Sdlg {
915a88a734Sdlg 	size_t i;
925a88a734Sdlg 
935a88a734Sdlg 	if (eb_entry_pool.pr_size == 0) {
945a88a734Sdlg 		pool_init(&eb_entry_pool, sizeof(struct eb_entry),
955a88a734Sdlg 		    0, IPL_SOFTNET, 0, "ebepl", NULL);
965a88a734Sdlg 	}
975a88a734Sdlg 
985a88a734Sdlg 	eb->eb_table = mallocarray(ETHERBRIDGE_TABLE_SIZE,
995a88a734Sdlg 	    sizeof(*eb->eb_table), M_DEVBUF, M_WAITOK|M_CANFAIL);
1005a88a734Sdlg 	if (eb->eb_table == NULL)
1015a88a734Sdlg 		return (ENOMEM);
1025a88a734Sdlg 
1035a88a734Sdlg 	eb->eb_name = name;
1045a88a734Sdlg 	eb->eb_ops = ops;
1055a88a734Sdlg 	eb->eb_cookie = cookie;
1065a88a734Sdlg 
1075a88a734Sdlg 	mtx_init(&eb->eb_lock, IPL_SOFTNET);
1085a88a734Sdlg 	RBT_INIT(eb_tree, &eb->eb_tree);
1095a88a734Sdlg 
1105a88a734Sdlg 	eb->eb_num = 0;
1115a88a734Sdlg 	eb->eb_max = 100;
1125a88a734Sdlg 	eb->eb_max_age = 240;
1135a88a734Sdlg 	timeout_set(&eb->eb_tmo_age, etherbridge_age, eb);
1145a88a734Sdlg 
1155a88a734Sdlg 	for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) {
1165a88a734Sdlg 		struct eb_list *ebl = &eb->eb_table[i];
1175a88a734Sdlg 		SMR_TAILQ_INIT(ebl);
1185a88a734Sdlg 	}
1195a88a734Sdlg 
1205a88a734Sdlg 	return (0);
1215a88a734Sdlg }
1225a88a734Sdlg 
1235a88a734Sdlg int
etherbridge_up(struct etherbridge * eb)1245a88a734Sdlg etherbridge_up(struct etherbridge *eb)
1255a88a734Sdlg {
1265a88a734Sdlg 	etherbridge_age(eb);
1275a88a734Sdlg 
1285a88a734Sdlg 	return (0);
1295a88a734Sdlg }
1305a88a734Sdlg 
1315a88a734Sdlg int
etherbridge_down(struct etherbridge * eb)1325a88a734Sdlg etherbridge_down(struct etherbridge *eb)
1335a88a734Sdlg {
1345a88a734Sdlg 	smr_barrier();
1355a88a734Sdlg 
1365a88a734Sdlg 	return (0);
1375a88a734Sdlg }
1385a88a734Sdlg 
1395a88a734Sdlg void
etherbridge_destroy(struct etherbridge * eb)1405a88a734Sdlg etherbridge_destroy(struct etherbridge *eb)
1415a88a734Sdlg {
1425a88a734Sdlg 	struct eb_entry *ebe, *nebe;
1435a88a734Sdlg 
1445a88a734Sdlg 	/* XXX assume that nothing will calling etherbridge_map now */
1455a88a734Sdlg 
1465a88a734Sdlg 	timeout_del_barrier(&eb->eb_tmo_age);
1475a88a734Sdlg 
1485a88a734Sdlg 	free(eb->eb_table, M_DEVBUF,
1495a88a734Sdlg 	    ETHERBRIDGE_TABLE_SIZE * sizeof(*eb->eb_table));
1505a88a734Sdlg 
1515a88a734Sdlg 	RBT_FOREACH_SAFE(ebe, eb_tree, &eb->eb_tree, nebe) {
1525a88a734Sdlg 		RBT_REMOVE(eb_tree, &eb->eb_tree, ebe);
1535a88a734Sdlg 		ebe_free(ebe);
1545a88a734Sdlg 	}
1555a88a734Sdlg }
1565a88a734Sdlg 
1575a88a734Sdlg static struct eb_list *
etherbridge_list(struct etherbridge * eb,uint64_t eba)1589703528fSdlg etherbridge_list(struct etherbridge *eb, uint64_t eba)
1595a88a734Sdlg {
1609703528fSdlg 	uint16_t hash;
1619703528fSdlg 
1629703528fSdlg 	hash = stoeplitz_h64(eba) & ETHERBRIDGE_TABLE_MASK;
1639703528fSdlg 
1645a88a734Sdlg 	return (&eb->eb_table[hash]);
1655a88a734Sdlg }
1665a88a734Sdlg 
1675a88a734Sdlg static struct eb_entry *
ebl_find(struct eb_list * ebl,uint64_t eba)1689703528fSdlg ebl_find(struct eb_list *ebl, uint64_t eba)
1695a88a734Sdlg {
1705a88a734Sdlg 	struct eb_entry *ebe;
1715a88a734Sdlg 
1725a88a734Sdlg 	SMR_TAILQ_FOREACH(ebe, ebl, ebe_lentry) {
1739703528fSdlg 		if (ebe->ebe_addr == eba)
1745a88a734Sdlg 			return (ebe);
1755a88a734Sdlg 	}
1765a88a734Sdlg 
1775a88a734Sdlg 	return (NULL);
1785a88a734Sdlg }
1795a88a734Sdlg 
1805a88a734Sdlg static inline void
ebl_insert(struct eb_list * ebl,struct eb_entry * ebe)1815a88a734Sdlg ebl_insert(struct eb_list *ebl, struct eb_entry *ebe)
1825a88a734Sdlg {
1835a88a734Sdlg 	SMR_TAILQ_INSERT_TAIL_LOCKED(ebl, ebe, ebe_lentry);
1845a88a734Sdlg }
1855a88a734Sdlg 
1865a88a734Sdlg static inline void
ebl_remove(struct eb_list * ebl,struct eb_entry * ebe)1875a88a734Sdlg ebl_remove(struct eb_list *ebl, struct eb_entry *ebe)
1885a88a734Sdlg {
1895a88a734Sdlg 	SMR_TAILQ_REMOVE_LOCKED(ebl, ebe, ebe_lentry);
1905a88a734Sdlg }
1915a88a734Sdlg 
1925a88a734Sdlg static inline int
ebt_cmp(const struct eb_entry * aebe,const struct eb_entry * bebe)1935a88a734Sdlg ebt_cmp(const struct eb_entry *aebe, const struct eb_entry *bebe)
1945a88a734Sdlg {
1959703528fSdlg 	if (aebe->ebe_addr > bebe->ebe_addr)
1969703528fSdlg 		return (1);
1979703528fSdlg 	if (aebe->ebe_addr < bebe->ebe_addr)
1989703528fSdlg 		return (-1);
1999703528fSdlg 	return (0);
2005a88a734Sdlg }
2015a88a734Sdlg 
2025a88a734Sdlg RBT_GENERATE(eb_tree, eb_entry, ebe_tentry, ebt_cmp);
2035a88a734Sdlg 
2045a88a734Sdlg static inline struct eb_entry *
ebt_insert(struct etherbridge * eb,struct eb_entry * ebe)2055a88a734Sdlg ebt_insert(struct etherbridge *eb, struct eb_entry *ebe)
2065a88a734Sdlg {
2075a88a734Sdlg 	return (RBT_INSERT(eb_tree, &eb->eb_tree, ebe));
2085a88a734Sdlg }
2095a88a734Sdlg 
21053d80efbSdlg static inline struct eb_entry *
ebt_find(struct etherbridge * eb,const struct eb_entry * ebe)21153d80efbSdlg ebt_find(struct etherbridge *eb, const struct eb_entry *ebe)
21253d80efbSdlg {
21353d80efbSdlg 	return (RBT_FIND(eb_tree, &eb->eb_tree, ebe));
21453d80efbSdlg }
21553d80efbSdlg 
2165a88a734Sdlg static inline void
ebt_replace(struct etherbridge * eb,struct eb_entry * oebe,struct eb_entry * nebe)2175a88a734Sdlg ebt_replace(struct etherbridge *eb, struct eb_entry *oebe,
2185a88a734Sdlg     struct eb_entry *nebe)
2195a88a734Sdlg {
2205a88a734Sdlg 	struct eb_entry *rvebe;
2215a88a734Sdlg 
2225a88a734Sdlg 	RBT_REMOVE(eb_tree, &eb->eb_tree, oebe);
2235a88a734Sdlg 	rvebe = RBT_INSERT(eb_tree, &eb->eb_tree, nebe);
2245a88a734Sdlg 	KASSERTMSG(rvebe == NULL, "ebt_replace eb %p nebe %p rvebe %p",
2255a88a734Sdlg 	    eb, nebe, rvebe);
2265a88a734Sdlg }
2275a88a734Sdlg 
2285a88a734Sdlg static inline void
ebt_remove(struct etherbridge * eb,struct eb_entry * ebe)2295a88a734Sdlg ebt_remove(struct etherbridge *eb, struct eb_entry *ebe)
2305a88a734Sdlg {
2315a88a734Sdlg 	RBT_REMOVE(eb_tree, &eb->eb_tree, ebe);
2325a88a734Sdlg }
2335a88a734Sdlg 
2345a88a734Sdlg static inline void
ebe_rele(struct eb_entry * ebe)2355a88a734Sdlg ebe_rele(struct eb_entry *ebe)
2365a88a734Sdlg {
2375a88a734Sdlg 	smr_call(&ebe->ebe_smr_entry, ebe_free, ebe);
2385a88a734Sdlg }
2395a88a734Sdlg 
2405a88a734Sdlg static void
ebe_free(void * arg)2415a88a734Sdlg ebe_free(void *arg)
2425a88a734Sdlg {
2435a88a734Sdlg 	struct eb_entry *ebe = arg;
2445a88a734Sdlg 	struct etherbridge *eb = ebe->ebe_etherbridge;
2455a88a734Sdlg 
2465a88a734Sdlg 	eb_port_rele(eb, ebe->ebe_port);
2475a88a734Sdlg 	pool_put(&eb_entry_pool, ebe);
2485a88a734Sdlg }
2495a88a734Sdlg 
2505a88a734Sdlg void *
etherbridge_resolve_ea(struct etherbridge * eb,const struct ether_addr * ea)2519703528fSdlg etherbridge_resolve_ea(struct etherbridge *eb,
2529703528fSdlg     const struct ether_addr *ea)
2535a88a734Sdlg {
2549703528fSdlg 	return (etherbridge_resolve(eb, ether_addr_to_e64(ea)));
2559703528fSdlg }
2569703528fSdlg 
2579703528fSdlg void *
etherbridge_resolve(struct etherbridge * eb,uint64_t eba)2589703528fSdlg etherbridge_resolve(struct etherbridge *eb, uint64_t eba)
2599703528fSdlg {
2609703528fSdlg 	struct eb_list *ebl = etherbridge_list(eb, eba);
2615a88a734Sdlg 	struct eb_entry *ebe;
2625a88a734Sdlg 
2635a88a734Sdlg 	SMR_ASSERT_CRITICAL();
2645a88a734Sdlg 
2659703528fSdlg 	ebe = ebl_find(ebl, eba);
2665a88a734Sdlg 	if (ebe != NULL) {
2675a88a734Sdlg 		if (ebe->ebe_type == EBE_DYNAMIC) {
2685a88a734Sdlg 			int diff = getuptime() - ebe->ebe_age;
2695a88a734Sdlg 			if (diff > eb->eb_max_age)
2705a88a734Sdlg 				return (NULL);
2715a88a734Sdlg 		}
2725a88a734Sdlg 
2735a88a734Sdlg 		return (ebe->ebe_port);
2745a88a734Sdlg 	}
2755a88a734Sdlg 
2765a88a734Sdlg 	return (NULL);
2775a88a734Sdlg }
2785a88a734Sdlg 
2795a88a734Sdlg void
etherbridge_map_ea(struct etherbridge * eb,void * port,const struct ether_addr * ea)2809703528fSdlg etherbridge_map_ea(struct etherbridge *eb, void *port,
2815a88a734Sdlg     const struct ether_addr *ea)
2825a88a734Sdlg {
2839703528fSdlg 	etherbridge_map(eb, port, ether_addr_to_e64(ea));
2849703528fSdlg }
2859703528fSdlg 
2869703528fSdlg void
etherbridge_map(struct etherbridge * eb,void * port,uint64_t eba)2879703528fSdlg etherbridge_map(struct etherbridge *eb, void *port, uint64_t eba)
2889703528fSdlg {
2895a88a734Sdlg 	struct eb_list *ebl;
2905a88a734Sdlg 	struct eb_entry *oebe, *nebe;
2915a88a734Sdlg 	unsigned int num;
2925a88a734Sdlg 	void *nport;
2935a88a734Sdlg 	int new = 0;
2942c44fd7eSdlg 	time_t now;
2955a88a734Sdlg 
2969703528fSdlg 	if (ETH64_IS_MULTICAST(eba) || ETH64_IS_ANYADDR(eba))
2975a88a734Sdlg 		return;
2985a88a734Sdlg 
2992c44fd7eSdlg 	now = getuptime();
3009703528fSdlg 	ebl = etherbridge_list(eb, eba);
3015a88a734Sdlg 
3025a88a734Sdlg 	smr_read_enter();
3039703528fSdlg 	oebe = ebl_find(ebl, eba);
304*dd76c598Sdlg 	if (oebe == NULL) {
305*dd76c598Sdlg 		/*
306*dd76c598Sdlg 		 * peek at the space to see if it's worth trying
307*dd76c598Sdlg 		 * to make a new entry.
308*dd76c598Sdlg 		 */
309*dd76c598Sdlg 		if (eb->eb_num < eb->eb_max)
3105a88a734Sdlg 			new = 1;
311*dd76c598Sdlg 	} else {
3122c44fd7eSdlg 		if (oebe->ebe_age != now)
3132c44fd7eSdlg 			oebe->ebe_age = now;
3145a88a734Sdlg 
3155a88a734Sdlg 		/* does this entry need to be replaced? */
3165a88a734Sdlg 		if (oebe->ebe_type == EBE_DYNAMIC &&
317*dd76c598Sdlg 		    !eb_port_eq(eb, oebe->ebe_port, port))
3185a88a734Sdlg 			new = 1;
3195a88a734Sdlg 	}
3205a88a734Sdlg 	smr_read_leave();
3215a88a734Sdlg 
3225a88a734Sdlg 	if (!new)
3235a88a734Sdlg 		return;
3245a88a734Sdlg 
3255a88a734Sdlg 	nport = eb_port_take(eb, port);
3265a88a734Sdlg 	if (nport == NULL) {
3275a88a734Sdlg 		/* XXX should we remove the old one and flood? */
3285a88a734Sdlg 		return;
3295a88a734Sdlg 	}
3305a88a734Sdlg 
3315a88a734Sdlg 	nebe = pool_get(&eb_entry_pool, PR_NOWAIT);
3325a88a734Sdlg 	if (nebe == NULL) {
3335a88a734Sdlg 		/* XXX should we remove the old one and flood? */
3345a88a734Sdlg 		eb_port_rele(eb, nport);
3355a88a734Sdlg 		return;
3365a88a734Sdlg 	}
3375a88a734Sdlg 
3385a88a734Sdlg 	smr_init(&nebe->ebe_smr_entry);
3395a88a734Sdlg 	nebe->ebe_etherbridge = eb;
3405a88a734Sdlg 
3419703528fSdlg 	nebe->ebe_addr = eba;
3425a88a734Sdlg 	nebe->ebe_port = nport;
3435a88a734Sdlg 	nebe->ebe_type = EBE_DYNAMIC;
3442c44fd7eSdlg 	nebe->ebe_age = now;
3455a88a734Sdlg 
3465a88a734Sdlg 	mtx_enter(&eb->eb_lock);
347*dd76c598Sdlg 	oebe = ebt_find(eb, nebe);
348*dd76c598Sdlg 	if (oebe == NULL) {
349*dd76c598Sdlg 		num = eb->eb_num + 1;
350*dd76c598Sdlg 		if (num <= eb->eb_max) {
3515a88a734Sdlg 			ebl_insert(ebl, nebe);
3525a88a734Sdlg 
353*dd76c598Sdlg 			oebe = ebt_insert(eb, nebe);
3545a88a734Sdlg 			if (oebe != NULL) {
355*dd76c598Sdlg 				panic("etherbridge %p changed while locked",
356*dd76c598Sdlg 				    eb);
357*dd76c598Sdlg 			}
358*dd76c598Sdlg 
359*dd76c598Sdlg 			/* great success */
360*dd76c598Sdlg 			eb->eb_num = num;
361*dd76c598Sdlg 			nebe = NULL; /* give ref to table */
362*dd76c598Sdlg 		}
363*dd76c598Sdlg 	} else if (oebe->ebe_type == EBE_DYNAMIC) {
364*dd76c598Sdlg 		/* do the update */
365*dd76c598Sdlg 		ebl_insert(ebl, nebe);
366*dd76c598Sdlg 
3675a88a734Sdlg 		ebl_remove(ebl, oebe);
3685a88a734Sdlg 		ebt_replace(eb, oebe, nebe);
3695a88a734Sdlg 
370*dd76c598Sdlg 		nebe = NULL; /* give ref to table */
371*dd76c598Sdlg 	} else {
372*dd76c598Sdlg 		/*
373*dd76c598Sdlg 		 * oebe is not a dynamic entry, so don't replace it.
374*dd76c598Sdlg 		 */
375*dd76c598Sdlg 		oebe = NULL;
3765a88a734Sdlg 	}
3775a88a734Sdlg 	mtx_leave(&eb->eb_lock);
3785a88a734Sdlg 
3795a88a734Sdlg 	if (nebe != NULL) {
3805a88a734Sdlg 		/*
381678831beSjsg 		 * the new entry didn't make it into the
382*dd76c598Sdlg 		 * table so it can be freed directly.
3835a88a734Sdlg 		 */
3845a88a734Sdlg 		ebe_free(nebe);
3855a88a734Sdlg 	}
3865a88a734Sdlg 
3875a88a734Sdlg 	if (oebe != NULL) {
3885a88a734Sdlg 		/*
389*dd76c598Sdlg 		 * we replaced this entry, it needs to be released.
3905a88a734Sdlg 		 */
3915a88a734Sdlg 		ebe_rele(oebe);
3925a88a734Sdlg 	}
3935a88a734Sdlg }
3945a88a734Sdlg 
39553d80efbSdlg int
etherbridge_add_addr(struct etherbridge * eb,void * port,const struct ether_addr * ea,unsigned int type)39653d80efbSdlg etherbridge_add_addr(struct etherbridge *eb, void *port,
39753d80efbSdlg     const struct ether_addr *ea, unsigned int type)
39853d80efbSdlg {
3999703528fSdlg 	uint64_t eba = ether_addr_to_e64(ea);
40053d80efbSdlg 	struct eb_list *ebl;
40153d80efbSdlg 	struct eb_entry *nebe;
40253d80efbSdlg 	unsigned int num;
40353d80efbSdlg 	void *nport;
40453d80efbSdlg 	int error = 0;
40553d80efbSdlg 
4069703528fSdlg 	if (ETH64_IS_MULTICAST(eba) || ETH64_IS_ANYADDR(eba))
40753d80efbSdlg 		return (EADDRNOTAVAIL);
40853d80efbSdlg 
40953d80efbSdlg 	nport = eb_port_take(eb, port);
41053d80efbSdlg 	if (nport == NULL)
41153d80efbSdlg 		return (ENOMEM);
41253d80efbSdlg 
41353d80efbSdlg 	nebe = pool_get(&eb_entry_pool, PR_NOWAIT);
41453d80efbSdlg 	if (nebe == NULL) {
41553d80efbSdlg 		eb_port_rele(eb, nport);
41653d80efbSdlg 		return (ENOMEM);
41753d80efbSdlg 	}
41853d80efbSdlg 
41953d80efbSdlg 	smr_init(&nebe->ebe_smr_entry);
42053d80efbSdlg 	nebe->ebe_etherbridge = eb;
42153d80efbSdlg 
4229703528fSdlg 	nebe->ebe_addr = eba;
42353d80efbSdlg 	nebe->ebe_port = nport;
42453d80efbSdlg 	nebe->ebe_type = type;
42553d80efbSdlg 	nebe->ebe_age = getuptime();
42653d80efbSdlg 
4279703528fSdlg 	ebl = etherbridge_list(eb, eba);
42853d80efbSdlg 
42953d80efbSdlg 	mtx_enter(&eb->eb_lock);
43053d80efbSdlg 	num = eb->eb_num + 1;
43153d80efbSdlg 	if (num >= eb->eb_max)
43253d80efbSdlg 		error = ENOSPC;
43353d80efbSdlg 	else if (ebt_insert(eb, nebe) != NULL)
43453d80efbSdlg 		error = EADDRINUSE;
43553d80efbSdlg 	else {
43653d80efbSdlg 		/* we win, do the insert */
43753d80efbSdlg 		ebl_insert(ebl, nebe); /* give the ref to etherbridge */
43853d80efbSdlg 		eb->eb_num = num;
43953d80efbSdlg 	}
44053d80efbSdlg 	mtx_leave(&eb->eb_lock);
44153d80efbSdlg 
44253d80efbSdlg 	if (error != 0) {
44353d80efbSdlg 		/*
444678831beSjsg 		 * the new entry didn't make it into the
44553d80efbSdlg 		 * table, so it can be freed directly.
44653d80efbSdlg 		 */
44753d80efbSdlg 		ebe_free(nebe);
44853d80efbSdlg 	}
44953d80efbSdlg 
45053d80efbSdlg 	return (error);
45153d80efbSdlg }
45253d80efbSdlg int
etherbridge_del_addr(struct etherbridge * eb,const struct ether_addr * ea)45353d80efbSdlg etherbridge_del_addr(struct etherbridge *eb, const struct ether_addr *ea)
45453d80efbSdlg {
4559703528fSdlg 	uint64_t eba = ether_addr_to_e64(ea);
45653d80efbSdlg 	struct eb_list *ebl;
45753d80efbSdlg 	struct eb_entry *oebe;
45853d80efbSdlg 	const struct eb_entry key = {
4599703528fSdlg 		.ebe_addr = eba,
46053d80efbSdlg 	};
46153d80efbSdlg 	int error = 0;
46253d80efbSdlg 
4639703528fSdlg 	ebl = etherbridge_list(eb, eba);
46453d80efbSdlg 
46553d80efbSdlg 	mtx_enter(&eb->eb_lock);
46653d80efbSdlg 	oebe = ebt_find(eb, &key);
46753d80efbSdlg 	if (oebe == NULL)
46853d80efbSdlg 		error = ESRCH;
46953d80efbSdlg 	else {
47053d80efbSdlg 		KASSERT(eb->eb_num > 0);
47153d80efbSdlg 		eb->eb_num--;
47253d80efbSdlg 
47353d80efbSdlg 		ebl_remove(ebl, oebe); /* it's our ref now */
47453d80efbSdlg 		ebt_remove(eb, oebe);
47553d80efbSdlg 	}
47653d80efbSdlg 	mtx_leave(&eb->eb_lock);
47753d80efbSdlg 
47853d80efbSdlg 	if (oebe != NULL)
47953d80efbSdlg 		ebe_rele(oebe);
48053d80efbSdlg 
48153d80efbSdlg 	return (error);
48253d80efbSdlg }
48353d80efbSdlg 
4845a88a734Sdlg static void
etherbridge_age(void * arg)4855a88a734Sdlg etherbridge_age(void *arg)
4865a88a734Sdlg {
4875a88a734Sdlg 	struct etherbridge *eb = arg;
4885a88a734Sdlg 	struct eb_entry *ebe, *nebe;
4895a88a734Sdlg 	struct eb_queue ebq = TAILQ_HEAD_INITIALIZER(ebq);
4905a88a734Sdlg 	int diff;
4915a88a734Sdlg 	unsigned int now = getuptime();
4925a88a734Sdlg 	size_t i;
4935a88a734Sdlg 
4945a88a734Sdlg 	timeout_add_sec(&eb->eb_tmo_age, 100);
4955a88a734Sdlg 
4965a88a734Sdlg 	for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) {
4975a88a734Sdlg 		struct eb_list *ebl = &eb->eb_table[i];
4985a88a734Sdlg #if 0
4995a88a734Sdlg 		if (SMR_TAILQ_EMPTY(ebl));
5005a88a734Sdlg 			continue;
5015a88a734Sdlg #endif
5025a88a734Sdlg 
5035a88a734Sdlg 		mtx_enter(&eb->eb_lock); /* don't block map too much */
5045a88a734Sdlg 		SMR_TAILQ_FOREACH_SAFE_LOCKED(ebe, ebl, ebe_lentry, nebe) {
5055a88a734Sdlg 			if (ebe->ebe_type != EBE_DYNAMIC)
5065a88a734Sdlg 				continue;
5075a88a734Sdlg 
5085a88a734Sdlg 			diff = now - ebe->ebe_age;
5095a88a734Sdlg 			if (diff < eb->eb_max_age)
5105a88a734Sdlg 				continue;
5115a88a734Sdlg 
5125a88a734Sdlg 			ebl_remove(ebl, ebe);
5135a88a734Sdlg 			ebt_remove(eb, ebe);
5145a88a734Sdlg 			eb->eb_num--;
5155a88a734Sdlg 
5165a88a734Sdlg 			/* we own the tables ref now */
5175a88a734Sdlg 
5185a88a734Sdlg 			TAILQ_INSERT_TAIL(&ebq, ebe, ebe_qentry);
5195a88a734Sdlg 		}
5205a88a734Sdlg 		mtx_leave(&eb->eb_lock);
5215a88a734Sdlg 	}
5225a88a734Sdlg 
5235a88a734Sdlg 	TAILQ_FOREACH_SAFE(ebe, &ebq, ebe_qentry, nebe) {
5245a88a734Sdlg 		TAILQ_REMOVE(&ebq, ebe, ebe_qentry);
5255a88a734Sdlg 		ebe_rele(ebe);
5265a88a734Sdlg 	}
5275a88a734Sdlg }
5285a88a734Sdlg 
5295a88a734Sdlg void
etherbridge_detach_port(struct etherbridge * eb,void * port)5305a88a734Sdlg etherbridge_detach_port(struct etherbridge *eb, void *port)
5315a88a734Sdlg {
5325a88a734Sdlg 	struct eb_entry *ebe, *nebe;
5335a88a734Sdlg 	struct eb_queue ebq = TAILQ_HEAD_INITIALIZER(ebq);
5345a88a734Sdlg 	size_t i;
5355a88a734Sdlg 
5365a88a734Sdlg 	for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) {
5375a88a734Sdlg 		struct eb_list *ebl = &eb->eb_table[i];
5385a88a734Sdlg 
5395a88a734Sdlg 		mtx_enter(&eb->eb_lock); /* don't block map too much */
5405a88a734Sdlg 		SMR_TAILQ_FOREACH_SAFE_LOCKED(ebe, ebl, ebe_lentry, nebe) {
5415a88a734Sdlg 			if (!eb_port_eq(eb, ebe->ebe_port, port))
5425a88a734Sdlg 				continue;
5435a88a734Sdlg 
5445a88a734Sdlg 			ebl_remove(ebl, ebe);
5455a88a734Sdlg 			ebt_remove(eb, ebe);
5465a88a734Sdlg 			eb->eb_num--;
5475a88a734Sdlg 
5485a88a734Sdlg 			/* we own the tables ref now */
5495a88a734Sdlg 
5505a88a734Sdlg 			TAILQ_INSERT_TAIL(&ebq, ebe, ebe_qentry);
5515a88a734Sdlg 		}
5525a88a734Sdlg 		mtx_leave(&eb->eb_lock);
5535a88a734Sdlg 	}
5545a88a734Sdlg 
555*dd76c598Sdlg 	if (TAILQ_EMPTY(&ebq))
556*dd76c598Sdlg 		return;
557*dd76c598Sdlg 
558*dd76c598Sdlg 	/*
559*dd76c598Sdlg 	 * do one smr barrier for all the entries rather than an
560*dd76c598Sdlg 	 * smr_call each.
561*dd76c598Sdlg 	 */
562*dd76c598Sdlg 	smr_barrier();
5635a88a734Sdlg 
5645a88a734Sdlg 	TAILQ_FOREACH_SAFE(ebe, &ebq, ebe_qentry, nebe) {
5655a88a734Sdlg 		TAILQ_REMOVE(&ebq, ebe, ebe_qentry);
5665a88a734Sdlg 		ebe_free(ebe);
5675a88a734Sdlg 	}
5685a88a734Sdlg }
5695a88a734Sdlg 
5705a88a734Sdlg void
etherbridge_flush(struct etherbridge * eb,uint32_t flags)5715a88a734Sdlg etherbridge_flush(struct etherbridge *eb, uint32_t flags)
5725a88a734Sdlg {
5735a88a734Sdlg 	struct eb_entry *ebe, *nebe;
5745a88a734Sdlg 	struct eb_queue ebq = TAILQ_HEAD_INITIALIZER(ebq);
5755a88a734Sdlg 	size_t i;
5765a88a734Sdlg 
5775a88a734Sdlg 	for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) {
5785a88a734Sdlg 		struct eb_list *ebl = &eb->eb_table[i];
5795a88a734Sdlg 
5805a88a734Sdlg 		mtx_enter(&eb->eb_lock); /* don't block map too much */
5815a88a734Sdlg 		SMR_TAILQ_FOREACH_SAFE_LOCKED(ebe, ebl, ebe_lentry, nebe) {
5825a88a734Sdlg 			if (flags == IFBF_FLUSHDYN &&
5835a88a734Sdlg 			    ebe->ebe_type != EBE_DYNAMIC)
5845a88a734Sdlg 				continue;
5855a88a734Sdlg 
5865a88a734Sdlg 			ebl_remove(ebl, ebe);
5875a88a734Sdlg 			ebt_remove(eb, ebe);
5885a88a734Sdlg 			eb->eb_num--;
5895a88a734Sdlg 
5905a88a734Sdlg 			/* we own the tables ref now */
5915a88a734Sdlg 
5925a88a734Sdlg 			TAILQ_INSERT_TAIL(&ebq, ebe, ebe_qentry);
5935a88a734Sdlg 		}
5945a88a734Sdlg 		mtx_leave(&eb->eb_lock);
5955a88a734Sdlg 	}
5965a88a734Sdlg 
597*dd76c598Sdlg 	if (TAILQ_EMPTY(&ebq))
598*dd76c598Sdlg 		return;
599*dd76c598Sdlg 
600*dd76c598Sdlg 	/*
601*dd76c598Sdlg 	 * do one smr barrier for all the entries rather than an
602*dd76c598Sdlg 	 * smr_call each.
603*dd76c598Sdlg 	 */
604*dd76c598Sdlg 	smr_barrier();
6055a88a734Sdlg 
6065a88a734Sdlg 	TAILQ_FOREACH_SAFE(ebe, &ebq, ebe_qentry, nebe) {
6075a88a734Sdlg 		TAILQ_REMOVE(&ebq, ebe, ebe_qentry);
6085a88a734Sdlg 		ebe_free(ebe);
6095a88a734Sdlg 	}
6105a88a734Sdlg }
6115a88a734Sdlg 
6125a88a734Sdlg int
etherbridge_rtfind(struct etherbridge * eb,struct ifbaconf * baconf)6135a88a734Sdlg etherbridge_rtfind(struct etherbridge *eb, struct ifbaconf *baconf)
6145a88a734Sdlg {
6155a88a734Sdlg 	struct eb_entry *ebe;
6165a88a734Sdlg 	struct ifbareq bareq;
6175a88a734Sdlg 	caddr_t buf;
6185a88a734Sdlg 	size_t len, nlen;
6195a88a734Sdlg 	time_t age, now = getuptime();
6205a88a734Sdlg 	int error;
6215a88a734Sdlg 
6225a88a734Sdlg 	if (baconf->ifbac_len == 0) {
6235a88a734Sdlg 		/* single read is atomic */
6245a88a734Sdlg 		baconf->ifbac_len = eb->eb_num * sizeof(bareq);
6255a88a734Sdlg 		return (0);
6265a88a734Sdlg 	}
6275a88a734Sdlg 
6285a88a734Sdlg 	buf = malloc(baconf->ifbac_len, M_TEMP, M_WAITOK|M_CANFAIL);
6295a88a734Sdlg 	if (buf == NULL)
6305a88a734Sdlg 		return (ENOMEM);
6315a88a734Sdlg 	len = 0;
6325a88a734Sdlg 
6335a88a734Sdlg 	mtx_enter(&eb->eb_lock);
6345a88a734Sdlg 	RBT_FOREACH(ebe, eb_tree, &eb->eb_tree) {
6355a88a734Sdlg 		nlen = len + sizeof(bareq);
6365a88a734Sdlg 		if (nlen > baconf->ifbac_len)
6375a88a734Sdlg 			break;
6385a88a734Sdlg 
6395a88a734Sdlg 		strlcpy(bareq.ifba_name, eb->eb_name,
6405a88a734Sdlg 		    sizeof(bareq.ifba_name));
6415a88a734Sdlg 		eb_port_ifname(eb,
6425a88a734Sdlg 		    bareq.ifba_ifsname, sizeof(bareq.ifba_ifsname),
6435a88a734Sdlg 		    ebe->ebe_port);
6449703528fSdlg 		ether_e64_to_addr(&bareq.ifba_dst, ebe->ebe_addr);
6455a88a734Sdlg 
6465a88a734Sdlg 		memset(&bareq.ifba_dstsa, 0, sizeof(bareq.ifba_dstsa));
6475a88a734Sdlg 		eb_port_sa(eb, &bareq.ifba_dstsa, ebe->ebe_port);
6485a88a734Sdlg 
6495a88a734Sdlg 		switch (ebe->ebe_type) {
6505a88a734Sdlg 		case EBE_DYNAMIC:
6515a88a734Sdlg 			age = now - ebe->ebe_age;
6525a88a734Sdlg 			bareq.ifba_age = MIN(age, 0xff);
6535a88a734Sdlg 			bareq.ifba_flags = IFBAF_DYNAMIC;
6545a88a734Sdlg 			break;
6555a88a734Sdlg 		case EBE_STATIC:
6565a88a734Sdlg 			bareq.ifba_age = 0;
6575a88a734Sdlg 			bareq.ifba_flags = IFBAF_STATIC;
6585a88a734Sdlg 			break;
6595a88a734Sdlg 		}
6605a88a734Sdlg 
6615a88a734Sdlg 		memcpy(buf + len, &bareq, sizeof(bareq));
6625a88a734Sdlg 		len = nlen;
6635a88a734Sdlg 	}
6645a88a734Sdlg 	nlen = baconf->ifbac_len;
6655a88a734Sdlg 	baconf->ifbac_len = eb->eb_num * sizeof(bareq);
6665a88a734Sdlg 	mtx_leave(&eb->eb_lock);
6675a88a734Sdlg 
6685a88a734Sdlg 	error = copyout(buf, baconf->ifbac_buf, len);
6695a88a734Sdlg 	free(buf, M_TEMP, nlen);
6705a88a734Sdlg 
6715a88a734Sdlg 	return (error);
6725a88a734Sdlg }
6735a88a734Sdlg 
6745a88a734Sdlg int
etherbridge_set_max(struct etherbridge * eb,struct ifbrparam * bparam)6755a88a734Sdlg etherbridge_set_max(struct etherbridge *eb, struct ifbrparam *bparam)
6765a88a734Sdlg {
6775a88a734Sdlg 	if (bparam->ifbrp_csize < 1 ||
6785a88a734Sdlg 	    bparam->ifbrp_csize > 4096) /* XXX */
6795a88a734Sdlg 		return (EINVAL);
6805a88a734Sdlg 
6815a88a734Sdlg 	/* commit */
6825a88a734Sdlg 	eb->eb_max = bparam->ifbrp_csize;
6835a88a734Sdlg 
6845a88a734Sdlg 	return (0);
6855a88a734Sdlg }
6865a88a734Sdlg 
6875a88a734Sdlg int
etherbridge_get_max(struct etherbridge * eb,struct ifbrparam * bparam)6885a88a734Sdlg etherbridge_get_max(struct etherbridge *eb, struct ifbrparam *bparam)
6895a88a734Sdlg {
6905a88a734Sdlg 	bparam->ifbrp_csize = eb->eb_max;
6915a88a734Sdlg 
6925a88a734Sdlg 	return (0);
6935a88a734Sdlg }
6945a88a734Sdlg 
6955a88a734Sdlg int
etherbridge_set_tmo(struct etherbridge * eb,struct ifbrparam * bparam)6965a88a734Sdlg etherbridge_set_tmo(struct etherbridge *eb, struct ifbrparam *bparam)
6975a88a734Sdlg {
6985a88a734Sdlg 	if (bparam->ifbrp_ctime < 8 ||
6995a88a734Sdlg 	    bparam->ifbrp_ctime > 3600)
7005a88a734Sdlg 		return (EINVAL);
7015a88a734Sdlg 
7025a88a734Sdlg 	/* commit */
7035a88a734Sdlg 	eb->eb_max_age = bparam->ifbrp_ctime;
7045a88a734Sdlg 
7055a88a734Sdlg 	return (0);
7065a88a734Sdlg }
7075a88a734Sdlg 
7085a88a734Sdlg int
etherbridge_get_tmo(struct etherbridge * eb,struct ifbrparam * bparam)7095a88a734Sdlg etherbridge_get_tmo(struct etherbridge *eb, struct ifbrparam *bparam)
7105a88a734Sdlg {
7115a88a734Sdlg 	bparam->ifbrp_ctime = eb->eb_max_age;
7125a88a734Sdlg 
7135a88a734Sdlg 	return (0);
7145a88a734Sdlg }
715