xref: /openbsd/sys/net/if_etherbridge.c (revision dd76c598)
1 /*	$OpenBSD: if_etherbridge.c,v 1.7 2021/07/05 04:17:41 dlg Exp $ */
2 
3 /*
4  * Copyright (c) 2018, 2021 David Gwynne <dlg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "bpfilter.h"
20 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/kernel.h>
24 #include <sys/mbuf.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <sys/timeout.h>
28 #include <sys/pool.h>
29 #include <sys/tree.h>
30 
31 #include <net/if.h>
32 #include <net/if_var.h>
33 #include <net/if_dl.h>
34 #include <net/if_media.h>
35 #include <net/if_types.h>
36 #include <net/rtable.h>
37 #include <net/toeplitz.h>
38 
39 #include <netinet/in.h>
40 #include <netinet/if_ether.h>
41 
42 /* for bridge stuff */
43 #include <net/if_bridge.h>
44 
45 #include <net/if_etherbridge.h>
46 
47 static inline void	ebe_rele(struct eb_entry *);
48 static void		ebe_free(void *);
49 
50 static void		etherbridge_age(void *);
51 
52 RBT_PROTOTYPE(eb_tree, eb_entry, ebe_tentry, ebt_cmp);
53 
54 static struct pool	eb_entry_pool;
55 
56 static inline int
eb_port_eq(struct etherbridge * eb,void * a,void * b)57 eb_port_eq(struct etherbridge *eb, void *a, void *b)
58 {
59 	return ((*eb->eb_ops->eb_op_port_eq)(eb->eb_cookie, a, b));
60 }
61 
62 static inline void *
eb_port_take(struct etherbridge * eb,void * port)63 eb_port_take(struct etherbridge *eb, void *port)
64 {
65 	return ((*eb->eb_ops->eb_op_port_take)(eb->eb_cookie, port));
66 }
67 
68 static inline void
eb_port_rele(struct etherbridge * eb,void * port)69 eb_port_rele(struct etherbridge *eb, void *port)
70 {
71 	return ((*eb->eb_ops->eb_op_port_rele)(eb->eb_cookie, port));
72 }
73 
74 static inline size_t
eb_port_ifname(struct etherbridge * eb,char * dst,size_t len,void * port)75 eb_port_ifname(struct etherbridge *eb, char *dst, size_t len, void *port)
76 {
77 	return ((*eb->eb_ops->eb_op_port_ifname)(eb->eb_cookie, dst, len,
78 	    port));
79 }
80 
81 static inline void
eb_port_sa(struct etherbridge * eb,struct sockaddr_storage * ss,void * port)82 eb_port_sa(struct etherbridge *eb, struct sockaddr_storage *ss, void *port)
83 {
84 	(*eb->eb_ops->eb_op_port_sa)(eb->eb_cookie, ss, port);
85 }
86 
87 int
etherbridge_init(struct etherbridge * eb,const char * name,const struct etherbridge_ops * ops,void * cookie)88 etherbridge_init(struct etherbridge *eb, const char *name,
89     const struct etherbridge_ops *ops, void *cookie)
90 {
91 	size_t i;
92 
93 	if (eb_entry_pool.pr_size == 0) {
94 		pool_init(&eb_entry_pool, sizeof(struct eb_entry),
95 		    0, IPL_SOFTNET, 0, "ebepl", NULL);
96 	}
97 
98 	eb->eb_table = mallocarray(ETHERBRIDGE_TABLE_SIZE,
99 	    sizeof(*eb->eb_table), M_DEVBUF, M_WAITOK|M_CANFAIL);
100 	if (eb->eb_table == NULL)
101 		return (ENOMEM);
102 
103 	eb->eb_name = name;
104 	eb->eb_ops = ops;
105 	eb->eb_cookie = cookie;
106 
107 	mtx_init(&eb->eb_lock, IPL_SOFTNET);
108 	RBT_INIT(eb_tree, &eb->eb_tree);
109 
110 	eb->eb_num = 0;
111 	eb->eb_max = 100;
112 	eb->eb_max_age = 240;
113 	timeout_set(&eb->eb_tmo_age, etherbridge_age, eb);
114 
115 	for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) {
116 		struct eb_list *ebl = &eb->eb_table[i];
117 		SMR_TAILQ_INIT(ebl);
118 	}
119 
120 	return (0);
121 }
122 
123 int
etherbridge_up(struct etherbridge * eb)124 etherbridge_up(struct etherbridge *eb)
125 {
126 	etherbridge_age(eb);
127 
128 	return (0);
129 }
130 
131 int
etherbridge_down(struct etherbridge * eb)132 etherbridge_down(struct etherbridge *eb)
133 {
134 	smr_barrier();
135 
136 	return (0);
137 }
138 
139 void
etherbridge_destroy(struct etherbridge * eb)140 etherbridge_destroy(struct etherbridge *eb)
141 {
142 	struct eb_entry *ebe, *nebe;
143 
144 	/* XXX assume that nothing will calling etherbridge_map now */
145 
146 	timeout_del_barrier(&eb->eb_tmo_age);
147 
148 	free(eb->eb_table, M_DEVBUF,
149 	    ETHERBRIDGE_TABLE_SIZE * sizeof(*eb->eb_table));
150 
151 	RBT_FOREACH_SAFE(ebe, eb_tree, &eb->eb_tree, nebe) {
152 		RBT_REMOVE(eb_tree, &eb->eb_tree, ebe);
153 		ebe_free(ebe);
154 	}
155 }
156 
157 static struct eb_list *
etherbridge_list(struct etherbridge * eb,uint64_t eba)158 etherbridge_list(struct etherbridge *eb, uint64_t eba)
159 {
160 	uint16_t hash;
161 
162 	hash = stoeplitz_h64(eba) & ETHERBRIDGE_TABLE_MASK;
163 
164 	return (&eb->eb_table[hash]);
165 }
166 
167 static struct eb_entry *
ebl_find(struct eb_list * ebl,uint64_t eba)168 ebl_find(struct eb_list *ebl, uint64_t eba)
169 {
170 	struct eb_entry *ebe;
171 
172 	SMR_TAILQ_FOREACH(ebe, ebl, ebe_lentry) {
173 		if (ebe->ebe_addr == eba)
174 			return (ebe);
175 	}
176 
177 	return (NULL);
178 }
179 
180 static inline void
ebl_insert(struct eb_list * ebl,struct eb_entry * ebe)181 ebl_insert(struct eb_list *ebl, struct eb_entry *ebe)
182 {
183 	SMR_TAILQ_INSERT_TAIL_LOCKED(ebl, ebe, ebe_lentry);
184 }
185 
186 static inline void
ebl_remove(struct eb_list * ebl,struct eb_entry * ebe)187 ebl_remove(struct eb_list *ebl, struct eb_entry *ebe)
188 {
189 	SMR_TAILQ_REMOVE_LOCKED(ebl, ebe, ebe_lentry);
190 }
191 
192 static inline int
ebt_cmp(const struct eb_entry * aebe,const struct eb_entry * bebe)193 ebt_cmp(const struct eb_entry *aebe, const struct eb_entry *bebe)
194 {
195 	if (aebe->ebe_addr > bebe->ebe_addr)
196 		return (1);
197 	if (aebe->ebe_addr < bebe->ebe_addr)
198 		return (-1);
199 	return (0);
200 }
201 
202 RBT_GENERATE(eb_tree, eb_entry, ebe_tentry, ebt_cmp);
203 
204 static inline struct eb_entry *
ebt_insert(struct etherbridge * eb,struct eb_entry * ebe)205 ebt_insert(struct etherbridge *eb, struct eb_entry *ebe)
206 {
207 	return (RBT_INSERT(eb_tree, &eb->eb_tree, ebe));
208 }
209 
210 static inline struct eb_entry *
ebt_find(struct etherbridge * eb,const struct eb_entry * ebe)211 ebt_find(struct etherbridge *eb, const struct eb_entry *ebe)
212 {
213 	return (RBT_FIND(eb_tree, &eb->eb_tree, ebe));
214 }
215 
216 static inline void
ebt_replace(struct etherbridge * eb,struct eb_entry * oebe,struct eb_entry * nebe)217 ebt_replace(struct etherbridge *eb, struct eb_entry *oebe,
218     struct eb_entry *nebe)
219 {
220 	struct eb_entry *rvebe;
221 
222 	RBT_REMOVE(eb_tree, &eb->eb_tree, oebe);
223 	rvebe = RBT_INSERT(eb_tree, &eb->eb_tree, nebe);
224 	KASSERTMSG(rvebe == NULL, "ebt_replace eb %p nebe %p rvebe %p",
225 	    eb, nebe, rvebe);
226 }
227 
228 static inline void
ebt_remove(struct etherbridge * eb,struct eb_entry * ebe)229 ebt_remove(struct etherbridge *eb, struct eb_entry *ebe)
230 {
231 	RBT_REMOVE(eb_tree, &eb->eb_tree, ebe);
232 }
233 
234 static inline void
ebe_rele(struct eb_entry * ebe)235 ebe_rele(struct eb_entry *ebe)
236 {
237 	smr_call(&ebe->ebe_smr_entry, ebe_free, ebe);
238 }
239 
240 static void
ebe_free(void * arg)241 ebe_free(void *arg)
242 {
243 	struct eb_entry *ebe = arg;
244 	struct etherbridge *eb = ebe->ebe_etherbridge;
245 
246 	eb_port_rele(eb, ebe->ebe_port);
247 	pool_put(&eb_entry_pool, ebe);
248 }
249 
250 void *
etherbridge_resolve_ea(struct etherbridge * eb,const struct ether_addr * ea)251 etherbridge_resolve_ea(struct etherbridge *eb,
252     const struct ether_addr *ea)
253 {
254 	return (etherbridge_resolve(eb, ether_addr_to_e64(ea)));
255 }
256 
257 void *
etherbridge_resolve(struct etherbridge * eb,uint64_t eba)258 etherbridge_resolve(struct etherbridge *eb, uint64_t eba)
259 {
260 	struct eb_list *ebl = etherbridge_list(eb, eba);
261 	struct eb_entry *ebe;
262 
263 	SMR_ASSERT_CRITICAL();
264 
265 	ebe = ebl_find(ebl, eba);
266 	if (ebe != NULL) {
267 		if (ebe->ebe_type == EBE_DYNAMIC) {
268 			int diff = getuptime() - ebe->ebe_age;
269 			if (diff > eb->eb_max_age)
270 				return (NULL);
271 		}
272 
273 		return (ebe->ebe_port);
274 	}
275 
276 	return (NULL);
277 }
278 
279 void
etherbridge_map_ea(struct etherbridge * eb,void * port,const struct ether_addr * ea)280 etherbridge_map_ea(struct etherbridge *eb, void *port,
281     const struct ether_addr *ea)
282 {
283 	etherbridge_map(eb, port, ether_addr_to_e64(ea));
284 }
285 
286 void
etherbridge_map(struct etherbridge * eb,void * port,uint64_t eba)287 etherbridge_map(struct etherbridge *eb, void *port, uint64_t eba)
288 {
289 	struct eb_list *ebl;
290 	struct eb_entry *oebe, *nebe;
291 	unsigned int num;
292 	void *nport;
293 	int new = 0;
294 	time_t now;
295 
296 	if (ETH64_IS_MULTICAST(eba) || ETH64_IS_ANYADDR(eba))
297 		return;
298 
299 	now = getuptime();
300 	ebl = etherbridge_list(eb, eba);
301 
302 	smr_read_enter();
303 	oebe = ebl_find(ebl, eba);
304 	if (oebe == NULL) {
305 		/*
306 		 * peek at the space to see if it's worth trying
307 		 * to make a new entry.
308 		 */
309 		if (eb->eb_num < eb->eb_max)
310 			new = 1;
311 	} else {
312 		if (oebe->ebe_age != now)
313 			oebe->ebe_age = now;
314 
315 		/* does this entry need to be replaced? */
316 		if (oebe->ebe_type == EBE_DYNAMIC &&
317 		    !eb_port_eq(eb, oebe->ebe_port, port))
318 			new = 1;
319 	}
320 	smr_read_leave();
321 
322 	if (!new)
323 		return;
324 
325 	nport = eb_port_take(eb, port);
326 	if (nport == NULL) {
327 		/* XXX should we remove the old one and flood? */
328 		return;
329 	}
330 
331 	nebe = pool_get(&eb_entry_pool, PR_NOWAIT);
332 	if (nebe == NULL) {
333 		/* XXX should we remove the old one and flood? */
334 		eb_port_rele(eb, nport);
335 		return;
336 	}
337 
338 	smr_init(&nebe->ebe_smr_entry);
339 	nebe->ebe_etherbridge = eb;
340 
341 	nebe->ebe_addr = eba;
342 	nebe->ebe_port = nport;
343 	nebe->ebe_type = EBE_DYNAMIC;
344 	nebe->ebe_age = now;
345 
346 	mtx_enter(&eb->eb_lock);
347 	oebe = ebt_find(eb, nebe);
348 	if (oebe == NULL) {
349 		num = eb->eb_num + 1;
350 		if (num <= eb->eb_max) {
351 			ebl_insert(ebl, nebe);
352 
353 			oebe = ebt_insert(eb, nebe);
354 			if (oebe != NULL) {
355 				panic("etherbridge %p changed while locked",
356 				    eb);
357 			}
358 
359 			/* great success */
360 			eb->eb_num = num;
361 			nebe = NULL; /* give ref to table */
362 		}
363 	} else if (oebe->ebe_type == EBE_DYNAMIC) {
364 		/* do the update */
365 		ebl_insert(ebl, nebe);
366 
367 		ebl_remove(ebl, oebe);
368 		ebt_replace(eb, oebe, nebe);
369 
370 		nebe = NULL; /* give ref to table */
371 	} else {
372 		/*
373 		 * oebe is not a dynamic entry, so don't replace it.
374 		 */
375 		oebe = NULL;
376 	}
377 	mtx_leave(&eb->eb_lock);
378 
379 	if (nebe != NULL) {
380 		/*
381 		 * the new entry didn't make it into the
382 		 * table so it can be freed directly.
383 		 */
384 		ebe_free(nebe);
385 	}
386 
387 	if (oebe != NULL) {
388 		/*
389 		 * we replaced this entry, it needs to be released.
390 		 */
391 		ebe_rele(oebe);
392 	}
393 }
394 
395 int
etherbridge_add_addr(struct etherbridge * eb,void * port,const struct ether_addr * ea,unsigned int type)396 etherbridge_add_addr(struct etherbridge *eb, void *port,
397     const struct ether_addr *ea, unsigned int type)
398 {
399 	uint64_t eba = ether_addr_to_e64(ea);
400 	struct eb_list *ebl;
401 	struct eb_entry *nebe;
402 	unsigned int num;
403 	void *nport;
404 	int error = 0;
405 
406 	if (ETH64_IS_MULTICAST(eba) || ETH64_IS_ANYADDR(eba))
407 		return (EADDRNOTAVAIL);
408 
409 	nport = eb_port_take(eb, port);
410 	if (nport == NULL)
411 		return (ENOMEM);
412 
413 	nebe = pool_get(&eb_entry_pool, PR_NOWAIT);
414 	if (nebe == NULL) {
415 		eb_port_rele(eb, nport);
416 		return (ENOMEM);
417 	}
418 
419 	smr_init(&nebe->ebe_smr_entry);
420 	nebe->ebe_etherbridge = eb;
421 
422 	nebe->ebe_addr = eba;
423 	nebe->ebe_port = nport;
424 	nebe->ebe_type = type;
425 	nebe->ebe_age = getuptime();
426 
427 	ebl = etherbridge_list(eb, eba);
428 
429 	mtx_enter(&eb->eb_lock);
430 	num = eb->eb_num + 1;
431 	if (num >= eb->eb_max)
432 		error = ENOSPC;
433 	else if (ebt_insert(eb, nebe) != NULL)
434 		error = EADDRINUSE;
435 	else {
436 		/* we win, do the insert */
437 		ebl_insert(ebl, nebe); /* give the ref to etherbridge */
438 		eb->eb_num = num;
439 	}
440 	mtx_leave(&eb->eb_lock);
441 
442 	if (error != 0) {
443 		/*
444 		 * the new entry didn't make it into the
445 		 * table, so it can be freed directly.
446 		 */
447 		ebe_free(nebe);
448 	}
449 
450 	return (error);
451 }
452 int
etherbridge_del_addr(struct etherbridge * eb,const struct ether_addr * ea)453 etherbridge_del_addr(struct etherbridge *eb, const struct ether_addr *ea)
454 {
455 	uint64_t eba = ether_addr_to_e64(ea);
456 	struct eb_list *ebl;
457 	struct eb_entry *oebe;
458 	const struct eb_entry key = {
459 		.ebe_addr = eba,
460 	};
461 	int error = 0;
462 
463 	ebl = etherbridge_list(eb, eba);
464 
465 	mtx_enter(&eb->eb_lock);
466 	oebe = ebt_find(eb, &key);
467 	if (oebe == NULL)
468 		error = ESRCH;
469 	else {
470 		KASSERT(eb->eb_num > 0);
471 		eb->eb_num--;
472 
473 		ebl_remove(ebl, oebe); /* it's our ref now */
474 		ebt_remove(eb, oebe);
475 	}
476 	mtx_leave(&eb->eb_lock);
477 
478 	if (oebe != NULL)
479 		ebe_rele(oebe);
480 
481 	return (error);
482 }
483 
484 static void
etherbridge_age(void * arg)485 etherbridge_age(void *arg)
486 {
487 	struct etherbridge *eb = arg;
488 	struct eb_entry *ebe, *nebe;
489 	struct eb_queue ebq = TAILQ_HEAD_INITIALIZER(ebq);
490 	int diff;
491 	unsigned int now = getuptime();
492 	size_t i;
493 
494 	timeout_add_sec(&eb->eb_tmo_age, 100);
495 
496 	for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) {
497 		struct eb_list *ebl = &eb->eb_table[i];
498 #if 0
499 		if (SMR_TAILQ_EMPTY(ebl));
500 			continue;
501 #endif
502 
503 		mtx_enter(&eb->eb_lock); /* don't block map too much */
504 		SMR_TAILQ_FOREACH_SAFE_LOCKED(ebe, ebl, ebe_lentry, nebe) {
505 			if (ebe->ebe_type != EBE_DYNAMIC)
506 				continue;
507 
508 			diff = now - ebe->ebe_age;
509 			if (diff < eb->eb_max_age)
510 				continue;
511 
512 			ebl_remove(ebl, ebe);
513 			ebt_remove(eb, ebe);
514 			eb->eb_num--;
515 
516 			/* we own the tables ref now */
517 
518 			TAILQ_INSERT_TAIL(&ebq, ebe, ebe_qentry);
519 		}
520 		mtx_leave(&eb->eb_lock);
521 	}
522 
523 	TAILQ_FOREACH_SAFE(ebe, &ebq, ebe_qentry, nebe) {
524 		TAILQ_REMOVE(&ebq, ebe, ebe_qentry);
525 		ebe_rele(ebe);
526 	}
527 }
528 
529 void
etherbridge_detach_port(struct etherbridge * eb,void * port)530 etherbridge_detach_port(struct etherbridge *eb, void *port)
531 {
532 	struct eb_entry *ebe, *nebe;
533 	struct eb_queue ebq = TAILQ_HEAD_INITIALIZER(ebq);
534 	size_t i;
535 
536 	for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) {
537 		struct eb_list *ebl = &eb->eb_table[i];
538 
539 		mtx_enter(&eb->eb_lock); /* don't block map too much */
540 		SMR_TAILQ_FOREACH_SAFE_LOCKED(ebe, ebl, ebe_lentry, nebe) {
541 			if (!eb_port_eq(eb, ebe->ebe_port, port))
542 				continue;
543 
544 			ebl_remove(ebl, ebe);
545 			ebt_remove(eb, ebe);
546 			eb->eb_num--;
547 
548 			/* we own the tables ref now */
549 
550 			TAILQ_INSERT_TAIL(&ebq, ebe, ebe_qentry);
551 		}
552 		mtx_leave(&eb->eb_lock);
553 	}
554 
555 	if (TAILQ_EMPTY(&ebq))
556 		return;
557 
558 	/*
559 	 * do one smr barrier for all the entries rather than an
560 	 * smr_call each.
561 	 */
562 	smr_barrier();
563 
564 	TAILQ_FOREACH_SAFE(ebe, &ebq, ebe_qentry, nebe) {
565 		TAILQ_REMOVE(&ebq, ebe, ebe_qentry);
566 		ebe_free(ebe);
567 	}
568 }
569 
570 void
etherbridge_flush(struct etherbridge * eb,uint32_t flags)571 etherbridge_flush(struct etherbridge *eb, uint32_t flags)
572 {
573 	struct eb_entry *ebe, *nebe;
574 	struct eb_queue ebq = TAILQ_HEAD_INITIALIZER(ebq);
575 	size_t i;
576 
577 	for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) {
578 		struct eb_list *ebl = &eb->eb_table[i];
579 
580 		mtx_enter(&eb->eb_lock); /* don't block map too much */
581 		SMR_TAILQ_FOREACH_SAFE_LOCKED(ebe, ebl, ebe_lentry, nebe) {
582 			if (flags == IFBF_FLUSHDYN &&
583 			    ebe->ebe_type != EBE_DYNAMIC)
584 				continue;
585 
586 			ebl_remove(ebl, ebe);
587 			ebt_remove(eb, ebe);
588 			eb->eb_num--;
589 
590 			/* we own the tables ref now */
591 
592 			TAILQ_INSERT_TAIL(&ebq, ebe, ebe_qentry);
593 		}
594 		mtx_leave(&eb->eb_lock);
595 	}
596 
597 	if (TAILQ_EMPTY(&ebq))
598 		return;
599 
600 	/*
601 	 * do one smr barrier for all the entries rather than an
602 	 * smr_call each.
603 	 */
604 	smr_barrier();
605 
606 	TAILQ_FOREACH_SAFE(ebe, &ebq, ebe_qentry, nebe) {
607 		TAILQ_REMOVE(&ebq, ebe, ebe_qentry);
608 		ebe_free(ebe);
609 	}
610 }
611 
612 int
etherbridge_rtfind(struct etherbridge * eb,struct ifbaconf * baconf)613 etherbridge_rtfind(struct etherbridge *eb, struct ifbaconf *baconf)
614 {
615 	struct eb_entry *ebe;
616 	struct ifbareq bareq;
617 	caddr_t buf;
618 	size_t len, nlen;
619 	time_t age, now = getuptime();
620 	int error;
621 
622 	if (baconf->ifbac_len == 0) {
623 		/* single read is atomic */
624 		baconf->ifbac_len = eb->eb_num * sizeof(bareq);
625 		return (0);
626 	}
627 
628 	buf = malloc(baconf->ifbac_len, M_TEMP, M_WAITOK|M_CANFAIL);
629 	if (buf == NULL)
630 		return (ENOMEM);
631 	len = 0;
632 
633 	mtx_enter(&eb->eb_lock);
634 	RBT_FOREACH(ebe, eb_tree, &eb->eb_tree) {
635 		nlen = len + sizeof(bareq);
636 		if (nlen > baconf->ifbac_len)
637 			break;
638 
639 		strlcpy(bareq.ifba_name, eb->eb_name,
640 		    sizeof(bareq.ifba_name));
641 		eb_port_ifname(eb,
642 		    bareq.ifba_ifsname, sizeof(bareq.ifba_ifsname),
643 		    ebe->ebe_port);
644 		ether_e64_to_addr(&bareq.ifba_dst, ebe->ebe_addr);
645 
646 		memset(&bareq.ifba_dstsa, 0, sizeof(bareq.ifba_dstsa));
647 		eb_port_sa(eb, &bareq.ifba_dstsa, ebe->ebe_port);
648 
649 		switch (ebe->ebe_type) {
650 		case EBE_DYNAMIC:
651 			age = now - ebe->ebe_age;
652 			bareq.ifba_age = MIN(age, 0xff);
653 			bareq.ifba_flags = IFBAF_DYNAMIC;
654 			break;
655 		case EBE_STATIC:
656 			bareq.ifba_age = 0;
657 			bareq.ifba_flags = IFBAF_STATIC;
658 			break;
659 		}
660 
661 		memcpy(buf + len, &bareq, sizeof(bareq));
662 		len = nlen;
663 	}
664 	nlen = baconf->ifbac_len;
665 	baconf->ifbac_len = eb->eb_num * sizeof(bareq);
666 	mtx_leave(&eb->eb_lock);
667 
668 	error = copyout(buf, baconf->ifbac_buf, len);
669 	free(buf, M_TEMP, nlen);
670 
671 	return (error);
672 }
673 
674 int
etherbridge_set_max(struct etherbridge * eb,struct ifbrparam * bparam)675 etherbridge_set_max(struct etherbridge *eb, struct ifbrparam *bparam)
676 {
677 	if (bparam->ifbrp_csize < 1 ||
678 	    bparam->ifbrp_csize > 4096) /* XXX */
679 		return (EINVAL);
680 
681 	/* commit */
682 	eb->eb_max = bparam->ifbrp_csize;
683 
684 	return (0);
685 }
686 
687 int
etherbridge_get_max(struct etherbridge * eb,struct ifbrparam * bparam)688 etherbridge_get_max(struct etherbridge *eb, struct ifbrparam *bparam)
689 {
690 	bparam->ifbrp_csize = eb->eb_max;
691 
692 	return (0);
693 }
694 
695 int
etherbridge_set_tmo(struct etherbridge * eb,struct ifbrparam * bparam)696 etherbridge_set_tmo(struct etherbridge *eb, struct ifbrparam *bparam)
697 {
698 	if (bparam->ifbrp_ctime < 8 ||
699 	    bparam->ifbrp_ctime > 3600)
700 		return (EINVAL);
701 
702 	/* commit */
703 	eb->eb_max_age = bparam->ifbrp_ctime;
704 
705 	return (0);
706 }
707 
708 int
etherbridge_get_tmo(struct etherbridge * eb,struct ifbrparam * bparam)709 etherbridge_get_tmo(struct etherbridge *eb, struct ifbrparam *bparam)
710 {
711 	bparam->ifbrp_ctime = eb->eb_max_age;
712 
713 	return (0);
714 }
715