src/sys/net/bridgectl.c

872 lines
20 KiB
C

/* $OpenBSD: bridgectl.c,v 1.25 2021/02/25 02:48:21 dlg Exp $ */
/*
* Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Effort sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F30602-01-2-0537.
*
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/timeout.h>
#include <sys/kernel.h>
#include <crypto/siphash.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/if_bridge.h>
int bridge_rtfind(struct bridge_softc *, struct ifbaconf *);
int bridge_rtdaddr(struct bridge_softc *, struct ether_addr *);
u_int32_t bridge_hash(struct bridge_softc *, struct ether_addr *);
int bridge_brlconf(struct bridge_iflist *, struct ifbrlconf *);
int bridge_addrule(struct bridge_iflist *, struct ifbrlreq *, int out);
int
bridgectl_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct bridge_softc *sc = (struct bridge_softc *)ifp->if_softc;
struct ifbreq *req = (struct ifbreq *)data;
struct ifbrlreq *brlreq = (struct ifbrlreq *)data;
struct ifbrlconf *bc = (struct ifbrlconf *)data;
struct ifbareq *bareq = (struct ifbareq *)data;
struct ifbrparam *bparam = (struct ifbrparam *)data;
struct bridge_iflist *bif;
struct ifnet *ifs;
int error = 0;
switch (cmd) {
case SIOCBRDGRTS:
error = bridge_rtfind(sc, (struct ifbaconf *)data);
break;
case SIOCBRDGFLUSH:
bridge_rtflush(sc, req->ifbr_ifsflags);
break;
case SIOCBRDGSADDR:
ifs = if_unit(bareq->ifba_ifsname);
if (ifs == NULL) { /* no such interface */
error = ENOENT;
break;
}
if (ifs->if_bridgeidx != ifp->if_index) {
if_put(ifs);
error = ESRCH;
break;
}
if (bridge_rtupdate(sc, &bareq->ifba_dst, ifs, 1,
bareq->ifba_flags, NULL))
error = ENOMEM;
if_put(ifs);
break;
case SIOCBRDGDADDR:
error = bridge_rtdaddr(sc, &bareq->ifba_dst);
break;
case SIOCBRDGGCACHE:
bparam->ifbrp_csize = sc->sc_brtmax;
break;
case SIOCBRDGSCACHE:
mtx_enter(&sc->sc_mtx);
sc->sc_brtmax = bparam->ifbrp_csize;
mtx_leave(&sc->sc_mtx);
break;
case SIOCBRDGSTO:
if (bparam->ifbrp_ctime < 0 ||
bparam->ifbrp_ctime > INT_MAX / hz) {
error = EINVAL;
break;
}
sc->sc_brttimeout = bparam->ifbrp_ctime;
if (bparam->ifbrp_ctime != 0)
timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
else
timeout_del(&sc->sc_brtimeout);
break;
case SIOCBRDGGTO:
bparam->ifbrp_ctime = sc->sc_brttimeout;
break;
case SIOCBRDGARL:
if ((brlreq->ifbr_action != BRL_ACTION_BLOCK &&
brlreq->ifbr_action != BRL_ACTION_PASS) ||
(brlreq->ifbr_flags & (BRL_FLAG_IN|BRL_FLAG_OUT)) == 0) {
error = EINVAL;
break;
}
error = bridge_findbif(sc, brlreq->ifbr_ifsname, &bif);
if (error != 0)
break;
if (brlreq->ifbr_flags & BRL_FLAG_IN) {
error = bridge_addrule(bif, brlreq, 0);
if (error)
break;
}
if (brlreq->ifbr_flags & BRL_FLAG_OUT) {
error = bridge_addrule(bif, brlreq, 1);
if (error)
break;
}
break;
case SIOCBRDGFRL:
error = bridge_findbif(sc, brlreq->ifbr_ifsname, &bif);
if (error != 0)
break;
bridge_flushrule(bif);
break;
case SIOCBRDGGRL:
error = bridge_findbif(sc, bc->ifbrl_ifsname, &bif);
if (error != 0)
break;
error = bridge_brlconf(bif, bc);
break;
default:
break;
}
return (error);
}
int
bridge_rtupdate(struct bridge_softc *sc, struct ether_addr *ea,
struct ifnet *ifp, int setflags, u_int8_t flags, struct mbuf *m)
{
struct bridge_rtnode *p, *q;
struct bridge_tunneltag *brtag = NULL;
u_int32_t h;
int dir, error = 0;
if (m != NULL) {
/* Check if the mbuf was tagged with a tunnel endpoint addr */
brtag = bridge_tunnel(m);
}
h = bridge_hash(sc, ea);
mtx_enter(&sc->sc_mtx);
p = LIST_FIRST(&sc->sc_rts[h]);
if (p == NULL) {
if (sc->sc_brtcnt >= sc->sc_brtmax)
goto done;
p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
if (p == NULL)
goto done;
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
p->brt_ifidx = ifp->if_index;
p->brt_age = 1;
bridge_copytag(brtag, &p->brt_tunnel);
if (setflags)
p->brt_flags = flags;
else
p->brt_flags = IFBAF_DYNAMIC;
LIST_INSERT_HEAD(&sc->sc_rts[h], p, brt_next);
sc->sc_brtcnt++;
goto want;
}
do {
q = p;
p = LIST_NEXT(p, brt_next);
dir = memcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
if (dir == 0) {
if (setflags) {
q->brt_ifidx = ifp->if_index;
q->brt_flags = flags;
} else if (!(q->brt_flags & IFBAF_STATIC))
q->brt_ifidx = ifp->if_index;
if (q->brt_ifidx == ifp->if_index)
q->brt_age = 1;
bridge_copytag(brtag, &q->brt_tunnel);
goto want;
}
if (dir > 0) {
if (sc->sc_brtcnt >= sc->sc_brtmax)
goto done;
p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
if (p == NULL)
goto done;
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
p->brt_ifidx = ifp->if_index;
p->brt_age = 1;
bridge_copytag(brtag, &p->brt_tunnel);
if (setflags)
p->brt_flags = flags;
else
p->brt_flags = IFBAF_DYNAMIC;
LIST_INSERT_BEFORE(q, p, brt_next);
sc->sc_brtcnt++;
goto want;
}
if (p == NULL) {
if (sc->sc_brtcnt >= sc->sc_brtmax)
goto done;
p = malloc(sizeof(*p), M_DEVBUF, M_NOWAIT);
if (p == NULL)
goto done;
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
p->brt_ifidx = ifp->if_index;
p->brt_age = 1;
bridge_copytag(brtag, &p->brt_tunnel);
if (setflags)
p->brt_flags = flags;
else
p->brt_flags = IFBAF_DYNAMIC;
LIST_INSERT_AFTER(q, p, brt_next);
sc->sc_brtcnt++;
goto want;
}
} while (p != NULL);
done:
error = 1;
want:
mtx_leave(&sc->sc_mtx);
return (error);
}
unsigned int
bridge_rtlookup(struct ifnet *brifp, struct ether_addr *ea, struct mbuf *m)
{
struct bridge_softc *sc = brifp->if_softc;
struct bridge_rtnode *p = NULL;
unsigned int ifidx = 0;
u_int32_t h;
int dir;
h = bridge_hash(sc, ea);
mtx_enter(&sc->sc_mtx);
LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
dir = memcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
if (dir == 0)
break;
if (dir > 0) {
p = NULL;
break;
}
}
if (p != NULL) {
ifidx = p->brt_ifidx;
if (p->brt_family != AF_UNSPEC && m != NULL) {
struct bridge_tunneltag *brtag;
brtag = bridge_tunneltag(m);
if (brtag != NULL)
bridge_copytag(&p->brt_tunnel, brtag);
}
}
mtx_leave(&sc->sc_mtx);
return (ifidx);
}
u_int32_t
bridge_hash(struct bridge_softc *sc, struct ether_addr *addr)
{
return SipHash24((SIPHASH_KEY *)sc->sc_hashkey, addr, ETHER_ADDR_LEN) &
BRIDGE_RTABLE_MASK;
}
/*
* Perform an aging cycle
*/
void
bridge_rtage(void *vsc)
{
struct bridge_softc *sc = vsc;
struct ifnet *ifp = &sc->sc_if;
struct bridge_rtnode *n, *p;
int i;
if (!ISSET(ifp->if_flags, IFF_RUNNING))
return;
mtx_enter(&sc->sc_mtx);
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
n = LIST_FIRST(&sc->sc_rts[i]);
while (n != NULL) {
if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) {
n->brt_age = !n->brt_age;
if (n->brt_age)
n->brt_age = 0;
n = LIST_NEXT(n, brt_next);
} else if (n->brt_age) {
n->brt_age = 0;
n = LIST_NEXT(n, brt_next);
} else {
p = LIST_NEXT(n, brt_next);
LIST_REMOVE(n, brt_next);
sc->sc_brtcnt--;
free(n, M_DEVBUF, sizeof *n);
n = p;
}
}
}
mtx_leave(&sc->sc_mtx);
if (sc->sc_brttimeout != 0)
timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
}
void
bridge_rtagenode(struct ifnet *ifp, int age)
{
struct bridge_softc *sc;
struct bridge_rtnode *n;
struct ifnet *bifp;
int i;
bifp = if_get(ifp->if_bridgeidx);
if (bifp == NULL)
return;
sc = bifp->if_softc;
/*
* If the age is zero then flush, otherwise set all the expiry times to
* age for the interface
*/
if (age == 0)
bridge_rtdelete(sc, ifp, 1);
else {
mtx_enter(&sc->sc_mtx);
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
LIST_FOREACH(n, &sc->sc_rts[i], brt_next) {
/* Cap the expiry time to 'age' */
if (n->brt_ifidx == ifp->if_index &&
n->brt_age > getuptime() + age &&
(n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
n->brt_age = getuptime() + age;
}
}
mtx_leave(&sc->sc_mtx);
}
if_put(bifp);
}
/*
* Remove all dynamic addresses from the cache
*/
void
bridge_rtflush(struct bridge_softc *sc, int full)
{
int i;
struct bridge_rtnode *p, *n;
mtx_enter(&sc->sc_mtx);
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
n = LIST_FIRST(&sc->sc_rts[i]);
while (n != NULL) {
if (full ||
(n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
p = LIST_NEXT(n, brt_next);
LIST_REMOVE(n, brt_next);
sc->sc_brtcnt--;
free(n, M_DEVBUF, sizeof *n);
n = p;
} else
n = LIST_NEXT(n, brt_next);
}
}
mtx_leave(&sc->sc_mtx);
}
/*
* Remove an address from the cache
*/
int
bridge_rtdaddr(struct bridge_softc *sc, struct ether_addr *ea)
{
int h;
struct bridge_rtnode *p;
h = bridge_hash(sc, ea);
mtx_enter(&sc->sc_mtx);
LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
if (memcmp(ea, &p->brt_addr, sizeof(p->brt_addr)) == 0) {
LIST_REMOVE(p, brt_next);
sc->sc_brtcnt--;
mtx_leave(&sc->sc_mtx);
free(p, M_DEVBUF, sizeof *p);
return (0);
}
}
mtx_leave(&sc->sc_mtx);
return (ENOENT);
}
/*
* Delete routes to a specific interface member.
*/
void
bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int dynonly)
{
int i;
struct bridge_rtnode *n, *p;
/*
* Loop through all of the hash buckets and traverse each
* chain looking for routes to this interface.
*/
mtx_enter(&sc->sc_mtx);
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
n = LIST_FIRST(&sc->sc_rts[i]);
while (n != NULL) {
if (n->brt_ifidx != ifp->if_index) {
/* Not ours */
n = LIST_NEXT(n, brt_next);
continue;
}
if (dynonly &&
(n->brt_flags & IFBAF_TYPEMASK) != IFBAF_DYNAMIC) {
/* only deleting dynamics */
n = LIST_NEXT(n, brt_next);
continue;
}
p = LIST_NEXT(n, brt_next);
LIST_REMOVE(n, brt_next);
sc->sc_brtcnt--;
free(n, M_DEVBUF, sizeof *n);
n = p;
}
}
mtx_leave(&sc->sc_mtx);
}
/*
* Gather all of the routes for this interface.
*/
int
bridge_rtfind(struct bridge_softc *sc, struct ifbaconf *baconf)
{
struct ifbareq *bareq, *bareqs = NULL;
struct bridge_rtnode *n;
u_int32_t i = 0, total = 0;
int k, error = 0;
mtx_enter(&sc->sc_mtx);
for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
LIST_FOREACH(n, &sc->sc_rts[k], brt_next)
total++;
}
mtx_leave(&sc->sc_mtx);
if (baconf->ifbac_len == 0) {
i = total;
goto done;
}
total = MIN(total, baconf->ifbac_len / sizeof(*bareqs));
bareqs = mallocarray(total, sizeof(*bareqs), M_TEMP, M_NOWAIT|M_ZERO);
if (bareqs == NULL)
goto done;
mtx_enter(&sc->sc_mtx);
for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
LIST_FOREACH(n, &sc->sc_rts[k], brt_next) {
struct ifnet *ifp;
if (i >= total) {
mtx_leave(&sc->sc_mtx);
goto done;
}
bareq = &bareqs[i];
ifp = if_get(n->brt_ifidx);
if (ifp == NULL)
continue;
bcopy(ifp->if_xname, bareq->ifba_ifsname,
sizeof(bareq->ifba_ifsname));
if_put(ifp);
bcopy(sc->sc_if.if_xname, bareq->ifba_name,
sizeof(bareq->ifba_name));
bcopy(&n->brt_addr, &bareq->ifba_dst,
sizeof(bareq->ifba_dst));
bridge_copyaddr(&n->brt_tunnel.brtag_peer.sa,
sstosa(&bareq->ifba_dstsa));
bareq->ifba_age = n->brt_age;
bareq->ifba_flags = n->brt_flags;
i++;
}
}
mtx_leave(&sc->sc_mtx);
error = copyout(bareqs, baconf->ifbac_req, i * sizeof(*bareqs));
done:
free(bareqs, M_TEMP, total * sizeof(*bareqs));
baconf->ifbac_len = i * sizeof(*bareqs);
return (error);
}
void
bridge_update(struct ifnet *ifp, struct ether_addr *ea, int delete)
{
struct bridge_softc *sc;
struct bridge_iflist *bif;
u_int8_t *addr;
addr = (u_int8_t *)ea;
bif = bridge_getbif(ifp);
if (bif == NULL)
return;
sc = bif->bridge_sc;
if (sc == NULL)
return;
/*
* Update the bridge interface if it is in
* the learning state.
*/
if ((bif->bif_flags & IFBIF_LEARNING) &&
(ETHER_IS_MULTICAST(addr) == 0) &&
!(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 &&
addr[3] == 0 && addr[4] == 0 && addr[5] == 0)) {
/* Care must be taken with spanning tree */
if ((bif->bif_flags & IFBIF_STP) &&
(bif->bif_state == BSTP_IFSTATE_DISCARDING))
return;
/* Delete the address from the bridge */
bridge_rtdaddr(sc, ea);
if (!delete) {
/* Update the bridge table */
bridge_rtupdate(sc, ea, ifp, 0, IFBAF_DYNAMIC, NULL);
}
}
}
/*
* bridge filter/matching rules
*/
int
bridge_brlconf(struct bridge_iflist *bif, struct ifbrlconf *bc)
{
struct bridge_softc *sc = bif->bridge_sc;
struct brl_node *n;
struct ifbrlreq *req, *reqs = NULL;
int error = 0;
u_int32_t i = 0, total = 0;
SIMPLEQ_FOREACH(n, &bif->bif_brlin, brl_next) {
total++;
}
SIMPLEQ_FOREACH(n, &bif->bif_brlout, brl_next) {
total++;
}
if (bc->ifbrl_len == 0) {
i = total;
goto done;
}
reqs = mallocarray(total, sizeof(*reqs), M_TEMP, M_NOWAIT|M_ZERO);
if (reqs == NULL)
goto done;
SIMPLEQ_FOREACH(n, &bif->bif_brlin, brl_next) {
if (bc->ifbrl_len < (i + 1) * sizeof(*reqs))
goto done;
req = &reqs[i];
strlcpy(req->ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
strlcpy(req->ifbr_ifsname, bif->ifp->if_xname, IFNAMSIZ);
req->ifbr_action = n->brl_action;
req->ifbr_flags = n->brl_flags;
req->ifbr_src = n->brl_src;
req->ifbr_dst = n->brl_dst;
req->ifbr_arpf = n->brl_arpf;
#if NPF > 0
req->ifbr_tagname[0] = '\0';
if (n->brl_tag)
pf_tag2tagname(n->brl_tag, req->ifbr_tagname);
#endif
i++;
}
SIMPLEQ_FOREACH(n, &bif->bif_brlout, brl_next) {
if (bc->ifbrl_len < (i + 1) * sizeof(*reqs))
goto done;
req = &reqs[i];
strlcpy(req->ifbr_name, sc->sc_if.if_xname, IFNAMSIZ);
strlcpy(req->ifbr_ifsname, bif->ifp->if_xname, IFNAMSIZ);
req->ifbr_action = n->brl_action;
req->ifbr_flags = n->brl_flags;
req->ifbr_src = n->brl_src;
req->ifbr_dst = n->brl_dst;
req->ifbr_arpf = n->brl_arpf;
#if NPF > 0
req->ifbr_tagname[0] = '\0';
if (n->brl_tag)
pf_tag2tagname(n->brl_tag, req->ifbr_tagname);
#endif
i++;
}
error = copyout(reqs, bc->ifbrl_buf, i * sizeof(*reqs));
done:
free(reqs, M_TEMP, total * sizeof(*reqs));
bc->ifbrl_len = i * sizeof(*reqs);
return (error);
}
u_int8_t
bridge_arpfilter(struct brl_node *n, struct ether_header *eh, struct mbuf *m)
{
struct ether_arp ea;
if (!(n->brl_arpf.brla_flags & (BRLA_ARP|BRLA_RARP)))
return (1);
if (ntohs(eh->ether_type) != ETHERTYPE_ARP)
return (0);
if (m->m_pkthdr.len < ETHER_HDR_LEN + sizeof(ea))
return (0); /* log error? */
m_copydata(m, ETHER_HDR_LEN, sizeof(ea), &ea);
if (ntohs(ea.arp_hrd) != ARPHRD_ETHER ||
ntohs(ea.arp_pro) != ETHERTYPE_IP ||
ea.arp_hln != ETHER_ADDR_LEN ||
ea.arp_pln != sizeof(struct in_addr))
return (0);
if ((n->brl_arpf.brla_flags & BRLA_ARP) &&
ntohs(ea.arp_op) != ARPOP_REQUEST &&
ntohs(ea.arp_op) != ARPOP_REPLY)
return (0);
if ((n->brl_arpf.brla_flags & BRLA_RARP) &&
ntohs(ea.arp_op) != ARPOP_REVREQUEST &&
ntohs(ea.arp_op) != ARPOP_REVREPLY)
return (0);
if (n->brl_arpf.brla_op && ntohs(ea.arp_op) != n->brl_arpf.brla_op)
return (0);
if (n->brl_arpf.brla_flags & BRLA_SHA &&
memcmp(ea.arp_sha, &n->brl_arpf.brla_sha, ETHER_ADDR_LEN))
return (0);
if (n->brl_arpf.brla_flags & BRLA_THA &&
memcmp(ea.arp_tha, &n->brl_arpf.brla_tha, ETHER_ADDR_LEN))
return (0);
if (n->brl_arpf.brla_flags & BRLA_SPA &&
memcmp(ea.arp_spa, &n->brl_arpf.brla_spa, sizeof(struct in_addr)))
return (0);
if (n->brl_arpf.brla_flags & BRLA_TPA &&
memcmp(ea.arp_tpa, &n->brl_arpf.brla_tpa, sizeof(struct in_addr)))
return (0);
return (1);
}
u_int8_t
bridge_filterrule(struct brl_head *h, struct ether_header *eh, struct mbuf *m)
{
struct brl_node *n;
u_int8_t action, flags;
if (SIMPLEQ_EMPTY(h))
return (BRL_ACTION_PASS);
KERNEL_LOCK();
SIMPLEQ_FOREACH(n, h, brl_next) {
if (!bridge_arpfilter(n, eh, m))
continue;
flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID);
if (flags == 0)
goto return_action;
if (flags == (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID)) {
if (memcmp(eh->ether_shost, &n->brl_src,
ETHER_ADDR_LEN))
continue;
if (memcmp(eh->ether_dhost, &n->brl_dst,
ETHER_ADDR_LEN))
continue;
goto return_action;
}
if (flags == BRL_FLAG_SRCVALID) {
if (memcmp(eh->ether_shost, &n->brl_src,
ETHER_ADDR_LEN))
continue;
goto return_action;
}
if (flags == BRL_FLAG_DSTVALID) {
if (memcmp(eh->ether_dhost, &n->brl_dst,
ETHER_ADDR_LEN))
continue;
goto return_action;
}
}
KERNEL_UNLOCK();
return (BRL_ACTION_PASS);
return_action:
#if NPF > 0
pf_tag_packet(m, n->brl_tag, -1);
#endif
action = n->brl_action;
KERNEL_UNLOCK();
return (action);
}
int
bridge_addrule(struct bridge_iflist *bif, struct ifbrlreq *req, int out)
{
struct brl_node *n;
n = malloc(sizeof(*n), M_DEVBUF, M_NOWAIT);
if (n == NULL)
return (ENOMEM);
bcopy(&req->ifbr_src, &n->brl_src, sizeof(struct ether_addr));
bcopy(&req->ifbr_dst, &n->brl_dst, sizeof(struct ether_addr));
n->brl_action = req->ifbr_action;
n->brl_flags = req->ifbr_flags;
n->brl_arpf = req->ifbr_arpf;
#if NPF > 0
if (req->ifbr_tagname[0])
n->brl_tag = pf_tagname2tag(req->ifbr_tagname, 1);
else
n->brl_tag = 0;
#endif
KERNEL_ASSERT_LOCKED();
if (out) {
n->brl_flags &= ~BRL_FLAG_IN;
n->brl_flags |= BRL_FLAG_OUT;
SIMPLEQ_INSERT_TAIL(&bif->bif_brlout, n, brl_next);
} else {
n->brl_flags &= ~BRL_FLAG_OUT;
n->brl_flags |= BRL_FLAG_IN;
SIMPLEQ_INSERT_TAIL(&bif->bif_brlin, n, brl_next);
}
return (0);
}
void
bridge_flushrule(struct bridge_iflist *bif)
{
struct brl_node *p;
KERNEL_ASSERT_LOCKED();
while (!SIMPLEQ_EMPTY(&bif->bif_brlin)) {
p = SIMPLEQ_FIRST(&bif->bif_brlin);
SIMPLEQ_REMOVE_HEAD(&bif->bif_brlin, brl_next);
#if NPF > 0
pf_tag_unref(p->brl_tag);
#endif
free(p, M_DEVBUF, sizeof *p);
}
while (!SIMPLEQ_EMPTY(&bif->bif_brlout)) {
p = SIMPLEQ_FIRST(&bif->bif_brlout);
SIMPLEQ_REMOVE_HEAD(&bif->bif_brlout, brl_next);
#if NPF > 0
pf_tag_unref(p->brl_tag);
#endif
free(p, M_DEVBUF, sizeof *p);
}
}
struct bridge_tunneltag *
bridge_tunnel(struct mbuf *m)
{
struct m_tag *mtag;
if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) == NULL)
return (NULL);
return ((struct bridge_tunneltag *)(mtag + 1));
}
struct bridge_tunneltag *
bridge_tunneltag(struct mbuf *m)
{
struct m_tag *mtag;
if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) == NULL) {
mtag = m_tag_get(PACKET_TAG_TUNNEL,
sizeof(struct bridge_tunneltag), M_NOWAIT);
if (mtag == NULL)
return (NULL);
bzero(mtag + 1, sizeof(struct bridge_tunneltag));
m_tag_prepend(m, mtag);
}
return ((struct bridge_tunneltag *)(mtag + 1));
}
void
bridge_tunneluntag(struct mbuf *m)
{
struct m_tag *mtag;
if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) != NULL)
m_tag_delete(m, mtag);
}
void
bridge_copyaddr(struct sockaddr *src, struct sockaddr *dst)
{
if (src != NULL && src->sa_family != AF_UNSPEC)
memcpy(dst, src, src->sa_len);
else {
dst->sa_family = AF_UNSPEC;
dst->sa_len = 0;
}
}
void
bridge_copytag(struct bridge_tunneltag *src, struct bridge_tunneltag *dst)
{
if (src == NULL) {
memset(dst, 0, sizeof(*dst));
} else {
bridge_copyaddr(&src->brtag_peer.sa, &dst->brtag_peer.sa);
bridge_copyaddr(&src->brtag_local.sa, &dst->brtag_local.sa);
dst->brtag_id = src->brtag_id;
}
}