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
47static inline void	ebe_rele(struct eb_entry *);
48static void		ebe_free(void *);
49
50static void		etherbridge_age(void *);
51
52RBT_PROTOTYPE(eb_tree, eb_entry, ebe_tentry, ebt_cmp);
53
54static struct pool	eb_entry_pool;
55
56static inline int
57eb_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
62static inline void *
63eb_port_take(struct etherbridge *eb, void *port)
64{
65	return ((*eb->eb_ops->eb_op_port_take)(eb->eb_cookie, port));
66}
67
68static inline void
69eb_port_rele(struct etherbridge *eb, void *port)
70{
71	return ((*eb->eb_ops->eb_op_port_rele)(eb->eb_cookie, port));
72}
73
74static inline size_t
75eb_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
81static inline void
82eb_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
87int
88etherbridge_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
123int
124etherbridge_up(struct etherbridge *eb)
125{
126	etherbridge_age(eb);
127
128	return (0);
129}
130
131int
132etherbridge_down(struct etherbridge *eb)
133{
134	smr_barrier();
135
136	return (0);
137}
138
139void
140etherbridge_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
157static struct eb_list *
158etherbridge_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
167static struct eb_entry *
168ebl_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
180static inline void
181ebl_insert(struct eb_list *ebl, struct eb_entry *ebe)
182{
183	SMR_TAILQ_INSERT_TAIL_LOCKED(ebl, ebe, ebe_lentry);
184}
185
186static inline void
187ebl_remove(struct eb_list *ebl, struct eb_entry *ebe)
188{
189	SMR_TAILQ_REMOVE_LOCKED(ebl, ebe, ebe_lentry);
190}
191
192static inline int
193ebt_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
202RBT_GENERATE(eb_tree, eb_entry, ebe_tentry, ebt_cmp);
203
204static inline struct eb_entry *
205ebt_insert(struct etherbridge *eb, struct eb_entry *ebe)
206{
207	return (RBT_INSERT(eb_tree, &eb->eb_tree, ebe));
208}
209
210static inline struct eb_entry *
211ebt_find(struct etherbridge *eb, const struct eb_entry *ebe)
212{
213	return (RBT_FIND(eb_tree, &eb->eb_tree, ebe));
214}
215
216static inline void
217ebt_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
228static inline void
229ebt_remove(struct etherbridge *eb, struct eb_entry *ebe)
230{
231	RBT_REMOVE(eb_tree, &eb->eb_tree, ebe);
232}
233
234static inline void
235ebe_rele(struct eb_entry *ebe)
236{
237	smr_call(&ebe->ebe_smr_entry, ebe_free, ebe);
238}
239
240static void
241ebe_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
250void *
251etherbridge_resolve_ea(struct etherbridge *eb,
252    const struct ether_addr *ea)
253{
254	return (etherbridge_resolve(eb, ether_addr_to_e64(ea)));
255}
256
257void *
258etherbridge_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
279void
280etherbridge_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
286void
287etherbridge_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
395int
396etherbridge_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}
452int
453etherbridge_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
484static void
485etherbridge_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
529void
530etherbridge_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
570void
571etherbridge_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
612int
613etherbridge_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
674int
675etherbridge_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
687int
688etherbridge_get_max(struct etherbridge *eb, struct ifbrparam *bparam)
689{
690	bparam->ifbrp_csize = eb->eb_max;
691
692	return (0);
693}
694
695int
696etherbridge_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
708int
709etherbridge_get_tmo(struct etherbridge *eb, struct ifbrparam *bparam)
710{
711	bparam->ifbrp_ctime = eb->eb_max_age;
712
713	return (0);
714}
715