xref: /openbsd/sys/net/bridgectl.c (revision 9c681c75)
1 /*	$OpenBSD: bridgectl.c,v 1.6 2017/05/04 15:00:24 bluhm 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_softc *, 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 ifbareq *bareq = (struct ifbareq *)data;
68 	struct ifbrparam *bparam = (struct ifbrparam *)data;
69 	struct bridge_iflist *p;
70 	struct ifnet *ifs;
71 	int error = 0;
72 
73 	switch (cmd) {
74 	case SIOCBRDGRTS:
75 		error = bridge_rtfind(sc, (struct ifbaconf *)data);
76 		break;
77 	case SIOCBRDGFLUSH:
78 		bridge_rtflush(sc, req->ifbr_ifsflags);
79 		break;
80 	case SIOCBRDGSADDR:
81 		ifs = ifunit(bareq->ifba_ifsname);
82 		if (ifs == NULL) {			/* no such interface */
83 			error = ENOENT;
84 			break;
85 		}
86 		p = (struct bridge_iflist *)ifs->if_bridgeport;
87 		if (p == NULL || p->bridge_sc != sc) {
88 			error = ESRCH;
89 			break;
90 		}
91 
92 		ifs = bridge_rtupdate(sc, &bareq->ifba_dst, ifs, 1,
93 		    bareq->ifba_flags, NULL);
94 		if (ifs == NULL)
95 			error = ENOMEM;
96 		break;
97 	case SIOCBRDGDADDR:
98 		error = bridge_rtdaddr(sc, &bareq->ifba_dst);
99 		break;
100 	case SIOCBRDGGCACHE:
101 		bparam->ifbrp_csize = sc->sc_brtmax;
102 		break;
103 	case SIOCBRDGSCACHE:
104 		sc->sc_brtmax = bparam->ifbrp_csize;
105 		break;
106 	case SIOCBRDGSTO:
107 		if (bparam->ifbrp_ctime < 0 ||
108 		    bparam->ifbrp_ctime > INT_MAX / hz) {
109 			error = EINVAL;
110 			break;
111 		}
112 		sc->sc_brttimeout = bparam->ifbrp_ctime;
113 		if (bparam->ifbrp_ctime != 0)
114 			timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
115 		else
116 			timeout_del(&sc->sc_brtimeout);
117 		break;
118 	case SIOCBRDGGTO:
119 		bparam->ifbrp_ctime = sc->sc_brttimeout;
120 		break;
121 	case SIOCBRDGARL:
122 		ifs = ifunit(brlreq->ifbr_ifsname);
123 		if (ifs == NULL) {
124 			error = ENOENT;
125 			break;
126 		}
127 		p = (struct bridge_iflist *)ifs->if_bridgeport;
128 		if (p == NULL || p->bridge_sc != sc) {
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 		if (brlreq->ifbr_flags & BRL_FLAG_IN) {
139 			error = bridge_addrule(p, brlreq, 0);
140 			if (error)
141 				break;
142 		}
143 		if (brlreq->ifbr_flags & BRL_FLAG_OUT) {
144 			error = bridge_addrule(p, brlreq, 1);
145 			if (error)
146 				break;
147 		}
148 		break;
149 	case SIOCBRDGFRL:
150 		ifs = ifunit(brlreq->ifbr_ifsname);
151 		if (ifs == NULL) {
152 			error = ENOENT;
153 			break;
154 		}
155 		p = (struct bridge_iflist *)ifs->if_bridgeport;
156 		if (p == NULL || p->bridge_sc != sc) {
157 			error = ESRCH;
158 			break;
159 		}
160 		bridge_flushrule(p);
161 		break;
162 	case SIOCBRDGGRL:
163 		error = bridge_brlconf(sc, (struct ifbrlconf *)data);
164 		break;
165 	default:
166 		break;
167 	}
168 
169 	return (error);
170 }
171 
172 struct ifnet *
173 bridge_rtupdate(struct bridge_softc *sc, struct ether_addr *ea,
174     struct ifnet *ifp, int setflags, u_int8_t flags, struct mbuf *m)
175 {
176 	struct bridge_rtnode *p, *q;
177 	struct bridge_tunneltag	*brtag = NULL;
178 	u_int32_t h;
179 	int dir;
180 
181 	if (m != NULL) {
182 		/* Check if the mbuf was tagged with a tunnel endpoint addr */
183 		brtag = bridge_tunnel(m);
184 	}
185 
186 	h = bridge_hash(sc, ea);
187 	p = LIST_FIRST(&sc->sc_rts[h]);
188 	if (p == NULL) {
189 		if (sc->sc_brtcnt >= sc->sc_brtmax)
190 			goto done;
191 		p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
192 		if (p == NULL)
193 			goto done;
194 
195 		bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
196 		p->brt_if = ifp;
197 		p->brt_age = 1;
198 		bridge_copytag(brtag, &p->brt_tunnel);
199 
200 		if (setflags)
201 			p->brt_flags = flags;
202 		else
203 			p->brt_flags = IFBAF_DYNAMIC;
204 
205 		LIST_INSERT_HEAD(&sc->sc_rts[h], p, brt_next);
206 		sc->sc_brtcnt++;
207 		goto want;
208 	}
209 
210 	do {
211 		q = p;
212 		p = LIST_NEXT(p, brt_next);
213 
214 		dir = memcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
215 		if (dir == 0) {
216 			if (setflags) {
217 				q->brt_if = ifp;
218 				q->brt_flags = flags;
219 			} else if (!(q->brt_flags & IFBAF_STATIC))
220 				q->brt_if = ifp;
221 
222 			if (q->brt_if == ifp)
223 				q->brt_age = 1;
224 			ifp = q->brt_if;
225 			bridge_copytag(brtag, &q->brt_tunnel);
226 
227 			goto want;
228 		}
229 
230 		if (dir > 0) {
231 			if (sc->sc_brtcnt >= sc->sc_brtmax)
232 				goto done;
233 			p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
234 			if (p == NULL)
235 				goto done;
236 
237 			bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
238 			p->brt_if = ifp;
239 			p->brt_age = 1;
240 			bridge_copytag(brtag, &p->brt_tunnel);
241 
242 			if (setflags)
243 				p->brt_flags = flags;
244 			else
245 				p->brt_flags = IFBAF_DYNAMIC;
246 
247 			LIST_INSERT_BEFORE(q, p, brt_next);
248 			sc->sc_brtcnt++;
249 			goto want;
250 		}
251 
252 		if (p == NULL) {
253 			if (sc->sc_brtcnt >= sc->sc_brtmax)
254 				goto done;
255 			p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
256 			if (p == NULL)
257 				goto done;
258 
259 			bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
260 			p->brt_if = ifp;
261 			p->brt_age = 1;
262 			bridge_copytag(brtag, &p->brt_tunnel);
263 
264 			if (setflags)
265 				p->brt_flags = flags;
266 			else
267 				p->brt_flags = IFBAF_DYNAMIC;
268 			LIST_INSERT_AFTER(q, p, brt_next);
269 			sc->sc_brtcnt++;
270 			goto want;
271 		}
272 	} while (p != NULL);
273 
274 done:
275 	ifp = NULL;
276 want:
277 	return (ifp);
278 }
279 
280 struct bridge_rtnode *
281 bridge_rtlookup(struct bridge_softc *sc, struct ether_addr *ea)
282 {
283 	struct bridge_rtnode *p;
284 	u_int32_t h;
285 	int dir;
286 
287 	h = bridge_hash(sc, ea);
288 	LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
289 		dir = memcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
290 		if (dir == 0)
291 			return (p);
292 		if (dir > 0)
293 			goto fail;
294 	}
295 fail:
296 	return (NULL);
297 }
298 
299 u_int32_t
300 bridge_hash(struct bridge_softc *sc, struct ether_addr *addr)
301 {
302 	return SipHash24((SIPHASH_KEY *)sc->sc_hashkey, addr, ETHER_ADDR_LEN) &
303 	    BRIDGE_RTABLE_MASK;
304 }
305 
306 /*
307  * Perform an aging cycle
308  */
309 void
310 bridge_rtage(void *vsc)
311 {
312 	struct bridge_softc *sc = vsc;
313 	struct bridge_rtnode *n, *p;
314 	int i;
315 
316 	KERNEL_ASSERT_LOCKED();
317 
318 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
319 		n = LIST_FIRST(&sc->sc_rts[i]);
320 		while (n != NULL) {
321 			if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) {
322 				n->brt_age = !n->brt_age;
323 				if (n->brt_age)
324 					n->brt_age = 0;
325 				n = LIST_NEXT(n, brt_next);
326 			} else if (n->brt_age) {
327 				n->brt_age = 0;
328 				n = LIST_NEXT(n, brt_next);
329 			} else {
330 				p = LIST_NEXT(n, brt_next);
331 				LIST_REMOVE(n, brt_next);
332 				sc->sc_brtcnt--;
333 				free(n, M_DEVBUF, sizeof *n);
334 				n = p;
335 			}
336 		}
337 	}
338 
339 	if (sc->sc_brttimeout != 0)
340 		timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
341 }
342 
343 void
344 bridge_rtagenode(struct ifnet *ifp, int age)
345 {
346 	struct bridge_softc *sc;
347 	struct bridge_rtnode *n;
348 	int i;
349 
350 	sc = ((struct bridge_iflist *)ifp->if_bridgeport)->bridge_sc;
351 	if (sc == NULL)
352 		return;
353 
354 	/*
355 	 * If the age is zero then flush, otherwise set all the expiry times to
356 	 * age for the interface
357 	 */
358 	if (age == 0)
359 		bridge_rtdelete(sc, ifp, 1);
360 	else {
361 		for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
362 			LIST_FOREACH(n, &sc->sc_rts[i], brt_next) {
363 				/* Cap the expiry time to 'age' */
364 				if (n->brt_if == ifp &&
365 				    n->brt_age > time_uptime + age &&
366 				    (n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
367 					n->brt_age = time_uptime + age;
368 			}
369 		}
370 	}
371 }
372 
373 /*
374  * Remove all dynamic addresses from the cache
375  */
376 void
377 bridge_rtflush(struct bridge_softc *sc, int full)
378 {
379 	int i;
380 	struct bridge_rtnode *p, *n;
381 
382 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
383 		n = LIST_FIRST(&sc->sc_rts[i]);
384 		while (n != NULL) {
385 			if (full ||
386 			    (n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
387 				p = LIST_NEXT(n, brt_next);
388 				LIST_REMOVE(n, brt_next);
389 				sc->sc_brtcnt--;
390 				free(n, M_DEVBUF, sizeof *n);
391 				n = p;
392 			} else
393 				n = LIST_NEXT(n, brt_next);
394 		}
395 	}
396 }
397 
398 /*
399  * Remove an address from the cache
400  */
401 int
402 bridge_rtdaddr(struct bridge_softc *sc, struct ether_addr *ea)
403 {
404 	int h;
405 	struct bridge_rtnode *p;
406 
407 	h = bridge_hash(sc, ea);
408 	LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
409 		if (bcmp(ea, &p->brt_addr, sizeof(p->brt_addr)) == 0) {
410 			LIST_REMOVE(p, brt_next);
411 			sc->sc_brtcnt--;
412 			free(p, M_DEVBUF, sizeof *p);
413 			return (0);
414 		}
415 	}
416 
417 	return (ENOENT);
418 }
419 
420 /*
421  * Delete routes to a specific interface member.
422  */
423 void
424 bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int dynonly)
425 {
426 	int i;
427 	struct bridge_rtnode *n, *p;
428 
429 	/*
430 	 * Loop through all of the hash buckets and traverse each
431 	 * chain looking for routes to this interface.
432 	 */
433 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
434 		n = LIST_FIRST(&sc->sc_rts[i]);
435 		while (n != NULL) {
436 			if (n->brt_if != ifp) {
437 				/* Not ours */
438 				n = LIST_NEXT(n, brt_next);
439 				continue;
440 			}
441 			if (dynonly &&
442 			    (n->brt_flags & IFBAF_TYPEMASK) != IFBAF_DYNAMIC) {
443 				/* only deleting dynamics */
444 				n = LIST_NEXT(n, brt_next);
445 				continue;
446 			}
447 			p = LIST_NEXT(n, brt_next);
448 			LIST_REMOVE(n, brt_next);
449 			sc->sc_brtcnt--;
450 			free(n, M_DEVBUF, sizeof *n);
451 			n = p;
452 		}
453 	}
454 }
455 
456 /*
457  * Gather all of the routes for this interface.
458  */
459 int
460 bridge_rtfind(struct bridge_softc *sc, struct ifbaconf *baconf)
461 {
462 	int i, error = 0, onlycnt = 0;
463 	u_int32_t cnt = 0;
464 	struct bridge_rtnode *n;
465 	struct ifbareq bareq;
466 
467 	if (baconf->ifbac_len == 0)
468 		onlycnt = 1;
469 
470 	for (i = 0, cnt = 0; i < BRIDGE_RTABLE_SIZE; i++) {
471 		LIST_FOREACH(n, &sc->sc_rts[i], brt_next) {
472 			if (!onlycnt) {
473 				if (baconf->ifbac_len < sizeof(struct ifbareq))
474 					goto done;
475 				bcopy(sc->sc_if.if_xname, bareq.ifba_name,
476 				    sizeof(bareq.ifba_name));
477 				bcopy(n->brt_if->if_xname, bareq.ifba_ifsname,
478 				    sizeof(bareq.ifba_ifsname));
479 				bcopy(&n->brt_addr, &bareq.ifba_dst,
480 				    sizeof(bareq.ifba_dst));
481 				bridge_copyaddr(&n->brt_tunnel.brtag_peer.sa,
482 				    sstosa(&bareq.ifba_dstsa));
483 				bareq.ifba_age = n->brt_age;
484 				bareq.ifba_flags = n->brt_flags;
485 				error = copyout((caddr_t)&bareq,
486 				    (caddr_t)(baconf->ifbac_req + cnt), sizeof(bareq));
487 				if (error)
488 					goto done;
489 				baconf->ifbac_len -= sizeof(struct ifbareq);
490 			}
491 			cnt++;
492 		}
493 	}
494 done:
495 	baconf->ifbac_len = cnt * sizeof(struct ifbareq);
496 	return (error);
497 }
498 
499 void
500 bridge_update(struct ifnet *ifp, struct ether_addr *ea, int delete)
501 {
502 	struct bridge_softc *sc;
503 	struct bridge_iflist *bif;
504 	u_int8_t *addr;
505 
506 	addr = (u_int8_t *)ea;
507 
508 	bif = (struct bridge_iflist *)ifp->if_bridgeport;
509 	sc = bif->bridge_sc;
510 
511 	/*
512 	 * Update the bridge interface if it is in
513 	 * the learning state.
514 	 */
515 	if ((bif->bif_flags & IFBIF_LEARNING) &&
516 	    (ETHER_IS_MULTICAST(addr) == 0) &&
517 	    !(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 &&
518 	      addr[3] == 0 && addr[4] == 0 && addr[5] == 0)) {
519 		/* Care must be taken with spanning tree */
520 		if ((bif->bif_flags & IFBIF_STP) &&
521 		    (bif->bif_state == BSTP_IFSTATE_DISCARDING))
522 			return;
523 
524 		/* Delete the address from the bridge */
525 		bridge_rtdaddr(sc, ea);
526 
527 		if (!delete) {
528 			/* Update the bridge table */
529 			bridge_rtupdate(sc, ea, ifp, 0, IFBAF_DYNAMIC, NULL);
530 		}
531 	}
532 }
533 
534 /*
535  * bridge filter/matching rules
536  */
537 int
538 bridge_brlconf(struct bridge_softc *sc, struct ifbrlconf *bc)
539 {
540 	struct ifnet *ifp;
541 	struct bridge_iflist *ifl;
542 	struct brl_node *n;
543 	struct ifbrlreq req;
544 	int error = 0;
545 	u_int32_t i = 0, total = 0;
546 
547 	ifp = ifunit(bc->ifbrl_ifsname);
548 	if (ifp == NULL)
549 		return (ENOENT);
550 	ifl = (struct bridge_iflist *)ifp->if_bridgeport;
551 	if (ifl == NULL || ifl->bridge_sc != sc)
552 		return (ESRCH);
553 
554 	SIMPLEQ_FOREACH(n, &ifl->bif_brlin, brl_next) {
555 		total++;
556 	}
557 	SIMPLEQ_FOREACH(n, &ifl->bif_brlout, brl_next) {
558 		total++;
559 	}
560 
561 	if (bc->ifbrl_len == 0) {
562 		i = total;
563 		goto done;
564 	}
565 
566 	SIMPLEQ_FOREACH(n, &ifl->bif_brlin, brl_next) {
567 		bzero(&req, sizeof req);
568 		if (bc->ifbrl_len < sizeof(req))
569 			goto done;
570 		strlcpy(req.ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
571 		strlcpy(req.ifbr_ifsname, ifl->ifp->if_xname, IFNAMSIZ);
572 		req.ifbr_action = n->brl_action;
573 		req.ifbr_flags = n->brl_flags;
574 		req.ifbr_src = n->brl_src;
575 		req.ifbr_dst = n->brl_dst;
576 #if NPF > 0
577 		req.ifbr_tagname[0] = '\0';
578 		if (n->brl_tag)
579 			pf_tag2tagname(n->brl_tag, req.ifbr_tagname);
580 #endif
581 		error = copyout((caddr_t)&req,
582 		    (caddr_t)(bc->ifbrl_buf + (i * sizeof(req))), sizeof(req));
583 		if (error)
584 			goto done;
585 		i++;
586 		bc->ifbrl_len -= sizeof(req);
587 	}
588 
589 	SIMPLEQ_FOREACH(n, &ifl->bif_brlout, brl_next) {
590 		bzero(&req, sizeof req);
591 		if (bc->ifbrl_len < sizeof(req))
592 			goto done;
593 		strlcpy(req.ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
594 		strlcpy(req.ifbr_ifsname, ifl->ifp->if_xname, IFNAMSIZ);
595 		req.ifbr_action = n->brl_action;
596 		req.ifbr_flags = n->brl_flags;
597 		req.ifbr_src = n->brl_src;
598 		req.ifbr_dst = n->brl_dst;
599 #if NPF > 0
600 		req.ifbr_tagname[0] = '\0';
601 		if (n->brl_tag)
602 			pf_tag2tagname(n->brl_tag, req.ifbr_tagname);
603 #endif
604 		error = copyout((caddr_t)&req,
605 		    (caddr_t)(bc->ifbrl_buf + (i * sizeof(req))), sizeof(req));
606 		if (error)
607 			goto done;
608 		i++;
609 		bc->ifbrl_len -= sizeof(req);
610 	}
611 
612 done:
613 	bc->ifbrl_len = i * sizeof(req);
614 	return (error);
615 }
616 
617 u_int8_t
618 bridge_filterrule(struct brl_head *h, struct ether_header *eh, struct mbuf *m)
619 {
620 	struct brl_node *n;
621 	u_int8_t flags;
622 
623 	SIMPLEQ_FOREACH(n, h, brl_next) {
624 		flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID);
625 		if (flags == 0)
626 			goto return_action;
627 		if (flags == (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID)) {
628 			if (bcmp(eh->ether_shost, &n->brl_src, ETHER_ADDR_LEN))
629 				continue;
630 			if (bcmp(eh->ether_dhost, &n->brl_dst, ETHER_ADDR_LEN))
631 				continue;
632 			goto return_action;
633 		}
634 		if (flags == BRL_FLAG_SRCVALID) {
635 			if (bcmp(eh->ether_shost, &n->brl_src, ETHER_ADDR_LEN))
636 				continue;
637 			goto return_action;
638 		}
639 		if (flags == BRL_FLAG_DSTVALID) {
640 			if (bcmp(eh->ether_dhost, &n->brl_dst, ETHER_ADDR_LEN))
641 				continue;
642 			goto return_action;
643 		}
644 	}
645 	return (BRL_ACTION_PASS);
646 
647 return_action:
648 #if NPF > 0
649 	pf_tag_packet(m, n->brl_tag, -1);
650 #endif
651 	return (n->brl_action);
652 }
653 
654 int
655 bridge_addrule(struct bridge_iflist *bif, struct ifbrlreq *req, int out)
656 {
657 	struct brl_node *n;
658 
659 	n = malloc(sizeof(*n), M_DEVBUF, M_NOWAIT);
660 	if (n == NULL)
661 		return (ENOMEM);
662 	bcopy(&req->ifbr_src, &n->brl_src, sizeof(struct ether_addr));
663 	bcopy(&req->ifbr_dst, &n->brl_dst, sizeof(struct ether_addr));
664 	n->brl_action = req->ifbr_action;
665 	n->brl_flags = req->ifbr_flags;
666 #if NPF > 0
667 	if (req->ifbr_tagname[0])
668 		n->brl_tag = pf_tagname2tag(req->ifbr_tagname, 1);
669 	else
670 		n->brl_tag = 0;
671 #endif
672 	if (out) {
673 		n->brl_flags &= ~BRL_FLAG_IN;
674 		n->brl_flags |= BRL_FLAG_OUT;
675 		SIMPLEQ_INSERT_TAIL(&bif->bif_brlout, n, brl_next);
676 	} else {
677 		n->brl_flags &= ~BRL_FLAG_OUT;
678 		n->brl_flags |= BRL_FLAG_IN;
679 		SIMPLEQ_INSERT_TAIL(&bif->bif_brlin, n, brl_next);
680 	}
681 	return (0);
682 }
683 
684 void
685 bridge_flushrule(struct bridge_iflist *bif)
686 {
687 	struct brl_node *p;
688 
689 	while (!SIMPLEQ_EMPTY(&bif->bif_brlin)) {
690 		p = SIMPLEQ_FIRST(&bif->bif_brlin);
691 		SIMPLEQ_REMOVE_HEAD(&bif->bif_brlin, brl_next);
692 #if NPF > 0
693 		pf_tag_unref(p->brl_tag);
694 #endif
695 		free(p, M_DEVBUF, sizeof *p);
696 	}
697 	while (!SIMPLEQ_EMPTY(&bif->bif_brlout)) {
698 		p = SIMPLEQ_FIRST(&bif->bif_brlout);
699 		SIMPLEQ_REMOVE_HEAD(&bif->bif_brlout, brl_next);
700 #if NPF > 0
701 		pf_tag_unref(p->brl_tag);
702 #endif
703 		free(p, M_DEVBUF, sizeof *p);
704 	}
705 }
706