xref: /openbsd/sys/net/bridgectl.c (revision c2e35fc4)
1 /*	$OpenBSD: bridgectl.c,v 1.20 2019/07/09 15:15:49 mpi Exp $	*/
2 
3 /*
4  * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  *
28  * Effort sponsored in part by the Defense Advanced Research Projects
29  * Agency (DARPA) and Air Force Research Laboratory, Air Force
30  * Materiel Command, USAF, under agreement number F30602-01-2-0537.
31  *
32  */
33 
34 #include "pf.h"
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/mbuf.h>
39 #include <sys/socket.h>
40 #include <sys/ioctl.h>
41 #include <sys/timeout.h>
42 #include <sys/kernel.h>
43 
44 #include <crypto/siphash.h>
45 
46 #include <net/if.h>
47 
48 #include <netinet/in.h>
49 #include <netinet/if_ether.h>
50 
51 #include <net/if_bridge.h>
52 
53 
54 int	bridge_rtfind(struct bridge_softc *, struct ifbaconf *);
55 int	bridge_rtdaddr(struct bridge_softc *, struct ether_addr *);
56 u_int32_t bridge_hash(struct bridge_softc *, struct ether_addr *);
57 
58 int	bridge_brlconf(struct bridge_iflist *, struct ifbrlconf *);
59 int	bridge_addrule(struct bridge_iflist *, struct ifbrlreq *, int out);
60 
61 int
62 bridgectl_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
63 {
64 	struct bridge_softc *sc = (struct bridge_softc *)ifp->if_softc;
65 	struct ifbreq *req = (struct ifbreq *)data;
66 	struct ifbrlreq *brlreq = (struct ifbrlreq *)data;
67 	struct ifbrlconf *bc = (struct ifbrlconf *)data;
68 	struct ifbareq *bareq = (struct ifbareq *)data;
69 	struct ifbrparam *bparam = (struct ifbrparam *)data;
70 	struct bridge_iflist *bif;
71 	struct ifnet *ifs;
72 	int error = 0;
73 
74 	switch (cmd) {
75 	case SIOCBRDGRTS:
76 		error = bridge_rtfind(sc, (struct ifbaconf *)data);
77 		break;
78 	case SIOCBRDGFLUSH:
79 		bridge_rtflush(sc, req->ifbr_ifsflags);
80 		break;
81 	case SIOCBRDGSADDR:
82 		ifs = ifunit(bareq->ifba_ifsname);
83 		if (ifs == NULL) {			/* no such interface */
84 			error = ENOENT;
85 			break;
86 		}
87 		if (ifs->if_bridgeidx != ifp->if_index) {
88 			error = ESRCH;
89 			break;
90 		}
91 
92 		if (bridge_rtupdate(sc, &bareq->ifba_dst, ifs, 1,
93 		    bareq->ifba_flags, NULL))
94 			error = ENOMEM;
95 		break;
96 	case SIOCBRDGDADDR:
97 		error = bridge_rtdaddr(sc, &bareq->ifba_dst);
98 		break;
99 	case SIOCBRDGGCACHE:
100 		bparam->ifbrp_csize = sc->sc_brtmax;
101 		break;
102 	case SIOCBRDGSCACHE:
103 		mtx_enter(&sc->sc_mtx);
104 		sc->sc_brtmax = bparam->ifbrp_csize;
105 		mtx_leave(&sc->sc_mtx);
106 		break;
107 	case SIOCBRDGSTO:
108 		if (bparam->ifbrp_ctime < 0 ||
109 		    bparam->ifbrp_ctime > INT_MAX / hz) {
110 			error = EINVAL;
111 			break;
112 		}
113 		sc->sc_brttimeout = bparam->ifbrp_ctime;
114 		if (bparam->ifbrp_ctime != 0)
115 			timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
116 		else
117 			timeout_del(&sc->sc_brtimeout);
118 		break;
119 	case SIOCBRDGGTO:
120 		bparam->ifbrp_ctime = sc->sc_brttimeout;
121 		break;
122 	case SIOCBRDGARL:
123 		ifs = ifunit(brlreq->ifbr_ifsname);
124 		if (ifs == NULL) {
125 			error = ENOENT;
126 			break;
127 		}
128 		if (ifs->if_bridgeidx != ifp->if_index) {
129 			error = ESRCH;
130 			break;
131 		}
132 		if ((brlreq->ifbr_action != BRL_ACTION_BLOCK &&
133 		    brlreq->ifbr_action != BRL_ACTION_PASS) ||
134 		    (brlreq->ifbr_flags & (BRL_FLAG_IN|BRL_FLAG_OUT)) == 0) {
135 			error = EINVAL;
136 			break;
137 		}
138 		bif = bridge_getbif(ifs);
139 		if (brlreq->ifbr_flags & BRL_FLAG_IN) {
140 			error = bridge_addrule(bif, brlreq, 0);
141 			if (error)
142 				break;
143 		}
144 		if (brlreq->ifbr_flags & BRL_FLAG_OUT) {
145 			error = bridge_addrule(bif, brlreq, 1);
146 			if (error)
147 				break;
148 		}
149 		break;
150 	case SIOCBRDGFRL:
151 		ifs = ifunit(brlreq->ifbr_ifsname);
152 		if (ifs == NULL) {
153 			error = ENOENT;
154 			break;
155 		}
156 		if (ifs->if_bridgeidx != ifp->if_index) {
157 			error = ESRCH;
158 			break;
159 		}
160 		bif = bridge_getbif(ifs);
161 		bridge_flushrule(bif);
162 		break;
163 	case SIOCBRDGGRL:
164 		ifs = ifunit(bc->ifbrl_ifsname);
165 		if (ifs == NULL) {
166 			error = ENOENT;
167 			break;
168 		}
169 		if (ifs->if_bridgeidx != ifp->if_index) {
170 			error = ESRCH;
171 			break;
172 		}
173 		bif = bridge_getbif(ifs);
174 		error = bridge_brlconf(bif, bc);
175 		break;
176 	default:
177 		break;
178 	}
179 
180 	return (error);
181 }
182 
183 int
184 bridge_rtupdate(struct bridge_softc *sc, struct ether_addr *ea,
185     struct ifnet *ifp, int setflags, u_int8_t flags, struct mbuf *m)
186 {
187 	struct bridge_rtnode *p, *q;
188 	struct bridge_tunneltag	*brtag = NULL;
189 	u_int32_t h;
190 	int dir, error = 0;
191 
192 	if (m != NULL) {
193 		/* Check if the mbuf was tagged with a tunnel endpoint addr */
194 		brtag = bridge_tunnel(m);
195 	}
196 
197 	h = bridge_hash(sc, ea);
198 	mtx_enter(&sc->sc_mtx);
199 	p = LIST_FIRST(&sc->sc_rts[h]);
200 	if (p == NULL) {
201 		if (sc->sc_brtcnt >= sc->sc_brtmax)
202 			goto done;
203 		p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
204 		if (p == NULL)
205 			goto done;
206 
207 		bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
208 		p->brt_ifidx = ifp->if_index;
209 		p->brt_age = 1;
210 		bridge_copytag(brtag, &p->brt_tunnel);
211 
212 		if (setflags)
213 			p->brt_flags = flags;
214 		else
215 			p->brt_flags = IFBAF_DYNAMIC;
216 
217 		LIST_INSERT_HEAD(&sc->sc_rts[h], p, brt_next);
218 		sc->sc_brtcnt++;
219 		goto want;
220 	}
221 
222 	do {
223 		q = p;
224 		p = LIST_NEXT(p, brt_next);
225 
226 		dir = memcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
227 		if (dir == 0) {
228 			if (setflags) {
229 				q->brt_ifidx = ifp->if_index;
230 				q->brt_flags = flags;
231 			} else if (!(q->brt_flags & IFBAF_STATIC))
232 				q->brt_ifidx = ifp->if_index;
233 
234 			if (q->brt_ifidx == ifp->if_index)
235 				q->brt_age = 1;
236 			bridge_copytag(brtag, &q->brt_tunnel);
237 			goto want;
238 		}
239 
240 		if (dir > 0) {
241 			if (sc->sc_brtcnt >= sc->sc_brtmax)
242 				goto done;
243 			p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
244 			if (p == NULL)
245 				goto done;
246 
247 			bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
248 			p->brt_ifidx = ifp->if_index;
249 			p->brt_age = 1;
250 			bridge_copytag(brtag, &p->brt_tunnel);
251 
252 			if (setflags)
253 				p->brt_flags = flags;
254 			else
255 				p->brt_flags = IFBAF_DYNAMIC;
256 
257 			LIST_INSERT_BEFORE(q, p, brt_next);
258 			sc->sc_brtcnt++;
259 			goto want;
260 		}
261 
262 		if (p == NULL) {
263 			if (sc->sc_brtcnt >= sc->sc_brtmax)
264 				goto done;
265 			p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
266 			if (p == NULL)
267 				goto done;
268 
269 			bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
270 			p->brt_ifidx = ifp->if_index;
271 			p->brt_age = 1;
272 			bridge_copytag(brtag, &p->brt_tunnel);
273 
274 			if (setflags)
275 				p->brt_flags = flags;
276 			else
277 				p->brt_flags = IFBAF_DYNAMIC;
278 			LIST_INSERT_AFTER(q, p, brt_next);
279 			sc->sc_brtcnt++;
280 			goto want;
281 		}
282 	} while (p != NULL);
283 
284 done:
285 	error = 1;
286 want:
287 	mtx_leave(&sc->sc_mtx);
288 	return (error);
289 }
290 
291 unsigned int
292 bridge_rtlookup(struct ifnet *brifp, struct ether_addr *ea, struct mbuf *m)
293 {
294 	struct bridge_softc *sc = brifp->if_softc;
295 	struct bridge_rtnode *p = NULL;
296 	unsigned int ifidx = 0;
297 	u_int32_t h;
298 	int dir;
299 
300 	h = bridge_hash(sc, ea);
301 	mtx_enter(&sc->sc_mtx);
302 	LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
303 		dir = memcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
304 		if (dir == 0)
305 			break;
306 		if (dir > 0) {
307 			p = NULL;
308 			break;
309 		}
310 	}
311 	if (p != NULL) {
312 		ifidx = p->brt_ifidx;
313 
314 		if (p->brt_family != AF_UNSPEC && m != NULL) {
315 			struct bridge_tunneltag *brtag;
316 
317 			brtag = bridge_tunneltag(m);
318 			if (brtag != NULL)
319 				bridge_copytag(&p->brt_tunnel, brtag);
320 		}
321 	}
322 	mtx_leave(&sc->sc_mtx);
323 
324 	return (ifidx);
325 }
326 
327 u_int32_t
328 bridge_hash(struct bridge_softc *sc, struct ether_addr *addr)
329 {
330 	return SipHash24((SIPHASH_KEY *)sc->sc_hashkey, addr, ETHER_ADDR_LEN) &
331 	    BRIDGE_RTABLE_MASK;
332 }
333 
334 /*
335  * Perform an aging cycle
336  */
337 void
338 bridge_rtage(void *vsc)
339 {
340 	struct bridge_softc *sc = vsc;
341 	struct ifnet *ifp = &sc->sc_if;
342 	struct bridge_rtnode *n, *p;
343 	int i;
344 
345 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
346 		return;
347 
348 	mtx_enter(&sc->sc_mtx);
349 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
350 		n = LIST_FIRST(&sc->sc_rts[i]);
351 		while (n != NULL) {
352 			if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) {
353 				n->brt_age = !n->brt_age;
354 				if (n->brt_age)
355 					n->brt_age = 0;
356 				n = LIST_NEXT(n, brt_next);
357 			} else if (n->brt_age) {
358 				n->brt_age = 0;
359 				n = LIST_NEXT(n, brt_next);
360 			} else {
361 				p = LIST_NEXT(n, brt_next);
362 				LIST_REMOVE(n, brt_next);
363 				sc->sc_brtcnt--;
364 				free(n, M_DEVBUF, sizeof *n);
365 				n = p;
366 			}
367 		}
368 	}
369 	mtx_leave(&sc->sc_mtx);
370 
371 	if (sc->sc_brttimeout != 0)
372 		timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
373 }
374 
375 void
376 bridge_rtagenode(struct ifnet *ifp, int age)
377 {
378 	struct bridge_softc *sc;
379 	struct bridge_rtnode *n;
380 	struct ifnet *bifp;
381 	int i;
382 
383 	bifp = if_get(ifp->if_bridgeidx);
384 	if (bifp == NULL)
385 		return;
386 	sc = bifp->if_softc;
387 
388 	/*
389 	 * If the age is zero then flush, otherwise set all the expiry times to
390 	 * age for the interface
391 	 */
392 	if (age == 0)
393 		bridge_rtdelete(sc, ifp, 1);
394 	else {
395 		mtx_enter(&sc->sc_mtx);
396 		for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
397 			LIST_FOREACH(n, &sc->sc_rts[i], brt_next) {
398 				/* Cap the expiry time to 'age' */
399 				if (n->brt_ifidx == ifp->if_index &&
400 				    n->brt_age > time_uptime + age &&
401 				    (n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
402 					n->brt_age = time_uptime + age;
403 			}
404 		}
405 		mtx_leave(&sc->sc_mtx);
406 	}
407 
408 	if_put(bifp);
409 }
410 
411 /*
412  * Remove all dynamic addresses from the cache
413  */
414 void
415 bridge_rtflush(struct bridge_softc *sc, int full)
416 {
417 	int i;
418 	struct bridge_rtnode *p, *n;
419 
420 	mtx_enter(&sc->sc_mtx);
421 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
422 		n = LIST_FIRST(&sc->sc_rts[i]);
423 		while (n != NULL) {
424 			if (full ||
425 			    (n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
426 				p = LIST_NEXT(n, brt_next);
427 				LIST_REMOVE(n, brt_next);
428 				sc->sc_brtcnt--;
429 				free(n, M_DEVBUF, sizeof *n);
430 				n = p;
431 			} else
432 				n = LIST_NEXT(n, brt_next);
433 		}
434 	}
435 	mtx_leave(&sc->sc_mtx);
436 }
437 
438 /*
439  * Remove an address from the cache
440  */
441 int
442 bridge_rtdaddr(struct bridge_softc *sc, struct ether_addr *ea)
443 {
444 	int h;
445 	struct bridge_rtnode *p;
446 
447 	h = bridge_hash(sc, ea);
448 	mtx_enter(&sc->sc_mtx);
449 	LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
450 		if (memcmp(ea, &p->brt_addr, sizeof(p->brt_addr)) == 0) {
451 			LIST_REMOVE(p, brt_next);
452 			sc->sc_brtcnt--;
453 			mtx_leave(&sc->sc_mtx);
454 			free(p, M_DEVBUF, sizeof *p);
455 			return (0);
456 		}
457 	}
458 	mtx_leave(&sc->sc_mtx);
459 
460 	return (ENOENT);
461 }
462 
463 /*
464  * Delete routes to a specific interface member.
465  */
466 void
467 bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int dynonly)
468 {
469 	int i;
470 	struct bridge_rtnode *n, *p;
471 
472 	/*
473 	 * Loop through all of the hash buckets and traverse each
474 	 * chain looking for routes to this interface.
475 	 */
476 	mtx_enter(&sc->sc_mtx);
477 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
478 		n = LIST_FIRST(&sc->sc_rts[i]);
479 		while (n != NULL) {
480 			if (n->brt_ifidx != ifp->if_index) {
481 				/* Not ours */
482 				n = LIST_NEXT(n, brt_next);
483 				continue;
484 			}
485 			if (dynonly &&
486 			    (n->brt_flags & IFBAF_TYPEMASK) != IFBAF_DYNAMIC) {
487 				/* only deleting dynamics */
488 				n = LIST_NEXT(n, brt_next);
489 				continue;
490 			}
491 			p = LIST_NEXT(n, brt_next);
492 			LIST_REMOVE(n, brt_next);
493 			sc->sc_brtcnt--;
494 			free(n, M_DEVBUF, sizeof *n);
495 			n = p;
496 		}
497 	}
498 	mtx_leave(&sc->sc_mtx);
499 }
500 
501 /*
502  * Gather all of the routes for this interface.
503  */
504 int
505 bridge_rtfind(struct bridge_softc *sc, struct ifbaconf *baconf)
506 {
507 	struct ifbareq *bareq, *bareqs = NULL;
508 	struct bridge_rtnode *n;
509 	u_int32_t i = 0, total = 0;
510 	int k, error = 0;
511 
512 	mtx_enter(&sc->sc_mtx);
513 	for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
514 		LIST_FOREACH(n, &sc->sc_rts[k], brt_next)
515 			total++;
516 	}
517 	mtx_leave(&sc->sc_mtx);
518 
519 	if (baconf->ifbac_len == 0) {
520 		i = total;
521 		goto done;
522 	}
523 
524 	total = MIN(total, baconf->ifbac_len / sizeof(*bareqs));
525 	bareqs = mallocarray(total, sizeof(*bareqs), M_TEMP, M_NOWAIT|M_ZERO);
526 	if (bareqs == NULL)
527 		goto done;
528 
529 	mtx_enter(&sc->sc_mtx);
530 	for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
531 		LIST_FOREACH(n, &sc->sc_rts[k], brt_next) {
532 			struct ifnet *ifp;
533 
534 			if (i >= total) {
535 				mtx_leave(&sc->sc_mtx);
536 				goto done;
537 			}
538 			bareq = &bareqs[i];
539 
540 			ifp = if_get(n->brt_ifidx);
541 			if (ifp == NULL)
542 				continue;
543 			bcopy(ifp->if_xname, bareq->ifba_ifsname,
544 			    sizeof(bareq->ifba_ifsname));
545 			if_put(ifp);
546 
547 			bcopy(sc->sc_if.if_xname, bareq->ifba_name,
548 			    sizeof(bareq->ifba_name));
549 			bcopy(&n->brt_addr, &bareq->ifba_dst,
550 			    sizeof(bareq->ifba_dst));
551 			bridge_copyaddr(&n->brt_tunnel.brtag_peer.sa,
552 			    sstosa(&bareq->ifba_dstsa));
553 			bareq->ifba_age = n->brt_age;
554 			bareq->ifba_flags = n->brt_flags;
555 			i++;
556 		}
557 	}
558 	mtx_leave(&sc->sc_mtx);
559 
560 	error = copyout(bareqs, baconf->ifbac_req, i * sizeof(*bareqs));
561 done:
562 	free(bareqs, M_TEMP, total * sizeof(*bareqs));
563 	baconf->ifbac_len = i * sizeof(*bareqs);
564 	return (error);
565 }
566 
567 void
568 bridge_update(struct ifnet *ifp, struct ether_addr *ea, int delete)
569 {
570 	struct bridge_softc *sc;
571 	struct bridge_iflist *bif;
572 	u_int8_t *addr;
573 
574 	addr = (u_int8_t *)ea;
575 
576 	bif = bridge_getbif(ifp);
577 	if (bif == NULL)
578 		return;
579 	sc = bif->bridge_sc;
580 	if (sc == NULL)
581 		return;
582 
583 	/*
584 	 * Update the bridge interface if it is in
585 	 * the learning state.
586 	 */
587 	if ((bif->bif_flags & IFBIF_LEARNING) &&
588 	    (ETHER_IS_MULTICAST(addr) == 0) &&
589 	    !(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 &&
590 	      addr[3] == 0 && addr[4] == 0 && addr[5] == 0)) {
591 		/* Care must be taken with spanning tree */
592 		if ((bif->bif_flags & IFBIF_STP) &&
593 		    (bif->bif_state == BSTP_IFSTATE_DISCARDING))
594 			return;
595 
596 		/* Delete the address from the bridge */
597 		bridge_rtdaddr(sc, ea);
598 
599 		if (!delete) {
600 			/* Update the bridge table */
601 			bridge_rtupdate(sc, ea, ifp, 0, IFBAF_DYNAMIC, NULL);
602 		}
603 	}
604 }
605 
606 /*
607  * bridge filter/matching rules
608  */
609 int
610 bridge_brlconf(struct bridge_iflist *bif, struct ifbrlconf *bc)
611 {
612 	struct bridge_softc *sc = bif->bridge_sc;
613 	struct brl_node *n;
614 	struct ifbrlreq *req, *reqs = NULL;
615 	int error = 0;
616 	u_int32_t i = 0, total = 0;
617 
618 	SIMPLEQ_FOREACH(n, &bif->bif_brlin, brl_next) {
619 		total++;
620 	}
621 	SIMPLEQ_FOREACH(n, &bif->bif_brlout, brl_next) {
622 		total++;
623 	}
624 
625 	if (bc->ifbrl_len == 0) {
626 		i = total;
627 		goto done;
628 	}
629 
630 	reqs = mallocarray(total, sizeof(*reqs), M_TEMP, M_NOWAIT|M_ZERO);
631 	if (reqs == NULL)
632 		goto done;
633 
634 	SIMPLEQ_FOREACH(n, &bif->bif_brlin, brl_next) {
635 		if (bc->ifbrl_len < (i + 1) * sizeof(*reqs))
636 			goto done;
637 		req = &reqs[i];
638 		strlcpy(req->ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
639 		strlcpy(req->ifbr_ifsname, bif->ifp->if_xname, IFNAMSIZ);
640 		req->ifbr_action = n->brl_action;
641 		req->ifbr_flags = n->brl_flags;
642 		req->ifbr_src = n->brl_src;
643 		req->ifbr_dst = n->brl_dst;
644 		req->ifbr_arpf = n->brl_arpf;
645 #if NPF > 0
646 		req->ifbr_tagname[0] = '\0';
647 		if (n->brl_tag)
648 			pf_tag2tagname(n->brl_tag, req->ifbr_tagname);
649 #endif
650 		i++;
651 	}
652 
653 	SIMPLEQ_FOREACH(n, &bif->bif_brlout, brl_next) {
654 		if (bc->ifbrl_len < (i + 1) * sizeof(*reqs))
655 			goto done;
656 		req = &reqs[i];
657 		strlcpy(req->ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
658 		strlcpy(req->ifbr_ifsname, bif->ifp->if_xname, IFNAMSIZ);
659 		req->ifbr_action = n->brl_action;
660 		req->ifbr_flags = n->brl_flags;
661 		req->ifbr_src = n->brl_src;
662 		req->ifbr_dst = n->brl_dst;
663 		req->ifbr_arpf = n->brl_arpf;
664 #if NPF > 0
665 		req->ifbr_tagname[0] = '\0';
666 		if (n->brl_tag)
667 			pf_tag2tagname(n->brl_tag, req->ifbr_tagname);
668 #endif
669 		i++;
670 	}
671 
672 	error = copyout(reqs, bc->ifbrl_buf, i * sizeof(*reqs));
673 done:
674 	free(reqs, M_TEMP, total * sizeof(*reqs));
675 	bc->ifbrl_len = i * sizeof(*reqs);
676 	return (error);
677 }
678 
679 u_int8_t
680 bridge_arpfilter(struct brl_node *n, struct ether_header *eh, struct mbuf *m)
681 {
682 	struct ether_arp	 ea;
683 
684 	if (!(n->brl_arpf.brla_flags & (BRLA_ARP|BRLA_RARP)))
685 		return (1);
686 
687 	if (ntohs(eh->ether_type) != ETHERTYPE_ARP)
688 		return (0);
689 	if (m->m_pkthdr.len <= ETHER_HDR_LEN + sizeof(ea))
690 		return (0);	/* log error? */
691 	m_copydata(m, ETHER_HDR_LEN, sizeof(ea), (caddr_t)&ea);
692 
693 	if (ntohs(ea.arp_hrd) != ARPHRD_ETHER ||
694 	    ntohs(ea.arp_pro) != ETHERTYPE_IP ||
695 	    ea.arp_hln != ETHER_ADDR_LEN ||
696 	    ea.arp_pln != sizeof(struct in_addr))
697 		return (0);
698 	if ((n->brl_arpf.brla_flags & BRLA_ARP) &&
699 	    ntohs(ea.arp_op) != ARPOP_REQUEST &&
700 	    ntohs(ea.arp_op) != ARPOP_REPLY)
701 		return (0);
702 	if ((n->brl_arpf.brla_flags & BRLA_RARP) &&
703 	    ntohs(ea.arp_op) != ARPOP_REVREQUEST &&
704 	    ntohs(ea.arp_op) != ARPOP_REVREPLY)
705 		return (0);
706 	if (n->brl_arpf.brla_op && ntohs(ea.arp_op) != n->brl_arpf.brla_op)
707 		return (0);
708 	if (n->brl_arpf.brla_flags & BRLA_SHA &&
709 	    memcmp(ea.arp_sha, &n->brl_arpf.brla_sha, ETHER_ADDR_LEN))
710 		return (0);
711 	if (n->brl_arpf.brla_flags & BRLA_THA &&
712 	    memcmp(ea.arp_tha, &n->brl_arpf.brla_tha, ETHER_ADDR_LEN))
713 		return (0);
714 	if (n->brl_arpf.brla_flags & BRLA_SPA &&
715 	    memcmp(ea.arp_spa, &n->brl_arpf.brla_spa, sizeof(struct in_addr)))
716 		return (0);
717 	if (n->brl_arpf.brla_flags & BRLA_TPA &&
718 	    memcmp(ea.arp_tpa, &n->brl_arpf.brla_tpa, sizeof(struct in_addr)))
719 		return (0);
720 
721 	return (1);
722 }
723 
724 u_int8_t
725 bridge_filterrule(struct brl_head *h, struct ether_header *eh, struct mbuf *m)
726 {
727 	struct brl_node *n;
728 	u_int8_t action, flags;
729 
730 	if (SIMPLEQ_EMPTY(h))
731 		return (BRL_ACTION_PASS);
732 
733 	KERNEL_LOCK();
734 	SIMPLEQ_FOREACH(n, h, brl_next) {
735 		if (!bridge_arpfilter(n, eh, m))
736 			continue;
737 		flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID);
738 		if (flags == 0)
739 			goto return_action;
740 		if (flags == (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID)) {
741 			if (memcmp(eh->ether_shost, &n->brl_src,
742 			    ETHER_ADDR_LEN))
743 				continue;
744 			if (memcmp(eh->ether_dhost, &n->brl_dst,
745 			    ETHER_ADDR_LEN))
746 				continue;
747 			goto return_action;
748 		}
749 		if (flags == BRL_FLAG_SRCVALID) {
750 			if (memcmp(eh->ether_shost, &n->brl_src,
751 			    ETHER_ADDR_LEN))
752 				continue;
753 			goto return_action;
754 		}
755 		if (flags == BRL_FLAG_DSTVALID) {
756 			if (memcmp(eh->ether_dhost, &n->brl_dst,
757 			    ETHER_ADDR_LEN))
758 				continue;
759 			goto return_action;
760 		}
761 	}
762 	KERNEL_UNLOCK();
763 	return (BRL_ACTION_PASS);
764 
765 return_action:
766 #if NPF > 0
767 	pf_tag_packet(m, n->brl_tag, -1);
768 #endif
769 	action = n->brl_action;
770 	KERNEL_UNLOCK();
771 	return (action);
772 }
773 
774 int
775 bridge_addrule(struct bridge_iflist *bif, struct ifbrlreq *req, int out)
776 {
777 	struct brl_node *n;
778 
779 	n = malloc(sizeof(*n), M_DEVBUF, M_NOWAIT);
780 	if (n == NULL)
781 		return (ENOMEM);
782 	bcopy(&req->ifbr_src, &n->brl_src, sizeof(struct ether_addr));
783 	bcopy(&req->ifbr_dst, &n->brl_dst, sizeof(struct ether_addr));
784 	n->brl_action = req->ifbr_action;
785 	n->brl_flags = req->ifbr_flags;
786 	n->brl_arpf = req->ifbr_arpf;
787 #if NPF > 0
788 	if (req->ifbr_tagname[0])
789 		n->brl_tag = pf_tagname2tag(req->ifbr_tagname, 1);
790 	else
791 		n->brl_tag = 0;
792 #endif
793 
794 	KERNEL_ASSERT_LOCKED();
795 
796 	if (out) {
797 		n->brl_flags &= ~BRL_FLAG_IN;
798 		n->brl_flags |= BRL_FLAG_OUT;
799 		SIMPLEQ_INSERT_TAIL(&bif->bif_brlout, n, brl_next);
800 	} else {
801 		n->brl_flags &= ~BRL_FLAG_OUT;
802 		n->brl_flags |= BRL_FLAG_IN;
803 		SIMPLEQ_INSERT_TAIL(&bif->bif_brlin, n, brl_next);
804 	}
805 	return (0);
806 }
807 
808 void
809 bridge_flushrule(struct bridge_iflist *bif)
810 {
811 	struct brl_node *p;
812 
813 	KERNEL_ASSERT_LOCKED();
814 
815 	while (!SIMPLEQ_EMPTY(&bif->bif_brlin)) {
816 		p = SIMPLEQ_FIRST(&bif->bif_brlin);
817 		SIMPLEQ_REMOVE_HEAD(&bif->bif_brlin, brl_next);
818 #if NPF > 0
819 		pf_tag_unref(p->brl_tag);
820 #endif
821 		free(p, M_DEVBUF, sizeof *p);
822 	}
823 	while (!SIMPLEQ_EMPTY(&bif->bif_brlout)) {
824 		p = SIMPLEQ_FIRST(&bif->bif_brlout);
825 		SIMPLEQ_REMOVE_HEAD(&bif->bif_brlout, brl_next);
826 #if NPF > 0
827 		pf_tag_unref(p->brl_tag);
828 #endif
829 		free(p, M_DEVBUF, sizeof *p);
830 	}
831 }
832 
833 struct bridge_tunneltag *
834 bridge_tunnel(struct mbuf *m)
835 {
836 	struct m_tag    *mtag;
837 
838 	if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) == NULL)
839 		return (NULL);
840 
841 	return ((struct bridge_tunneltag *)(mtag + 1));
842 }
843 
844 struct bridge_tunneltag *
845 bridge_tunneltag(struct mbuf *m)
846 {
847 	struct m_tag	*mtag;
848 
849 	if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) == NULL) {
850 		mtag = m_tag_get(PACKET_TAG_TUNNEL,
851 		    sizeof(struct bridge_tunneltag), M_NOWAIT);
852 		if (mtag == NULL)
853 			return (NULL);
854 		bzero(mtag + 1, sizeof(struct bridge_tunneltag));
855 		m_tag_prepend(m, mtag);
856 	}
857 
858 	return ((struct bridge_tunneltag *)(mtag + 1));
859 }
860 
861 void
862 bridge_tunneluntag(struct mbuf *m)
863 {
864 	struct m_tag    *mtag;
865 	if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) != NULL)
866 		m_tag_delete(m, mtag);
867 }
868 
869 void
870 bridge_copyaddr(struct sockaddr *src, struct sockaddr *dst)
871 {
872 	if (src != NULL && src->sa_family != AF_UNSPEC)
873 		memcpy(dst, src, src->sa_len);
874 	else {
875 		dst->sa_family = AF_UNSPEC;
876 		dst->sa_len = 0;
877 	}
878 }
879 
880 void
881 bridge_copytag(struct bridge_tunneltag *src, struct bridge_tunneltag *dst)
882 {
883 	if (src == NULL) {
884 		memset(dst, 0, sizeof(*dst));
885 	} else {
886 		bridge_copyaddr(&src->brtag_peer.sa, &dst->brtag_peer.sa);
887 		bridge_copyaddr(&src->brtag_local.sa, &dst->brtag_local.sa);
888 		dst->brtag_id = src->brtag_id;
889 	}
890 }
891