xref: /openbsd/sys/net/bridgectl.c (revision 33d9f1d5)
1 /*	$OpenBSD: bridgectl.c,v 1.17 2019/03/08 17:48:35 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 		mtx_enter(&sc->sc_mtx);
105 		sc->sc_brtmax = bparam->ifbrp_csize;
106 		mtx_leave(&sc->sc_mtx);
107 		break;
108 	case SIOCBRDGSTO:
109 		if (bparam->ifbrp_ctime < 0 ||
110 		    bparam->ifbrp_ctime > INT_MAX / hz) {
111 			error = EINVAL;
112 			break;
113 		}
114 		sc->sc_brttimeout = bparam->ifbrp_ctime;
115 		if (bparam->ifbrp_ctime != 0)
116 			timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
117 		else
118 			timeout_del(&sc->sc_brtimeout);
119 		break;
120 	case SIOCBRDGGTO:
121 		bparam->ifbrp_ctime = sc->sc_brttimeout;
122 		break;
123 	case SIOCBRDGARL:
124 		ifs = ifunit(brlreq->ifbr_ifsname);
125 		if (ifs == NULL) {
126 			error = ENOENT;
127 			break;
128 		}
129 		bif = (struct bridge_iflist *)ifs->if_bridgeport;
130 		if (bif == NULL || bif->bridge_sc != sc) {
131 			error = ESRCH;
132 			break;
133 		}
134 		if ((brlreq->ifbr_action != BRL_ACTION_BLOCK &&
135 		    brlreq->ifbr_action != BRL_ACTION_PASS) ||
136 		    (brlreq->ifbr_flags & (BRL_FLAG_IN|BRL_FLAG_OUT)) == 0) {
137 			error = EINVAL;
138 			break;
139 		}
140 		if (brlreq->ifbr_flags & BRL_FLAG_IN) {
141 			error = bridge_addrule(bif, brlreq, 0);
142 			if (error)
143 				break;
144 		}
145 		if (brlreq->ifbr_flags & BRL_FLAG_OUT) {
146 			error = bridge_addrule(bif, brlreq, 1);
147 			if (error)
148 				break;
149 		}
150 		break;
151 	case SIOCBRDGFRL:
152 		ifs = ifunit(brlreq->ifbr_ifsname);
153 		if (ifs == NULL) {
154 			error = ENOENT;
155 			break;
156 		}
157 		bif = (struct bridge_iflist *)ifs->if_bridgeport;
158 		if (bif == NULL || bif->bridge_sc != sc) {
159 			error = ESRCH;
160 			break;
161 		}
162 		bridge_flushrule(bif);
163 		break;
164 	case SIOCBRDGGRL:
165 		ifs = ifunit(bc->ifbrl_ifsname);
166 		if (ifs == NULL) {
167 			error = ENOENT;
168 			break;
169 		}
170 		bif = (struct bridge_iflist *)ifs->if_bridgeport;
171 		if (bif == NULL || bif->bridge_sc != sc) {
172 			error = ESRCH;
173 			break;
174 		}
175 		error = bridge_brlconf(bif, bc);
176 		break;
177 	default:
178 		break;
179 	}
180 
181 	return (error);
182 }
183 
184 int
185 bridge_rtupdate(struct bridge_softc *sc, struct ether_addr *ea,
186     struct ifnet *ifp, int setflags, u_int8_t flags, struct mbuf *m)
187 {
188 	struct bridge_rtnode *p, *q;
189 	struct bridge_tunneltag	*brtag = NULL;
190 	u_int32_t h;
191 	int dir, error = 0;
192 
193 	if (m != NULL) {
194 		/* Check if the mbuf was tagged with a tunnel endpoint addr */
195 		brtag = bridge_tunnel(m);
196 	}
197 
198 	h = bridge_hash(sc, ea);
199 	mtx_enter(&sc->sc_mtx);
200 	p = LIST_FIRST(&sc->sc_rts[h]);
201 	if (p == NULL) {
202 		if (sc->sc_brtcnt >= sc->sc_brtmax)
203 			goto done;
204 		p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
205 		if (p == NULL)
206 			goto done;
207 
208 		bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
209 		p->brt_if = ifp;
210 		p->brt_age = 1;
211 		bridge_copytag(brtag, &p->brt_tunnel);
212 
213 		if (setflags)
214 			p->brt_flags = flags;
215 		else
216 			p->brt_flags = IFBAF_DYNAMIC;
217 
218 		LIST_INSERT_HEAD(&sc->sc_rts[h], p, brt_next);
219 		sc->sc_brtcnt++;
220 		goto want;
221 	}
222 
223 	do {
224 		q = p;
225 		p = LIST_NEXT(p, brt_next);
226 
227 		dir = memcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
228 		if (dir == 0) {
229 			if (setflags) {
230 				q->brt_if = ifp;
231 				q->brt_flags = flags;
232 			} else if (!(q->brt_flags & IFBAF_STATIC))
233 				q->brt_if = ifp;
234 
235 			if (q->brt_if == ifp)
236 				q->brt_age = 1;
237 			ifp = q->brt_if;
238 			bridge_copytag(brtag, &q->brt_tunnel);
239 
240 			goto want;
241 		}
242 
243 		if (dir > 0) {
244 			if (sc->sc_brtcnt >= sc->sc_brtmax)
245 				goto done;
246 			p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
247 			if (p == NULL)
248 				goto done;
249 
250 			bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
251 			p->brt_if = ifp;
252 			p->brt_age = 1;
253 			bridge_copytag(brtag, &p->brt_tunnel);
254 
255 			if (setflags)
256 				p->brt_flags = flags;
257 			else
258 				p->brt_flags = IFBAF_DYNAMIC;
259 
260 			LIST_INSERT_BEFORE(q, p, brt_next);
261 			sc->sc_brtcnt++;
262 			goto want;
263 		}
264 
265 		if (p == NULL) {
266 			if (sc->sc_brtcnt >= sc->sc_brtmax)
267 				goto done;
268 			p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
269 			if (p == NULL)
270 				goto done;
271 
272 			bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
273 			p->brt_if = ifp;
274 			p->brt_age = 1;
275 			bridge_copytag(brtag, &p->brt_tunnel);
276 
277 			if (setflags)
278 				p->brt_flags = flags;
279 			else
280 				p->brt_flags = IFBAF_DYNAMIC;
281 			LIST_INSERT_AFTER(q, p, brt_next);
282 			sc->sc_brtcnt++;
283 			goto want;
284 		}
285 	} while (p != NULL);
286 
287 done:
288 	error = 1;
289 want:
290 	mtx_leave(&sc->sc_mtx);
291 	return (error);
292 }
293 
294 struct ifnet *
295 bridge_rtlookup(struct bridge_softc *sc, struct ether_addr *ea, struct mbuf *m)
296 {
297 	struct bridge_rtnode *p = NULL;
298 	struct ifnet *ifp = NULL;
299 	u_int32_t h;
300 	int dir;
301 
302 	h = bridge_hash(sc, ea);
303 	mtx_enter(&sc->sc_mtx);
304 	LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
305 		dir = memcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
306 		if (dir == 0)
307 			break;
308 		if (dir > 0) {
309 			p = NULL;
310 			break;
311 		}
312 	}
313 	if (p != NULL) {
314 		ifp = p->brt_if;
315 
316 		if (p->brt_family != AF_UNSPEC && m != NULL) {
317 			struct bridge_tunneltag *brtag;
318 
319 			brtag = bridge_tunneltag(m);
320 			if (brtag != NULL)
321 				bridge_copytag(&p->brt_tunnel, brtag);
322 		}
323 	}
324 	mtx_leave(&sc->sc_mtx);
325 
326 	return (ifp);
327 }
328 
329 u_int32_t
330 bridge_hash(struct bridge_softc *sc, struct ether_addr *addr)
331 {
332 	return SipHash24((SIPHASH_KEY *)sc->sc_hashkey, addr, ETHER_ADDR_LEN) &
333 	    BRIDGE_RTABLE_MASK;
334 }
335 
336 /*
337  * Perform an aging cycle
338  */
339 void
340 bridge_rtage(void *vsc)
341 {
342 	struct bridge_softc *sc = vsc;
343 	struct ifnet *ifp = &sc->sc_if;
344 	struct bridge_rtnode *n, *p;
345 	int i;
346 
347 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
348 		return;
349 
350 	mtx_enter(&sc->sc_mtx);
351 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
352 		n = LIST_FIRST(&sc->sc_rts[i]);
353 		while (n != NULL) {
354 			if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) {
355 				n->brt_age = !n->brt_age;
356 				if (n->brt_age)
357 					n->brt_age = 0;
358 				n = LIST_NEXT(n, brt_next);
359 			} else if (n->brt_age) {
360 				n->brt_age = 0;
361 				n = LIST_NEXT(n, brt_next);
362 			} else {
363 				p = LIST_NEXT(n, brt_next);
364 				LIST_REMOVE(n, brt_next);
365 				sc->sc_brtcnt--;
366 				free(n, M_DEVBUF, sizeof *n);
367 				n = p;
368 			}
369 		}
370 	}
371 	mtx_leave(&sc->sc_mtx);
372 
373 	if (sc->sc_brttimeout != 0)
374 		timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
375 }
376 
377 void
378 bridge_rtagenode(struct ifnet *ifp, int age)
379 {
380 	struct bridge_softc *sc;
381 	struct bridge_iflist *bif;
382 	struct bridge_rtnode *n;
383 	int i;
384 
385 	bif = (struct bridge_iflist *)ifp->if_bridgeport;
386 	if (bif == NULL)
387 		return;
388 	sc = bif->bridge_sc;
389 	if (sc == NULL)
390 		return;
391 
392 	/*
393 	 * If the age is zero then flush, otherwise set all the expiry times to
394 	 * age for the interface
395 	 */
396 	if (age == 0)
397 		bridge_rtdelete(sc, ifp, 1);
398 	else {
399 		mtx_enter(&sc->sc_mtx);
400 		for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
401 			LIST_FOREACH(n, &sc->sc_rts[i], brt_next) {
402 				/* Cap the expiry time to 'age' */
403 				if (n->brt_if == ifp &&
404 				    n->brt_age > time_uptime + age &&
405 				    (n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
406 					n->brt_age = time_uptime + age;
407 			}
408 		}
409 		mtx_leave(&sc->sc_mtx);
410 	}
411 }
412 
413 /*
414  * Remove all dynamic addresses from the cache
415  */
416 void
417 bridge_rtflush(struct bridge_softc *sc, int full)
418 {
419 	int i;
420 	struct bridge_rtnode *p, *n;
421 
422 	mtx_enter(&sc->sc_mtx);
423 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
424 		n = LIST_FIRST(&sc->sc_rts[i]);
425 		while (n != NULL) {
426 			if (full ||
427 			    (n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
428 				p = LIST_NEXT(n, brt_next);
429 				LIST_REMOVE(n, brt_next);
430 				sc->sc_brtcnt--;
431 				free(n, M_DEVBUF, sizeof *n);
432 				n = p;
433 			} else
434 				n = LIST_NEXT(n, brt_next);
435 		}
436 	}
437 	mtx_leave(&sc->sc_mtx);
438 }
439 
440 /*
441  * Remove an address from the cache
442  */
443 int
444 bridge_rtdaddr(struct bridge_softc *sc, struct ether_addr *ea)
445 {
446 	int h;
447 	struct bridge_rtnode *p;
448 
449 	h = bridge_hash(sc, ea);
450 	mtx_enter(&sc->sc_mtx);
451 	LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
452 		if (memcmp(ea, &p->brt_addr, sizeof(p->brt_addr)) == 0) {
453 			LIST_REMOVE(p, brt_next);
454 			sc->sc_brtcnt--;
455 			mtx_leave(&sc->sc_mtx);
456 			free(p, M_DEVBUF, sizeof *p);
457 			return (0);
458 		}
459 	}
460 	mtx_leave(&sc->sc_mtx);
461 
462 	return (ENOENT);
463 }
464 
465 /*
466  * Delete routes to a specific interface member.
467  */
468 void
469 bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int dynonly)
470 {
471 	int i;
472 	struct bridge_rtnode *n, *p;
473 
474 	/*
475 	 * Loop through all of the hash buckets and traverse each
476 	 * chain looking for routes to this interface.
477 	 */
478 	mtx_enter(&sc->sc_mtx);
479 	for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
480 		n = LIST_FIRST(&sc->sc_rts[i]);
481 		while (n != NULL) {
482 			if (n->brt_if != ifp) {
483 				/* Not ours */
484 				n = LIST_NEXT(n, brt_next);
485 				continue;
486 			}
487 			if (dynonly &&
488 			    (n->brt_flags & IFBAF_TYPEMASK) != IFBAF_DYNAMIC) {
489 				/* only deleting dynamics */
490 				n = LIST_NEXT(n, brt_next);
491 				continue;
492 			}
493 			p = LIST_NEXT(n, brt_next);
494 			LIST_REMOVE(n, brt_next);
495 			sc->sc_brtcnt--;
496 			free(n, M_DEVBUF, sizeof *n);
497 			n = p;
498 		}
499 	}
500 	mtx_leave(&sc->sc_mtx);
501 }
502 
503 /*
504  * Gather all of the routes for this interface.
505  */
506 int
507 bridge_rtfind(struct bridge_softc *sc, struct ifbaconf *baconf)
508 {
509 	struct ifbareq *bareq, *bareqs = NULL;
510 	struct bridge_rtnode *n;
511 	u_int32_t i = 0, total = 0;
512 	int k, error = 0;
513 
514 	mtx_enter(&sc->sc_mtx);
515 	for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
516 		LIST_FOREACH(n, &sc->sc_rts[k], brt_next)
517 			total++;
518 	}
519 	mtx_leave(&sc->sc_mtx);
520 
521 	if (baconf->ifbac_len == 0) {
522 		i = total;
523 		goto done;
524 	}
525 
526 	total = MIN(total, baconf->ifbac_len / sizeof(*bareqs));
527 	bareqs = mallocarray(total, sizeof(*bareqs), M_TEMP, M_NOWAIT|M_ZERO);
528 	if (bareqs == NULL)
529 		goto done;
530 
531 	mtx_enter(&sc->sc_mtx);
532 	for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
533 		LIST_FOREACH(n, &sc->sc_rts[k], brt_next) {
534 			if (i >= total)
535 				goto done;
536 			bareq = &bareqs[i];
537 			bcopy(sc->sc_if.if_xname, bareq->ifba_name,
538 			    sizeof(bareq->ifba_name));
539 			bcopy(n->brt_if->if_xname, bareq->ifba_ifsname,
540 			    sizeof(bareq->ifba_ifsname));
541 			bcopy(&n->brt_addr, &bareq->ifba_dst,
542 			    sizeof(bareq->ifba_dst));
543 			bridge_copyaddr(&n->brt_tunnel.brtag_peer.sa,
544 			    sstosa(&bareq->ifba_dstsa));
545 			bareq->ifba_age = n->brt_age;
546 			bareq->ifba_flags = n->brt_flags;
547 			i++;
548 		}
549 	}
550 	mtx_leave(&sc->sc_mtx);
551 
552 	error = copyout(bareqs, baconf->ifbac_req, i * sizeof(*bareqs));
553 done:
554 	free(bareqs, M_TEMP, total * sizeof(*bareqs));
555 	baconf->ifbac_len = i * sizeof(*bareqs);
556 	return (error);
557 }
558 
559 void
560 bridge_update(struct ifnet *ifp, struct ether_addr *ea, int delete)
561 {
562 	struct bridge_softc *sc;
563 	struct bridge_iflist *bif;
564 	u_int8_t *addr;
565 
566 	addr = (u_int8_t *)ea;
567 
568 	bif = (struct bridge_iflist *)ifp->if_bridgeport;
569 	if (bif == NULL)
570 		return;
571 	sc = bif->bridge_sc;
572 	if (sc == NULL)
573 		return;
574 
575 	/*
576 	 * Update the bridge interface if it is in
577 	 * the learning state.
578 	 */
579 	if ((bif->bif_flags & IFBIF_LEARNING) &&
580 	    (ETHER_IS_MULTICAST(addr) == 0) &&
581 	    !(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 &&
582 	      addr[3] == 0 && addr[4] == 0 && addr[5] == 0)) {
583 		/* Care must be taken with spanning tree */
584 		if ((bif->bif_flags & IFBIF_STP) &&
585 		    (bif->bif_state == BSTP_IFSTATE_DISCARDING))
586 			return;
587 
588 		/* Delete the address from the bridge */
589 		bridge_rtdaddr(sc, ea);
590 
591 		if (!delete) {
592 			/* Update the bridge table */
593 			bridge_rtupdate(sc, ea, ifp, 0, IFBAF_DYNAMIC, NULL);
594 		}
595 	}
596 }
597 
598 /*
599  * bridge filter/matching rules
600  */
601 int
602 bridge_brlconf(struct bridge_iflist *bif, struct ifbrlconf *bc)
603 {
604 	struct bridge_softc *sc = bif->bridge_sc;
605 	struct brl_node *n;
606 	struct ifbrlreq *req, *reqs = NULL;
607 	int error = 0;
608 	u_int32_t i = 0, total = 0;
609 
610 	SIMPLEQ_FOREACH(n, &bif->bif_brlin, brl_next) {
611 		total++;
612 	}
613 	SIMPLEQ_FOREACH(n, &bif->bif_brlout, brl_next) {
614 		total++;
615 	}
616 
617 	if (bc->ifbrl_len == 0) {
618 		i = total;
619 		goto done;
620 	}
621 
622 	reqs = mallocarray(total, sizeof(*reqs), M_TEMP, M_NOWAIT|M_ZERO);
623 	if (reqs == NULL)
624 		goto done;
625 
626 	SIMPLEQ_FOREACH(n, &bif->bif_brlin, brl_next) {
627 		if (bc->ifbrl_len < (i + 1) * sizeof(*reqs))
628 			goto done;
629 		req = &reqs[i];
630 		strlcpy(req->ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
631 		strlcpy(req->ifbr_ifsname, bif->ifp->if_xname, IFNAMSIZ);
632 		req->ifbr_action = n->brl_action;
633 		req->ifbr_flags = n->brl_flags;
634 		req->ifbr_src = n->brl_src;
635 		req->ifbr_dst = n->brl_dst;
636 		req->ifbr_arpf = n->brl_arpf;
637 #if NPF > 0
638 		req->ifbr_tagname[0] = '\0';
639 		if (n->brl_tag)
640 			pf_tag2tagname(n->brl_tag, req->ifbr_tagname);
641 #endif
642 		i++;
643 	}
644 
645 	SIMPLEQ_FOREACH(n, &bif->bif_brlout, brl_next) {
646 		if (bc->ifbrl_len < (i + 1) * sizeof(*reqs))
647 			goto done;
648 		req = &reqs[i];
649 		strlcpy(req->ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
650 		strlcpy(req->ifbr_ifsname, bif->ifp->if_xname, IFNAMSIZ);
651 		req->ifbr_action = n->brl_action;
652 		req->ifbr_flags = n->brl_flags;
653 		req->ifbr_src = n->brl_src;
654 		req->ifbr_dst = n->brl_dst;
655 		req->ifbr_arpf = n->brl_arpf;
656 #if NPF > 0
657 		req->ifbr_tagname[0] = '\0';
658 		if (n->brl_tag)
659 			pf_tag2tagname(n->brl_tag, req->ifbr_tagname);
660 #endif
661 		i++;
662 	}
663 
664 	error = copyout(reqs, bc->ifbrl_buf, i * sizeof(*reqs));
665 done:
666 	free(reqs, M_TEMP, total * sizeof(*reqs));
667 	bc->ifbrl_len = i * sizeof(*reqs);
668 	return (error);
669 }
670 
671 u_int8_t
672 bridge_arpfilter(struct brl_node *n, struct ether_header *eh, struct mbuf *m)
673 {
674 	struct ether_arp	 ea;
675 
676 	if (!(n->brl_arpf.brla_flags & (BRLA_ARP|BRLA_RARP)))
677 		return (1);
678 
679 	if (ntohs(eh->ether_type) != ETHERTYPE_ARP)
680 		return (0);
681 	if (m->m_pkthdr.len <= ETHER_HDR_LEN + sizeof(ea))
682 		return (0);	/* log error? */
683 	m_copydata(m, ETHER_HDR_LEN, sizeof(ea), (caddr_t)&ea);
684 
685 	if (ntohs(ea.arp_hrd) != ARPHRD_ETHER ||
686 	    ntohs(ea.arp_pro) != ETHERTYPE_IP ||
687 	    ea.arp_hln != ETHER_ADDR_LEN ||
688 	    ea.arp_pln != sizeof(struct in_addr))
689 		return (0);
690 	if ((n->brl_arpf.brla_flags & BRLA_ARP) &&
691 	    ntohs(ea.arp_op) != ARPOP_REQUEST &&
692 	    ntohs(ea.arp_op) != ARPOP_REPLY)
693 		return (0);
694 	if ((n->brl_arpf.brla_flags & BRLA_RARP) &&
695 	    ntohs(ea.arp_op) != ARPOP_REVREQUEST &&
696 	    ntohs(ea.arp_op) != ARPOP_REVREPLY)
697 		return (0);
698 	if (n->brl_arpf.brla_op && ntohs(ea.arp_op) != n->brl_arpf.brla_op)
699 		return (0);
700 	if (n->brl_arpf.brla_flags & BRLA_SHA &&
701 	    memcmp(ea.arp_sha, &n->brl_arpf.brla_sha, ETHER_ADDR_LEN))
702 		return (0);
703 	if (n->brl_arpf.brla_flags & BRLA_THA &&
704 	    memcmp(ea.arp_tha, &n->brl_arpf.brla_tha, ETHER_ADDR_LEN))
705 		return (0);
706 	if (n->brl_arpf.brla_flags & BRLA_SPA &&
707 	    memcmp(ea.arp_spa, &n->brl_arpf.brla_spa, sizeof(struct in_addr)))
708 		return (0);
709 	if (n->brl_arpf.brla_flags & BRLA_TPA &&
710 	    memcmp(ea.arp_tpa, &n->brl_arpf.brla_tpa, sizeof(struct in_addr)))
711 		return (0);
712 
713 	return (1);
714 }
715 
716 u_int8_t
717 bridge_filterrule(struct brl_head *h, struct ether_header *eh, struct mbuf *m)
718 {
719 	struct brl_node *n;
720 	u_int8_t flags;
721 
722 	SIMPLEQ_FOREACH(n, h, brl_next) {
723 		if (!bridge_arpfilter(n, eh, m))
724 			continue;
725 		flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID);
726 		if (flags == 0)
727 			goto return_action;
728 		if (flags == (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID)) {
729 			if (memcmp(eh->ether_shost, &n->brl_src,
730 			    ETHER_ADDR_LEN))
731 				continue;
732 			if (memcmp(eh->ether_dhost, &n->brl_dst,
733 			    ETHER_ADDR_LEN))
734 				continue;
735 			goto return_action;
736 		}
737 		if (flags == BRL_FLAG_SRCVALID) {
738 			if (memcmp(eh->ether_shost, &n->brl_src,
739 			    ETHER_ADDR_LEN))
740 				continue;
741 			goto return_action;
742 		}
743 		if (flags == BRL_FLAG_DSTVALID) {
744 			if (memcmp(eh->ether_dhost, &n->brl_dst,
745 			    ETHER_ADDR_LEN))
746 				continue;
747 			goto return_action;
748 		}
749 	}
750 	return (BRL_ACTION_PASS);
751 
752 return_action:
753 #if NPF > 0
754 	pf_tag_packet(m, n->brl_tag, -1);
755 #endif
756 	return (n->brl_action);
757 }
758 
759 int
760 bridge_addrule(struct bridge_iflist *bif, struct ifbrlreq *req, int out)
761 {
762 	struct brl_node *n;
763 
764 	n = malloc(sizeof(*n), M_DEVBUF, M_NOWAIT);
765 	if (n == NULL)
766 		return (ENOMEM);
767 	bcopy(&req->ifbr_src, &n->brl_src, sizeof(struct ether_addr));
768 	bcopy(&req->ifbr_dst, &n->brl_dst, sizeof(struct ether_addr));
769 	n->brl_action = req->ifbr_action;
770 	n->brl_flags = req->ifbr_flags;
771 	n->brl_arpf = req->ifbr_arpf;
772 #if NPF > 0
773 	if (req->ifbr_tagname[0])
774 		n->brl_tag = pf_tagname2tag(req->ifbr_tagname, 1);
775 	else
776 		n->brl_tag = 0;
777 #endif
778 	if (out) {
779 		n->brl_flags &= ~BRL_FLAG_IN;
780 		n->brl_flags |= BRL_FLAG_OUT;
781 		SIMPLEQ_INSERT_TAIL(&bif->bif_brlout, n, brl_next);
782 	} else {
783 		n->brl_flags &= ~BRL_FLAG_OUT;
784 		n->brl_flags |= BRL_FLAG_IN;
785 		SIMPLEQ_INSERT_TAIL(&bif->bif_brlin, n, brl_next);
786 	}
787 	return (0);
788 }
789 
790 void
791 bridge_flushrule(struct bridge_iflist *bif)
792 {
793 	struct brl_node *p;
794 
795 	while (!SIMPLEQ_EMPTY(&bif->bif_brlin)) {
796 		p = SIMPLEQ_FIRST(&bif->bif_brlin);
797 		SIMPLEQ_REMOVE_HEAD(&bif->bif_brlin, brl_next);
798 #if NPF > 0
799 		pf_tag_unref(p->brl_tag);
800 #endif
801 		free(p, M_DEVBUF, sizeof *p);
802 	}
803 	while (!SIMPLEQ_EMPTY(&bif->bif_brlout)) {
804 		p = SIMPLEQ_FIRST(&bif->bif_brlout);
805 		SIMPLEQ_REMOVE_HEAD(&bif->bif_brlout, brl_next);
806 #if NPF > 0
807 		pf_tag_unref(p->brl_tag);
808 #endif
809 		free(p, M_DEVBUF, sizeof *p);
810 	}
811 }
812 
813 struct bridge_tunneltag *
814 bridge_tunnel(struct mbuf *m)
815 {
816 	struct m_tag    *mtag;
817 
818 	if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) == NULL)
819 		return (NULL);
820 
821 	return ((struct bridge_tunneltag *)(mtag + 1));
822 }
823 
824 struct bridge_tunneltag *
825 bridge_tunneltag(struct mbuf *m)
826 {
827 	struct m_tag	*mtag;
828 
829 	if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) == NULL) {
830 		mtag = m_tag_get(PACKET_TAG_TUNNEL,
831 		    sizeof(struct bridge_tunneltag), M_NOWAIT);
832 		if (mtag == NULL)
833 			return (NULL);
834 		bzero(mtag + 1, sizeof(struct bridge_tunneltag));
835 		m_tag_prepend(m, mtag);
836 	}
837 
838 	return ((struct bridge_tunneltag *)(mtag + 1));
839 }
840 
841 void
842 bridge_tunneluntag(struct mbuf *m)
843 {
844 	struct m_tag    *mtag;
845 	if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) != NULL)
846 		m_tag_delete(m, mtag);
847 }
848 
849 void
850 bridge_copyaddr(struct sockaddr *src, struct sockaddr *dst)
851 {
852 	if (src != NULL && src->sa_family != AF_UNSPEC)
853 		memcpy(dst, src, src->sa_len);
854 	else {
855 		dst->sa_family = AF_UNSPEC;
856 		dst->sa_len = 0;
857 	}
858 }
859 
860 void
861 bridge_copytag(struct bridge_tunneltag *src, struct bridge_tunneltag *dst)
862 {
863 	if (src == NULL) {
864 		memset(dst, 0, sizeof(*dst));
865 	} else {
866 		bridge_copyaddr(&src->brtag_peer.sa, &dst->brtag_peer.sa);
867 		bridge_copyaddr(&src->brtag_local.sa, &dst->brtag_local.sa);
868 		dst->brtag_id = src->brtag_id;
869 	}
870 }
871