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