xref: /openbsd/sys/net/bridgectl.c (revision 37ceae02)
1 /*	$OpenBSD: bridgectl.c,v 1.13 2018/12/12 14:19:15 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 		bif = (struct bridge_iflist *)ifs->if_bridgeport;
88 		if (bif == NULL || bif->bridge_sc != sc) {
89 			error = ESRCH;
90 			break;
91 		}
92 
93 		ifs = bridge_rtupdate(sc, &bareq->ifba_dst, ifs, 1,
94 		    bareq->ifba_flags, NULL);
95 		if (ifs == NULL)
96 			error = ENOMEM;
97 		break;
98 	case SIOCBRDGDADDR:
99 		error = bridge_rtdaddr(sc, &bareq->ifba_dst);
100 		break;
101 	case SIOCBRDGGCACHE:
102 		bparam->ifbrp_csize = sc->sc_brtmax;
103 		break;
104 	case SIOCBRDGSCACHE:
105 		sc->sc_brtmax = bparam->ifbrp_csize;
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 		bif = (struct bridge_iflist *)ifs->if_bridgeport;
129 		if (bif == NULL || bif->bridge_sc != sc) {
130 			error = ESRCH;
131 			break;
132 		}
133 		if ((brlreq->ifbr_action != BRL_ACTION_BLOCK &&
134 		    brlreq->ifbr_action != BRL_ACTION_PASS) ||
135 		    (brlreq->ifbr_flags & (BRL_FLAG_IN|BRL_FLAG_OUT)) == 0) {
136 			error = EINVAL;
137 			break;
138 		}
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 		bif = (struct bridge_iflist *)ifs->if_bridgeport;
157 		if (bif == NULL || bif->bridge_sc != sc) {
158 			error = ESRCH;
159 			break;
160 		}
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 		bif = (struct bridge_iflist *)ifs->if_bridgeport;
170 		if (bif == NULL || bif->bridge_sc != sc) {
171 			error = ESRCH;
172 			break;
173 		}
174 		error = bridge_brlconf(bif, bc);
175 		break;
176 	default:
177 		break;
178 	}
179 
180 	return (error);
181 }
182 
183 struct ifnet *
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;
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 	p = LIST_FIRST(&sc->sc_rts[h]);
199 	if (p == NULL) {
200 		if (sc->sc_brtcnt >= sc->sc_brtmax)
201 			goto done;
202 		p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
203 		if (p == NULL)
204 			goto done;
205 
206 		bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
207 		p->brt_if = ifp;
208 		p->brt_age = 1;
209 		bridge_copytag(brtag, &p->brt_tunnel);
210 
211 		if (setflags)
212 			p->brt_flags = flags;
213 		else
214 			p->brt_flags = IFBAF_DYNAMIC;
215 
216 		LIST_INSERT_HEAD(&sc->sc_rts[h], p, brt_next);
217 		sc->sc_brtcnt++;
218 		goto want;
219 	}
220 
221 	do {
222 		q = p;
223 		p = LIST_NEXT(p, brt_next);
224 
225 		dir = memcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
226 		if (dir == 0) {
227 			if (setflags) {
228 				q->brt_if = ifp;
229 				q->brt_flags = flags;
230 			} else if (!(q->brt_flags & IFBAF_STATIC))
231 				q->brt_if = ifp;
232 
233 			if (q->brt_if == ifp)
234 				q->brt_age = 1;
235 			ifp = q->brt_if;
236 			bridge_copytag(brtag, &q->brt_tunnel);
237 
238 			goto want;
239 		}
240 
241 		if (dir > 0) {
242 			if (sc->sc_brtcnt >= sc->sc_brtmax)
243 				goto done;
244 			p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
245 			if (p == NULL)
246 				goto done;
247 
248 			bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
249 			p->brt_if = ifp;
250 			p->brt_age = 1;
251 			bridge_copytag(brtag, &p->brt_tunnel);
252 
253 			if (setflags)
254 				p->brt_flags = flags;
255 			else
256 				p->brt_flags = IFBAF_DYNAMIC;
257 
258 			LIST_INSERT_BEFORE(q, p, brt_next);
259 			sc->sc_brtcnt++;
260 			goto want;
261 		}
262 
263 		if (p == NULL) {
264 			if (sc->sc_brtcnt >= sc->sc_brtmax)
265 				goto done;
266 			p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
267 			if (p == NULL)
268 				goto done;
269 
270 			bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
271 			p->brt_if = ifp;
272 			p->brt_age = 1;
273 			bridge_copytag(brtag, &p->brt_tunnel);
274 
275 			if (setflags)
276 				p->brt_flags = flags;
277 			else
278 				p->brt_flags = IFBAF_DYNAMIC;
279 			LIST_INSERT_AFTER(q, p, brt_next);
280 			sc->sc_brtcnt++;
281 			goto want;
282 		}
283 	} while (p != NULL);
284 
285 done:
286 	ifp = NULL;
287 want:
288 	return (ifp);
289 }
290 
291 struct bridge_rtnode *
292 bridge_rtlookup(struct bridge_softc *sc, struct ether_addr *ea)
293 {
294 	struct bridge_rtnode *p;
295 	u_int32_t h;
296 	int dir;
297 
298 	h = bridge_hash(sc, ea);
299 	LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
300 		dir = memcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
301 		if (dir == 0)
302 			return (p);
303 		if (dir > 0)
304 			goto fail;
305 	}
306 fail:
307 	return (NULL);
308 }
309 
310 u_int32_t
311 bridge_hash(struct bridge_softc *sc, struct ether_addr *addr)
312 {
313 	return SipHash24((SIPHASH_KEY *)sc->sc_hashkey, addr, ETHER_ADDR_LEN) &
314 	    BRIDGE_RTABLE_MASK;
315 }
316 
317 /*
318  * Perform an aging cycle
319  */
320 void
321 bridge_rtage(void *vsc)
322 {
323 	struct bridge_softc *sc = vsc;
324 	struct bridge_rtnode *n, *p;
325 	int i;
326 
327 	KERNEL_ASSERT_LOCKED();
328 
329 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
330 		n = LIST_FIRST(&sc->sc_rts[i]);
331 		while (n != NULL) {
332 			if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) {
333 				n->brt_age = !n->brt_age;
334 				if (n->brt_age)
335 					n->brt_age = 0;
336 				n = LIST_NEXT(n, brt_next);
337 			} else if (n->brt_age) {
338 				n->brt_age = 0;
339 				n = LIST_NEXT(n, brt_next);
340 			} else {
341 				p = LIST_NEXT(n, brt_next);
342 				LIST_REMOVE(n, brt_next);
343 				sc->sc_brtcnt--;
344 				free(n, M_DEVBUF, sizeof *n);
345 				n = p;
346 			}
347 		}
348 	}
349 
350 	if (sc->sc_brttimeout != 0)
351 		timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
352 }
353 
354 void
355 bridge_rtagenode(struct ifnet *ifp, int age)
356 {
357 	struct bridge_softc *sc;
358 	struct bridge_iflist *bif;
359 	struct bridge_rtnode *n;
360 	int i;
361 
362 	bif = (struct bridge_iflist *)ifp->if_bridgeport;
363 	if (bif == NULL)
364 		return;
365 	sc = bif->bridge_sc;
366 	if (sc == NULL)
367 		return;
368 
369 	/*
370 	 * If the age is zero then flush, otherwise set all the expiry times to
371 	 * age for the interface
372 	 */
373 	if (age == 0)
374 		bridge_rtdelete(sc, ifp, 1);
375 	else {
376 		for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
377 			LIST_FOREACH(n, &sc->sc_rts[i], brt_next) {
378 				/* Cap the expiry time to 'age' */
379 				if (n->brt_if == ifp &&
380 				    n->brt_age > time_uptime + age &&
381 				    (n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
382 					n->brt_age = time_uptime + age;
383 			}
384 		}
385 	}
386 }
387 
388 /*
389  * Remove all dynamic addresses from the cache
390  */
391 void
392 bridge_rtflush(struct bridge_softc *sc, int full)
393 {
394 	int i;
395 	struct bridge_rtnode *p, *n;
396 
397 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
398 		n = LIST_FIRST(&sc->sc_rts[i]);
399 		while (n != NULL) {
400 			if (full ||
401 			    (n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
402 				p = LIST_NEXT(n, brt_next);
403 				LIST_REMOVE(n, brt_next);
404 				sc->sc_brtcnt--;
405 				free(n, M_DEVBUF, sizeof *n);
406 				n = p;
407 			} else
408 				n = LIST_NEXT(n, brt_next);
409 		}
410 	}
411 }
412 
413 /*
414  * Remove an address from the cache
415  */
416 int
417 bridge_rtdaddr(struct bridge_softc *sc, struct ether_addr *ea)
418 {
419 	int h;
420 	struct bridge_rtnode *p;
421 
422 	h = bridge_hash(sc, ea);
423 	LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
424 		if (memcmp(ea, &p->brt_addr, sizeof(p->brt_addr)) == 0) {
425 			LIST_REMOVE(p, brt_next);
426 			sc->sc_brtcnt--;
427 			free(p, M_DEVBUF, sizeof *p);
428 			return (0);
429 		}
430 	}
431 
432 	return (ENOENT);
433 }
434 
435 /*
436  * Delete routes to a specific interface member.
437  */
438 void
439 bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int dynonly)
440 {
441 	int i;
442 	struct bridge_rtnode *n, *p;
443 
444 	/*
445 	 * Loop through all of the hash buckets and traverse each
446 	 * chain looking for routes to this interface.
447 	 */
448 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
449 		n = LIST_FIRST(&sc->sc_rts[i]);
450 		while (n != NULL) {
451 			if (n->brt_if != ifp) {
452 				/* Not ours */
453 				n = LIST_NEXT(n, brt_next);
454 				continue;
455 			}
456 			if (dynonly &&
457 			    (n->brt_flags & IFBAF_TYPEMASK) != IFBAF_DYNAMIC) {
458 				/* only deleting dynamics */
459 				n = LIST_NEXT(n, brt_next);
460 				continue;
461 			}
462 			p = LIST_NEXT(n, brt_next);
463 			LIST_REMOVE(n, brt_next);
464 			sc->sc_brtcnt--;
465 			free(n, M_DEVBUF, sizeof *n);
466 			n = p;
467 		}
468 	}
469 }
470 
471 /*
472  * Gather all of the routes for this interface.
473  */
474 int
475 bridge_rtfind(struct bridge_softc *sc, struct ifbaconf *baconf)
476 {
477 	struct ifbareq *bareq, *bareqs = NULL;
478 	struct bridge_rtnode *n;
479 	u_int32_t i = 0, total = 0;
480 	int k, error = 0;
481 
482 	for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
483 		LIST_FOREACH(n, &sc->sc_rts[k], brt_next)
484 			total++;
485 	}
486 
487 	if (baconf->ifbac_len == 0) {
488 		i = total;
489 		goto done;
490 	}
491 
492 	bareqs = mallocarray(total, sizeof(*bareqs), M_TEMP, M_NOWAIT|M_ZERO);
493 	if (bareqs == NULL)
494 		goto done;
495 
496 	for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
497 		LIST_FOREACH(n, &sc->sc_rts[k], brt_next) {
498 			if (baconf->ifbac_len < (i + 1) * sizeof(*bareqs))
499 				goto done;
500 			bareq = &bareqs[i];
501 			bcopy(sc->sc_if.if_xname, bareq->ifba_name,
502 			    sizeof(bareq->ifba_name));
503 			bcopy(n->brt_if->if_xname, bareq->ifba_ifsname,
504 			    sizeof(bareq->ifba_ifsname));
505 			bcopy(&n->brt_addr, &bareq->ifba_dst,
506 			    sizeof(bareq->ifba_dst));
507 			bridge_copyaddr(&n->brt_tunnel.brtag_peer.sa,
508 			    sstosa(&bareq->ifba_dstsa));
509 			bareq->ifba_age = n->brt_age;
510 			bareq->ifba_flags = n->brt_flags;
511 			i++;
512 		}
513 	}
514 
515 	error = copyout(bareqs, baconf->ifbac_req, i * sizeof(*bareqs));
516 done:
517 	free(bareqs, M_TEMP, total * sizeof(*bareqs));
518 	baconf->ifbac_len = i * sizeof(*bareqs);
519 	return (error);
520 }
521 
522 void
523 bridge_update(struct ifnet *ifp, struct ether_addr *ea, int delete)
524 {
525 	struct bridge_softc *sc;
526 	struct bridge_iflist *bif;
527 	u_int8_t *addr;
528 
529 	addr = (u_int8_t *)ea;
530 
531 	bif = (struct bridge_iflist *)ifp->if_bridgeport;
532 	if (bif == NULL)
533 		return;
534 	sc = bif->bridge_sc;
535 	if (sc == NULL)
536 		return;
537 
538 	/*
539 	 * Update the bridge interface if it is in
540 	 * the learning state.
541 	 */
542 	if ((bif->bif_flags & IFBIF_LEARNING) &&
543 	    (ETHER_IS_MULTICAST(addr) == 0) &&
544 	    !(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 &&
545 	      addr[3] == 0 && addr[4] == 0 && addr[5] == 0)) {
546 		/* Care must be taken with spanning tree */
547 		if ((bif->bif_flags & IFBIF_STP) &&
548 		    (bif->bif_state == BSTP_IFSTATE_DISCARDING))
549 			return;
550 
551 		/* Delete the address from the bridge */
552 		bridge_rtdaddr(sc, ea);
553 
554 		if (!delete) {
555 			/* Update the bridge table */
556 			bridge_rtupdate(sc, ea, ifp, 0, IFBAF_DYNAMIC, NULL);
557 		}
558 	}
559 }
560 
561 /*
562  * bridge filter/matching rules
563  */
564 int
565 bridge_brlconf(struct bridge_iflist *bif, struct ifbrlconf *bc)
566 {
567 	struct bridge_softc *sc = bif->bridge_sc;
568 	struct brl_node *n;
569 	struct ifbrlreq *req, *reqs = NULL;
570 	int error = 0;
571 	u_int32_t i = 0, total = 0;
572 
573 	SIMPLEQ_FOREACH(n, &bif->bif_brlin, brl_next) {
574 		total++;
575 	}
576 	SIMPLEQ_FOREACH(n, &bif->bif_brlout, brl_next) {
577 		total++;
578 	}
579 
580 	if (bc->ifbrl_len == 0) {
581 		i = total;
582 		goto done;
583 	}
584 
585 	reqs = mallocarray(total, sizeof(*reqs), M_TEMP, M_NOWAIT|M_ZERO);
586 	if (reqs == NULL)
587 		goto done;
588 
589 	SIMPLEQ_FOREACH(n, &bif->bif_brlin, brl_next) {
590 		if (bc->ifbrl_len < (i + 1) * sizeof(*reqs))
591 			goto done;
592 		req = &reqs[i];
593 		strlcpy(req->ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
594 		strlcpy(req->ifbr_ifsname, bif->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 		req->ifbr_arpf = n->brl_arpf;
600 #if NPF > 0
601 		req->ifbr_tagname[0] = '\0';
602 		if (n->brl_tag)
603 			pf_tag2tagname(n->brl_tag, req->ifbr_tagname);
604 #endif
605 		i++;
606 	}
607 
608 	SIMPLEQ_FOREACH(n, &bif->bif_brlout, brl_next) {
609 		if (bc->ifbrl_len < (i + 1) * sizeof(*reqs))
610 			goto done;
611 		req = &reqs[i];
612 		strlcpy(req->ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
613 		strlcpy(req->ifbr_ifsname, bif->ifp->if_xname, IFNAMSIZ);
614 		req->ifbr_action = n->brl_action;
615 		req->ifbr_flags = n->brl_flags;
616 		req->ifbr_src = n->brl_src;
617 		req->ifbr_dst = n->brl_dst;
618 		req->ifbr_arpf = n->brl_arpf;
619 #if NPF > 0
620 		req->ifbr_tagname[0] = '\0';
621 		if (n->brl_tag)
622 			pf_tag2tagname(n->brl_tag, req->ifbr_tagname);
623 #endif
624 		i++;
625 	}
626 
627 	error = copyout(reqs, bc->ifbrl_buf, i * sizeof(*reqs));
628 done:
629 	free(reqs, M_TEMP, total * sizeof(*reqs));
630 	bc->ifbrl_len = i * sizeof(*reqs);
631 	return (error);
632 }
633 
634 u_int8_t
635 bridge_arpfilter(struct brl_node *n, struct ether_header *eh, struct mbuf *m)
636 {
637 	struct ether_arp	 ea;
638 
639 	if (!(n->brl_arpf.brla_flags & (BRLA_ARP|BRLA_RARP)))
640 		return (1);
641 
642 	if (ntohs(eh->ether_type) != ETHERTYPE_ARP)
643 		return (0);
644 	if (m->m_pkthdr.len <= ETHER_HDR_LEN + sizeof(ea))
645 		return (0);	/* log error? */
646 	m_copydata(m, ETHER_HDR_LEN, sizeof(ea), (caddr_t)&ea);
647 
648 	if (ntohs(ea.arp_hrd) != ARPHRD_ETHER ||
649 	    ntohs(ea.arp_pro) != ETHERTYPE_IP ||
650 	    ea.arp_hln != ETHER_ADDR_LEN ||
651 	    ea.arp_pln != sizeof(struct in_addr))
652 		return (0);
653 	if ((n->brl_arpf.brla_flags & BRLA_ARP) &&
654 	    ntohs(ea.arp_op) != ARPOP_REQUEST &&
655 	    ntohs(ea.arp_op) != ARPOP_REPLY)
656 		return (0);
657 	if ((n->brl_arpf.brla_flags & BRLA_RARP) &&
658 	    ntohs(ea.arp_op) != ARPOP_REVREQUEST &&
659 	    ntohs(ea.arp_op) != ARPOP_REVREPLY)
660 		return (0);
661 	if (n->brl_arpf.brla_op && ntohs(ea.arp_op) != n->brl_arpf.brla_op)
662 		return (0);
663 	if (n->brl_arpf.brla_flags & BRLA_SHA &&
664 	    memcmp(ea.arp_sha, &n->brl_arpf.brla_sha, ETHER_ADDR_LEN))
665 		return (0);
666 	if (n->brl_arpf.brla_flags & BRLA_THA &&
667 	    memcmp(ea.arp_tha, &n->brl_arpf.brla_tha, ETHER_ADDR_LEN))
668 		return (0);
669 	if (n->brl_arpf.brla_flags & BRLA_SPA &&
670 	    memcmp(ea.arp_spa, &n->brl_arpf.brla_spa, sizeof(struct in_addr)))
671 		return (0);
672 	if (n->brl_arpf.brla_flags & BRLA_TPA &&
673 	    memcmp(ea.arp_tpa, &n->brl_arpf.brla_tpa, sizeof(struct in_addr)))
674 		return (0);
675 
676 	return (1);
677 }
678 
679 u_int8_t
680 bridge_filterrule(struct brl_head *h, struct ether_header *eh, struct mbuf *m)
681 {
682 	struct brl_node *n;
683 	u_int8_t flags;
684 
685 	SIMPLEQ_FOREACH(n, h, brl_next) {
686 		if (!bridge_arpfilter(n, eh, m))
687 			continue;
688 		flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID);
689 		if (flags == 0)
690 			goto return_action;
691 		if (flags == (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID)) {
692 			if (memcmp(eh->ether_shost, &n->brl_src,
693 			    ETHER_ADDR_LEN))
694 				continue;
695 			if (memcmp(eh->ether_dhost, &n->brl_dst,
696 			    ETHER_ADDR_LEN))
697 				continue;
698 			goto return_action;
699 		}
700 		if (flags == BRL_FLAG_SRCVALID) {
701 			if (memcmp(eh->ether_shost, &n->brl_src,
702 			    ETHER_ADDR_LEN))
703 				continue;
704 			goto return_action;
705 		}
706 		if (flags == BRL_FLAG_DSTVALID) {
707 			if (memcmp(eh->ether_dhost, &n->brl_dst,
708 			    ETHER_ADDR_LEN))
709 				continue;
710 			goto return_action;
711 		}
712 	}
713 	return (BRL_ACTION_PASS);
714 
715 return_action:
716 #if NPF > 0
717 	pf_tag_packet(m, n->brl_tag, -1);
718 #endif
719 	return (n->brl_action);
720 }
721 
722 int
723 bridge_addrule(struct bridge_iflist *bif, struct ifbrlreq *req, int out)
724 {
725 	struct brl_node *n;
726 
727 	n = malloc(sizeof(*n), M_DEVBUF, M_NOWAIT);
728 	if (n == NULL)
729 		return (ENOMEM);
730 	bcopy(&req->ifbr_src, &n->brl_src, sizeof(struct ether_addr));
731 	bcopy(&req->ifbr_dst, &n->brl_dst, sizeof(struct ether_addr));
732 	n->brl_action = req->ifbr_action;
733 	n->brl_flags = req->ifbr_flags;
734 	n->brl_arpf = req->ifbr_arpf;
735 #if NPF > 0
736 	if (req->ifbr_tagname[0])
737 		n->brl_tag = pf_tagname2tag(req->ifbr_tagname, 1);
738 	else
739 		n->brl_tag = 0;
740 #endif
741 	if (out) {
742 		n->brl_flags &= ~BRL_FLAG_IN;
743 		n->brl_flags |= BRL_FLAG_OUT;
744 		SIMPLEQ_INSERT_TAIL(&bif->bif_brlout, n, brl_next);
745 	} else {
746 		n->brl_flags &= ~BRL_FLAG_OUT;
747 		n->brl_flags |= BRL_FLAG_IN;
748 		SIMPLEQ_INSERT_TAIL(&bif->bif_brlin, n, brl_next);
749 	}
750 	return (0);
751 }
752 
753 void
754 bridge_flushrule(struct bridge_iflist *bif)
755 {
756 	struct brl_node *p;
757 
758 	while (!SIMPLEQ_EMPTY(&bif->bif_brlin)) {
759 		p = SIMPLEQ_FIRST(&bif->bif_brlin);
760 		SIMPLEQ_REMOVE_HEAD(&bif->bif_brlin, brl_next);
761 #if NPF > 0
762 		pf_tag_unref(p->brl_tag);
763 #endif
764 		free(p, M_DEVBUF, sizeof *p);
765 	}
766 	while (!SIMPLEQ_EMPTY(&bif->bif_brlout)) {
767 		p = SIMPLEQ_FIRST(&bif->bif_brlout);
768 		SIMPLEQ_REMOVE_HEAD(&bif->bif_brlout, brl_next);
769 #if NPF > 0
770 		pf_tag_unref(p->brl_tag);
771 #endif
772 		free(p, M_DEVBUF, sizeof *p);
773 	}
774 }
775