Add ipf packet-filtering option to if_bridge. The option is controlled at

compile-time by BRIDGE_IPF, and at runtime by brconfig with the {ipf,-ipf}
option on a per-bridge basis.

As a side-effect, add PFIL_HOOKS processing to if_bridge.
This commit is contained in:
perseant 2003-02-15 00:46:30 +00:00
parent baa98646a0
commit 6b857c229e
4 changed files with 392 additions and 6 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: brconfig.8,v 1.5 2002/11/06 05:23:57 enami Exp $
.\" $NetBSD: brconfig.8,v 1.6 2003/02/15 00:46:30 perseant Exp $
.\"
.\" Copyright 2001 Wasabi Systems, Inc.
.\" All rights reserved.
@ -142,6 +142,12 @@ For packets without the
attribute, the only packets forwarded on the interface are broadcast
or multicast packets and packets for which the destination address
is known to be on the interface's segment.
.It Cm ipf
Enable IPF packet filtering on the bridge. The current implementation passes
all ARP and RARP packets through the bridge while filtering IP and ICMP
packets through IPF.
.It Cm -ipf
Disable IPF packet filtering on the bridge (the default).
.It Cm learn Ar interface
Mark an interface as a
.Dq learning
@ -230,7 +236,8 @@ create
.Sh SEE ALSO
.Xr bridge 4 ,
.Xr ifconfig.if 5 ,
.Xr ifconfig 8
.Xr ifconfig 8 ,
.Xr ipf 8
.Sh HISTORY
The
.Nm

View File

@ -1,4 +1,4 @@
/* $NetBSD: brconfig.c,v 1.2 2002/11/06 05:26:54 enami Exp $ */
/* $NetBSD: brconfig.c,v 1.3 2003/02/15 00:46:31 perseant Exp $ */
/*
* Copyright 2001 Wasabi Systems, Inc.
@ -87,6 +87,7 @@ void cmd_priority(const struct command *, int, const char *, char **);
void cmd_ifpriority(const struct command *, int, const char *, char **);
void cmd_timeout(const struct command *, int, const char *, char **);
void cmd_stp(const struct command *, int, const char *, char **);
void cmd_ipf(const struct command *, int, const char *, char **);
const struct command command_table[] = {
{ "add", 1, 0, cmd_add },
@ -119,6 +120,9 @@ const struct command command_table[] = {
{ "stp", 1, 0, cmd_stp },
{ "-stp", 1, CMD_INVERT, cmd_stp },
{ "ipf", 0, 0, cmd_ipf },
{ "-ipf", 0, CMD_INVERT, cmd_ipf },
{ NULL, 0, 0, NULL },
};
@ -248,6 +252,7 @@ usage(void)
"<bridge> deladdr <address>",
"<bridge> flush",
"<bridge> flushall",
"<bridge> ipf|-ipf",
"<bridge> discover|-discover <interface>",
"<bridge> learn|-learn <interface>",
"<bridge> stp|-stp <interface>",
@ -788,3 +793,18 @@ cmd_timeout(const struct command *cmd, int sock, const char *bridge,
if (do_cmd(sock, bridge, BRDGSTO, &param, sizeof(param), 1) < 0)
err(1, "%s %s", cmd->cmd_keyword, argv[0]);
}
void
cmd_ipf(const struct command *cmd, int sock, const char *bridge,
char **argv)
{
struct ifbrparam param;
if (do_cmd(sock, bridge, BRDGGFILT, &param, sizeof(param), 0) < 0)
err(1, "%s", cmd->cmd_keyword);
param.ifbrp_filter &= ~IFBF_FILT_USEIPF;
param.ifbrp_filter |= (cmd->cmd_flags & CMD_INVERT) ? 0 : IFBF_FILT_USEIPF;
if (do_cmd(sock, bridge, BRDGSFILT, &param, sizeof(param), 1) < 0)
err(1, "%s %x", cmd->cmd_keyword, param.ifbrp_filter);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_bridge.c,v 1.8 2002/08/24 19:00:31 martin Exp $ */
/* $NetBSD: if_bridge.c,v 1.9 2003/02/15 00:46:30 perseant Exp $ */
/*
* Copyright 2001 Wasabi Systems, Inc.
@ -82,7 +82,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.8 2002/08/24 19:00:31 martin Exp $");
__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.9 2003/02/15 00:46:30 perseant Exp $");
#include "bpfilter.h"
#include "rnd.h"
@ -112,6 +112,17 @@ __KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.8 2002/08/24 19:00:31 martin Exp $")
#include <net/if_ether.h>
#include <net/if_bridgevar.h>
#ifdef BRIDGE_IPF /* Used for bridge_ip[6]_checkbasic */
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip6.h>
#include <netinet6/in6_var.h>
#include <netinet6/ip6_var.h>
#endif /* BRIDGE_IPF */
/*
* Size of the route hash table. Must be a power of two.
*/
@ -218,6 +229,15 @@ int bridge_ioctl_sfd(struct bridge_softc *, void *);
int bridge_ioctl_gma(struct bridge_softc *, void *);
int bridge_ioctl_sma(struct bridge_softc *, void *);
int bridge_ioctl_sifprio(struct bridge_softc *, void *);
#ifdef BRIDGE_IPF
int bridge_ioctl_gfilt(struct bridge_softc *, void *);
int bridge_ioctl_sfilt(struct bridge_softc *, void *);
static int bridge_ipf(void *, struct mbuf **, struct ifnet *, int);
static int bridge_ip_checkbasic(struct mbuf **mp);
# ifdef INET6
static int bridge_ip6_checkbasic(struct mbuf **mp);
# endif /* INET6 */
#endif /* BRIDGE_IPF */
struct bridge_control {
int (*bc_func)(struct bridge_softc *, void *);
@ -286,6 +306,12 @@ const struct bridge_control bridge_control_table[] = {
{ bridge_ioctl_sifprio, sizeof(struct ifbreq),
BC_F_COPYIN|BC_F_SUSER },
#ifdef BRIDGE_IPF
{ bridge_ioctl_gfilt, sizeof(struct ifbrparam),
BC_F_COPYOUT },
{ bridge_ioctl_sfilt, sizeof(struct ifbrparam),
BC_F_COPYIN|BC_F_SUSER },
#endif /* BRIDGE_IPF */
};
const int bridge_control_table_size =
sizeof(bridge_control_table) / sizeof(bridge_control_table[0]);
@ -334,6 +360,7 @@ bridge_clone_create(struct if_clone *ifc, int unit)
sc->sc_bridge_forward_delay = BSTP_DEFAULT_FORWARD_DELAY;
sc->sc_bridge_priority = BSTP_DEFAULT_BRIDGE_PRIORITY;
sc->sc_hold_time = BSTP_DEFAULT_HOLD_TIME;
sc->sc_filter_flags = 0;
/* Initialize our routing table. */
bridge_rtable_init(sc);
@ -953,6 +980,44 @@ bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg)
return (0);
}
#ifdef BRIDGE_IPF
int
bridge_ioctl_gfilt(struct bridge_softc *sc, void *arg)
{
struct ifbrparam *param = arg;
param->ifbrp_filter = sc->sc_filter_flags;
return (0);
}
int
bridge_ioctl_sfilt(struct bridge_softc *sc, void *arg)
{
struct ifbrparam *param = arg;
uint32_t nflags, oflags;
if (param->ifbrp_filter & ~IFBF_FILT_MASK)
return (EINVAL);
nflags = param->ifbrp_filter;
oflags = sc->sc_filter_flags;
if ((nflags & IFBF_FILT_USEIPF) && !(oflags & IFBF_FILT_USEIPF)) {
pfil_add_hook((void *)bridge_ipf, NULL, PFIL_IN|PFIL_OUT,
&sc->sc_if.if_pfil);
}
if (!(nflags & IFBF_FILT_USEIPF) && (oflags & IFBF_FILT_USEIPF)) {
pfil_remove_hook((void *)bridge_ipf, NULL, PFIL_IN|PFIL_OUT,
&sc->sc_if.if_pfil);
}
sc->sc_filter_flags = nflags;
return (0);
}
#endif /* BRIDGE_IPF */
/*
* bridge_ifdetach:
*
@ -1028,6 +1093,15 @@ bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m)
int len, error;
short mflags;
#ifdef PFIL_HOOKS
if (pfil_run_hooks(&sc->sc_if.if_pfil, &m, dst_ifp, PFIL_OUT) != 0) {
m_freem(m);
return;
}
if (m == NULL)
return;
#endif /* PFIL_HOOKS */
#ifdef ALTQ
/*
* If ALTQ is enabled on the member interface, do
@ -1271,6 +1345,15 @@ bridge_forward(struct bridge_softc *sc, struct mbuf *m)
dst_if = NULL;
}
#ifdef PFIL_HOOKS
if (pfil_run_hooks(&sc->sc_if.if_pfil, &m, m->m_pkthdr.rcvif, PFIL_IN) != 0) {
m_freem(m);
return;
}
if (m == NULL)
return;
#endif /* PFIL_HOOKS */
if (dst_if == NULL) {
bridge_broadcast(sc, src_if, m);
return;
@ -1801,3 +1884,271 @@ bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt)
sc->sc_brtcnt--;
pool_put(&bridge_rtnode_pool, brt);
}
#ifdef BRIDGE_IPF
extern struct pfil_head inet_pfil_hook; /* XXX */
extern struct pfil_head inet6_pfil_hook; /* XXX */
/*
* Send bridge packets through IPF if they are one of the types IPF can deal
* with, or if they are ARP or REVARP. (IPF will pass ARP and REVARP without
* question.)
*/
static int bridge_ipf(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
{
int snap, error;
struct ether_header *eh;
struct mbuf *m1, *m2;
u_int16_t ether_type;
snap = 0;
error = -1; /* Default error if not error == 0 */
eh = mtod(*mp, struct ether_header *);
ether_type = ntohs(eh->ether_type);
/*
* Check for SNAP/LLC.
*/
if (ether_type < ETHERMTU) {
struct llc *llc = (struct llc *)(eh + 1);
if ((*mp)->m_len >= ETHER_HDR_LEN + 8 &&
llc->llc_dsap == LLC_SNAP_LSAP &&
llc->llc_ssap == LLC_SNAP_LSAP &&
llc->llc_control == LLC_UI) {
ether_type = htons(llc->llc_un.type_snap.ether_type);
snap = 1;
}
}
/*
* If we're trying to filter bridge traffic, don't look at anything
* other than IP and ARP traffic. If the filter doesn't understand
* IPv6, don't allow IPv6 through the bridge either. This is lame
* since if we really wanted, say, an AppleTalk filter, we are hosed,
* but of course we don't have an AppleTalk filter to begin with.
* (Note that since IPF doesn't understand ARP it will pass *ALL*
* ARP traffic.)
*/
switch (ether_type) {
case ETHERTYPE_ARP:
case ETHERTYPE_REVARP:
return 0; /* Automatically pass */
case ETHERTYPE_IP:
# ifdef INET6
case ETHERTYPE_IPV6:
# endif /* INET6 */
break;
default:
goto bad;
}
/* Strip off the Ethernet header---but keep a copy. */
if ((m1 = m_split(*mp, sizeof(struct ether_header), M_NOWAIT)) == NULL)
goto bad;
/* Strip off snap header, if present */
if (snap) {
if ((m2 = m_split(m1, sizeof(struct llc), M_NOWAIT)) == NULL)
goto bad2;
} else
m2 = m1;
/*
* Check basic packet sanity, if the packet is outbound, and
* run IPF filter.
*/
if (ether_type == ETHERTYPE_IP &&
(dir == PFIL_OUT || bridge_ip_checkbasic(&m2) == 0)) {
error = pfil_run_hooks(&inet_pfil_hook, &m2, ifp, dir);
if (error) goto bad2;
}
# ifdef INET6
if (ether_type == ETHERTYPE_IPV6 &&
(dir == PFIL_OUT || bridge_ip6_checkbasic(&m2) == 0)) {
error = pfil_run_hooks(&inet6_pfil_hook, &m2, ifp, dir);
if (error) goto bad2;
}
# endif
if (m2 == NULL) goto bad2;
/*
* Finally, put everything back the way it was and return
*/
if (snap)
m_cat(m1, m2);
else
m1 = m2;
m_cat(*mp, m1);
return 0;
bad2:
if (snap)
m_freem(m1);
m_freem(m2);
bad:
m_freem(*mp);
*mp = NULL;
return error;
}
/*
* Perform basic checks on header size since
* IPF assumes ip_input has already processed
* it for it. Cut-and-pasted from ip_input.c.
* Given how simple the IPv6 version is,
* does the IPv4 version really need to be
* this complicated?
*
* XXX Should we update ipstat here, or not?
* XXX Right now we update ipstat but not
* XXX csum_counter.
*/
static int
bridge_ip_checkbasic(struct mbuf **mp)
{
struct mbuf *m = *mp;
struct ip *ip;
int len, hlen;
if (*mp == NULL)
return -1;
if (IP_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) {
if ((m = m_copyup(m, sizeof(struct ip),
(max_linkhdr + 3) & ~3)) == NULL) {
/* XXXJRT new stat, please */
ipstat.ips_toosmall++;
goto bad;
}
} else if (__predict_false(m->m_len < sizeof (struct ip))) {
if ((m = m_pullup(m, sizeof (struct ip))) == NULL) {
ipstat.ips_toosmall++;
goto bad;
}
}
ip = mtod(m, struct ip *);
if (ip == NULL) goto bad;
if (ip->ip_v != IPVERSION) {
ipstat.ips_badvers++;
goto bad;
}
hlen = ip->ip_hl << 2;
if (hlen < sizeof(struct ip)) { /* minimum header length */
ipstat.ips_badhlen++;
goto bad;
}
if (hlen > m->m_len) {
if ((m = m_pullup(m, hlen)) == 0) {
ipstat.ips_badhlen++;
goto bad;
}
ip = mtod(m, struct ip *);
if (ip == NULL) goto bad;
}
switch (m->m_pkthdr.csum_flags &
((m->m_pkthdr.rcvif->if_csum_flags_rx & M_CSUM_IPv4) |
M_CSUM_IPv4_BAD)) {
case M_CSUM_IPv4|M_CSUM_IPv4_BAD:
/* INET_CSUM_COUNTER_INCR(&ip_hwcsum_bad); */
goto bad;
case M_CSUM_IPv4:
/* Checksum was okay. */
/* INET_CSUM_COUNTER_INCR(&ip_hwcsum_ok); */
break;
default:
/* Must compute it ourselves. */
/* INET_CSUM_COUNTER_INCR(&ip_swcsum); */
if (in_cksum(m, hlen) != 0)
goto bad;
break;
}
/* Retrieve the packet length. */
len = ntohs(ip->ip_len);
/*
* Check for additional length bogosity
*/
if (len < hlen) {
ipstat.ips_badlen++;
goto bad;
}
/*
* Check that the amount of data in the buffers
* is as at least much as the IP header would have us expect.
* Drop packet if shorter than we expect.
*/
if (m->m_pkthdr.len < len) {
ipstat.ips_tooshort++;
goto bad;
}
/* Checks out, proceed */
*mp = m;
return 0;
bad:
*mp = m;
return -1;
}
# ifdef INET6
/*
* Same as above, but for IPv6.
* Cut-and-pasted from ip6_input.c.
* XXX Should we update ip6stat, or not?
*/
static int
bridge_ip6_checkbasic(struct mbuf **mp)
{
struct mbuf *m = *mp;
struct ip6 *ip6;
/*
* If the IPv6 header is not aligned, slurp it up into a new
* mbuf with space for link headers, in the event we forward
* it. Otherwise, if it is aligned, make sure the entire base
* IPv6 header is in the first mbuf of the chain.
*/
if (IP6_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) {
struct ifnet *inifp = m->m_pkthdr.rcvif;
if ((m = m_copyup(m, sizeof(struct ip6_hdr),
(max_linkhdr + 3) & ~3)) == NULL) {
/* XXXJRT new stat, please */
ip6stat.ip6s_toosmall++;
in6_ifstat_inc(inifp, ifs6_in_hdrerr);
goto bad;
}
} else if (__predict_false(m->m_len < sizeof(struct ip6_hdr))) {
struct ifnet *inifp = m->m_pkthdr.rcvif;
if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
ip6stat.ip6s_toosmall++;
in6_ifstat_inc(inifp, ifs6_in_hdrerr);
goto bad;
}
}
ip6 = mtod(m, struct ip6_hdr *);
if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
ip6stat.ip6s_badvers++;
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
goto bad;
}
/* Checks out, proceed */
*mp = m;
return 0;
bad:
*mp = m;
return -1;
}
# endif /* INET6 */
#endif /* BRIDGE_IPF */

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_bridgevar.h,v 1.1 2001/08/17 21:37:28 thorpej Exp $ */
/* $NetBSD: if_bridgevar.h,v 1.2 2003/02/15 00:46:30 perseant Exp $ */
/*
* Copyright 2001 Wasabi Systems, Inc.
@ -102,6 +102,8 @@
#define BRDGGMA 19 /* get max age (ifbrparam) */
#define BRDGSMA 20 /* set max age (ifbrparam) */
#define BRDGSIFPRIO 21 /* set if priority (ifbreq) */
#define BRDGGFILT 22 /* get filter flags (ifbrparam) */
#define BRDGSFILT 23 /* set filter flags (ifbrparam) */
/*
* Generic bridge control request.
@ -125,6 +127,10 @@ struct ifbreq {
#define IFBF_FLUSHDYN 0x00 /* flush learned addresses only */
#define IFBF_FLUSHALL 0x01 /* flush all addresses */
/* BRDGSFILT */
#define IFBF_FILT_USEIPF 0x00000001 /* enable ipf on bridge */
#define IFBF_FILT_MASK 0x00000001 /* mask of valid values */
/* STP port states */
#define BSTP_IFSTATE_DISABLED 0
#define BSTP_IFSTATE_LISTENING 1
@ -190,6 +196,7 @@ struct ifbrparam {
#define ifbrp_hellotime ifbrp_ifbrpu.ifbrpu_int8 /* hello time (sec) */
#define ifbrp_fwddelay ifbrp_ifbrpu.ifbrpu_int8 /* fwd time (sec) */
#define ifbrp_maxage ifbrp_ifbrpu.ifbrpu_int8 /* max age (sec) */
#define ifbrp_filter ifbrp_ifbrpu.ifbrpu_int32 /* filtering flags */
#ifdef _KERNEL
/*
@ -287,6 +294,7 @@ struct bridge_softc {
LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */
LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */
uint32_t sc_rthash_key; /* key for hash */
uint32_t sc_filter_flags; /* ipf and flags */
};
extern const uint8_t bstp_etheraddr[];