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