summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sbin/brconfig/brconfig.845
-rw-r--r--sbin/brconfig/brconfig.c284
-rw-r--r--share/man/man4/bridge.4108
-rw-r--r--sys/net/if_bridge.c568
-rw-r--r--sys/net/if_bridge.h35
-rw-r--r--sys/sys/sockio.h6
6 files changed, 891 insertions, 155 deletions
diff --git a/sbin/brconfig/brconfig.8 b/sbin/brconfig/brconfig.8
index 53ea857e976..a75847100dc 100644
--- a/sbin/brconfig/brconfig.8
+++ b/sbin/brconfig/brconfig.8
@@ -1,6 +1,6 @@
-.\" $OpenBSD: brconfig.8,v 1.4 2000/01/10 22:14:25 angelos Exp $
+.\" $OpenBSD: brconfig.8,v 1.5 2000/01/25 22:06:26 jason Exp $
.\"
-.\" Copyright (c) 1999 Jason L. Wright (jason@thought.net)
+.\" Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -57,11 +57,18 @@
.Op Ar -discover interface-name
.Op Ar learn interface-name
.Op Ar -learn interface-name
+.Op Ar rulefile file-name
+.Op Ar flushrule interface-name
.Op Ar link0
.Op Ar link1
.Op Ar -link0
.Op Ar -link1
.Op Ar ...
+.Nm brconfig
+.Ar bridge-name rule {block|pass} {in|out|in/out} on
+.Ar interface-name
+.Op Ar src address
+.Op Ar dst address
.Sh DESCRIPTION
The
.Nm brconfig
@@ -75,6 +82,8 @@ the order they were specified. If no command is specified in
the second synopsis, the
.Nm brconfig
will display status information about the bridge.
+With the third synopsis, rules for filtering Ethernet MAC addresses can
+be added to a bridge.
.Pp
The available commands are:
.Bl -tag -width Ds
@@ -154,6 +163,9 @@ This is the default for interfaces added to the bridge.
Mark an interface so that the source address of packets received from
.Cm interface
are not entered into the address cache.
+.It Ar flushrule interface
+Remove all Ethernet MAC filtering rules from
+.Cm interface
.It Ar link0
Setting this flag stops all non-IP multicast packets from
being forwarded by the bridge.
@@ -168,6 +180,18 @@ being forwarded by the bridge.
Clear the
.Ar link1
flag on the bridge interface.
+.It Ar rule [rulespec]
+Add a filtering rule to an interface.
+Rules have a similiar syntax to
+.Xr ipf 4 .
+Rules can be used to selectively block or pass frames based on Ethernet
+MAC address. Rules are processed in the order in which they were added
+to the interface, and the first rule matched takes the action (block or pass)
+of the rule. If no source or destination address is specified, the
+rule will match all frames (good for creating a catchall policy).
+.It Ar rulefile filename
+Load a set of rules from the file
+.Cm filename .
.El
.Sh EXAMPLES
.Bl -tag -width brconfig
@@ -197,6 +221,14 @@ and means that bogus MAC addresses seen by the xl0 side of the bridge
will not be propagated to the rest of the network.
Also, no packets will be sent on xl0 segment by the bridge unless they are
broadcast packets or are for 8:0:20:1e:2f:2b.
+.It Cm "brconfig bridge0 rule pass in on fxp0 8:0:1:2:3:4:5 dst 5:4:3:2:1:0"
+.It Cm "brconfig bridge0 rule pass out on fxp0 src 5:4:3:2:1:0 dst 0:1:2:3:4:5"
+.It Cm brconfig bridge0 rule block in on fxp0
+.It Cm brconfig bridge0 rule block out on fxp0
+The above commands will set up a filter so that 0:1:2:3:4:5 can send frames
+through fxp0 only to 5:4:3:2:1, and 5:4:3:2:1:0 can return frames through
+fxp0 to 0:1:2:3:4:5. All other traffic trying to go into and be sent from
+fxp0 will be blocked.
.El
.Sh SEE ALSO
.Xr bridge 4 ,
@@ -216,10 +248,9 @@ part of an undergraduate independent study
at the University of North Carolina at Greensboro.
.Sh BUGS
There are some rather special network interface chipsets which will
-not work in a bridge configuration. Some, like the Lite-On PNIC (see
-.Xr pn 4 ),
-have serious flaws when running in promiscuous mode, and others, like the
+not work in a bridge configuration.
+Some chipsets have serious flaws when running in promiscuous mode, like the
TI ThunderLAN (see
.Xr tl 4 ),
-receive their own transmissions, which makes the address learning code
-ineffective. Most other chipsets work fine though.
+which receives its own transmissions (this renders the address learning
+cache useless). Most other chipsets work fine though.
diff --git a/sbin/brconfig/brconfig.c b/sbin/brconfig/brconfig.c
index ff65659d922..82a14d9b6a9 100644
--- a/sbin/brconfig/brconfig.c
+++ b/sbin/brconfig/brconfig.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: brconfig.c,v 1.4 2000/01/10 22:14:25 angelos Exp $ */
+/* $OpenBSD: brconfig.c,v 1.5 2000/01/25 22:06:27 jason Exp $ */
/*
- * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
+ * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -70,6 +70,12 @@ int bridge_status __P((int, char *));
int is_bridge __P((int, char *));
int bridge_show_all __P((int));
void printb __P((char *, unsigned short, char *));
+int bridge_rule __P((int, char *, int, char **, int));
+int bridge_rules __P((int, char *, char *));
+int bridge_flushrule __P((int, char *, char *));
+void bridge_badrule __P((int, char **, int));
+void bridge_showrule __P((struct ifbrlreq *));
+int bridge_rulefile __P((int, char *, char *));
/* if_flags bits: borrowed from ifconfig.c */
#define IFFBITS \
@@ -294,6 +300,40 @@ main(argc, argv)
if (error)
return (error);
}
+ else if (strcmp("rules", argv[0]) == 0) {
+ argc--; argv++;
+ if (argc == 0) {
+ warnx("rules requires an argument");
+ return (EX_USAGE);
+ }
+ error = bridge_rules(sock, brdg, argv[0]);
+ if (error)
+ return (error);
+ }
+ else if (strcmp("rule", argv[0]) == 0) {
+ argc--; argv++;
+ return (bridge_rule(sock, brdg, argc, argv, -1));
+ }
+ else if (strcmp("rulefile", argv[0]) == 0) {
+ argc--; argv++;
+ if (argc == 0) {
+ warnx("rulefile requires an argument");
+ return (EX_USAGE);
+ }
+ error = bridge_rulefile(sock, brdg, argv[0]);
+ if (error)
+ return (error);
+ }
+ else if (strcmp("flushrule", argv[0]) == 0) {
+ argc--; argv++;
+ if (argc == 0) {
+ warnx("flushrule requires an argument");
+ return (EX_USAGE);
+ }
+ error = bridge_flushrule(sock, brdg, argv[0]);
+ if (error)
+ return (error);
+ }
else if (strcmp("timeout", argv[0]) == 0) {
argc--; argv++;
if (argc == 0) {
@@ -766,8 +806,246 @@ bridge_status(s, brdg)
return (err);
}
+int
+bridge_flushrule(s, brdg, ifname)
+ int s;
+ char *brdg, *ifname;
+{
+ struct ifbrlreq req;
+
+ strlcpy(req.ifbr_name, brdg, sizeof(req.ifbr_name));
+ strlcpy(req.ifbr_ifsname, ifname, sizeof(req.ifbr_ifsname));
+ if (ioctl(s, SIOCBRDGFRL, &req) < 0) {
+ warn("%s: %s", brdg, ifname);
+ return (EX_USAGE);
+ }
+ return (0);
+}
+
+int
+bridge_rules(s, brdg, ifname)
+ int s;
+ char *brdg, *ifname;
+{
+ char *inbuf = NULL;
+ struct ifbrlconf ifc;
+ struct ifbrlreq *ifrp, ifreq;
+ int len = 8192, i;
+
+ while (1) {
+ ifc.ifbrl_len = len;
+ ifc.ifbrl_buf = inbuf = realloc(inbuf, len);
+ strlcpy(ifc.ifbrl_name, brdg, sizeof(ifc.ifbrl_name));
+ strlcpy(ifc.ifbrl_ifsname, ifname, sizeof(ifc.ifbrl_ifsname));
+ if (inbuf == NULL)
+ err(1, "malloc");
+ if (ioctl(s, SIOCBRDGGRL, &ifc) < 0)
+ err(1, "ioctl(SIOCBRDGGRL)");
+ if (ifc.ifbrl_len + sizeof(ifreq) < len)
+ break;
+ len *= 2;
+ }
+ ifrp = ifc.ifbrl_req;
+ for (i = 0; i < ifc.ifbrl_len; i += sizeof(ifreq)) {
+ ifrp = (struct ifbrlreq *)((caddr_t)ifc.ifbrl_req + i);
+ bridge_showrule(ifrp);
+ }
+ return (0);
+}
+
+void
+bridge_showrule(r)
+ struct ifbrlreq *r;
+{
+ printf("%s: ", r->ifbr_name);
+
+ if (r->ifbr_action == BRL_ACTION_BLOCK)
+ printf("block ");
+ else if (r->ifbr_action == BRL_ACTION_PASS)
+ printf("pass ");
+ else
+ printf("[neither block nor pass?]\n");
+
+ if ((r->ifbr_flags & (BRL_FLAG_IN | BRL_FLAG_OUT)) ==
+ (BRL_FLAG_IN | BRL_FLAG_OUT))
+ printf("in/out ");
+ else if (r->ifbr_flags & BRL_FLAG_IN)
+ printf("in ");
+ else if (r->ifbr_flags & BRL_FLAG_OUT)
+ printf("out ");
+ else
+ printf("[neither in nor out?]\n");
+
+ printf("on %s", r->ifbr_ifsname);
+
+ if (r->ifbr_flags & BRL_FLAG_SRCVALID)
+ printf(" src %s", ether_ntoa(&r->ifbr_src));
+ if (r->ifbr_flags & BRL_FLAG_DSTVALID)
+ printf(" dst %s", ether_ntoa(&r->ifbr_dst));
+
+ printf("\n");
+}
+
+/*
+ * Parse a rule definition and send it upwards.
+ *
+ * Syntax:
+ * {block|pass} {in|out|in/out} on {ifs} [src {mac}] [dst {mac}]
+ */
+int
+bridge_rule(int s, char *brdg, int targc, char **targv, int ln)
+{
+ char **argv = targv;
+ int argc = targc;
+ struct ifbrlreq rule;
+ struct ether_addr *ea, *dea;
+
+ if (argc == 0) {
+ fprintf(stderr, "invalid rule\n");
+ return (EX_USAGE);
+ }
+ rule.ifbr_flags = 0;
+ rule.ifbr_action = 0;
+ strlcpy(rule.ifbr_name, brdg, sizeof(rule.ifbr_name));
+
+ if (strcmp(argv[0], "block") == 0)
+ rule.ifbr_action = BRL_ACTION_BLOCK;
+ else if (strcmp(argv[0], "pass") == 0)
+ rule.ifbr_action = BRL_ACTION_PASS;
+ else
+ goto bad_rule;
+ argc--; argv++;
+
+ if (argc == 0) {
+ bridge_badrule(targc, targv, ln);
+ return (EX_USAGE);
+ }
+ if (strcmp(argv[0], "in") == 0)
+ rule.ifbr_flags |= BRL_FLAG_IN;
+ else if (strcmp(argv[0], "out") == 0)
+ rule.ifbr_flags |= BRL_FLAG_OUT;
+ else if (strcmp(argv[0], "in/out") == 0)
+ rule.ifbr_flags |= BRL_FLAG_IN | BRL_FLAG_OUT;
+ else
+ goto bad_rule;
+ argc--; argv++;
+
+ if (argc == 0 || strcmp(argv[0], "on"))
+ goto bad_rule;
+ argc--; argv++;
+
+ if (argc == 0)
+ goto bad_rule;
+ strlcpy(rule.ifbr_ifsname, argv[0], sizeof(rule.ifbr_ifsname));
+ argc--; argv++;
+
+ while (argc) {
+ if (strcmp(argv[0], "dst") == 0) {
+ if (rule.ifbr_flags & BRL_FLAG_DSTVALID)
+ goto bad_rule;
+ rule.ifbr_flags |= BRL_FLAG_DSTVALID;
+ dea = &rule.ifbr_dst;
+ }
+ else if (strcmp(argv[0], "src") == 0) {
+ if (rule.ifbr_flags & BRL_FLAG_SRCVALID)
+ goto bad_rule;
+ rule.ifbr_flags |= BRL_FLAG_SRCVALID;
+ dea = &rule.ifbr_src;
+ }
+ else
+ goto bad_rule;
+
+ argc--; argv++;
+
+ if (argc == 0)
+ goto bad_rule;
+ ea = ether_aton(argv[0]);
+ if (ea == NULL) {
+ warnx("Invalid address: %s", argv[0]);
+ return (EX_USAGE);
+ }
+ bcopy(ea, dea, sizeof(*dea));
+ argc--; argv++;
+ }
+
+ if (ioctl(s, SIOCBRDGARL, &rule) < 0) {
+ warn("%s", brdg);
+ return (EX_IOERR);
+ }
+ return (0);
+
+bad_rule:
+ bridge_badrule(targc, targv, ln);
+ return (EX_USAGE);
+}
+
+#define MAXRULEWORDS 8
+
+int
+bridge_rulefile(s, brdg, fname)
+ int s;
+ char *brdg, *fname;
+{
+ FILE *f;
+ char *str, *argv[MAXRULEWORDS], buf[1024], xbuf[1024];
+ int ln = 1, argc = 0, err = 0, xerr;
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ warn("%s", fname);
+ return (EX_IOERR);
+ }
+
+ while (1) {
+ fgets(buf, sizeof(buf), f);
+ if (feof(f))
+ break;
+ ln++;
+ if (buf[0] == '#')
+ continue;
+
+ argc = 0;
+ str = strtok(buf, "\n\t\r ");
+ strlcpy(xbuf, buf, sizeof(xbuf));
+ while (str != NULL) {
+ argv[argc++] = str;
+ if (argc > MAXRULEWORDS) {
+ fprintf(stderr, "invalid rule: %d: %s\n",
+ ln, xbuf);
+ break;
+ }
+ str = strtok(NULL, "\n\t\r ");
+ }
+
+ if (argc > MAXRULEWORDS)
+ continue;
+
+ xerr = bridge_rule(s, brdg, argc, argv, ln);
+ if (xerr)
+ err = xerr;
+ }
+ fclose(f);
+ return (err);
+}
+
+void
+bridge_badrule(argc, argv, ln)
+ int argc, ln;
+ char **argv;
+{
+ int i;
+
+ fprintf(stderr, "invalid rule: ");
+ if (ln != -1)
+ fprintf(stderr, "%d: ", ln);
+ for (i = 0; i < argc; i++) {
+ fprintf(stderr, "%s ", argv[i]);
+ }
+ fprintf(stderr, "\n");
+}
+
/*
- * Print a value a la the %b format of the kernel's printf
+ * Print a value ala the %b format of the kernel's printf
* (borrowed from ifconfig.c)
*/
void
diff --git a/share/man/man4/bridge.4 b/share/man/man4/bridge.4
index 26fc7f46ed6..9035c5a75e4 100644
--- a/share/man/man4/bridge.4
+++ b/share/man/man4/bridge.4
@@ -1,6 +1,6 @@
-.\" $OpenBSD: bridge.4,v 1.16 2000/01/10 22:46:12 angelos Exp $
+.\" $OpenBSD: bridge.4,v 1.17 2000/01/25 22:06:27 jason Exp $
.\"
-.\" Copyright (c) 1999 Jason L. Wright (jason@thought.net)
+.\" Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -302,6 +302,110 @@ should be set to
to flush all addresses from the cache or
.Ar IFBF_FLUSHDYN
to flush only the dynamically learned addresses from the cache.
+.It Dv SIOCBRDGARL
+.Pq Li "struct ifbrlreq"
+Add a filtering rule to the bridge named in
+.Ar ifbr_name
+on the interface named in
+.Ar ifbr_ifsname .
+The argument structure is as follows:
+.Bd -literal -offset indent
+struct ifbrlreq {
+ char ifbr_name[IFNAMSIZ]; /* bridge */
+ char ifbr_ifsname[IFNAMSIZ]; /* ifs */
+ u_int8_t ifbr_action; /* handling */
+ u_int8_t ifbr_flags; /* flags */
+ struct ether_addr ifbr_src; /* src mac */
+ struct ether_addr ifbr_dst; /* dst mac */
+};
+#define BRL_ACTION_BLOCK 0x01
+#define BRL_ACTION_PASS 0x02
+#define BRL_FLAG_IN 0x08
+#define BRL_FLAG_OUT 0x04
+#define BRL_FLAG_SRCVALID 0x02
+#define BRL_FLAG_DSTVALID 0x01
+.Ed
+.Pp
+Rules are applied in the order in which they were added to the bridge,
+and the first matching rule's action parameter determines the fate of
+the packet.
+The
+.Ar ifbr_action
+parameter specifies whether a frame matching the rule is to
+be blocked or passed.
+.Pp
+If the
+.Ar BRL_FLAG_IN
+bit is set in
+.Ar ifbr_flags ,
+then the rule applies to frames received by the interface.
+If the
+.Ar BRL_FLAG_OUT
+bit is set,
+then the rule applies to frame transmitted by the interface.
+At least one of
+.Ar BRL_FLAG_IN
+or
+.Ar BRL_FLAG_OUT
+must be set.
+.Pp
+The source ethernet address in
+.Ar ifbr_src
+is checked if the
+.Ar BRL_FLAG_SRCVALID
+bit is set in
+.Ar ifbr_flags .
+The destination address in
+.Ar ifbr_dst
+is check if the
+.Ar BRL_FLAG_DSTVALID
+bit is set.
+If neither bit is set, the rule is matches all frames.
+.It Dv SIOCBRDGFRL
+.Pq Li "struct ifbrlreq"
+Flush rules from the bridge
+.Ar ifbr_name
+on the interface
+.Ar ifbr_ifsname .
+.It Dv SIOCBRDGGRL
+.Pq Li "struct ifbrlconf"
+Retrieve an array of rules from the bridge for a
+particular interface. This request takes an
+.Ar ifbrlconf
+structure (see below) as a value-result parameter.
+The
+.Ar ifbrl_len
+field should be initially set to the size of the buffer
+pointed to by
+.Ar ifbrl_buf .
+On return it will contain the length, in bytes, of the rule list.
+Alternatively, if the
+.Ar ifbrl_len
+passed in is set to 0,
+SIOCBRDGGRL will set
+.Ar ifbrl_len
+to the size that
+.Ar ifbrl_buf
+needs to be to fit the entire configuration list,
+and will not fill in the other parameters.
+This is useful for determining the exact size that
+.Ar ifbrl_buf
+needs to be in advance.
+.Pp
+The argument structure is as follows:
+.Bd -literal -offset indent
+struct ifbrlconf {
+ char ifbrl_name[IFNAMSIZ]; /* bridge */
+ char ifbrl_ifsname[IFNAMSIZ];/* member */
+ u_int32_t ifbrl_len; /* buflen */
+ union {
+ caddr_t ifbrlu_buf;
+ struct ifbrlreq *ifbrlu_req;
+ } ifbrl_ifbrlu;
+#define ifbrl_buf ifbrl_ifbrlu.ifbrlu_buf
+#define ifbrl_req ifbrl_ifbrlu.ifbrlu_req
+};
+.Ed
.El
.Sh ERRORS
If the
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index 961f7c2f448..51b76ba0ddc 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: if_bridge.c,v 1.24 2000/01/15 20:02:36 angelos Exp $ */
+/* $OpenBSD: if_bridge.c,v 1.25 2000/01/25 22:06:27 jason Exp $ */
/*
- * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
+ * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -104,10 +104,23 @@
extern int ifqmaxlen;
/*
+ * Bridge filtering rules
+ */
+struct brl_node {
+ SIMPLEQ_ENTRY(brl_node) brl_next; /* next rule */
+ struct ether_addr brl_src; /* source mac address */
+ struct ether_addr brl_dst; /* destination mac address */
+ u_int8_t brl_action; /* what to do with match */
+ u_int8_t brl_flags; /* comparision flags */
+};
+
+/*
* Bridge interface list
*/
struct bridge_iflist {
LIST_ENTRY(bridge_iflist) next; /* next in list */
+ SIMPLEQ_HEAD(, brl_node) bif_brlin; /* input rules */
+ SIMPLEQ_HEAD(, brl_node) bif_brlout; /* output rules */
struct ifnet *ifp; /* member interface */
u_int32_t bif_flags; /* member flags */
};
@@ -148,6 +161,7 @@ struct bridge_softc bridgectl[NBRIDGE];
void bridgeattach __P((int));
int bridge_ioctl __P((struct ifnet *, u_long, caddr_t));
void bridge_start __P((struct ifnet *));
+void bridgeintr_frame __P((struct bridge_softc *, struct mbuf *));
void bridge_broadcast __P((struct bridge_softc *, struct ifnet *,
struct ether_header *, struct mbuf *));
void bridge_stop __P((struct bridge_softc *));
@@ -166,6 +180,11 @@ struct ifnet * bridge_rtlookup __P((struct bridge_softc *,
struct ether_addr *));
u_int32_t bridge_hash __P((struct ether_addr *));
struct mbuf *bridge_blocknonip __P((struct mbuf *));
+int bridge_addrule __P((struct bridge_iflist *,
+ struct ifbrlreq *, int out));
+int bridge_flushrule __P((struct bridge_iflist *));
+int bridge_brlconf __P((struct bridge_softc *, struct ifbrlconf *));
+u_int8_t bridge_filterrule __P((struct brl_node *, struct ether_header *));
#define ETHERADDR_IS_IP_MCAST(a) \
/* struct etheraddr *a; */ \
@@ -173,6 +192,7 @@ struct mbuf *bridge_blocknonip __P((struct mbuf *));
(a)->ether_addr_octet[1] == 0x00 && \
(a)->ether_addr_octet[2] == 0x5e)
+
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
/*
* Filter hooks
@@ -227,6 +247,8 @@ bridge_ioctl(ifp, cmd, data)
struct ifbcachereq *bcachereq = (struct ifbcachereq *)data;
struct ifbifconf *bifconf = (struct ifbifconf *)data;
struct ifbcachetoreq *bcacheto = (struct ifbcachetoreq *)data;
+ struct ifbrlreq *brlreq = (struct ifbrlreq *)data;
+ struct ifbrlconf *brlconf = (struct ifbrlconf *)data;
struct ifreq ifreq;
int error = 0, s;
struct bridge_iflist *p;
@@ -310,6 +332,8 @@ bridge_ioctl(ifp, cmd, data)
p->ifp = ifs;
p->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
+ SIMPLEQ_INIT(&p->bif_brlin);
+ SIMPLEQ_INIT(&p->bif_brlout);
LIST_INSERT_HEAD(&sc->sc_iflist, p, next);
ifs->if_bridge = (caddr_t)sc;
break;
@@ -327,6 +351,7 @@ bridge_ioctl(ifp, cmd, data)
LIST_REMOVE(p, next);
bridge_rtdelete(sc, p->ifp);
+ bridge_flushrule(p);
free(p, M_DEVBUF);
break;
}
@@ -445,6 +470,70 @@ bridge_ioctl(ifp, cmd, data)
bridge_stop(sc);
break;
+ case SIOCBRDGARL:
+ if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
+ break;
+ ifs = ifunit(brlreq->ifbr_ifsname);
+ if (ifs == NULL) {
+ error = ENOENT;
+ break;
+ }
+ if (ifs->if_bridge == NULL ||
+ ifs->if_bridge != (caddr_t)sc) {
+ error = ESRCH;
+ break;
+ }
+ p = LIST_FIRST(&sc->sc_iflist);
+ while (p != NULL && p->ifp != ifs) {
+ p = LIST_NEXT(p, next);
+ }
+ if (p == NULL) {
+ error = ESRCH;
+ break;
+ }
+ 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;
+ }
+ if (brlreq->ifbr_flags & BRL_FLAG_IN) {
+ error = bridge_addrule(p, brlreq, 0);
+ if (error)
+ break;
+ }
+ if (brlreq->ifbr_flags & BRL_FLAG_OUT) {
+ error = bridge_addrule(p, brlreq, 1);
+ if (error)
+ break;
+ }
+ break;
+ case SIOCBRDGFRL:
+ if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
+ break;
+ ifs = ifunit(brlreq->ifbr_ifsname);
+ if (ifs == NULL) {
+ error = ENOENT;
+ break;
+ }
+ if (ifs->if_bridge == NULL ||
+ ifs->if_bridge != (caddr_t)sc) {
+ error = ESRCH;
+ break;
+ }
+ p = LIST_FIRST(&sc->sc_iflist);
+ while (p != NULL && p->ifp != ifs) {
+ p = LIST_NEXT(p, next);
+ }
+ if (p == NULL) {
+ error = ESRCH;
+ break;
+ }
+ error = bridge_flushrule(p);
+ break;
+ case SIOCBRDGGRL:
+ error = bridge_brlconf(sc, brlconf);
+ break;
default:
error = EINVAL;
}
@@ -465,6 +554,7 @@ bridge_ifdetach(ifp)
if (bif->ifp == ifp) {
LIST_REMOVE(bif, next);
bridge_rtdelete(bsc, ifp);
+ bridge_flushrule(bif);
free(bif, M_DEVBUF);
break;
}
@@ -514,6 +604,93 @@ done:
return (error);
}
+int
+bridge_brlconf(sc, bc)
+ struct bridge_softc *sc;
+ struct ifbrlconf *bc;
+{
+ struct ifnet *ifp;
+ struct bridge_iflist *ifl;
+ struct brl_node *n;
+ struct ifbrlreq req;
+ int error = 0;
+ u_int32_t i, total;
+
+ ifp = ifunit(bc->ifbrl_ifsname);
+ if (ifp == NULL)
+ return (ENOENT);
+ if (ifp->if_bridge == NULL || ifp->if_bridge != (caddr_t)sc)
+ return (ESRCH);
+ ifl = LIST_FIRST(&sc->sc_iflist);
+ while (ifl != NULL && ifl->ifp != ifp)
+ ifl = LIST_NEXT(ifl, next);
+ if (ifl == NULL)
+ return (ESRCH);
+
+ n = SIMPLEQ_FIRST(&ifl->bif_brlin);
+ while (n != NULL) {
+ total++;
+ n = SIMPLEQ_NEXT(n, brl_next);
+ }
+ n = SIMPLEQ_FIRST(&ifl->bif_brlout);
+ while (n != NULL) {
+ total++;
+ n = SIMPLEQ_NEXT(n, brl_next);
+ }
+
+ if (bc->ifbrl_len == 0) {
+ i = total;
+ goto done;
+ }
+
+ i = 0;
+ n = SIMPLEQ_FIRST(&ifl->bif_brlin);
+ while (n != NULL && bc->ifbrl_len > i * sizeof(req)) {
+ strncpy(req.ifbr_name, sc->sc_if.if_xname,
+ sizeof(req.ifbr_name) - 1);
+ req.ifbr_name[sizeof(req.ifbr_name) - 1] = '\0';
+ strncpy(req.ifbr_ifsname, ifl->ifp->if_xname,
+ sizeof(req.ifbr_ifsname) - 1);
+ req.ifbr_ifsname[sizeof(req.ifbr_ifsname) - 1] = '\0';
+ req.ifbr_action = n->brl_action;
+ req.ifbr_flags = n->brl_flags;
+ req.ifbr_src = n->brl_src;
+ req.ifbr_dst = n->brl_dst;
+ error = copyout((caddr_t)&req,
+ (caddr_t)(bc->ifbrl_buf + (i * sizeof(req))), sizeof(req));
+ if (error)
+ goto done;
+ n = SIMPLEQ_NEXT(n, brl_next);
+ i++;
+ bc->ifbrl_len -= sizeof(req);
+ }
+
+ n = SIMPLEQ_FIRST(&ifl->bif_brlout);
+ while (n != NULL && bc->ifbrl_len > i * sizeof(req)) {
+ strncpy(req.ifbr_name, sc->sc_if.if_xname,
+ sizeof(req.ifbr_name) - 1);
+ req.ifbr_name[sizeof(req.ifbr_name) - 1] = '\0';
+ strncpy(req.ifbr_ifsname, ifl->ifp->if_xname,
+ sizeof(req.ifbr_ifsname) - 1);
+ req.ifbr_ifsname[sizeof(req.ifbr_ifsname) - 1] = '\0';
+ req.ifbr_action = n->brl_action;
+ req.ifbr_flags = n->brl_flags;
+ req.ifbr_src = n->brl_src;
+ req.ifbr_dst = n->brl_dst;
+ error = copyout((caddr_t)&req,
+ (caddr_t)(bc->ifbrl_buf + (i * sizeof(req))), sizeof(req));
+ if (error)
+ goto done;
+ n = SIMPLEQ_NEXT(n, brl_next);
+ i++;
+ bc->ifbrl_len -= sizeof(req);
+ }
+
+done:
+ bc->ifbrl_len = i * sizeof(req);
+ return (error);
+}
+
void
bridge_init(sc)
struct bridge_softc *sc;
@@ -670,171 +847,195 @@ bridge_start(ifp)
{
}
-/*
- * Loop through each bridge interface and process their input queues.
- */
void
bridgeintr(void)
{
- int i, s;
struct bridge_softc *sc;
- struct ifnet *bifp, *src_if, *dst_if;
- struct bridge_iflist *ifl;
- struct ether_addr *dst, *src;
- struct ether_header *eh;
struct mbuf *m;
+ int i, s;
for (i = 0; i < NBRIDGE; i++) {
sc = &bridgectl[i];
- bifp = &sc->sc_if;
for (;;) {
s = splimp();
- IF_DEQUEUE(&bifp->if_snd, m);
+ IF_DEQUEUE(&sc->sc_if.if_snd, m);
splx(s);
if (m == NULL)
break;
+ bridgeintr_frame(sc, m);
+ }
+ }
+}
- src_if = m->m_pkthdr.rcvif;
- if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
- m_freem(m);
- continue;
- }
+/*
+ * Loop through each bridge interface and process their input queues.
+ */
+void
+bridgeintr_frame(sc, m)
+ struct bridge_softc *sc;
+ struct mbuf *m;
+{
+ int s;
+ struct ifnet *src_if, *dst_if;
+ struct bridge_iflist *ifl;
+ struct ether_addr *dst, *src;
+ struct ether_header *eh;
+
+ if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
+ m_freem(m);
+ return;
+ }
+
+ src_if = m->m_pkthdr.rcvif;
#if NBPFILTER > 0
- if (sc->sc_if.if_bpf)
- bpf_mtap(sc->sc_if.if_bpf, m);
+ if (sc->sc_if.if_bpf)
+ bpf_mtap(sc->sc_if.if_bpf, m);
#endif
- sc->sc_if.if_lastchange = time;
- sc->sc_if.if_ipackets++;
- sc->sc_if.if_ibytes += m->m_pkthdr.len;
+ sc->sc_if.if_lastchange = time;
+ sc->sc_if.if_ipackets++;
+ sc->sc_if.if_ibytes += m->m_pkthdr.len;
- ifl = LIST_FIRST(&sc->sc_iflist);
- while (ifl != NULL && ifl->ifp != src_if) {
- ifl = LIST_NEXT(ifl, next);
- }
- if (ifl == NULL) {
- m_freem(m);
- continue;
- }
+ ifl = LIST_FIRST(&sc->sc_iflist);
+ while (ifl != NULL && ifl->ifp != src_if) {
+ ifl = LIST_NEXT(ifl, next);
+ }
+ if (ifl == NULL) {
+ m_freem(m);
+ return;
+ }
- if (m->m_len < sizeof(*eh)) {
- m = m_pullup(m, sizeof(*eh));
- if (m == NULL)
- continue;
- }
- eh = mtod(m, struct ether_header *);
- dst = (struct ether_addr *)&eh->ether_dhost[0];
- src = (struct ether_addr *)&eh->ether_shost[0];
-
- /*
- * If interface is learning, and if source address
- * is not broadcast or multicast, record it's address.
- */
- if ((ifl->bif_flags & IFBIF_LEARNING) &&
- (eh->ether_shost[0] & 1) == 0 &&
- !(eh->ether_shost[0] == 0 &&
- eh->ether_shost[1] == 0 &&
- eh->ether_shost[2] == 0 &&
- eh->ether_shost[3] == 0 &&
- eh->ether_shost[4] == 0 &&
- eh->ether_shost[5] == 0))
- bridge_rtupdate(sc, src, src_if, 0, IFBAF_DYNAMIC);
-
- /*
- * If packet is unicast, destined for someone on "this"
- * side of the bridge, drop it.
- */
- dst_if = bridge_rtlookup(sc, dst);
- if ((m->m_flags & (M_BCAST | M_MCAST)) == 0 &&
- dst_if == src_if) {
- m_freem(m);
- continue;
- }
+ if (m->m_len < sizeof(*eh)) {
+ m = m_pullup(m, sizeof(*eh));
+ if (m == NULL)
+ return;
+ }
+ eh = mtod(m, struct ether_header *);
+ dst = (struct ether_addr *)&eh->ether_dhost[0];
+ src = (struct ether_addr *)&eh->ether_shost[0];
- /*
- * Multicast packets get handled a little differently:
- * If interface is:
- * -link0,-link1 (default) Forward all multicast
- * as broadcast.
- * -link0,link1 Drop non-IP multicast, forward
- * as broadcast IP multicast.
- * link0,-link1 Drop IP multicast, forward as
- * broadcast non-IP multicast.
- * link0,link1 Drop all multicast.
- */
- if (m->m_flags & M_MCAST) {
- if ((sc->sc_if.if_flags &
- (IFF_LINK0 | IFF_LINK1)) ==
- (IFF_LINK0 | IFF_LINK1)) {
- m_freem(m);
- continue;
- }
- if (sc->sc_if.if_flags & IFF_LINK0 &&
- ETHERADDR_IS_IP_MCAST(dst)) {
- m_freem(m);
- continue;
- }
- if (sc->sc_if.if_flags & IFF_LINK1 &&
- !ETHERADDR_IS_IP_MCAST(dst)) {
- m_freem(m);
- continue;
- }
- }
+ /*
+ * If interface is learning, and if source address
+ * is not broadcast or multicast, record it's address.
+ */
+ if ((ifl->bif_flags & IFBIF_LEARNING) &&
+ (eh->ether_shost[0] & 1) == 0 &&
+ !(eh->ether_shost[0] == 0 &&
+ eh->ether_shost[1] == 0 &&
+ eh->ether_shost[2] == 0 &&
+ eh->ether_shost[3] == 0 &&
+ eh->ether_shost[4] == 0 &&
+ eh->ether_shost[5] == 0))
+ bridge_rtupdate(sc, src, src_if, 0, IFBAF_DYNAMIC);
- if (ifl->bif_flags & IFBIF_BLOCKNONIP) {
- m = bridge_blocknonip(m);
- if (m == NULL)
- continue;
- eh = mtod(m, struct ether_header *);
- dst = (struct ether_addr *)&eh->ether_dhost[0];
- src = (struct ether_addr *)&eh->ether_shost[0];
- }
+ /*
+ * If packet is unicast, destined for someone on "this"
+ * side of the bridge, drop it.
+ */
+ if (m->m_flags & (M_BCAST | M_MCAST)) {
+ dst_if = bridge_rtlookup(sc, dst);
+ if (dst_if == src_if) {
+ m_freem(m);
+ return;
+ }
+ } else
+ dst_if = NULL;
+
+ /*
+ * Multicast packets get handled a little differently:
+ * If interface is:
+ * -link0,-link1 (default) Forward all multicast
+ * as broadcast.
+ * -link0,link1 Drop non-IP multicast, forward
+ * as broadcast IP multicast.
+ * link0,-link1 Drop IP multicast, forward as
+ * broadcast non-IP multicast.
+ * link0,link1 Drop all multicast.
+ */
+ if (m->m_flags & M_MCAST) {
+ if ((sc->sc_if.if_flags &
+ (IFF_LINK0 | IFF_LINK1)) ==
+ (IFF_LINK0 | IFF_LINK1)) {
+ m_freem(m);
+ return;
+ }
+ if (sc->sc_if.if_flags & IFF_LINK0 &&
+ ETHERADDR_IS_IP_MCAST(dst)) {
+ m_freem(m);
+ return;
+ }
+ if (sc->sc_if.if_flags & IFF_LINK1 &&
+ !ETHERADDR_IS_IP_MCAST(dst)) {
+ m_freem(m);
+ return;
+ }
+ }
+
+ if (ifl->bif_flags & IFBIF_BLOCKNONIP) {
+ m = bridge_blocknonip(m);
+ if (m == NULL)
+ return;
+ eh = mtod(m, struct ether_header *);
+ dst = (struct ether_addr *)&eh->ether_dhost[0];
+ src = (struct ether_addr *)&eh->ether_shost[0];
+ }
+
+ if (SIMPLEQ_FIRST(&ifl->bif_brlin) &&
+ bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlin), eh) ==
+ BRL_ACTION_BLOCK) {
+ m_freem(m);
+ return;
+ }
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
- if (bridge_filter(sc, src_if, eh, &m) ==
- BRIDGE_FILTER_DROP) {
- if (m != NULL)
- m_freem(m);
- continue;
- }
+ if (bridge_filter(sc, src_if, eh, &m) == BRIDGE_FILTER_DROP) {
+ if (m != NULL)
+ m_freem(m);
+ return;
+ }
#endif
- /*
- * If the packet is a multicast or broadcast, then
- * forward it to all interfaces.
- */
- if (m->m_flags & (M_BCAST | M_MCAST)) {
- bifp->if_imcasts++;
- bridge_broadcast(sc, src_if, eh, m);
- continue;
- }
-
- if (dst_if != NULL) {
- if ((dst_if->if_flags & IFF_RUNNING) == 0) {
- m_freem(m);
- continue;
- }
- s = splimp();
- if (IF_QFULL(&dst_if->if_snd)) {
- sc->sc_if.if_oerrors++;
- m_freem(m);
- splx(s);
- continue;
- }
- sc->sc_if.if_opackets++;
- sc->sc_if.if_obytes += m->m_pkthdr.len;
- IF_ENQUEUE(&dst_if->if_snd, m);
- if ((dst_if->if_flags & IFF_OACTIVE) == 0)
- (*dst_if->if_start)(dst_if);
- splx(s);
- continue;
- }
+ /*
+ * If the packet is a multicast or broadcast OR if we don't
+ * know any better, forward it to all interfaces.
+ */
+ if ((m->m_flags & (M_BCAST | M_MCAST)) || dst_if == NULL) {
+ sc->sc_if.if_imcasts++;
+ bridge_broadcast(sc, src_if, eh, m);
+ return;
+ }
- bridge_broadcast(sc, src_if, eh, m);
- dst_if = NULL;
- }
+ /*
+ * At this point, we're dealing with a unicast frame going to a
+ * different interface
+ */
+ if ((dst_if->if_flags & IFF_RUNNING) == 0) {
+ m_freem(m);
+ return;
+ }
+ ifl = LIST_FIRST(&sc->sc_iflist);
+ while (ifl != NULL && ifl->ifp != dst_if)
+ ifl = LIST_NEXT(ifl, next);
+ if (SIMPLEQ_FIRST(&ifl->bif_brlout) &&
+ bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlout), eh) ==
+ BRL_ACTION_BLOCK) {
+ m_freem(m);
+ return;
}
+ s = splimp();
+ if (IF_QFULL(&dst_if->if_snd)) {
+ sc->sc_if.if_oerrors++;
+ m_freem(m);
+ splx(s);
+ return;
+ }
+ sc->sc_if.if_opackets++;
+ sc->sc_if.if_obytes += m->m_pkthdr.len;
+ IF_ENQUEUE(&dst_if->if_snd, m);
+ if ((dst_if->if_flags & IFF_OACTIVE) == 0)
+ (*dst_if->if_start)(dst_if);
+ splx(s);
}
/*
@@ -965,6 +1166,12 @@ bridge_broadcast(sc, ifp, eh, m)
continue;
}
+ if (SIMPLEQ_FIRST(&p->bif_brlout) &&
+ bridge_filterrule(SIMPLEQ_FIRST(&p->bif_brlout), eh) ==
+ BRL_ACTION_BLOCK) {
+ continue;
+ }
+
/* If last one, reuse the passed-in mbuf */
if (LIST_NEXT(p, next) == NULL) {
mc = m;
@@ -1491,6 +1698,89 @@ notip:
return (NULL);
}
+u_int8_t
+bridge_filterrule(n, eh)
+ struct brl_node *n;
+ struct ether_header *eh;
+{
+ u_int8_t flags;
+
+ for (; n != NULL; n = SIMPLEQ_NEXT(n, brl_next)) {
+ flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID);
+ if (flags == 0)
+ return (n->brl_action);
+ if (flags == (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID)) {
+ if (bcmp(eh->ether_shost, &n->brl_src, ETHER_ADDR_LEN))
+ continue;
+ if (bcmp(eh->ether_dhost, &n->brl_src, ETHER_ADDR_LEN))
+ continue;
+ return (n->brl_action);
+ }
+ if (flags == BRL_FLAG_SRCVALID) {
+ if (bcmp(eh->ether_shost, &n->brl_src, ETHER_ADDR_LEN))
+ continue;
+ return (n->brl_action);
+ }
+ if (flags == BRL_FLAG_DSTVALID) {
+ if (bcmp(eh->ether_dhost, &n->brl_dst, ETHER_ADDR_LEN))
+ continue;
+ return (n->brl_action);
+ }
+ }
+ return (BRL_ACTION_PASS);
+}
+
+int
+bridge_addrule(bif, req, out)
+ struct bridge_iflist *bif;
+ struct ifbrlreq *req;
+ int out;
+{
+ struct brl_node *n;
+
+ n = (struct brl_node *)malloc(sizeof(struct brl_node), 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;
+ 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);
+}
+
+int
+bridge_flushrule(bif)
+ struct bridge_iflist *bif;
+{
+ struct brl_node *p, *q;
+
+ p = SIMPLEQ_FIRST(&bif->bif_brlin);
+ while (p != NULL) {
+ q = SIMPLEQ_NEXT(p, brl_next);
+ SIMPLEQ_REMOVE_HEAD(&bif->bif_brlin, p, brl_next);
+ free(p, M_DEVBUF);
+ p = q;
+ }
+ p = SIMPLEQ_FIRST(&bif->bif_brlout);
+ while (p != NULL) {
+ q = SIMPLEQ_NEXT(p, brl_next);
+ SIMPLEQ_REMOVE_HEAD(&bif->bif_brlout, p, brl_next);
+ free(p, M_DEVBUF);
+ p = q;
+ }
+ return (0);
+}
+
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
/*
diff --git a/sys/net/if_bridge.h b/sys/net/if_bridge.h
index fe6aa8e8fa1..209c6113752 100644
--- a/sys/net/if_bridge.h
+++ b/sys/net/if_bridge.h
@@ -1,7 +1,7 @@
-/* $OpenBSD: if_bridge.h,v 1.11 2000/01/10 22:18:29 angelos Exp $ */
+/* $OpenBSD: if_bridge.h,v 1.12 2000/01/25 22:06:27 jason Exp $ */
/*
- * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
+ * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,7 @@ struct ifbreq {
/* SIOCBRDGIFFLGS, SIOCBRDGIFFLGS */
#define IFBIF_LEARNING 0x1 /* ifs can learn */
#define IFBIF_DISCOVER 0x2 /* ifs sends packets w/unknown dest */
-#define IFBIF_BLOCKNONIP 0x04 /* ifs does not allow non-IP/ARP traffic in/out */
+#define IFBIF_BLOCKNONIP 0x04 /* ifs blocks non-IP/ARP traffic in/out */
/* SIOCBRDGFLUSH */
#define IFBF_FLUSHDYN 0x0 /* flush dynamic addresses only */
#define IFBF_FLUSHALL 0x1 /* flush all addresses from cache */
@@ -103,6 +103,35 @@ struct ifbcachetoreq {
u_int32_t ifbct_time; /* cache time (sec) */
};
+/*
+ * Bridge mac rules
+ */
+struct ifbrlreq {
+ char ifbr_name[IFNAMSIZ]; /* bridge ifs name */
+ char ifbr_ifsname[IFNAMSIZ]; /* member ifs name */
+ u_int8_t ifbr_action; /* disposition */
+ u_int8_t ifbr_flags; /* flags */
+ struct ether_addr ifbr_src; /* source mac */
+ struct ether_addr ifbr_dst; /* destination mac */
+};
+#define BRL_ACTION_BLOCK 0x01 /* block frame */
+#define BRL_ACTION_PASS 0x02 /* pass frame */
+#define BRL_FLAG_IN 0x08 /* input rule */
+#define BRL_FLAG_OUT 0x04 /* output rule */
+#define BRL_FLAG_SRCVALID 0x02 /* src valid */
+#define BRL_FLAG_DSTVALID 0x01 /* dst valid */
+
+struct ifbrlconf {
+ char ifbrl_name[IFNAMSIZ]; /* bridge ifs name */
+ char ifbrl_ifsname[IFNAMSIZ];/* member ifs name */
+ u_int32_t ifbrl_len; /* buffer size */
+ union {
+ caddr_t ifbrlu_buf;
+ struct ifbrlreq *ifbrlu_req;
+ } ifbrl_ifbrlu;
+#define ifbrl_buf ifbrl_ifbrlu.ifbrlu_buf
+#define ifbrl_req ifbrl_ifbrlu.ifbrlu_req
+};
#ifdef _KERNEL
void bridge_ifdetach __P((struct ifnet *));
diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h
index 420d88fac9d..d4a3721aa76 100644
--- a/sys/sys/sockio.h
+++ b/sys/sys/sockio.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sockio.h,v 1.14 2000/01/08 05:40:39 angelos Exp $ */
+/* $OpenBSD: sockio.h,v 1.15 2000/01/25 22:06:28 jason Exp $ */
/* $NetBSD: sockio.h,v 1.5 1995/08/23 00:40:47 thorpej Exp $ */
/*-
@@ -112,6 +112,10 @@
#define SIOCSENCSRCSA _IOW('i', 75, struct ifsa) /* set enc sa */
#define SIOCSENCCLEARSA _IOW('i', 76, struct ifsa) /* set enc sa */
+#define SIOCBRDGARL _IOWR('i', 77, struct ifbrlreq) /* add bridge rule */
+#define SIOCBRDGFRL _IOWR('i', 78, struct ifbrlreq) /* flush brdg rules */
+#define SIOCBRDGGRL _IOWR('i', 79, struct ifbrlconf)/* get bridge rules */
+
#define GRESADDRS _IOW('i', 101, struct ifreq)
#define GRESADDRD _IOW('i', 102, struct ifreq)
#define GREGADDRS _IOWR('i', 103, struct ifreq)