//==========================================================================
|
//==========================================================================
|
//
|
//
|
// sys/net/if_bridge.c
|
// sys/net/if_bridge.c
|
//
|
//
|
//
|
//
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
//####BSDCOPYRIGHTBEGIN####
|
//####BSDCOPYRIGHTBEGIN####
|
//
|
//
|
// -------------------------------------------
|
// -------------------------------------------
|
//
|
//
|
// Portions of this software may have been derived from OpenBSD or other sources,
|
// Portions of this software may have been derived from OpenBSD or other sources,
|
// and are covered by the appropriate copyright disclaimers included herein.
|
// and are covered by the appropriate copyright disclaimers included herein.
|
//
|
//
|
// -------------------------------------------
|
// -------------------------------------------
|
//
|
//
|
//####BSDCOPYRIGHTEND####
|
//####BSDCOPYRIGHTEND####
|
//==========================================================================
|
//==========================================================================
|
//#####DESCRIPTIONBEGIN####
|
//#####DESCRIPTIONBEGIN####
|
//
|
//
|
// Author(s): Jason L. Wright (jason@thought.net)
|
// Author(s): Jason L. Wright (jason@thought.net)
|
// Contributors: andrew.lunn@ascom.ch (Andrew Lunn), hmt
|
// Contributors: andrew.lunn@ascom.ch (Andrew Lunn), hmt
|
// Date: 2000-07-18
|
// Date: 2000-07-18
|
// Purpose: Ethernet bridge
|
// Purpose: Ethernet bridge
|
// Description:
|
// Description:
|
//
|
//
|
//
|
//
|
//####DESCRIPTIONEND####
|
//####DESCRIPTIONEND####
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
/* $OpenBSD: if_bridge.c,v 1.33 2000/06/20 05:50:16 jason Exp $ */
|
/* $OpenBSD: if_bridge.c,v 1.33 2000/06/20 05:50:16 jason Exp $ */
|
|
|
/*
|
/*
|
* Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
|
* Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
|
* All rights reserved.
|
* All rights reserved.
|
*
|
*
|
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
* are met:
|
* are met:
|
* 1. Redistributions of source code must retain the above copyright
|
* 1. Redistributions of source code must retain the above copyright
|
* notice, this list of conditions and the following disclaimer.
|
* notice, this list of conditions and the following disclaimer.
|
* 2. Redistributions in binary form must reproduce the above copyright
|
* 2. Redistributions in binary form must reproduce the above copyright
|
* notice, this list of conditions and the following disclaimer in the
|
* notice, this list of conditions and the following disclaimer in the
|
* documentation and/or other materials provided with the distribution.
|
* documentation and/or other materials provided with the distribution.
|
* 3. All advertising materials mentioning features or use of this software
|
* 3. All advertising materials mentioning features or use of this software
|
* must display the following acknowledgement:
|
* must display the following acknowledgement:
|
* This product includes software developed by Jason L. Wright
|
* This product includes software developed by Jason L. Wright
|
* 4. The name of the author may not be used to endorse or promote products
|
* 4. The name of the author may not be used to endorse or promote products
|
* derived from this software without specific prior written permission.
|
* derived from this software without specific prior written permission.
|
*
|
*
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
* POSSIBILITY OF SUCH DAMAGE.
|
* POSSIBILITY OF SUCH DAMAGE.
|
*/
|
*/
|
|
|
#ifdef __ECOS
|
#ifdef __ECOS
|
#include <pkgconf/net.h>
|
#include <pkgconf/net.h>
|
#else
|
#else
|
#include "bridge.h"
|
#include "bridge.h"
|
#include "bpfilter.h"
|
#include "bpfilter.h"
|
#include "enc.h"
|
#include "enc.h"
|
#endif
|
#endif
|
|
|
#include <sys/param.h>
|
#include <sys/param.h>
|
#ifndef __ECOS
|
#ifndef __ECOS
|
#include <sys/proc.h>
|
#include <sys/proc.h>
|
#include <sys/systm.h>
|
#include <sys/systm.h>
|
#endif
|
#endif
|
#include <sys/mbuf.h>
|
#include <sys/mbuf.h>
|
#include <sys/socket.h>
|
#include <sys/socket.h>
|
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
#include <sys/errno.h>
|
#include <sys/errno.h>
|
#ifndef __ECOS
|
#ifndef __ECOS
|
#include <sys/device.h>
|
#include <sys/device.h>
|
#endif
|
#endif
|
#include <sys/kernel.h>
|
#include <sys/kernel.h>
|
#include <machine/cpu.h>
|
#include <machine/cpu.h>
|
|
|
#include <net/if.h>
|
#include <net/if.h>
|
#include <net/if_types.h>
|
#include <net/if_types.h>
|
#include <net/if_llc.h>
|
#include <net/if_llc.h>
|
#include <net/route.h>
|
#include <net/route.h>
|
#include <net/netisr.h>
|
#include <net/netisr.h>
|
|
|
#ifdef INET
|
#ifdef INET
|
#include <netinet/in.h>
|
#include <netinet/in.h>
|
#include <netinet/in_systm.h>
|
#include <netinet/in_systm.h>
|
#include <netinet/in_var.h>
|
#include <netinet/in_var.h>
|
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
#include <netinet/if_ether.h>
|
#include <netinet/if_ether.h>
|
#include <netinet/ip_ipsp.h>
|
#include <netinet/ip_ipsp.h>
|
|
|
#ifndef __ECOS
|
#ifndef __ECOS
|
#include <net/if_enc.h>
|
#include <net/if_enc.h>
|
#endif
|
#endif
|
#ifdef IPFILTER
|
#ifdef IPFILTER
|
#include <netinet/ip_fil_compat.h>
|
#include <netinet/ip_fil_compat.h>
|
#include <netinet/ip_fil.h>
|
#include <netinet/ip_fil.h>
|
#endif
|
#endif
|
#endif
|
#endif
|
|
|
#if NBPFILTER > 0
|
#if NBPFILTER > 0
|
#include <net/bpf.h>
|
#include <net/bpf.h>
|
#endif
|
#endif
|
|
|
#include <net/if_bridge.h>
|
#include <net/if_bridge.h>
|
|
|
#ifdef __ECOS
|
#ifdef __ECOS
|
#include <stdio.h> /* for sprintf */
|
#include <stdio.h> /* for sprintf */
|
#endif
|
#endif
|
|
|
#ifndef BRIDGE_RTABLE_SIZE
|
#ifndef BRIDGE_RTABLE_SIZE
|
#define BRIDGE_RTABLE_SIZE 1024
|
#define BRIDGE_RTABLE_SIZE 1024
|
#endif
|
#endif
|
#define BRIDGE_RTABLE_MASK (BRIDGE_RTABLE_SIZE - 1)
|
#define BRIDGE_RTABLE_MASK (BRIDGE_RTABLE_SIZE - 1)
|
|
|
/*
|
/*
|
* Maximum number of addresses to cache
|
* Maximum number of addresses to cache
|
*/
|
*/
|
#ifndef BRIDGE_RTABLE_MAX
|
#ifndef BRIDGE_RTABLE_MAX
|
#define BRIDGE_RTABLE_MAX 100
|
#define BRIDGE_RTABLE_MAX 100
|
#endif
|
#endif
|
|
|
/*
|
/*
|
* Timeout (in seconds) for entries learned dynamically
|
* Timeout (in seconds) for entries learned dynamically
|
*/
|
*/
|
#ifndef BRIDGE_RTABLE_TIMEOUT
|
#ifndef BRIDGE_RTABLE_TIMEOUT
|
#define BRIDGE_RTABLE_TIMEOUT 300
|
#define BRIDGE_RTABLE_TIMEOUT 300
|
#endif
|
#endif
|
|
|
extern int ifqmaxlen;
|
extern int ifqmaxlen;
|
|
|
/*
|
/*
|
* Bridge filtering rules
|
* Bridge filtering rules
|
*/
|
*/
|
struct brl_node {
|
struct brl_node {
|
SIMPLEQ_ENTRY(brl_node) brl_next; /* next rule */
|
SIMPLEQ_ENTRY(brl_node) brl_next; /* next rule */
|
struct ether_addr brl_src; /* source mac address */
|
struct ether_addr brl_src; /* source mac address */
|
struct ether_addr brl_dst; /* destination mac address */
|
struct ether_addr brl_dst; /* destination mac address */
|
u_int8_t brl_action; /* what to do with match */
|
u_int8_t brl_action; /* what to do with match */
|
u_int8_t brl_flags; /* comparision flags */
|
u_int8_t brl_flags; /* comparision flags */
|
};
|
};
|
|
|
/*
|
/*
|
* Bridge interface list
|
* Bridge interface list
|
*/
|
*/
|
struct bridge_iflist {
|
struct bridge_iflist {
|
LIST_ENTRY(bridge_iflist) next; /* next in list */
|
LIST_ENTRY(bridge_iflist) next; /* next in list */
|
SIMPLEQ_HEAD(, brl_node) bif_brlin; /* input rules */
|
SIMPLEQ_HEAD(, brl_node) bif_brlin; /* input rules */
|
SIMPLEQ_HEAD(, brl_node) bif_brlout; /* output rules */
|
SIMPLEQ_HEAD(, brl_node) bif_brlout; /* output rules */
|
struct ifnet *ifp; /* member interface */
|
struct ifnet *ifp; /* member interface */
|
u_int32_t bif_flags; /* member flags */
|
u_int32_t bif_flags; /* member flags */
|
};
|
};
|
|
|
/*
|
/*
|
* Bridge route node
|
* Bridge route node
|
*/
|
*/
|
struct bridge_rtnode {
|
struct bridge_rtnode {
|
LIST_ENTRY(bridge_rtnode) brt_next; /* next in list */
|
LIST_ENTRY(bridge_rtnode) brt_next; /* next in list */
|
struct ifnet *brt_if; /* destination ifs */
|
struct ifnet *brt_if; /* destination ifs */
|
u_int8_t brt_flags; /* address flags */
|
u_int8_t brt_flags; /* address flags */
|
u_int8_t brt_age; /* age counter */
|
u_int8_t brt_age; /* age counter */
|
struct ether_addr brt_addr; /* dst addr */
|
struct ether_addr brt_addr; /* dst addr */
|
};
|
};
|
|
|
/*
|
/*
|
* Software state for each bridge
|
* Software state for each bridge
|
*/
|
*/
|
struct bridge_softc {
|
struct bridge_softc {
|
struct ifnet sc_if; /* the interface */
|
struct ifnet sc_if; /* the interface */
|
u_int32_t sc_brtmax; /* max # addresses */
|
u_int32_t sc_brtmax; /* max # addresses */
|
u_int32_t sc_brtcnt; /* current # addrs */
|
u_int32_t sc_brtcnt; /* current # addrs */
|
u_int32_t sc_brttimeout; /* timeout ticks */
|
u_int32_t sc_brttimeout; /* timeout ticks */
|
LIST_HEAD(, bridge_iflist) sc_iflist; /* interface list */
|
LIST_HEAD(, bridge_iflist) sc_iflist; /* interface list */
|
LIST_HEAD(bridge_rthead, bridge_rtnode) *sc_rts;/* hash table */
|
LIST_HEAD(bridge_rthead, bridge_rtnode) *sc_rts;/* hash table */
|
};
|
};
|
|
|
/* SNAP LLC header */
|
/* SNAP LLC header */
|
struct snap {
|
struct snap {
|
u_int8_t dsap;
|
u_int8_t dsap;
|
u_int8_t ssap;
|
u_int8_t ssap;
|
u_int8_t control;
|
u_int8_t control;
|
u_int8_t org[3];
|
u_int8_t org[3];
|
u_int16_t type;
|
u_int16_t type;
|
};
|
};
|
|
|
struct bridge_softc bridgectl[CYGNUM_NET_BRIDGES];
|
struct bridge_softc bridgectl[CYGNUM_NET_BRIDGES];
|
|
|
void bridgeattach __P((int));
|
void bridgeattach __P((int));
|
int bridge_ioctl __P((struct ifnet *, u_long, caddr_t));
|
int bridge_ioctl __P((struct ifnet *, u_long, caddr_t));
|
void bridge_start __P((struct ifnet *));
|
void bridge_start __P((struct ifnet *));
|
void bridgeintr_frame __P((struct bridge_softc *, struct mbuf *));
|
void bridgeintr_frame __P((struct bridge_softc *, struct mbuf *));
|
void bridge_broadcast __P((struct bridge_softc *, struct ifnet *,
|
void bridge_broadcast __P((struct bridge_softc *, struct ifnet *,
|
struct ether_header *, struct mbuf *)) __attribute ((weak));
|
struct ether_header *, struct mbuf *)) __attribute ((weak));
|
void bridge_stop __P((struct bridge_softc *));
|
void bridge_stop __P((struct bridge_softc *));
|
void bridge_init __P((struct bridge_softc *));
|
void bridge_init __P((struct bridge_softc *));
|
int bridge_bifconf __P((struct bridge_softc *, struct ifbifconf *));
|
int bridge_bifconf __P((struct bridge_softc *, struct ifbifconf *));
|
|
|
int bridge_rtfind __P((struct bridge_softc *, struct ifbaconf *));
|
int bridge_rtfind __P((struct bridge_softc *, struct ifbaconf *));
|
void bridge_rtage __P((void *));
|
void bridge_rtage __P((void *));
|
void bridge_rttrim __P((struct bridge_softc *));
|
void bridge_rttrim __P((struct bridge_softc *));
|
void bridge_rtdelete __P((struct bridge_softc *, struct ifnet *));
|
void bridge_rtdelete __P((struct bridge_softc *, struct ifnet *));
|
int bridge_rtdaddr __P((struct bridge_softc *, struct ether_addr *));
|
int bridge_rtdaddr __P((struct bridge_softc *, struct ether_addr *));
|
int bridge_rtflush __P((struct bridge_softc *, int));
|
int bridge_rtflush __P((struct bridge_softc *, int));
|
struct ifnet * bridge_rtupdate __P((struct bridge_softc *,
|
struct ifnet * bridge_rtupdate __P((struct bridge_softc *,
|
struct ether_addr *, struct ifnet *ifp, int, u_int8_t));
|
struct ether_addr *, struct ifnet *ifp, int, u_int8_t));
|
struct ifnet * bridge_rtlookup __P((struct bridge_softc *,
|
struct ifnet * bridge_rtlookup __P((struct bridge_softc *,
|
struct ether_addr *));
|
struct ether_addr *));
|
u_int32_t bridge_hash __P((struct ether_addr *));
|
u_int32_t bridge_hash __P((struct ether_addr *));
|
int bridge_blocknonip __P((struct ether_header *, struct mbuf *));
|
int bridge_blocknonip __P((struct ether_header *, struct mbuf *));
|
int bridge_addrule __P((struct bridge_iflist *,
|
int bridge_addrule __P((struct bridge_iflist *,
|
struct ifbrlreq *, int out));
|
struct ifbrlreq *, int out));
|
int bridge_flushrule __P((struct bridge_iflist *));
|
int bridge_flushrule __P((struct bridge_iflist *));
|
int bridge_brlconf __P((struct bridge_softc *, struct ifbrlconf *));
|
int bridge_brlconf __P((struct bridge_softc *, struct ifbrlconf *));
|
u_int8_t bridge_filterrule __P((struct brl_node *, struct ether_header *));
|
u_int8_t bridge_filterrule __P((struct brl_node *, struct ether_header *));
|
|
|
#define ETHERADDR_IS_IP_MCAST(a) \
|
#define ETHERADDR_IS_IP_MCAST(a) \
|
/* struct etheraddr *a; */ \
|
/* struct etheraddr *a; */ \
|
((a)->ether_addr_octet[0] == 0x01 && \
|
((a)->ether_addr_octet[0] == 0x01 && \
|
(a)->ether_addr_octet[1] == 0x00 && \
|
(a)->ether_addr_octet[1] == 0x00 && \
|
(a)->ether_addr_octet[2] == 0x5e)
|
(a)->ether_addr_octet[2] == 0x5e)
|
|
|
|
|
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
|
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
|
/*
|
/*
|
* Filter hooks
|
* Filter hooks
|
*/
|
*/
|
struct mbuf *bridge_filter __P((struct bridge_softc *, struct ifnet *,
|
struct mbuf *bridge_filter __P((struct bridge_softc *, struct ifnet *,
|
struct ether_header *, struct mbuf *m));
|
struct ether_header *, struct mbuf *m));
|
#endif
|
#endif
|
|
|
void
|
void
|
bridgeattach(unused)
|
bridgeattach(unused)
|
int unused;
|
int unused;
|
{
|
{
|
int i;
|
int i;
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
|
|
for (i = 0; i < CYGNUM_NET_BRIDGES; i++) {
|
for (i = 0; i < CYGNUM_NET_BRIDGES; i++) {
|
bridgectl[i].sc_brtmax = BRIDGE_RTABLE_MAX;
|
bridgectl[i].sc_brtmax = BRIDGE_RTABLE_MAX;
|
bridgectl[i].sc_brttimeout = (BRIDGE_RTABLE_TIMEOUT * hz) / 2;
|
bridgectl[i].sc_brttimeout = (BRIDGE_RTABLE_TIMEOUT * hz) / 2;
|
LIST_INIT(&bridgectl[i].sc_iflist);
|
LIST_INIT(&bridgectl[i].sc_iflist);
|
ifp = &bridgectl[i].sc_if;
|
ifp = &bridgectl[i].sc_if;
|
sprintf(ifp->if_xname, "bridge%d", i);
|
sprintf(ifp->if_xname, "bridge%d", i);
|
ifp->if_softc = &bridgectl[i];
|
ifp->if_softc = &bridgectl[i];
|
ifp->if_mtu = ETHERMTU;
|
ifp->if_mtu = ETHERMTU;
|
ifp->if_ioctl = bridge_ioctl;
|
ifp->if_ioctl = bridge_ioctl;
|
ifp->if_output = bridge_output;
|
ifp->if_output = bridge_output;
|
ifp->if_start = bridge_start;
|
ifp->if_start = bridge_start;
|
ifp->if_type = IFT_PROPVIRTUAL;
|
ifp->if_type = IFT_PROPVIRTUAL;
|
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
ifp->if_hdrlen = sizeof(struct ether_header);
|
ifp->if_hdrlen = sizeof(struct ether_header);
|
if_attach(ifp);
|
if_attach(ifp);
|
#if NBPFILTER > 0
|
#if NBPFILTER > 0
|
bpfattach(&bridgectl[i].sc_if.if_bpf, ifp,
|
bpfattach(&bridgectl[i].sc_if.if_bpf, ifp,
|
DLT_EN10MB, sizeof(struct ether_header));
|
DLT_EN10MB, sizeof(struct ether_header));
|
#endif
|
#endif
|
}
|
}
|
}
|
}
|
|
|
int
|
int
|
bridge_ioctl(ifp, cmd, data)
|
bridge_ioctl(ifp, cmd, data)
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
u_long cmd;
|
u_long cmd;
|
caddr_t data;
|
caddr_t data;
|
{
|
{
|
#ifndef __ECOS
|
#ifndef __ECOS
|
struct proc *prc = curproc; /* XXX */
|
struct proc *prc = curproc; /* XXX */
|
#endif
|
#endif
|
struct ifnet *ifs;
|
struct ifnet *ifs;
|
struct bridge_softc *sc = (struct bridge_softc *)ifp->if_softc;
|
struct bridge_softc *sc = (struct bridge_softc *)ifp->if_softc;
|
struct ifbreq *req = (struct ifbreq *)data;
|
struct ifbreq *req = (struct ifbreq *)data;
|
struct ifbaconf *baconf = (struct ifbaconf *)data;
|
struct ifbaconf *baconf = (struct ifbaconf *)data;
|
struct ifbareq *bareq = (struct ifbareq *)data;
|
struct ifbareq *bareq = (struct ifbareq *)data;
|
struct ifbcachereq *bcachereq = (struct ifbcachereq *)data;
|
struct ifbcachereq *bcachereq = (struct ifbcachereq *)data;
|
struct ifbifconf *bifconf = (struct ifbifconf *)data;
|
struct ifbifconf *bifconf = (struct ifbifconf *)data;
|
struct ifbcachetoreq *bcacheto = (struct ifbcachetoreq *)data;
|
struct ifbcachetoreq *bcacheto = (struct ifbcachetoreq *)data;
|
struct ifbrlreq *brlreq = (struct ifbrlreq *)data;
|
struct ifbrlreq *brlreq = (struct ifbrlreq *)data;
|
struct ifbrlconf *brlconf = (struct ifbrlconf *)data;
|
struct ifbrlconf *brlconf = (struct ifbrlconf *)data;
|
struct ifreq ifreq;
|
struct ifreq ifreq;
|
int error = 0, s;
|
int error = 0, s;
|
struct bridge_iflist *p;
|
struct bridge_iflist *p;
|
|
|
s = splimp();
|
s = splimp();
|
switch (cmd) {
|
switch (cmd) {
|
case SIOCBRDGADD:
|
case SIOCBRDGADD:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
ifs = ifunit(req->ifbr_ifsname);
|
ifs = ifunit(req->ifbr_ifsname);
|
if (ifs == NULL) { /* no such interface */
|
if (ifs == NULL) { /* no such interface */
|
error = ENOENT;
|
error = ENOENT;
|
break;
|
break;
|
}
|
}
|
if (ifs->if_bridge == (caddr_t)sc) {
|
if (ifs->if_bridge == (caddr_t)sc) {
|
error = EEXIST;
|
error = EEXIST;
|
break;
|
break;
|
}
|
}
|
if (ifs->if_bridge != NULL) {
|
if (ifs->if_bridge != NULL) {
|
error = EBUSY;
|
error = EBUSY;
|
break;
|
break;
|
}
|
}
|
|
|
if (ifs->if_type == IFT_ETHER) {
|
if (ifs->if_type == IFT_ETHER) {
|
if ((ifs->if_flags & IFF_UP) == 0) {
|
if ((ifs->if_flags & IFF_UP) == 0) {
|
/*
|
/*
|
* Bring interface up long enough to set
|
* Bring interface up long enough to set
|
* promiscuous flag, then shut it down again.
|
* promiscuous flag, then shut it down again.
|
*/
|
*/
|
strncpy(ifreq.ifr_name, req->ifbr_ifsname,
|
strncpy(ifreq.ifr_name, req->ifbr_ifsname,
|
sizeof(ifreq.ifr_name) - 1);
|
sizeof(ifreq.ifr_name) - 1);
|
ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0';
|
ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0';
|
ifs->if_flags |= IFF_UP;
|
ifs->if_flags |= IFF_UP;
|
ifreq.ifr_flags = ifs->if_flags;
|
ifreq.ifr_flags = ifs->if_flags;
|
error = (*ifs->if_ioctl)(ifs, SIOCSIFFLAGS,
|
error = (*ifs->if_ioctl)(ifs, SIOCSIFFLAGS,
|
(caddr_t)&ifreq);
|
(caddr_t)&ifreq);
|
if (error != 0)
|
if (error != 0)
|
break;
|
break;
|
|
|
error = ifpromisc(ifs, 1);
|
error = ifpromisc(ifs, 1);
|
if (error != 0)
|
if (error != 0)
|
break;
|
break;
|
|
|
strncpy(ifreq.ifr_name, req->ifbr_ifsname,
|
strncpy(ifreq.ifr_name, req->ifbr_ifsname,
|
sizeof(ifreq.ifr_name) - 1);
|
sizeof(ifreq.ifr_name) - 1);
|
ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0';
|
ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0';
|
ifs->if_flags &= ~IFF_UP;
|
ifs->if_flags &= ~IFF_UP;
|
ifreq.ifr_flags = ifs->if_flags;
|
ifreq.ifr_flags = ifs->if_flags;
|
error = (*ifs->if_ioctl)(ifs, SIOCSIFFLAGS,
|
error = (*ifs->if_ioctl)(ifs, SIOCSIFFLAGS,
|
(caddr_t)&ifreq);
|
(caddr_t)&ifreq);
|
if (error != 0) {
|
if (error != 0) {
|
ifpromisc(ifs, 0);
|
ifpromisc(ifs, 0);
|
break;
|
break;
|
}
|
}
|
} else {
|
} else {
|
error = ifpromisc(ifs, 1);
|
error = ifpromisc(ifs, 1);
|
if (error != 0)
|
if (error != 0)
|
break;
|
break;
|
}
|
}
|
}
|
}
|
#ifndef __ECOS
|
#ifndef __ECOS
|
#if NENC > 0
|
#if NENC > 0
|
else if (ifs->if_type == IFT_ENC) {
|
else if (ifs->if_type == IFT_ENC) {
|
/* Can't bind enc0 to a bridge */
|
/* Can't bind enc0 to a bridge */
|
if (ifs->if_softc == &encif[0]) {
|
if (ifs->if_softc == &encif[0]) {
|
error = EINVAL;
|
error = EINVAL;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
#endif /* NENC */
|
#endif /* NENC */
|
#endif
|
#endif
|
else {
|
else {
|
error = EINVAL;
|
error = EINVAL;
|
break;
|
break;
|
}
|
}
|
|
|
p = (struct bridge_iflist *) malloc(
|
p = (struct bridge_iflist *) malloc(
|
sizeof(struct bridge_iflist), M_DEVBUF, M_NOWAIT);
|
sizeof(struct bridge_iflist), M_DEVBUF, M_NOWAIT);
|
if (p == NULL && ifs->if_type == IFT_ETHER) {
|
if (p == NULL && ifs->if_type == IFT_ETHER) {
|
error = ENOMEM;
|
error = ENOMEM;
|
ifpromisc(ifs, 0);
|
ifpromisc(ifs, 0);
|
break;
|
break;
|
}
|
}
|
|
|
p->ifp = ifs;
|
p->ifp = ifs;
|
p->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
|
p->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
|
SIMPLEQ_INIT(&p->bif_brlin);
|
SIMPLEQ_INIT(&p->bif_brlin);
|
SIMPLEQ_INIT(&p->bif_brlout);
|
SIMPLEQ_INIT(&p->bif_brlout);
|
LIST_INSERT_HEAD(&sc->sc_iflist, p, next);
|
LIST_INSERT_HEAD(&sc->sc_iflist, p, next);
|
ifs->if_bridge = (caddr_t)sc;
|
ifs->if_bridge = (caddr_t)sc;
|
break;
|
break;
|
case SIOCBRDGDEL:
|
case SIOCBRDGDEL:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
p = LIST_FIRST(&sc->sc_iflist);
|
p = LIST_FIRST(&sc->sc_iflist);
|
while (p != NULL) {
|
while (p != NULL) {
|
if (strncmp(p->ifp->if_xname, req->ifbr_ifsname,
|
if (strncmp(p->ifp->if_xname, req->ifbr_ifsname,
|
sizeof(p->ifp->if_xname)) == 0) {
|
sizeof(p->ifp->if_xname)) == 0) {
|
p->ifp->if_bridge = NULL;
|
p->ifp->if_bridge = NULL;
|
|
|
error = ifpromisc(p->ifp, 0);
|
error = ifpromisc(p->ifp, 0);
|
|
|
LIST_REMOVE(p, next);
|
LIST_REMOVE(p, next);
|
bridge_rtdelete(sc, p->ifp);
|
bridge_rtdelete(sc, p->ifp);
|
bridge_flushrule(p);
|
bridge_flushrule(p);
|
free(p, M_DEVBUF);
|
free(p, M_DEVBUF);
|
break;
|
break;
|
}
|
}
|
p = LIST_NEXT(p, next);
|
p = LIST_NEXT(p, next);
|
}
|
}
|
if (p == NULL) {
|
if (p == NULL) {
|
error = ENOENT;
|
error = ENOENT;
|
break;
|
break;
|
}
|
}
|
break;
|
break;
|
case SIOCBRDGIFS:
|
case SIOCBRDGIFS:
|
error = bridge_bifconf(sc, bifconf);
|
error = bridge_bifconf(sc, bifconf);
|
break;
|
break;
|
case SIOCBRDGGIFFLGS:
|
case SIOCBRDGGIFFLGS:
|
ifs = ifunit(req->ifbr_ifsname);
|
ifs = ifunit(req->ifbr_ifsname);
|
if (ifs == NULL) {
|
if (ifs == NULL) {
|
error = ENOENT;
|
error = ENOENT;
|
break;
|
break;
|
}
|
}
|
if ((caddr_t)sc != ifs->if_bridge) {
|
if ((caddr_t)sc != ifs->if_bridge) {
|
error = ESRCH;
|
error = ESRCH;
|
break;
|
break;
|
}
|
}
|
p = LIST_FIRST(&sc->sc_iflist);
|
p = LIST_FIRST(&sc->sc_iflist);
|
while (p != NULL && p->ifp != ifs) {
|
while (p != NULL && p->ifp != ifs) {
|
p = LIST_NEXT(p, next);
|
p = LIST_NEXT(p, next);
|
}
|
}
|
if (p == NULL) {
|
if (p == NULL) {
|
error = ESRCH;
|
error = ESRCH;
|
break;
|
break;
|
}
|
}
|
req->ifbr_ifsflags = p->bif_flags;
|
req->ifbr_ifsflags = p->bif_flags;
|
break;
|
break;
|
case SIOCBRDGSIFFLGS:
|
case SIOCBRDGSIFFLGS:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
ifs = ifunit(req->ifbr_ifsname);
|
ifs = ifunit(req->ifbr_ifsname);
|
if (ifs == NULL) {
|
if (ifs == NULL) {
|
error = ENOENT;
|
error = ENOENT;
|
break;
|
break;
|
}
|
}
|
if ((caddr_t)sc != ifs->if_bridge) {
|
if ((caddr_t)sc != ifs->if_bridge) {
|
error = ESRCH;
|
error = ESRCH;
|
break;
|
break;
|
}
|
}
|
p = LIST_FIRST(&sc->sc_iflist);
|
p = LIST_FIRST(&sc->sc_iflist);
|
while (p != NULL && p->ifp != ifs) {
|
while (p != NULL && p->ifp != ifs) {
|
p = LIST_NEXT(p, next);
|
p = LIST_NEXT(p, next);
|
}
|
}
|
if (p == NULL) {
|
if (p == NULL) {
|
error = ESRCH;
|
error = ESRCH;
|
break;
|
break;
|
}
|
}
|
p->bif_flags = req->ifbr_ifsflags;
|
p->bif_flags = req->ifbr_ifsflags;
|
break;
|
break;
|
case SIOCBRDGRTS:
|
case SIOCBRDGRTS:
|
error = bridge_rtfind(sc, baconf);
|
error = bridge_rtfind(sc, baconf);
|
break;
|
break;
|
case SIOCBRDGFLUSH:
|
case SIOCBRDGFLUSH:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
error = bridge_rtflush(sc, req->ifbr_ifsflags);
|
error = bridge_rtflush(sc, req->ifbr_ifsflags);
|
break;
|
break;
|
case SIOCBRDGSADDR:
|
case SIOCBRDGSADDR:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
ifs = ifunit(bareq->ifba_ifsname);
|
ifs = ifunit(bareq->ifba_ifsname);
|
if (ifs == NULL) { /* no such interface */
|
if (ifs == NULL) { /* no such interface */
|
error = ENOENT;
|
error = ENOENT;
|
break;
|
break;
|
}
|
}
|
|
|
if (ifs->if_bridge == NULL ||
|
if (ifs->if_bridge == NULL ||
|
ifs->if_bridge != (caddr_t)sc) {
|
ifs->if_bridge != (caddr_t)sc) {
|
error = ESRCH;
|
error = ESRCH;
|
break;
|
break;
|
}
|
}
|
|
|
ifs = bridge_rtupdate(sc, &bareq->ifba_dst, ifs, 1,
|
ifs = bridge_rtupdate(sc, &bareq->ifba_dst, ifs, 1,
|
bareq->ifba_flags);
|
bareq->ifba_flags);
|
if (ifs == NULL)
|
if (ifs == NULL)
|
error = ENOMEM;
|
error = ENOMEM;
|
break;
|
break;
|
case SIOCBRDGDADDR:
|
case SIOCBRDGDADDR:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
error = bridge_rtdaddr(sc, &bareq->ifba_dst);
|
error = bridge_rtdaddr(sc, &bareq->ifba_dst);
|
break;
|
break;
|
case SIOCBRDGGCACHE:
|
case SIOCBRDGGCACHE:
|
bcachereq->ifbc_size = sc->sc_brtmax;
|
bcachereq->ifbc_size = sc->sc_brtmax;
|
break;
|
break;
|
case SIOCBRDGSCACHE:
|
case SIOCBRDGSCACHE:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
sc->sc_brtmax = bcachereq->ifbc_size;
|
sc->sc_brtmax = bcachereq->ifbc_size;
|
bridge_rttrim(sc);
|
bridge_rttrim(sc);
|
break;
|
break;
|
case SIOCBRDGSTO:
|
case SIOCBRDGSTO:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
sc->sc_brttimeout = (bcacheto->ifbct_time * hz) / 2;
|
sc->sc_brttimeout = (bcacheto->ifbct_time * hz) / 2;
|
untimeout(bridge_rtage, sc);
|
untimeout(bridge_rtage, sc);
|
if (bcacheto->ifbct_time != 0)
|
if (bcacheto->ifbct_time != 0)
|
timeout(bridge_rtage, sc, sc->sc_brttimeout);
|
timeout(bridge_rtage, sc, sc->sc_brttimeout);
|
break;
|
break;
|
case SIOCBRDGGTO:
|
case SIOCBRDGGTO:
|
bcacheto->ifbct_time = (2 * sc->sc_brttimeout) / hz;
|
bcacheto->ifbct_time = (2 * sc->sc_brttimeout) / hz;
|
break;
|
break;
|
case SIOCSIFFLAGS:
|
case SIOCSIFFLAGS:
|
if ((ifp->if_flags & IFF_UP) == IFF_UP)
|
if ((ifp->if_flags & IFF_UP) == IFF_UP)
|
bridge_init(sc);
|
bridge_init(sc);
|
|
|
if ((ifp->if_flags & IFF_UP) == 0)
|
if ((ifp->if_flags & IFF_UP) == 0)
|
bridge_stop(sc);
|
bridge_stop(sc);
|
|
|
break;
|
break;
|
case SIOCBRDGARL:
|
case SIOCBRDGARL:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
ifs = ifunit(brlreq->ifbr_ifsname);
|
ifs = ifunit(brlreq->ifbr_ifsname);
|
if (ifs == NULL) {
|
if (ifs == NULL) {
|
error = ENOENT;
|
error = ENOENT;
|
break;
|
break;
|
}
|
}
|
if (ifs->if_bridge == NULL ||
|
if (ifs->if_bridge == NULL ||
|
ifs->if_bridge != (caddr_t)sc) {
|
ifs->if_bridge != (caddr_t)sc) {
|
error = ESRCH;
|
error = ESRCH;
|
break;
|
break;
|
}
|
}
|
p = LIST_FIRST(&sc->sc_iflist);
|
p = LIST_FIRST(&sc->sc_iflist);
|
while (p != NULL && p->ifp != ifs) {
|
while (p != NULL && p->ifp != ifs) {
|
p = LIST_NEXT(p, next);
|
p = LIST_NEXT(p, next);
|
}
|
}
|
if (p == NULL) {
|
if (p == NULL) {
|
error = ESRCH;
|
error = ESRCH;
|
break;
|
break;
|
}
|
}
|
if ((brlreq->ifbr_action != BRL_ACTION_BLOCK &&
|
if ((brlreq->ifbr_action != BRL_ACTION_BLOCK &&
|
brlreq->ifbr_action != BRL_ACTION_PASS) ||
|
brlreq->ifbr_action != BRL_ACTION_PASS) ||
|
(brlreq->ifbr_flags & (BRL_FLAG_IN|BRL_FLAG_OUT)) == 0) {
|
(brlreq->ifbr_flags & (BRL_FLAG_IN|BRL_FLAG_OUT)) == 0) {
|
error = EINVAL;
|
error = EINVAL;
|
break;
|
break;
|
}
|
}
|
if (brlreq->ifbr_flags & BRL_FLAG_IN) {
|
if (brlreq->ifbr_flags & BRL_FLAG_IN) {
|
error = bridge_addrule(p, brlreq, 0);
|
error = bridge_addrule(p, brlreq, 0);
|
if (error)
|
if (error)
|
break;
|
break;
|
}
|
}
|
if (brlreq->ifbr_flags & BRL_FLAG_OUT) {
|
if (brlreq->ifbr_flags & BRL_FLAG_OUT) {
|
error = bridge_addrule(p, brlreq, 1);
|
error = bridge_addrule(p, brlreq, 1);
|
if (error)
|
if (error)
|
break;
|
break;
|
}
|
}
|
break;
|
break;
|
case SIOCBRDGFRL:
|
case SIOCBRDGFRL:
|
#ifndef __ECOS
|
#ifndef __ECOS
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
|
break;
|
break;
|
#endif
|
#endif
|
ifs = ifunit(brlreq->ifbr_ifsname);
|
ifs = ifunit(brlreq->ifbr_ifsname);
|
if (ifs == NULL) {
|
if (ifs == NULL) {
|
error = ENOENT;
|
error = ENOENT;
|
break;
|
break;
|
}
|
}
|
if (ifs->if_bridge == NULL ||
|
if (ifs->if_bridge == NULL ||
|
ifs->if_bridge != (caddr_t)sc) {
|
ifs->if_bridge != (caddr_t)sc) {
|
error = ESRCH;
|
error = ESRCH;
|
break;
|
break;
|
}
|
}
|
p = LIST_FIRST(&sc->sc_iflist);
|
p = LIST_FIRST(&sc->sc_iflist);
|
while (p != NULL && p->ifp != ifs) {
|
while (p != NULL && p->ifp != ifs) {
|
p = LIST_NEXT(p, next);
|
p = LIST_NEXT(p, next);
|
}
|
}
|
if (p == NULL) {
|
if (p == NULL) {
|
error = ESRCH;
|
error = ESRCH;
|
break;
|
break;
|
}
|
}
|
error = bridge_flushrule(p);
|
error = bridge_flushrule(p);
|
break;
|
break;
|
case SIOCBRDGGRL:
|
case SIOCBRDGGRL:
|
error = bridge_brlconf(sc, brlconf);
|
error = bridge_brlconf(sc, brlconf);
|
break;
|
break;
|
default:
|
default:
|
error = EINVAL;
|
error = EINVAL;
|
}
|
}
|
splx(s);
|
splx(s);
|
return (error);
|
return (error);
|
}
|
}
|
|
|
/* Detach an interface from a bridge. */
|
/* Detach an interface from a bridge. */
|
void
|
void
|
bridge_ifdetach(ifp)
|
bridge_ifdetach(ifp)
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
{
|
{
|
struct bridge_softc *bsc = (struct bridge_softc *)ifp->if_bridge;
|
struct bridge_softc *bsc = (struct bridge_softc *)ifp->if_bridge;
|
struct bridge_iflist *bif;
|
struct bridge_iflist *bif;
|
|
|
for (bif = LIST_FIRST(&bsc->sc_iflist); bif;
|
for (bif = LIST_FIRST(&bsc->sc_iflist); bif;
|
bif = LIST_NEXT(bif, next))
|
bif = LIST_NEXT(bif, next))
|
if (bif->ifp == ifp) {
|
if (bif->ifp == ifp) {
|
LIST_REMOVE(bif, next);
|
LIST_REMOVE(bif, next);
|
bridge_rtdelete(bsc, ifp);
|
bridge_rtdelete(bsc, ifp);
|
bridge_flushrule(bif);
|
bridge_flushrule(bif);
|
free(bif, M_DEVBUF);
|
free(bif, M_DEVBUF);
|
ifp->if_bridge = NULL;
|
ifp->if_bridge = NULL;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
int
|
int
|
bridge_bifconf(sc, bifc)
|
bridge_bifconf(sc, bifc)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct ifbifconf *bifc;
|
struct ifbifconf *bifc;
|
{
|
{
|
struct bridge_iflist *p;
|
struct bridge_iflist *p;
|
u_int32_t total = 0, i;
|
u_int32_t total = 0, i;
|
int error = 0;
|
int error = 0;
|
struct ifbreq breq;
|
struct ifbreq breq;
|
|
|
p = LIST_FIRST(&sc->sc_iflist);
|
p = LIST_FIRST(&sc->sc_iflist);
|
while (p != NULL) {
|
while (p != NULL) {
|
total++;
|
total++;
|
p = LIST_NEXT(p, next);
|
p = LIST_NEXT(p, next);
|
}
|
}
|
|
|
if (bifc->ifbic_len == 0) {
|
if (bifc->ifbic_len == 0) {
|
i = total;
|
i = total;
|
goto done;
|
goto done;
|
}
|
}
|
|
|
p = LIST_FIRST(&sc->sc_iflist);
|
p = LIST_FIRST(&sc->sc_iflist);
|
i = 0;
|
i = 0;
|
while (p != NULL && bifc->ifbic_len > i * sizeof(breq)) {
|
while (p != NULL && bifc->ifbic_len > i * sizeof(breq)) {
|
strncpy(breq.ifbr_name, sc->sc_if.if_xname,
|
strncpy(breq.ifbr_name, sc->sc_if.if_xname,
|
sizeof(breq.ifbr_name)-1);
|
sizeof(breq.ifbr_name)-1);
|
breq.ifbr_name[sizeof(breq.ifbr_name) - 1] = '\0';
|
breq.ifbr_name[sizeof(breq.ifbr_name) - 1] = '\0';
|
strncpy(breq.ifbr_ifsname, p->ifp->if_xname,
|
strncpy(breq.ifbr_ifsname, p->ifp->if_xname,
|
sizeof(breq.ifbr_ifsname)-1);
|
sizeof(breq.ifbr_ifsname)-1);
|
breq.ifbr_ifsname[sizeof(breq.ifbr_ifsname) - 1] = '\0';
|
breq.ifbr_ifsname[sizeof(breq.ifbr_ifsname) - 1] = '\0';
|
breq.ifbr_ifsflags = p->bif_flags;
|
breq.ifbr_ifsflags = p->bif_flags;
|
error = copyout((caddr_t)&breq,
|
error = copyout((caddr_t)&breq,
|
(caddr_t)(bifc->ifbic_req + i), sizeof(breq));
|
(caddr_t)(bifc->ifbic_req + i), sizeof(breq));
|
if (error)
|
if (error)
|
goto done;
|
goto done;
|
p = LIST_NEXT(p, next);
|
p = LIST_NEXT(p, next);
|
i++;
|
i++;
|
bifc->ifbic_len -= sizeof(breq);
|
bifc->ifbic_len -= sizeof(breq);
|
}
|
}
|
done:
|
done:
|
bifc->ifbic_len = i * sizeof(breq);
|
bifc->ifbic_len = i * sizeof(breq);
|
return (error);
|
return (error);
|
}
|
}
|
|
|
int
|
int
|
bridge_brlconf(sc, bc)
|
bridge_brlconf(sc, bc)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct ifbrlconf *bc;
|
struct ifbrlconf *bc;
|
{
|
{
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
struct bridge_iflist *ifl;
|
struct bridge_iflist *ifl;
|
struct brl_node *n;
|
struct brl_node *n;
|
struct ifbrlreq req;
|
struct ifbrlreq req;
|
int error = 0;
|
int error = 0;
|
u_int32_t i, total=0;
|
u_int32_t i, total=0;
|
|
|
ifp = ifunit(bc->ifbrl_ifsname);
|
ifp = ifunit(bc->ifbrl_ifsname);
|
if (ifp == NULL)
|
if (ifp == NULL)
|
return (ENOENT);
|
return (ENOENT);
|
if (ifp->if_bridge == NULL || ifp->if_bridge != (caddr_t)sc)
|
if (ifp->if_bridge == NULL || ifp->if_bridge != (caddr_t)sc)
|
return (ESRCH);
|
return (ESRCH);
|
ifl = LIST_FIRST(&sc->sc_iflist);
|
ifl = LIST_FIRST(&sc->sc_iflist);
|
while (ifl != NULL && ifl->ifp != ifp)
|
while (ifl != NULL && ifl->ifp != ifp)
|
ifl = LIST_NEXT(ifl, next);
|
ifl = LIST_NEXT(ifl, next);
|
if (ifl == NULL)
|
if (ifl == NULL)
|
return (ESRCH);
|
return (ESRCH);
|
|
|
n = SIMPLEQ_FIRST(&ifl->bif_brlin);
|
n = SIMPLEQ_FIRST(&ifl->bif_brlin);
|
while (n != NULL) {
|
while (n != NULL) {
|
total++;
|
total++;
|
n = SIMPLEQ_NEXT(n, brl_next);
|
n = SIMPLEQ_NEXT(n, brl_next);
|
}
|
}
|
n = SIMPLEQ_FIRST(&ifl->bif_brlout);
|
n = SIMPLEQ_FIRST(&ifl->bif_brlout);
|
while (n != NULL) {
|
while (n != NULL) {
|
total++;
|
total++;
|
n = SIMPLEQ_NEXT(n, brl_next);
|
n = SIMPLEQ_NEXT(n, brl_next);
|
}
|
}
|
|
|
if (bc->ifbrl_len == 0) {
|
if (bc->ifbrl_len == 0) {
|
i = total;
|
i = total;
|
goto done;
|
goto done;
|
}
|
}
|
|
|
i = 0;
|
i = 0;
|
n = SIMPLEQ_FIRST(&ifl->bif_brlin);
|
n = SIMPLEQ_FIRST(&ifl->bif_brlin);
|
while (n != NULL && bc->ifbrl_len > i * sizeof(req)) {
|
while (n != NULL && bc->ifbrl_len > i * sizeof(req)) {
|
strncpy(req.ifbr_name, sc->sc_if.if_xname,
|
strncpy(req.ifbr_name, sc->sc_if.if_xname,
|
sizeof(req.ifbr_name) - 1);
|
sizeof(req.ifbr_name) - 1);
|
req.ifbr_name[sizeof(req.ifbr_name) - 1] = '\0';
|
req.ifbr_name[sizeof(req.ifbr_name) - 1] = '\0';
|
strncpy(req.ifbr_ifsname, ifl->ifp->if_xname,
|
strncpy(req.ifbr_ifsname, ifl->ifp->if_xname,
|
sizeof(req.ifbr_ifsname) - 1);
|
sizeof(req.ifbr_ifsname) - 1);
|
req.ifbr_ifsname[sizeof(req.ifbr_ifsname) - 1] = '\0';
|
req.ifbr_ifsname[sizeof(req.ifbr_ifsname) - 1] = '\0';
|
req.ifbr_action = n->brl_action;
|
req.ifbr_action = n->brl_action;
|
req.ifbr_flags = n->brl_flags;
|
req.ifbr_flags = n->brl_flags;
|
req.ifbr_src = n->brl_src;
|
req.ifbr_src = n->brl_src;
|
req.ifbr_dst = n->brl_dst;
|
req.ifbr_dst = n->brl_dst;
|
error = copyout((caddr_t)&req,
|
error = copyout((caddr_t)&req,
|
(caddr_t)(bc->ifbrl_buf + (i * sizeof(req))), sizeof(req));
|
(caddr_t)(bc->ifbrl_buf + (i * sizeof(req))), sizeof(req));
|
if (error)
|
if (error)
|
goto done;
|
goto done;
|
n = SIMPLEQ_NEXT(n, brl_next);
|
n = SIMPLEQ_NEXT(n, brl_next);
|
i++;
|
i++;
|
bc->ifbrl_len -= sizeof(req);
|
bc->ifbrl_len -= sizeof(req);
|
}
|
}
|
|
|
n = SIMPLEQ_FIRST(&ifl->bif_brlout);
|
n = SIMPLEQ_FIRST(&ifl->bif_brlout);
|
while (n != NULL && bc->ifbrl_len > i * sizeof(req)) {
|
while (n != NULL && bc->ifbrl_len > i * sizeof(req)) {
|
strncpy(req.ifbr_name, sc->sc_if.if_xname,
|
strncpy(req.ifbr_name, sc->sc_if.if_xname,
|
sizeof(req.ifbr_name) - 1);
|
sizeof(req.ifbr_name) - 1);
|
req.ifbr_name[sizeof(req.ifbr_name) - 1] = '\0';
|
req.ifbr_name[sizeof(req.ifbr_name) - 1] = '\0';
|
strncpy(req.ifbr_ifsname, ifl->ifp->if_xname,
|
strncpy(req.ifbr_ifsname, ifl->ifp->if_xname,
|
sizeof(req.ifbr_ifsname) - 1);
|
sizeof(req.ifbr_ifsname) - 1);
|
req.ifbr_ifsname[sizeof(req.ifbr_ifsname) - 1] = '\0';
|
req.ifbr_ifsname[sizeof(req.ifbr_ifsname) - 1] = '\0';
|
req.ifbr_action = n->brl_action;
|
req.ifbr_action = n->brl_action;
|
req.ifbr_flags = n->brl_flags;
|
req.ifbr_flags = n->brl_flags;
|
req.ifbr_src = n->brl_src;
|
req.ifbr_src = n->brl_src;
|
req.ifbr_dst = n->brl_dst;
|
req.ifbr_dst = n->brl_dst;
|
error = copyout((caddr_t)&req,
|
error = copyout((caddr_t)&req,
|
(caddr_t)(bc->ifbrl_buf + (i * sizeof(req))), sizeof(req));
|
(caddr_t)(bc->ifbrl_buf + (i * sizeof(req))), sizeof(req));
|
if (error)
|
if (error)
|
goto done;
|
goto done;
|
n = SIMPLEQ_NEXT(n, brl_next);
|
n = SIMPLEQ_NEXT(n, brl_next);
|
i++;
|
i++;
|
bc->ifbrl_len -= sizeof(req);
|
bc->ifbrl_len -= sizeof(req);
|
}
|
}
|
|
|
done:
|
done:
|
bc->ifbrl_len = i * sizeof(req);
|
bc->ifbrl_len = i * sizeof(req);
|
return (error);
|
return (error);
|
}
|
}
|
|
|
void
|
void
|
bridge_init(sc)
|
bridge_init(sc)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
{
|
{
|
struct ifnet *ifp = &sc->sc_if;
|
struct ifnet *ifp = &sc->sc_if;
|
int i, s;
|
int i, s;
|
|
|
if ((ifp->if_flags & IFF_RUNNING) == IFF_RUNNING)
|
if ((ifp->if_flags & IFF_RUNNING) == IFF_RUNNING)
|
return;
|
return;
|
|
|
s = splhigh();
|
s = splhigh();
|
if (sc->sc_rts == NULL) {
|
if (sc->sc_rts == NULL) {
|
sc->sc_rts = (struct bridge_rthead *)malloc(
|
sc->sc_rts = (struct bridge_rthead *)malloc(
|
BRIDGE_RTABLE_SIZE * (sizeof(struct bridge_rthead)),
|
BRIDGE_RTABLE_SIZE * (sizeof(struct bridge_rthead)),
|
M_DEVBUF, M_NOWAIT);
|
M_DEVBUF, M_NOWAIT);
|
if (sc->sc_rts == NULL) {
|
if (sc->sc_rts == NULL) {
|
splx(s);
|
splx(s);
|
return;
|
return;
|
}
|
}
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
LIST_INIT(&sc->sc_rts[i]);
|
LIST_INIT(&sc->sc_rts[i]);
|
}
|
}
|
}
|
}
|
ifp->if_flags |= IFF_RUNNING;
|
ifp->if_flags |= IFF_RUNNING;
|
splx(s);
|
splx(s);
|
|
|
if (sc->sc_brttimeout != 0)
|
if (sc->sc_brttimeout != 0)
|
timeout(bridge_rtage, sc, sc->sc_brttimeout);
|
timeout(bridge_rtage, sc, sc->sc_brttimeout);
|
}
|
}
|
|
|
/*
|
/*
|
* Stop the bridge and deallocate the routing table.
|
* Stop the bridge and deallocate the routing table.
|
*/
|
*/
|
void
|
void
|
bridge_stop(sc)
|
bridge_stop(sc)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
{
|
{
|
struct ifnet *ifp = &sc->sc_if;
|
struct ifnet *ifp = &sc->sc_if;
|
|
|
/*
|
/*
|
* If we're not running, there's nothing to do.
|
* If we're not running, there's nothing to do.
|
*/
|
*/
|
if ((ifp->if_flags & IFF_RUNNING) == 0)
|
if ((ifp->if_flags & IFF_RUNNING) == 0)
|
return;
|
return;
|
|
|
untimeout(bridge_rtage, sc);
|
untimeout(bridge_rtage, sc);
|
|
|
bridge_rtflush(sc, IFBF_FLUSHDYN);
|
bridge_rtflush(sc, IFBF_FLUSHDYN);
|
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
ifp->if_flags &= ~IFF_RUNNING;
|
}
|
}
|
|
|
/*
|
/*
|
* Send output from the bridge. The mbuf has the ethernet header
|
* Send output from the bridge. The mbuf has the ethernet header
|
* already attached. We must enqueue or free the mbuf before exiting.
|
* already attached. We must enqueue or free the mbuf before exiting.
|
*/
|
*/
|
int
|
int
|
bridge_output(ifp, m, sa, rt)
|
bridge_output(ifp, m, sa, rt)
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
struct mbuf *m;
|
struct mbuf *m;
|
struct sockaddr *sa;
|
struct sockaddr *sa;
|
struct rtentry *rt;
|
struct rtentry *rt;
|
{
|
{
|
struct ether_header *eh;
|
struct ether_header *eh;
|
struct ifnet *dst_if;
|
struct ifnet *dst_if;
|
struct ether_addr *src, *dst;
|
struct ether_addr *src, *dst;
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
int s;
|
int s;
|
|
|
if (m->m_len < sizeof(*eh)) {
|
if (m->m_len < sizeof(*eh)) {
|
m = m_pullup(m, sizeof(*eh));
|
m = m_pullup(m, sizeof(*eh));
|
if (m == NULL)
|
if (m == NULL)
|
return (0);
|
return (0);
|
}
|
}
|
eh = mtod(m, struct ether_header *);
|
eh = mtod(m, struct ether_header *);
|
dst = (struct ether_addr *)&eh->ether_dhost[0];
|
dst = (struct ether_addr *)&eh->ether_dhost[0];
|
src = (struct ether_addr *)&eh->ether_shost[0];
|
src = (struct ether_addr *)&eh->ether_shost[0];
|
sc = (struct bridge_softc *)ifp->if_bridge;
|
sc = (struct bridge_softc *)ifp->if_bridge;
|
|
|
s = splimp();
|
s = splimp();
|
|
|
/*
|
/*
|
* If bridge is down, but original output interface is up,
|
* If bridge is down, but original output interface is up,
|
* go ahead and send out that interface. Otherwise the packet
|
* go ahead and send out that interface. Otherwise the packet
|
* is dropped below.
|
* is dropped below.
|
*/
|
*/
|
if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
|
if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
|
dst_if = ifp;
|
dst_if = ifp;
|
goto sendunicast;
|
goto sendunicast;
|
}
|
}
|
|
|
/*
|
/*
|
* If the packet is a broadcast or we don't know a better way to
|
* If the packet is a broadcast or we don't know a better way to
|
* get there, send to all interfaces.
|
* get there, send to all interfaces.
|
*/
|
*/
|
dst_if = bridge_rtlookup(sc, dst);
|
dst_if = bridge_rtlookup(sc, dst);
|
if (dst_if == NULL || eh->ether_dhost[0] & 1) {
|
if (dst_if == NULL || eh->ether_dhost[0] & 1) {
|
struct bridge_iflist *p;
|
struct bridge_iflist *p;
|
struct mbuf *mc;
|
struct mbuf *mc;
|
int used = 0;
|
int used = 0;
|
|
|
for (p = LIST_FIRST(&sc->sc_iflist); p != NULL;
|
for (p = LIST_FIRST(&sc->sc_iflist); p != NULL;
|
p = LIST_NEXT(p, next)) {
|
p = LIST_NEXT(p, next)) {
|
if ((p->ifp->if_flags & IFF_RUNNING) == 0)
|
if ((p->ifp->if_flags & IFF_RUNNING) == 0)
|
continue;
|
continue;
|
if (IF_QFULL(&p->ifp->if_snd)) {
|
if (IF_QFULL(&p->ifp->if_snd)) {
|
sc->sc_if.if_oerrors++;
|
sc->sc_if.if_oerrors++;
|
continue;
|
continue;
|
}
|
}
|
|
|
if (LIST_NEXT(p, next) == NULL) {
|
if (LIST_NEXT(p, next) == NULL) {
|
used = 1;
|
used = 1;
|
mc = m;
|
mc = m;
|
} else {
|
} else {
|
mc = m_copym(m, 0, M_COPYALL, M_NOWAIT);
|
mc = m_copym(m, 0, M_COPYALL, M_NOWAIT);
|
if (mc == NULL) {
|
if (mc == NULL) {
|
sc->sc_if.if_oerrors++;
|
sc->sc_if.if_oerrors++;
|
continue;
|
continue;
|
}
|
}
|
}
|
}
|
|
|
sc->sc_if.if_opackets++;
|
sc->sc_if.if_opackets++;
|
sc->sc_if.if_obytes += m->m_pkthdr.len;
|
sc->sc_if.if_obytes += m->m_pkthdr.len;
|
// Also count the bytes in the outgoing interface; normally
|
// Also count the bytes in the outgoing interface; normally
|
// done in if_ethersubr.c but here we bypass that route.
|
// done in if_ethersubr.c but here we bypass that route.
|
p->ifp->if_obytes += m->m_pkthdr.len;
|
p->ifp->if_obytes += m->m_pkthdr.len;
|
IF_ENQUEUE(&p->ifp->if_snd, mc);
|
IF_ENQUEUE(&p->ifp->if_snd, mc);
|
if ((p->ifp->if_flags & IFF_OACTIVE) == 0)
|
if ((p->ifp->if_flags & IFF_OACTIVE) == 0)
|
(*p->ifp->if_start)(p->ifp);
|
(*p->ifp->if_start)(p->ifp);
|
}
|
}
|
if (!used)
|
if (!used)
|
m_freem(m);
|
m_freem(m);
|
splx(s);
|
splx(s);
|
return (0);
|
return (0);
|
}
|
}
|
|
|
sendunicast:
|
sendunicast:
|
if ((dst_if->if_flags & IFF_RUNNING) == 0) {
|
if ((dst_if->if_flags & IFF_RUNNING) == 0) {
|
m_freem(m);
|
m_freem(m);
|
splx(s);
|
splx(s);
|
return (0);
|
return (0);
|
}
|
}
|
if (IF_QFULL(&dst_if->if_snd)) {
|
if (IF_QFULL(&dst_if->if_snd)) {
|
sc->sc_if.if_oerrors++;
|
sc->sc_if.if_oerrors++;
|
m_freem(m);
|
m_freem(m);
|
splx(s);
|
splx(s);
|
return (0);
|
return (0);
|
}
|
}
|
sc->sc_if.if_opackets++;
|
sc->sc_if.if_opackets++;
|
sc->sc_if.if_obytes += m->m_pkthdr.len;
|
sc->sc_if.if_obytes += m->m_pkthdr.len;
|
// Also count the bytes in the outgoing interface; normally
|
// Also count the bytes in the outgoing interface; normally
|
// done in if_ethersubr.c but here we bypass that route.
|
// done in if_ethersubr.c but here we bypass that route.
|
dst_if->if_obytes += m->m_pkthdr.len;
|
dst_if->if_obytes += m->m_pkthdr.len;
|
IF_ENQUEUE(&dst_if->if_snd, m);
|
IF_ENQUEUE(&dst_if->if_snd, m);
|
if ((dst_if->if_flags & IFF_OACTIVE) == 0)
|
if ((dst_if->if_flags & IFF_OACTIVE) == 0)
|
(*dst_if->if_start)(dst_if);
|
(*dst_if->if_start)(dst_if);
|
splx(s);
|
splx(s);
|
return (0);
|
return (0);
|
}
|
}
|
|
|
/*
|
/*
|
* Start output on the bridge. This function should never be called.
|
* Start output on the bridge. This function should never be called.
|
*/
|
*/
|
void
|
void
|
bridge_start(ifp)
|
bridge_start(ifp)
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
{
|
{
|
}
|
}
|
|
|
void
|
void
|
bridgeintr(void)
|
bridgeintr(void)
|
{
|
{
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct mbuf *m;
|
struct mbuf *m;
|
int i, s;
|
int i, s;
|
|
|
for (i = 0; i < CYGNUM_NET_BRIDGES; i++) {
|
for (i = 0; i < CYGNUM_NET_BRIDGES; i++) {
|
sc = &bridgectl[i];
|
sc = &bridgectl[i];
|
for (;;) {
|
for (;;) {
|
s = splimp();
|
s = splimp();
|
IF_DEQUEUE(&sc->sc_if.if_snd, m);
|
IF_DEQUEUE(&sc->sc_if.if_snd, m);
|
splx(s);
|
splx(s);
|
if (m == NULL)
|
if (m == NULL)
|
break;
|
break;
|
bridgeintr_frame(sc, m);
|
bridgeintr_frame(sc, m);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Loop through each bridge interface and process their input queues.
|
* Loop through each bridge interface and process their input queues.
|
*/
|
*/
|
void
|
void
|
bridgeintr_frame(sc, m)
|
bridgeintr_frame(sc, m)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct mbuf *m;
|
struct mbuf *m;
|
{
|
{
|
int s;
|
int s;
|
struct ifnet *src_if, *dst_if;
|
struct ifnet *src_if, *dst_if;
|
struct bridge_iflist *ifl;
|
struct bridge_iflist *ifl;
|
struct ether_addr *dst, *src;
|
struct ether_addr *dst, *src;
|
struct ether_header eh;
|
struct ether_header eh;
|
|
|
if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
|
if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
|
|
src_if = m->m_pkthdr.rcvif;
|
src_if = m->m_pkthdr.rcvif;
|
|
|
#if NBPFILTER > 0
|
#if NBPFILTER > 0
|
if (sc->sc_if.if_bpf)
|
if (sc->sc_if.if_bpf)
|
bpf_mtap(sc->sc_if.if_bpf, m);
|
bpf_mtap(sc->sc_if.if_bpf, m);
|
#endif
|
#endif
|
|
|
sc->sc_if.if_lastchange = time;
|
sc->sc_if.if_lastchange = time;
|
sc->sc_if.if_ipackets++;
|
sc->sc_if.if_ipackets++;
|
sc->sc_if.if_ibytes += m->m_pkthdr.len;
|
sc->sc_if.if_ibytes += m->m_pkthdr.len;
|
|
|
ifl = LIST_FIRST(&sc->sc_iflist);
|
ifl = LIST_FIRST(&sc->sc_iflist);
|
while (ifl != NULL && ifl->ifp != src_if) {
|
while (ifl != NULL && ifl->ifp != src_if) {
|
ifl = LIST_NEXT(ifl, next);
|
ifl = LIST_NEXT(ifl, next);
|
}
|
}
|
if (ifl == NULL) {
|
if (ifl == NULL) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
|
|
if (m->m_pkthdr.len < sizeof(eh)) {
|
if (m->m_pkthdr.len < sizeof(eh)) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
m_copydata(m, 0, sizeof(struct ether_header), (caddr_t)&eh);
|
m_copydata(m, 0, sizeof(struct ether_header), (caddr_t)&eh);
|
dst = (struct ether_addr *)&eh.ether_dhost[0];
|
dst = (struct ether_addr *)&eh.ether_dhost[0];
|
src = (struct ether_addr *)&eh.ether_shost[0];
|
src = (struct ether_addr *)&eh.ether_shost[0];
|
|
|
/*
|
/*
|
* If interface is learning, and if source address
|
* If interface is learning, and if source address
|
* is not broadcast or multicast, record it's address.
|
* is not broadcast or multicast, record it's address.
|
*/
|
*/
|
if ((ifl->bif_flags & IFBIF_LEARNING) &&
|
if ((ifl->bif_flags & IFBIF_LEARNING) &&
|
(eh.ether_shost[0] & 1) == 0 &&
|
(eh.ether_shost[0] & 1) == 0 &&
|
!(eh.ether_shost[0] == 0 &&
|
!(eh.ether_shost[0] == 0 &&
|
eh.ether_shost[1] == 0 &&
|
eh.ether_shost[1] == 0 &&
|
eh.ether_shost[2] == 0 &&
|
eh.ether_shost[2] == 0 &&
|
eh.ether_shost[3] == 0 &&
|
eh.ether_shost[3] == 0 &&
|
eh.ether_shost[4] == 0 &&
|
eh.ether_shost[4] == 0 &&
|
eh.ether_shost[5] == 0))
|
eh.ether_shost[5] == 0))
|
bridge_rtupdate(sc, src, src_if, 0, IFBAF_DYNAMIC);
|
bridge_rtupdate(sc, src, src_if, 0, IFBAF_DYNAMIC);
|
|
|
/*
|
/*
|
* If packet is unicast, destined for someone on "this"
|
* If packet is unicast, destined for someone on "this"
|
* side of the bridge, drop it.
|
* side of the bridge, drop it.
|
*/
|
*/
|
if ((m->m_flags & (M_BCAST | M_MCAST)) == 0) {
|
if ((m->m_flags & (M_BCAST | M_MCAST)) == 0) {
|
dst_if = bridge_rtlookup(sc, dst);
|
dst_if = bridge_rtlookup(sc, dst);
|
if (dst_if == src_if) {
|
if (dst_if == src_if) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
} else
|
} else
|
dst_if = NULL;
|
dst_if = NULL;
|
|
|
/*
|
/*
|
* Multicast packets get handled a little differently:
|
* Multicast packets get handled a little differently:
|
* If interface is:
|
* If interface is:
|
* -link0,-link1 (default) Forward all multicast
|
* -link0,-link1 (default) Forward all multicast
|
* as broadcast.
|
* as broadcast.
|
* -link0,link1 Drop non-IP multicast, forward
|
* -link0,link1 Drop non-IP multicast, forward
|
* as broadcast IP multicast.
|
* as broadcast IP multicast.
|
* link0,-link1 Drop IP multicast, forward as
|
* link0,-link1 Drop IP multicast, forward as
|
* broadcast non-IP multicast.
|
* broadcast non-IP multicast.
|
* link0,link1 Drop all multicast.
|
* link0,link1 Drop all multicast.
|
*/
|
*/
|
if (m->m_flags & M_MCAST) {
|
if (m->m_flags & M_MCAST) {
|
if ((sc->sc_if.if_flags &
|
if ((sc->sc_if.if_flags &
|
(IFF_LINK0 | IFF_LINK1)) ==
|
(IFF_LINK0 | IFF_LINK1)) ==
|
(IFF_LINK0 | IFF_LINK1)) {
|
(IFF_LINK0 | IFF_LINK1)) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
if (sc->sc_if.if_flags & IFF_LINK0 &&
|
if (sc->sc_if.if_flags & IFF_LINK0 &&
|
ETHERADDR_IS_IP_MCAST(dst)) {
|
ETHERADDR_IS_IP_MCAST(dst)) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
if (sc->sc_if.if_flags & IFF_LINK1 &&
|
if (sc->sc_if.if_flags & IFF_LINK1 &&
|
!ETHERADDR_IS_IP_MCAST(dst)) {
|
!ETHERADDR_IS_IP_MCAST(dst)) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
}
|
}
|
|
|
if (ifl->bif_flags & IFBIF_BLOCKNONIP && bridge_blocknonip(&eh, m)) {
|
if (ifl->bif_flags & IFBIF_BLOCKNONIP && bridge_blocknonip(&eh, m)) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
|
|
if (SIMPLEQ_FIRST(&ifl->bif_brlin) &&
|
if (SIMPLEQ_FIRST(&ifl->bif_brlin) &&
|
bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlin), &eh) ==
|
bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlin), &eh) ==
|
BRL_ACTION_BLOCK) {
|
BRL_ACTION_BLOCK) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
|
|
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
|
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
|
m = bridge_filter(sc, src_if, &eh, m);
|
m = bridge_filter(sc, src_if, &eh, m);
|
if (m == NULL)
|
if (m == NULL)
|
return;
|
return;
|
#endif
|
#endif
|
|
|
/*
|
/*
|
* If the packet is a multicast or broadcast OR if we don't
|
* If the packet is a multicast or broadcast OR if we don't
|
* know any better, forward it to all interfaces.
|
* know any better, forward it to all interfaces.
|
*/
|
*/
|
if ((m->m_flags & (M_BCAST | M_MCAST)) || dst_if == NULL) {
|
if ((m->m_flags & (M_BCAST | M_MCAST)) || dst_if == NULL) {
|
sc->sc_if.if_imcasts++;
|
sc->sc_if.if_imcasts++;
|
s = splimp();
|
s = splimp();
|
bridge_broadcast(sc, src_if, &eh, m);
|
bridge_broadcast(sc, src_if, &eh, m);
|
splx(s);
|
splx(s);
|
return;
|
return;
|
}
|
}
|
|
|
/*
|
/*
|
* At this point, we're dealing with a unicast frame going to a
|
* At this point, we're dealing with a unicast frame going to a
|
* different interface
|
* different interface
|
*/
|
*/
|
if ((dst_if->if_flags & IFF_RUNNING) == 0) {
|
if ((dst_if->if_flags & IFF_RUNNING) == 0) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
ifl = LIST_FIRST(&sc->sc_iflist);
|
ifl = LIST_FIRST(&sc->sc_iflist);
|
while (ifl != NULL && ifl->ifp != dst_if)
|
while (ifl != NULL && ifl->ifp != dst_if)
|
ifl = LIST_NEXT(ifl, next);
|
ifl = LIST_NEXT(ifl, next);
|
if (SIMPLEQ_FIRST(&ifl->bif_brlout) &&
|
if (SIMPLEQ_FIRST(&ifl->bif_brlout) &&
|
bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlout), &eh) ==
|
bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlout), &eh) ==
|
BRL_ACTION_BLOCK) {
|
BRL_ACTION_BLOCK) {
|
m_freem(m);
|
m_freem(m);
|
return;
|
return;
|
}
|
}
|
s = splimp();
|
s = splimp();
|
if (IF_QFULL(&dst_if->if_snd)) {
|
if (IF_QFULL(&dst_if->if_snd)) {
|
sc->sc_if.if_oerrors++;
|
sc->sc_if.if_oerrors++;
|
m_freem(m);
|
m_freem(m);
|
splx(s);
|
splx(s);
|
return;
|
return;
|
}
|
}
|
sc->sc_if.if_opackets++;
|
sc->sc_if.if_opackets++;
|
sc->sc_if.if_obytes += m->m_pkthdr.len;
|
sc->sc_if.if_obytes += m->m_pkthdr.len;
|
// Also count the bytes in the outgoing interface; normally
|
// Also count the bytes in the outgoing interface; normally
|
// done in if_ethersubr.c but here we bypass that route.
|
// done in if_ethersubr.c but here we bypass that route.
|
dst_if->if_obytes += m->m_pkthdr.len;
|
dst_if->if_obytes += m->m_pkthdr.len;
|
IF_ENQUEUE(&dst_if->if_snd, m);
|
IF_ENQUEUE(&dst_if->if_snd, m);
|
if ((dst_if->if_flags & IFF_OACTIVE) == 0)
|
if ((dst_if->if_flags & IFF_OACTIVE) == 0)
|
(*dst_if->if_start)(dst_if);
|
(*dst_if->if_start)(dst_if);
|
splx(s);
|
splx(s);
|
}
|
}
|
|
|
/*
|
/*
|
* Receive input from an interface. Queue the packet for bridging if its
|
* Receive input from an interface. Queue the packet for bridging if its
|
* not for us, and schedule an interrupt.
|
* not for us, and schedule an interrupt.
|
*/
|
*/
|
struct mbuf *
|
struct mbuf *
|
bridge_input(ifp, eh, m)
|
bridge_input(ifp, eh, m)
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
struct ether_header *eh;
|
struct ether_header *eh;
|
struct mbuf *m;
|
struct mbuf *m;
|
{
|
{
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
int s;
|
int s;
|
struct bridge_iflist *ifl;
|
struct bridge_iflist *ifl;
|
struct arpcom *ac;
|
struct arpcom *ac;
|
struct mbuf *mc;
|
struct mbuf *mc;
|
|
|
/*
|
/*
|
* Make sure this interface is a bridge member.
|
* Make sure this interface is a bridge member.
|
*/
|
*/
|
if (ifp == NULL || ifp->if_bridge == NULL || m == NULL)
|
if (ifp == NULL || ifp->if_bridge == NULL || m == NULL)
|
return (m);
|
return (m);
|
|
|
if ((m->m_flags & M_PKTHDR) == 0)
|
if ((m->m_flags & M_PKTHDR) == 0)
|
panic("bridge_input(): no HDR");
|
panic("bridge_input(): no HDR");
|
|
|
sc = (struct bridge_softc *)ifp->if_bridge;
|
sc = (struct bridge_softc *)ifp->if_bridge;
|
if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
|
if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
|
return (m);
|
return (m);
|
|
|
if (m->m_flags & (M_BCAST | M_MCAST)) {
|
if (m->m_flags & (M_BCAST | M_MCAST)) {
|
/*
|
/*
|
* make a copy of 'm' with 'eh' tacked on to the
|
* make a copy of 'm' with 'eh' tacked on to the
|
* beginning. Return 'm' for local processing
|
* beginning. Return 'm' for local processing
|
* and enqueue the copy. Schedule netisr.
|
* and enqueue the copy. Schedule netisr.
|
*/
|
*/
|
mc = m_copym2(m, 0, M_COPYALL, M_NOWAIT);
|
mc = m_copym2(m, 0, M_COPYALL, M_NOWAIT);
|
if (mc == NULL)
|
if (mc == NULL)
|
return (m);
|
return (m);
|
M_PREPEND(mc, sizeof(struct ether_header), M_DONTWAIT);
|
M_PREPEND(mc, sizeof(struct ether_header), M_DONTWAIT);
|
if (mc == NULL)
|
if (mc == NULL)
|
return (m);
|
return (m);
|
bcopy(eh, mtod(mc, caddr_t), sizeof(struct ether_header));
|
bcopy(eh, mtod(mc, caddr_t), sizeof(struct ether_header));
|
s = splimp();
|
s = splimp();
|
if (IF_QFULL(&sc->sc_if.if_snd)) {
|
if (IF_QFULL(&sc->sc_if.if_snd)) {
|
m_freem(mc);
|
m_freem(mc);
|
splx(s);
|
splx(s);
|
return (m);
|
return (m);
|
}
|
}
|
IF_ENQUEUE(&sc->sc_if.if_snd, mc);
|
IF_ENQUEUE(&sc->sc_if.if_snd, mc);
|
splx(s);
|
splx(s);
|
schednetisr(NETISR_BRIDGE);
|
schednetisr(NETISR_BRIDGE);
|
return (m);
|
return (m);
|
}
|
}
|
|
|
/*
|
/*
|
* Unicast, make sure it's not for us.
|
* Unicast, make sure it's not for us.
|
*/
|
*/
|
for (ifl = LIST_FIRST(&sc->sc_iflist);ifl; ifl = LIST_NEXT(ifl,next)) {
|
for (ifl = LIST_FIRST(&sc->sc_iflist);ifl; ifl = LIST_NEXT(ifl,next)) {
|
if (ifl->ifp->if_type != IFT_ETHER)
|
if (ifl->ifp->if_type != IFT_ETHER)
|
continue;
|
continue;
|
ac = (struct arpcom *)ifl->ifp;
|
ac = (struct arpcom *)ifl->ifp;
|
if (bcmp(ac->ac_enaddr, eh->ether_dhost, ETHER_ADDR_LEN) == 0) {
|
if (bcmp(ac->ac_enaddr, eh->ether_dhost, ETHER_ADDR_LEN) == 0) {
|
if (ifl->bif_flags & IFBIF_LEARNING)
|
if (ifl->bif_flags & IFBIF_LEARNING)
|
bridge_rtupdate(sc,
|
bridge_rtupdate(sc,
|
(struct ether_addr *)&eh->ether_shost,
|
(struct ether_addr *)&eh->ether_shost,
|
ifp, 0, IFBAF_DYNAMIC);
|
ifp, 0, IFBAF_DYNAMIC);
|
m->m_pkthdr.rcvif = ifl->ifp;
|
m->m_pkthdr.rcvif = ifl->ifp;
|
return (m);
|
return (m);
|
}
|
}
|
if (bcmp(ac->ac_enaddr, eh->ether_shost, ETHER_ADDR_LEN) == 0) {
|
if (bcmp(ac->ac_enaddr, eh->ether_shost, ETHER_ADDR_LEN) == 0) {
|
m_freem(m);
|
m_freem(m);
|
return (NULL);
|
return (NULL);
|
}
|
}
|
}
|
}
|
M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
|
M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
|
if (m == NULL)
|
if (m == NULL)
|
return (NULL);
|
return (NULL);
|
bcopy(eh, mtod(m, caddr_t), sizeof(struct ether_header));
|
bcopy(eh, mtod(m, caddr_t), sizeof(struct ether_header));
|
s = splimp();
|
s = splimp();
|
if (IF_QFULL(&sc->sc_if.if_snd)) {
|
if (IF_QFULL(&sc->sc_if.if_snd)) {
|
m_freem(m);
|
m_freem(m);
|
splx(s);
|
splx(s);
|
return (NULL);
|
return (NULL);
|
}
|
}
|
IF_ENQUEUE(&sc->sc_if.if_snd, m);
|
IF_ENQUEUE(&sc->sc_if.if_snd, m);
|
splx(s);
|
splx(s);
|
schednetisr(NETISR_BRIDGE);
|
schednetisr(NETISR_BRIDGE);
|
return (NULL);
|
return (NULL);
|
}
|
}
|
|
|
/*
|
/*
|
* Send a frame to all interfaces that are members of the bridge
|
* Send a frame to all interfaces that are members of the bridge
|
* (except the one it came in on). This code assumes that it is
|
* (except the one it came in on). This code assumes that it is
|
* running at splnet or higher.
|
* running at splnet or higher.
|
*/
|
*/
|
void
|
void
|
bridge_broadcast(sc, ifp, eh, m)
|
bridge_broadcast(sc, ifp, eh, m)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
struct ether_header *eh;
|
struct ether_header *eh;
|
struct mbuf *m;
|
struct mbuf *m;
|
{
|
{
|
struct bridge_iflist *p;
|
struct bridge_iflist *p;
|
struct mbuf *mc;
|
struct mbuf *mc;
|
int used = 0;
|
int used = 0;
|
|
|
for (p = LIST_FIRST(&sc->sc_iflist); p; p = LIST_NEXT(p, next)) {
|
for (p = LIST_FIRST(&sc->sc_iflist); p; p = LIST_NEXT(p, next)) {
|
/*
|
/*
|
* Don't retransmit out of the same interface where
|
* Don't retransmit out of the same interface where
|
* the packet was received from.
|
* the packet was received from.
|
*/
|
*/
|
if (p->ifp->if_index == ifp->if_index)
|
if (p->ifp->if_index == ifp->if_index)
|
continue;
|
continue;
|
|
|
if ((p->bif_flags & IFBIF_DISCOVER) == 0 &&
|
if ((p->bif_flags & IFBIF_DISCOVER) == 0 &&
|
(m->m_flags & (M_BCAST | M_MCAST)) == 0)
|
(m->m_flags & (M_BCAST | M_MCAST)) == 0)
|
continue;
|
continue;
|
|
|
if ((p->ifp->if_flags & IFF_RUNNING) == 0)
|
if ((p->ifp->if_flags & IFF_RUNNING) == 0)
|
continue;
|
continue;
|
|
|
if (IF_QFULL(&p->ifp->if_snd)) {
|
if (IF_QFULL(&p->ifp->if_snd)) {
|
sc->sc_if.if_oerrors++;
|
sc->sc_if.if_oerrors++;
|
continue;
|
continue;
|
}
|
}
|
|
|
if (SIMPLEQ_FIRST(&p->bif_brlout) &&
|
if (SIMPLEQ_FIRST(&p->bif_brlout) &&
|
bridge_filterrule(SIMPLEQ_FIRST(&p->bif_brlout), eh) ==
|
bridge_filterrule(SIMPLEQ_FIRST(&p->bif_brlout), eh) ==
|
BRL_ACTION_BLOCK)
|
BRL_ACTION_BLOCK)
|
continue;
|
continue;
|
|
|
/* If last one, reuse the passed-in mbuf */
|
/* If last one, reuse the passed-in mbuf */
|
if (LIST_NEXT(p, next) == NULL) {
|
if (LIST_NEXT(p, next) == NULL) {
|
mc = m;
|
mc = m;
|
used = 1;
|
used = 1;
|
} else {
|
} else {
|
mc = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
|
mc = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
|
if (mc == NULL) {
|
if (mc == NULL) {
|
sc->sc_if.if_oerrors++;
|
sc->sc_if.if_oerrors++;
|
continue;
|
continue;
|
}
|
}
|
}
|
}
|
|
|
if (p->bif_flags & IFBIF_BLOCKNONIP &&
|
if (p->bif_flags & IFBIF_BLOCKNONIP &&
|
bridge_blocknonip(eh, mc)) {
|
bridge_blocknonip(eh, mc)) {
|
m_freem(mc);
|
m_freem(mc);
|
continue;
|
continue;
|
}
|
}
|
|
|
sc->sc_if.if_opackets++;
|
sc->sc_if.if_opackets++;
|
sc->sc_if.if_obytes += mc->m_pkthdr.len;
|
sc->sc_if.if_obytes += mc->m_pkthdr.len;
|
if (ifp && ((eh->ether_shost[0] & 1) == 0) )
|
if (ifp && ((eh->ether_shost[0] & 1) == 0) )
|
ifp->if_omcasts++;
|
ifp->if_omcasts++;
|
// Also count the bytes in the outgoing interface; normally
|
// Also count the bytes in the outgoing interface; normally
|
// done in if_ethersubr.c but here we bypass that route.
|
// done in if_ethersubr.c but here we bypass that route.
|
p->ifp->if_obytes += m->m_pkthdr.len;
|
p->ifp->if_obytes += m->m_pkthdr.len;
|
IF_ENQUEUE(&p->ifp->if_snd, mc);
|
IF_ENQUEUE(&p->ifp->if_snd, mc);
|
if ((p->ifp->if_flags & IFF_OACTIVE) == 0)
|
if ((p->ifp->if_flags & IFF_OACTIVE) == 0)
|
(*p->ifp->if_start)(p->ifp);
|
(*p->ifp->if_start)(p->ifp);
|
}
|
}
|
|
|
if (!used)
|
if (!used)
|
m_freem(m);
|
m_freem(m);
|
}
|
}
|
|
|
struct ifnet *
|
struct ifnet *
|
bridge_rtupdate(sc, ea, ifp, setflags, flags)
|
bridge_rtupdate(sc, ea, ifp, setflags, flags)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct ether_addr *ea;
|
struct ether_addr *ea;
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
int setflags;
|
int setflags;
|
u_int8_t flags;
|
u_int8_t flags;
|
{
|
{
|
struct bridge_rtnode *p, *q;
|
struct bridge_rtnode *p, *q;
|
u_int32_t h;
|
u_int32_t h;
|
int s, dir;
|
int s, dir;
|
|
|
s = splhigh();
|
s = splhigh();
|
if (sc->sc_rts == NULL) {
|
if (sc->sc_rts == NULL) {
|
if (setflags && flags == IFBAF_STATIC) {
|
if (setflags && flags == IFBAF_STATIC) {
|
sc->sc_rts = (struct bridge_rthead *)malloc(
|
sc->sc_rts = (struct bridge_rthead *)malloc(
|
BRIDGE_RTABLE_SIZE *
|
BRIDGE_RTABLE_SIZE *
|
(sizeof(struct bridge_rthead)),M_DEVBUF,M_NOWAIT);
|
(sizeof(struct bridge_rthead)),M_DEVBUF,M_NOWAIT);
|
|
|
if (sc->sc_rts == NULL)
|
if (sc->sc_rts == NULL)
|
goto done;
|
goto done;
|
|
|
for (h = 0; h < BRIDGE_RTABLE_SIZE; h++)
|
for (h = 0; h < BRIDGE_RTABLE_SIZE; h++)
|
LIST_INIT(&sc->sc_rts[h]);
|
LIST_INIT(&sc->sc_rts[h]);
|
} else
|
} else
|
goto done;
|
goto done;
|
}
|
}
|
|
|
h = bridge_hash(ea);
|
h = bridge_hash(ea);
|
p = LIST_FIRST(&sc->sc_rts[h]);
|
p = LIST_FIRST(&sc->sc_rts[h]);
|
if (p == NULL) {
|
if (p == NULL) {
|
if (sc->sc_brtcnt >= sc->sc_brtmax)
|
if (sc->sc_brtcnt >= sc->sc_brtmax)
|
goto done;
|
goto done;
|
p = (struct bridge_rtnode *)malloc(
|
p = (struct bridge_rtnode *)malloc(
|
sizeof(struct bridge_rtnode), M_DEVBUF, M_NOWAIT);
|
sizeof(struct bridge_rtnode), M_DEVBUF, M_NOWAIT);
|
if (p == NULL)
|
if (p == NULL)
|
goto done;
|
goto done;
|
|
|
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
|
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
|
p->brt_if = ifp;
|
p->brt_if = ifp;
|
p->brt_age = 1;
|
p->brt_age = 1;
|
|
|
if (setflags)
|
if (setflags)
|
p->brt_flags = flags;
|
p->brt_flags = flags;
|
else
|
else
|
p->brt_flags = IFBAF_DYNAMIC;
|
p->brt_flags = IFBAF_DYNAMIC;
|
|
|
LIST_INSERT_HEAD(&sc->sc_rts[h], p, brt_next);
|
LIST_INSERT_HEAD(&sc->sc_rts[h], p, brt_next);
|
sc->sc_brtcnt++;
|
sc->sc_brtcnt++;
|
goto want;
|
goto want;
|
}
|
}
|
|
|
do {
|
do {
|
q = p;
|
q = p;
|
p = LIST_NEXT(p, brt_next);
|
p = LIST_NEXT(p, brt_next);
|
|
|
dir = memcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
|
dir = memcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
|
if (dir == 0) {
|
if (dir == 0) {
|
if (setflags) {
|
if (setflags) {
|
q->brt_if = ifp;
|
q->brt_if = ifp;
|
q->brt_flags = flags;
|
q->brt_flags = flags;
|
}
|
}
|
|
|
if (q->brt_if == ifp)
|
if (q->brt_if == ifp)
|
q->brt_age = 1;
|
q->brt_age = 1;
|
ifp = q->brt_if;
|
ifp = q->brt_if;
|
goto want;
|
goto want;
|
}
|
}
|
|
|
if (dir > 0) {
|
if (dir > 0) {
|
if (sc->sc_brtcnt >= sc->sc_brtmax)
|
if (sc->sc_brtcnt >= sc->sc_brtmax)
|
goto done;
|
goto done;
|
p = (struct bridge_rtnode *)malloc(
|
p = (struct bridge_rtnode *)malloc(
|
sizeof(struct bridge_rtnode), M_DEVBUF, M_NOWAIT);
|
sizeof(struct bridge_rtnode), M_DEVBUF, M_NOWAIT);
|
if (p == NULL)
|
if (p == NULL)
|
goto done;
|
goto done;
|
|
|
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
|
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
|
p->brt_if = ifp;
|
p->brt_if = ifp;
|
p->brt_age = 1;
|
p->brt_age = 1;
|
|
|
if (setflags)
|
if (setflags)
|
p->brt_flags = flags;
|
p->brt_flags = flags;
|
else
|
else
|
p->brt_flags = IFBAF_DYNAMIC;
|
p->brt_flags = IFBAF_DYNAMIC;
|
|
|
LIST_INSERT_BEFORE(q, p, brt_next);
|
LIST_INSERT_BEFORE(q, p, brt_next);
|
sc->sc_brtcnt++;
|
sc->sc_brtcnt++;
|
goto want;
|
goto want;
|
}
|
}
|
|
|
if (p == NULL) {
|
if (p == NULL) {
|
if (sc->sc_brtcnt >= sc->sc_brtmax)
|
if (sc->sc_brtcnt >= sc->sc_brtmax)
|
goto done;
|
goto done;
|
p = (struct bridge_rtnode *)malloc(
|
p = (struct bridge_rtnode *)malloc(
|
sizeof(struct bridge_rtnode), M_DEVBUF, M_NOWAIT);
|
sizeof(struct bridge_rtnode), M_DEVBUF, M_NOWAIT);
|
if (p == NULL)
|
if (p == NULL)
|
goto done;
|
goto done;
|
|
|
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
|
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
|
p->brt_if = ifp;
|
p->brt_if = ifp;
|
p->brt_age = 1;
|
p->brt_age = 1;
|
|
|
if (setflags)
|
if (setflags)
|
p->brt_flags = flags;
|
p->brt_flags = flags;
|
else
|
else
|
p->brt_flags = IFBAF_DYNAMIC;
|
p->brt_flags = IFBAF_DYNAMIC;
|
LIST_INSERT_AFTER(q, p, brt_next);
|
LIST_INSERT_AFTER(q, p, brt_next);
|
sc->sc_brtcnt++;
|
sc->sc_brtcnt++;
|
goto want;
|
goto want;
|
}
|
}
|
} while (p != NULL);
|
} while (p != NULL);
|
|
|
done:
|
done:
|
ifp = NULL;
|
ifp = NULL;
|
want:
|
want:
|
splx(s);
|
splx(s);
|
return (ifp);
|
return (ifp);
|
}
|
}
|
|
|
struct ifnet *
|
struct ifnet *
|
bridge_rtlookup(sc, ea)
|
bridge_rtlookup(sc, ea)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct ether_addr *ea;
|
struct ether_addr *ea;
|
{
|
{
|
struct bridge_rtnode *p;
|
struct bridge_rtnode *p;
|
u_int32_t h;
|
u_int32_t h;
|
int s, dir;
|
int s, dir;
|
|
|
/*
|
/*
|
* Lock out everything else
|
* Lock out everything else
|
*/
|
*/
|
s = splhigh();
|
s = splhigh();
|
|
|
if (sc->sc_rts == NULL)
|
if (sc->sc_rts == NULL)
|
goto fail;
|
goto fail;
|
|
|
h = bridge_hash(ea);
|
h = bridge_hash(ea);
|
p = LIST_FIRST(&sc->sc_rts[h]);
|
p = LIST_FIRST(&sc->sc_rts[h]);
|
while (p != NULL) {
|
while (p != NULL) {
|
dir = memcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
|
dir = memcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
|
if (dir == 0) {
|
if (dir == 0) {
|
splx(s);
|
splx(s);
|
return (p->brt_if);
|
return (p->brt_if);
|
}
|
}
|
if (dir > 0)
|
if (dir > 0)
|
goto fail;
|
goto fail;
|
p = LIST_NEXT(p, brt_next);
|
p = LIST_NEXT(p, brt_next);
|
}
|
}
|
fail:
|
fail:
|
splx(s);
|
splx(s);
|
return (NULL);
|
return (NULL);
|
}
|
}
|
|
|
/*
|
/*
|
* The following hash function is adapted from 'Hash Functions' by Bob Jenkins
|
* The following hash function is adapted from 'Hash Functions' by Bob Jenkins
|
* ("Algorithm Alley", Dr. Dobbs Journal, September 1997).
|
* ("Algorithm Alley", Dr. Dobbs Journal, September 1997).
|
* "You may use this code any way you wish, private, educational, or
|
* "You may use this code any way you wish, private, educational, or
|
* commercial. It's free."
|
* commercial. It's free."
|
*/
|
*/
|
#define mix(a,b,c) \
|
#define mix(a,b,c) \
|
do { \
|
do { \
|
a -= b; a -= c; a ^= (c >> 13); \
|
a -= b; a -= c; a ^= (c >> 13); \
|
b -= c; b -= a; b ^= (a << 8); \
|
b -= c; b -= a; b ^= (a << 8); \
|
c -= a; c -= b; c ^= (b >> 13); \
|
c -= a; c -= b; c ^= (b >> 13); \
|
a -= b; a -= c; a ^= (c >> 12); \
|
a -= b; a -= c; a ^= (c >> 12); \
|
b -= c; b -= a; b ^= (a << 16); \
|
b -= c; b -= a; b ^= (a << 16); \
|
c -= a; c -= b; c ^= (b >> 5); \
|
c -= a; c -= b; c ^= (b >> 5); \
|
a -= b; a -= c; a ^= (c >> 3); \
|
a -= b; a -= c; a ^= (c >> 3); \
|
b -= c; b -= a; b ^= (a << 10); \
|
b -= c; b -= a; b ^= (a << 10); \
|
c -= a; c -= b; c ^= (b >> 15); \
|
c -= a; c -= b; c ^= (b >> 15); \
|
} while(0)
|
} while(0)
|
|
|
u_int32_t
|
u_int32_t
|
bridge_hash(addr)
|
bridge_hash(addr)
|
struct ether_addr *addr;
|
struct ether_addr *addr;
|
{
|
{
|
u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = 0xdeadbeef;
|
u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = 0xdeadbeef;
|
|
|
b += addr->ether_addr_octet[5] << 8;
|
b += addr->ether_addr_octet[5] << 8;
|
b += addr->ether_addr_octet[4];
|
b += addr->ether_addr_octet[4];
|
a += addr->ether_addr_octet[3] << 24;
|
a += addr->ether_addr_octet[3] << 24;
|
a += addr->ether_addr_octet[2] << 16;
|
a += addr->ether_addr_octet[2] << 16;
|
a += addr->ether_addr_octet[1] << 8;
|
a += addr->ether_addr_octet[1] << 8;
|
a += addr->ether_addr_octet[0];
|
a += addr->ether_addr_octet[0];
|
|
|
mix(a, b, c);
|
mix(a, b, c);
|
return (c & BRIDGE_RTABLE_MASK);
|
return (c & BRIDGE_RTABLE_MASK);
|
}
|
}
|
|
|
/*
|
/*
|
* Trim the routing table so that we've got a number of routes
|
* Trim the routing table so that we've got a number of routes
|
* less than or equal to the maximum.
|
* less than or equal to the maximum.
|
*/
|
*/
|
void
|
void
|
bridge_rttrim(sc)
|
bridge_rttrim(sc)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
{
|
{
|
struct bridge_rtnode *n, *p;
|
struct bridge_rtnode *n, *p;
|
int s, i;
|
int s, i;
|
|
|
s = splhigh();
|
s = splhigh();
|
if (sc->sc_rts == NULL)
|
if (sc->sc_rts == NULL)
|
goto done;
|
goto done;
|
|
|
/*
|
/*
|
* Make sure we have to trim the address table
|
* Make sure we have to trim the address table
|
*/
|
*/
|
if (sc->sc_brtcnt <= sc->sc_brtmax)
|
if (sc->sc_brtcnt <= sc->sc_brtmax)
|
goto done;
|
goto done;
|
|
|
/*
|
/*
|
* Force an aging cycle, this might trim enough addresses.
|
* Force an aging cycle, this might trim enough addresses.
|
*/
|
*/
|
splx(s);
|
splx(s);
|
bridge_rtage(sc);
|
bridge_rtage(sc);
|
s = splhigh();
|
s = splhigh();
|
|
|
if (sc->sc_brtcnt <= sc->sc_brtmax)
|
if (sc->sc_brtcnt <= sc->sc_brtmax)
|
goto done;
|
goto done;
|
|
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
while (n != NULL) {
|
while (n != NULL) {
|
p = LIST_NEXT(n, brt_next);
|
p = LIST_NEXT(n, brt_next);
|
if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
|
if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
|
LIST_REMOVE(n, brt_next);
|
LIST_REMOVE(n, brt_next);
|
sc->sc_brtcnt--;
|
sc->sc_brtcnt--;
|
free(n, M_DEVBUF);
|
free(n, M_DEVBUF);
|
n = p;
|
n = p;
|
if (sc->sc_brtcnt <= sc->sc_brtmax)
|
if (sc->sc_brtcnt <= sc->sc_brtmax)
|
goto done;
|
goto done;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
done:
|
done:
|
if (sc->sc_rts != NULL && sc->sc_brtcnt == 0 &&
|
if (sc->sc_rts != NULL && sc->sc_brtcnt == 0 &&
|
(sc->sc_if.if_flags & IFF_UP) == 0) {
|
(sc->sc_if.if_flags & IFF_UP) == 0) {
|
free(sc->sc_rts, M_DEVBUF);
|
free(sc->sc_rts, M_DEVBUF);
|
sc->sc_rts = NULL;
|
sc->sc_rts = NULL;
|
}
|
}
|
|
|
splx(s);
|
splx(s);
|
}
|
}
|
|
|
/*
|
/*
|
* Perform an aging cycle
|
* Perform an aging cycle
|
*/
|
*/
|
void
|
void
|
bridge_rtage(vsc)
|
bridge_rtage(vsc)
|
void *vsc;
|
void *vsc;
|
{
|
{
|
struct bridge_softc *sc = (struct bridge_softc *)vsc;
|
struct bridge_softc *sc = (struct bridge_softc *)vsc;
|
struct bridge_rtnode *n, *p;
|
struct bridge_rtnode *n, *p;
|
int s, i;
|
int s, i;
|
|
|
s = splhigh();
|
s = splhigh();
|
if (sc->sc_rts == NULL) {
|
if (sc->sc_rts == NULL) {
|
splx(s);
|
splx(s);
|
return;
|
return;
|
}
|
}
|
|
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
while (n != NULL) {
|
while (n != NULL) {
|
if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) {
|
if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) {
|
n->brt_age = !n->brt_age;
|
n->brt_age = !n->brt_age;
|
if (n->brt_age)
|
if (n->brt_age)
|
n->brt_age = 0;
|
n->brt_age = 0;
|
n = LIST_NEXT(n, brt_next);
|
n = LIST_NEXT(n, brt_next);
|
} else if (n->brt_age) {
|
} else if (n->brt_age) {
|
n->brt_age = 0;
|
n->brt_age = 0;
|
n = LIST_NEXT(n, brt_next);
|
n = LIST_NEXT(n, brt_next);
|
} else {
|
} else {
|
p = LIST_NEXT(n, brt_next);
|
p = LIST_NEXT(n, brt_next);
|
LIST_REMOVE(n, brt_next);
|
LIST_REMOVE(n, brt_next);
|
sc->sc_brtcnt--;
|
sc->sc_brtcnt--;
|
free(n, M_DEVBUF);
|
free(n, M_DEVBUF);
|
n = p;
|
n = p;
|
}
|
}
|
}
|
}
|
}
|
}
|
splx(s);
|
splx(s);
|
|
|
if (sc->sc_brttimeout != 0)
|
if (sc->sc_brttimeout != 0)
|
timeout(bridge_rtage, sc, sc->sc_brttimeout);
|
timeout(bridge_rtage, sc, sc->sc_brttimeout);
|
}
|
}
|
|
|
/*
|
/*
|
* Remove all dynamic addresses from the cache
|
* Remove all dynamic addresses from the cache
|
*/
|
*/
|
int
|
int
|
bridge_rtflush(sc, full)
|
bridge_rtflush(sc, full)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
int full;
|
int full;
|
{
|
{
|
int s, i;
|
int s, i;
|
struct bridge_rtnode *p, *n;
|
struct bridge_rtnode *p, *n;
|
|
|
s = splhigh();
|
s = splhigh();
|
if (sc->sc_rts == NULL)
|
if (sc->sc_rts == NULL)
|
goto done;
|
goto done;
|
|
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
while (n != NULL) {
|
while (n != NULL) {
|
if (full ||
|
if (full ||
|
(n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
|
(n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
|
p = LIST_NEXT(n, brt_next);
|
p = LIST_NEXT(n, brt_next);
|
LIST_REMOVE(n, brt_next);
|
LIST_REMOVE(n, brt_next);
|
sc->sc_brtcnt--;
|
sc->sc_brtcnt--;
|
free(n, M_DEVBUF);
|
free(n, M_DEVBUF);
|
n = p;
|
n = p;
|
} else
|
} else
|
n = LIST_NEXT(n, brt_next);
|
n = LIST_NEXT(n, brt_next);
|
}
|
}
|
}
|
}
|
|
|
if (sc->sc_brtcnt == 0 && (sc->sc_if.if_flags & IFF_UP) == 0) {
|
if (sc->sc_brtcnt == 0 && (sc->sc_if.if_flags & IFF_UP) == 0) {
|
free(sc->sc_rts, M_DEVBUF);
|
free(sc->sc_rts, M_DEVBUF);
|
sc->sc_rts = NULL;
|
sc->sc_rts = NULL;
|
}
|
}
|
|
|
done:
|
done:
|
splx(s);
|
splx(s);
|
return (0);
|
return (0);
|
}
|
}
|
|
|
/*
|
/*
|
* Remove an address from the cache
|
* Remove an address from the cache
|
*/
|
*/
|
int
|
int
|
bridge_rtdaddr(sc, ea)
|
bridge_rtdaddr(sc, ea)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct ether_addr *ea;
|
struct ether_addr *ea;
|
{
|
{
|
int h, s;
|
int h, s;
|
struct bridge_rtnode *p;
|
struct bridge_rtnode *p;
|
|
|
s = splhigh();
|
s = splhigh();
|
if (sc->sc_rts == NULL)
|
if (sc->sc_rts == NULL)
|
goto done;
|
goto done;
|
|
|
h = bridge_hash(ea);
|
h = bridge_hash(ea);
|
p = LIST_FIRST(&sc->sc_rts[h]);
|
p = LIST_FIRST(&sc->sc_rts[h]);
|
while (p != NULL) {
|
while (p != NULL) {
|
if (bcmp(ea, &p->brt_addr, sizeof(p->brt_addr)) == 0) {
|
if (bcmp(ea, &p->brt_addr, sizeof(p->brt_addr)) == 0) {
|
LIST_REMOVE(p, brt_next);
|
LIST_REMOVE(p, brt_next);
|
sc->sc_brtcnt--;
|
sc->sc_brtcnt--;
|
free(p, M_DEVBUF);
|
free(p, M_DEVBUF);
|
if (sc->sc_brtcnt == 0 &&
|
if (sc->sc_brtcnt == 0 &&
|
(sc->sc_if.if_flags & IFF_UP) == 0) {
|
(sc->sc_if.if_flags & IFF_UP) == 0) {
|
free(sc->sc_rts, M_DEVBUF);
|
free(sc->sc_rts, M_DEVBUF);
|
sc->sc_rts = NULL;
|
sc->sc_rts = NULL;
|
}
|
}
|
splx(s);
|
splx(s);
|
return (0);
|
return (0);
|
}
|
}
|
p = LIST_NEXT(p, brt_next);
|
p = LIST_NEXT(p, brt_next);
|
}
|
}
|
|
|
done:
|
done:
|
splx(s);
|
splx(s);
|
return (ENOENT);
|
return (ENOENT);
|
}
|
}
|
/*
|
/*
|
* Delete routes to a specific interface member.
|
* Delete routes to a specific interface member.
|
*/
|
*/
|
void
|
void
|
bridge_rtdelete(sc, ifp)
|
bridge_rtdelete(sc, ifp)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
{
|
{
|
int i, s;
|
int i, s;
|
struct bridge_rtnode *n, *p;
|
struct bridge_rtnode *n, *p;
|
|
|
s = splhigh();
|
s = splhigh();
|
if (sc->sc_rts == NULL)
|
if (sc->sc_rts == NULL)
|
goto done;
|
goto done;
|
|
|
/*
|
/*
|
* Loop through all of the hash buckets and traverse each
|
* Loop through all of the hash buckets and traverse each
|
* chain looking for routes to this interface.
|
* chain looking for routes to this interface.
|
*/
|
*/
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
while (n != NULL) {
|
while (n != NULL) {
|
if (n->brt_if == ifp) { /* found one */
|
if (n->brt_if == ifp) { /* found one */
|
p = LIST_NEXT(n, brt_next);
|
p = LIST_NEXT(n, brt_next);
|
LIST_REMOVE(n, brt_next);
|
LIST_REMOVE(n, brt_next);
|
sc->sc_brtcnt--;
|
sc->sc_brtcnt--;
|
free(n, M_DEVBUF);
|
free(n, M_DEVBUF);
|
n = p;
|
n = p;
|
} else
|
} else
|
n = LIST_NEXT(n, brt_next);
|
n = LIST_NEXT(n, brt_next);
|
}
|
}
|
}
|
}
|
if (sc->sc_brtcnt == 0 && (sc->sc_if.if_flags & IFF_UP) == 0) {
|
if (sc->sc_brtcnt == 0 && (sc->sc_if.if_flags & IFF_UP) == 0) {
|
free(sc->sc_rts, M_DEVBUF);
|
free(sc->sc_rts, M_DEVBUF);
|
sc->sc_rts = NULL;
|
sc->sc_rts = NULL;
|
}
|
}
|
|
|
done:
|
done:
|
splx(s);
|
splx(s);
|
}
|
}
|
|
|
/*
|
/*
|
* Gather all of the routes for this interface.
|
* Gather all of the routes for this interface.
|
*/
|
*/
|
int
|
int
|
bridge_rtfind(sc, baconf)
|
bridge_rtfind(sc, baconf)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct ifbaconf *baconf;
|
struct ifbaconf *baconf;
|
{
|
{
|
int i, s, error = 0;
|
int i, s, error = 0;
|
u_int32_t cnt = 0;
|
u_int32_t cnt = 0;
|
struct bridge_rtnode *n;
|
struct bridge_rtnode *n;
|
struct ifbareq bareq;
|
struct ifbareq bareq;
|
|
|
s = splhigh();
|
s = splhigh();
|
|
|
if (sc->sc_rts == NULL || baconf->ifbac_len == 0)
|
if (sc->sc_rts == NULL || baconf->ifbac_len == 0)
|
goto done;
|
goto done;
|
|
|
for (i = 0, cnt = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
for (i = 0, cnt = 0; i < BRIDGE_RTABLE_SIZE; i++) {
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
n = LIST_FIRST(&sc->sc_rts[i]);
|
while (n != NULL) {
|
while (n != NULL) {
|
if (baconf->ifbac_len <
|
if (baconf->ifbac_len <
|
(cnt + 1) * sizeof(struct ifbareq))
|
(cnt + 1) * sizeof(struct ifbareq))
|
goto done;
|
goto done;
|
bcopy(sc->sc_if.if_xname, bareq.ifba_name,
|
bcopy(sc->sc_if.if_xname, bareq.ifba_name,
|
sizeof(bareq.ifba_name));
|
sizeof(bareq.ifba_name));
|
bcopy(n->brt_if->if_xname, bareq.ifba_ifsname,
|
bcopy(n->brt_if->if_xname, bareq.ifba_ifsname,
|
sizeof(bareq.ifba_ifsname));
|
sizeof(bareq.ifba_ifsname));
|
bcopy(&n->brt_addr, &bareq.ifba_dst,
|
bcopy(&n->brt_addr, &bareq.ifba_dst,
|
sizeof(bareq.ifba_dst));
|
sizeof(bareq.ifba_dst));
|
bareq.ifba_age = n->brt_age;
|
bareq.ifba_age = n->brt_age;
|
bareq.ifba_flags = n->brt_flags;
|
bareq.ifba_flags = n->brt_flags;
|
error = copyout((caddr_t)&bareq,
|
error = copyout((caddr_t)&bareq,
|
(caddr_t)(baconf->ifbac_req + cnt), sizeof(bareq));
|
(caddr_t)(baconf->ifbac_req + cnt), sizeof(bareq));
|
if (error)
|
if (error)
|
goto done;
|
goto done;
|
n = LIST_NEXT(n, brt_next);
|
n = LIST_NEXT(n, brt_next);
|
cnt++;
|
cnt++;
|
}
|
}
|
}
|
}
|
done:
|
done:
|
baconf->ifbac_len = cnt * sizeof(struct ifbareq);
|
baconf->ifbac_len = cnt * sizeof(struct ifbareq);
|
splx(s);
|
splx(s);
|
return (error);
|
return (error);
|
}
|
}
|
|
|
/*
|
/*
|
* Block non-ip frames:
|
* Block non-ip frames:
|
* Returns 0 if frame is ip, and 1 if it should be dropped.
|
* Returns 0 if frame is ip, and 1 if it should be dropped.
|
*/
|
*/
|
int
|
int
|
bridge_blocknonip(eh, m)
|
bridge_blocknonip(eh, m)
|
struct ether_header *eh;
|
struct ether_header *eh;
|
struct mbuf *m;
|
struct mbuf *m;
|
{
|
{
|
struct snap snap;
|
struct snap snap;
|
u_int16_t etype;
|
u_int16_t etype;
|
|
|
if (m->m_pkthdr.len < sizeof(struct ether_header))
|
if (m->m_pkthdr.len < sizeof(struct ether_header))
|
return (1);
|
return (1);
|
|
|
etype = ntohs(eh->ether_type);
|
etype = ntohs(eh->ether_type);
|
switch (etype) {
|
switch (etype) {
|
case ETHERTYPE_ARP:
|
case ETHERTYPE_ARP:
|
case ETHERTYPE_REVARP:
|
case ETHERTYPE_REVARP:
|
case ETHERTYPE_IP:
|
case ETHERTYPE_IP:
|
case ETHERTYPE_IPV6:
|
case ETHERTYPE_IPV6:
|
return (0);
|
return (0);
|
}
|
}
|
|
|
if (etype > ETHERMTU)
|
if (etype > ETHERMTU)
|
return (1);
|
return (1);
|
|
|
if (m->m_pkthdr.len <
|
if (m->m_pkthdr.len <
|
(sizeof(struct ether_header) + sizeof(struct snap)))
|
(sizeof(struct ether_header) + sizeof(struct snap)))
|
return (1);
|
return (1);
|
|
|
m_copydata(m, sizeof(struct ether_header), sizeof(struct snap),
|
m_copydata(m, sizeof(struct ether_header), sizeof(struct snap),
|
(caddr_t)&snap);
|
(caddr_t)&snap);
|
|
|
etype = ntohs(snap.type);
|
etype = ntohs(snap.type);
|
if (snap.dsap == LLC_SNAP_LSAP && snap.ssap == LLC_SNAP_LSAP &&
|
if (snap.dsap == LLC_SNAP_LSAP && snap.ssap == LLC_SNAP_LSAP &&
|
snap.control == LLC_UI &&
|
snap.control == LLC_UI &&
|
snap.org[0] == 0 && snap.org[1] == 0 && snap.org[2] == 0 &&
|
snap.org[0] == 0 && snap.org[1] == 0 && snap.org[2] == 0 &&
|
(etype == ETHERTYPE_ARP ||
|
(etype == ETHERTYPE_ARP ||
|
etype == ETHERTYPE_REVARP ||
|
etype == ETHERTYPE_REVARP ||
|
etype == ETHERTYPE_IP ||
|
etype == ETHERTYPE_IP ||
|
etype == ETHERTYPE_IPV6)) {
|
etype == ETHERTYPE_IPV6)) {
|
return (0);
|
return (0);
|
}
|
}
|
|
|
return (1);
|
return (1);
|
}
|
}
|
|
|
u_int8_t
|
u_int8_t
|
bridge_filterrule(n, eh)
|
bridge_filterrule(n, eh)
|
struct brl_node *n;
|
struct brl_node *n;
|
struct ether_header *eh;
|
struct ether_header *eh;
|
{
|
{
|
u_int8_t flags;
|
u_int8_t flags;
|
|
|
for (; n != NULL; n = SIMPLEQ_NEXT(n, brl_next)) {
|
for (; n != NULL; n = SIMPLEQ_NEXT(n, brl_next)) {
|
flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID);
|
flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID);
|
if (flags == 0)
|
if (flags == 0)
|
return (n->brl_action);
|
return (n->brl_action);
|
if (flags == (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID)) {
|
if (flags == (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID)) {
|
if (bcmp(eh->ether_shost, &n->brl_src, ETHER_ADDR_LEN))
|
if (bcmp(eh->ether_shost, &n->brl_src, ETHER_ADDR_LEN))
|
continue;
|
continue;
|
if (bcmp(eh->ether_dhost, &n->brl_src, ETHER_ADDR_LEN))
|
if (bcmp(eh->ether_dhost, &n->brl_src, ETHER_ADDR_LEN))
|
continue;
|
continue;
|
return (n->brl_action);
|
return (n->brl_action);
|
}
|
}
|
if (flags == BRL_FLAG_SRCVALID) {
|
if (flags == BRL_FLAG_SRCVALID) {
|
if (bcmp(eh->ether_shost, &n->brl_src, ETHER_ADDR_LEN))
|
if (bcmp(eh->ether_shost, &n->brl_src, ETHER_ADDR_LEN))
|
continue;
|
continue;
|
return (n->brl_action);
|
return (n->brl_action);
|
}
|
}
|
if (flags == BRL_FLAG_DSTVALID) {
|
if (flags == BRL_FLAG_DSTVALID) {
|
if (bcmp(eh->ether_dhost, &n->brl_dst, ETHER_ADDR_LEN))
|
if (bcmp(eh->ether_dhost, &n->brl_dst, ETHER_ADDR_LEN))
|
continue;
|
continue;
|
return (n->brl_action);
|
return (n->brl_action);
|
}
|
}
|
}
|
}
|
return (BRL_ACTION_PASS);
|
return (BRL_ACTION_PASS);
|
}
|
}
|
|
|
int
|
int
|
bridge_addrule(bif, req, out)
|
bridge_addrule(bif, req, out)
|
struct bridge_iflist *bif;
|
struct bridge_iflist *bif;
|
struct ifbrlreq *req;
|
struct ifbrlreq *req;
|
int out;
|
int out;
|
{
|
{
|
struct brl_node *n;
|
struct brl_node *n;
|
|
|
n = (struct brl_node *)malloc(sizeof(struct brl_node), M_DEVBUF, M_NOWAIT);
|
n = (struct brl_node *)malloc(sizeof(struct brl_node), M_DEVBUF, M_NOWAIT);
|
if (n == NULL)
|
if (n == NULL)
|
return (ENOMEM);
|
return (ENOMEM);
|
bcopy(&req->ifbr_src, &n->brl_src, sizeof(struct ether_addr));
|
bcopy(&req->ifbr_src, &n->brl_src, sizeof(struct ether_addr));
|
bcopy(&req->ifbr_dst, &n->brl_dst, sizeof(struct ether_addr));
|
bcopy(&req->ifbr_dst, &n->brl_dst, sizeof(struct ether_addr));
|
n->brl_action = req->ifbr_action;
|
n->brl_action = req->ifbr_action;
|
n->brl_flags = req->ifbr_flags;
|
n->brl_flags = req->ifbr_flags;
|
if (out) {
|
if (out) {
|
n->brl_flags &= ~BRL_FLAG_IN;
|
n->brl_flags &= ~BRL_FLAG_IN;
|
n->brl_flags |= BRL_FLAG_OUT;
|
n->brl_flags |= BRL_FLAG_OUT;
|
SIMPLEQ_INSERT_TAIL(&bif->bif_brlout, n, brl_next);
|
SIMPLEQ_INSERT_TAIL(&bif->bif_brlout, n, brl_next);
|
} else {
|
} else {
|
n->brl_flags &= ~BRL_FLAG_OUT;
|
n->brl_flags &= ~BRL_FLAG_OUT;
|
n->brl_flags |= BRL_FLAG_IN;
|
n->brl_flags |= BRL_FLAG_IN;
|
SIMPLEQ_INSERT_TAIL(&bif->bif_brlin, n, brl_next);
|
SIMPLEQ_INSERT_TAIL(&bif->bif_brlin, n, brl_next);
|
}
|
}
|
return (0);
|
return (0);
|
}
|
}
|
|
|
int
|
int
|
bridge_flushrule(bif)
|
bridge_flushrule(bif)
|
struct bridge_iflist *bif;
|
struct bridge_iflist *bif;
|
{
|
{
|
struct brl_node *p, *q;
|
struct brl_node *p, *q;
|
|
|
p = SIMPLEQ_FIRST(&bif->bif_brlin);
|
p = SIMPLEQ_FIRST(&bif->bif_brlin);
|
while (p != NULL) {
|
while (p != NULL) {
|
q = SIMPLEQ_NEXT(p, brl_next);
|
q = SIMPLEQ_NEXT(p, brl_next);
|
SIMPLEQ_REMOVE_HEAD(&bif->bif_brlin, p, brl_next);
|
SIMPLEQ_REMOVE_HEAD(&bif->bif_brlin, p, brl_next);
|
free(p, M_DEVBUF);
|
free(p, M_DEVBUF);
|
p = q;
|
p = q;
|
}
|
}
|
p = SIMPLEQ_FIRST(&bif->bif_brlout);
|
p = SIMPLEQ_FIRST(&bif->bif_brlout);
|
while (p != NULL) {
|
while (p != NULL) {
|
q = SIMPLEQ_NEXT(p, brl_next);
|
q = SIMPLEQ_NEXT(p, brl_next);
|
SIMPLEQ_REMOVE_HEAD(&bif->bif_brlout, p, brl_next);
|
SIMPLEQ_REMOVE_HEAD(&bif->bif_brlout, p, brl_next);
|
free(p, M_DEVBUF);
|
free(p, M_DEVBUF);
|
p = q;
|
p = q;
|
}
|
}
|
return (0);
|
return (0);
|
}
|
}
|
|
|
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
|
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
|
|
|
/*
|
/*
|
* Maximum sized IP header
|
* Maximum sized IP header
|
*/
|
*/
|
union maxip {
|
union maxip {
|
struct ip ip;
|
struct ip ip;
|
u_int32_t _padding[16];
|
u_int32_t _padding[16];
|
};
|
};
|
|
|
/*
|
/*
|
* Filter IP packets by peeking into the ethernet frame. This violates
|
* Filter IP packets by peeking into the ethernet frame. This violates
|
* the ISO model, but allows us to act as a IP filter at the data link
|
* the ISO model, but allows us to act as a IP filter at the data link
|
* layer. As a result, most of this code will look familiar to those
|
* layer. As a result, most of this code will look familiar to those
|
* who've read net/if_ethersubr.c and netinet/ip_input.c
|
* who've read net/if_ethersubr.c and netinet/ip_input.c
|
*/
|
*/
|
struct mbuf *
|
struct mbuf *
|
bridge_filter(sc, ifp, eh, m)
|
bridge_filter(sc, ifp, eh, m)
|
struct bridge_softc *sc;
|
struct bridge_softc *sc;
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
struct ether_header *eh;
|
struct ether_header *eh;
|
struct mbuf *m;
|
struct mbuf *m;
|
{
|
{
|
struct snap snap;
|
struct snap snap;
|
int hassnap = 0;
|
int hassnap = 0;
|
struct ip *ip;
|
struct ip *ip;
|
int hlen;
|
int hlen;
|
|
|
if (fr_checkp == NULL)
|
if (fr_checkp == NULL)
|
return (m);
|
return (m);
|
|
|
if (eh->ether_type != htons(ETHERTYPE_IP)) {
|
if (eh->ether_type != htons(ETHERTYPE_IP)) {
|
if (eh->ether_type > ETHERMTU ||
|
if (eh->ether_type > ETHERMTU ||
|
m->m_pkthdr.len < (sizeof(struct snap) +
|
m->m_pkthdr.len < (sizeof(struct snap) +
|
sizeof(struct ether_header)))
|
sizeof(struct ether_header)))
|
return (m);
|
return (m);
|
|
|
m_copydata(m, sizeof(struct ether_header),
|
m_copydata(m, sizeof(struct ether_header),
|
sizeof(struct snap), (caddr_t)&snap);
|
sizeof(struct snap), (caddr_t)&snap);
|
|
|
if (snap.dsap != LLC_SNAP_LSAP || snap.ssap != LLC_SNAP_LSAP ||
|
if (snap.dsap != LLC_SNAP_LSAP || snap.ssap != LLC_SNAP_LSAP ||
|
snap.control != LLC_UI ||
|
snap.control != LLC_UI ||
|
snap.org[0] != 0 || snap.org[1] != 0 || snap.org[2] ||
|
snap.org[0] != 0 || snap.org[1] != 0 || snap.org[2] ||
|
snap.type != htons(ETHERTYPE_IP))
|
snap.type != htons(ETHERTYPE_IP))
|
return (m);
|
return (m);
|
hassnap = 1;
|
hassnap = 1;
|
}
|
}
|
|
|
m_adj(m, sizeof(struct ether_header));
|
m_adj(m, sizeof(struct ether_header));
|
if (hassnap)
|
if (hassnap)
|
m_adj(m, sizeof(struct snap));
|
m_adj(m, sizeof(struct snap));
|
|
|
if (m->m_pkthdr.len < sizeof(struct ip))
|
if (m->m_pkthdr.len < sizeof(struct ip))
|
goto dropit;
|
goto dropit;
|
|
|
/* Copy minimal header, and drop invalids */
|
/* Copy minimal header, and drop invalids */
|
if (m->m_len < sizeof(struct ip) &&
|
if (m->m_len < sizeof(struct ip) &&
|
(m = m_pullup(m, sizeof(struct ip))) == NULL)
|
(m = m_pullup(m, sizeof(struct ip))) == NULL)
|
return (NULL);
|
return (NULL);
|
ip = mtod(m, struct ip *);
|
ip = mtod(m, struct ip *);
|
|
|
if (ip->ip_v != IPVERSION)
|
if (ip->ip_v != IPVERSION)
|
goto dropit;
|
goto dropit;
|
|
|
hlen = ip->ip_hl << 2; /* get whole header length */
|
hlen = ip->ip_hl << 2; /* get whole header length */
|
if (hlen < sizeof(struct ip))
|
if (hlen < sizeof(struct ip))
|
goto dropit;
|
goto dropit;
|
if (hlen > m->m_len) {
|
if (hlen > m->m_len) {
|
if ((m = m_pullup(m, sizeof(struct ip))) == NULL)
|
if ((m = m_pullup(m, sizeof(struct ip))) == NULL)
|
return (NULL);
|
return (NULL);
|
ip = mtod(m, struct ip *);
|
ip = mtod(m, struct ip *);
|
}
|
}
|
|
|
if ((ip->ip_sum = in_cksum(m, hlen)) != 0)
|
if ((ip->ip_sum = in_cksum(m, hlen)) != 0)
|
goto dropit;
|
goto dropit;
|
|
|
NTOHS(ip->ip_len);
|
NTOHS(ip->ip_len);
|
if (ip->ip_len < hlen)
|
if (ip->ip_len < hlen)
|
goto dropit;
|
goto dropit;
|
NTOHS(ip->ip_id);
|
NTOHS(ip->ip_id);
|
NTOHS(ip->ip_off);
|
NTOHS(ip->ip_off);
|
|
|
if (m->m_pkthdr.len < ip->ip_len)
|
if (m->m_pkthdr.len < ip->ip_len)
|
goto dropit;
|
goto dropit;
|
if (m->m_pkthdr.len > ip->ip_len) {
|
if (m->m_pkthdr.len > ip->ip_len) {
|
if (m->m_len == m->m_pkthdr.len) {
|
if (m->m_len == m->m_pkthdr.len) {
|
m->m_len = ip->ip_len;
|
m->m_len = ip->ip_len;
|
m->m_pkthdr.len = ip->ip_len;
|
m->m_pkthdr.len = ip->ip_len;
|
} else
|
} else
|
m_adj(m, ip->ip_len - m->m_pkthdr.len);
|
m_adj(m, ip->ip_len - m->m_pkthdr.len);
|
}
|
}
|
|
|
/* Finally, we get to filter the packet! */
|
/* Finally, we get to filter the packet! */
|
if (fr_checkp && (*fr_checkp)(ip, hlen, ifp, 0, &m))
|
if (fr_checkp && (*fr_checkp)(ip, hlen, ifp, 0, &m))
|
return (NULL);
|
return (NULL);
|
|
|
/* Rebuild the IP header */
|
/* Rebuild the IP header */
|
if (m->m_len < hlen && ((m = m_pullup(m, hlen)) == NULL))
|
if (m->m_len < hlen && ((m = m_pullup(m, hlen)) == NULL))
|
return (NULL);
|
return (NULL);
|
if (m->m_len < sizeof(struct ip))
|
if (m->m_len < sizeof(struct ip))
|
goto dropit;
|
goto dropit;
|
ip = mtod(m, struct ip *);
|
ip = mtod(m, struct ip *);
|
HTONS(ip->ip_len);
|
HTONS(ip->ip_len);
|
HTONS(ip->ip_id);
|
HTONS(ip->ip_id);
|
HTONS(ip->ip_off);
|
HTONS(ip->ip_off);
|
ip->ip_sum = in_cksum(m, hlen);
|
ip->ip_sum = in_cksum(m, hlen);
|
|
|
/* Reattach SNAP header */
|
/* Reattach SNAP header */
|
if (hassnap) {
|
if (hassnap) {
|
M_PREPEND(m, sizeof(snap), M_DONTWAIT);
|
M_PREPEND(m, sizeof(snap), M_DONTWAIT);
|
if (m == NULL)
|
if (m == NULL)
|
goto dropit;
|
goto dropit;
|
bcopy(&snap, mtod(m, caddr_t), sizeof(snap));
|
bcopy(&snap, mtod(m, caddr_t), sizeof(snap));
|
}
|
}
|
|
|
/* Reattach ethernet header */
|
/* Reattach ethernet header */
|
M_PREPEND(m, sizeof(*eh), M_DONTWAIT);
|
M_PREPEND(m, sizeof(*eh), M_DONTWAIT);
|
if (m == NULL)
|
if (m == NULL)
|
goto dropit;
|
goto dropit;
|
bcopy(eh, mtod(m, caddr_t), sizeof(*eh));
|
bcopy(eh, mtod(m, caddr_t), sizeof(*eh));
|
|
|
return (m);
|
return (m);
|
|
|
dropit:
|
dropit:
|
if (m != NULL)
|
if (m != NULL)
|
m_freem(m);
|
m_freem(m);
|
return (NULL);
|
return (NULL);
|
}
|
}
|
#endif
|
#endif
|
|
|
int
|
int
|
ifpromisc(ifp, pswitch)
|
ifpromisc(ifp, pswitch)
|
struct ifnet *ifp;
|
struct ifnet *ifp;
|
int pswitch;
|
int pswitch;
|
{
|
{
|
struct ifreq ifr;
|
struct ifreq ifr;
|
|
|
if (pswitch) {
|
if (pswitch) {
|
/*
|
/*
|
* If the device is not configured up, we cannot put it in
|
* If the device is not configured up, we cannot put it in
|
* promiscuous mode.
|
* promiscuous mode.
|
*/
|
*/
|
if ((ifp->if_flags & IFF_UP) == 0)
|
if ((ifp->if_flags & IFF_UP) == 0)
|
return (ENETDOWN);
|
return (ENETDOWN);
|
if (ifp->if_pcount++ != 0)
|
if (ifp->if_pcount++ != 0)
|
return (0);
|
return (0);
|
ifp->if_flags |= IFF_PROMISC;
|
ifp->if_flags |= IFF_PROMISC;
|
} else {
|
} else {
|
if (--ifp->if_pcount > 0)
|
if (--ifp->if_pcount > 0)
|
return (0);
|
return (0);
|
ifp->if_flags &= ~IFF_PROMISC;
|
ifp->if_flags &= ~IFF_PROMISC;
|
/*
|
/*
|
* If the device is not configured up, we should not need to
|
* If the device is not configured up, we should not need to
|
* turn off promiscuous mode (device should have turned it
|
* turn off promiscuous mode (device should have turned it
|
* off when interface went down; and will look at IFF_PROMISC
|
* off when interface went down; and will look at IFF_PROMISC
|
* again next time interface comes up).
|
* again next time interface comes up).
|
*/
|
*/
|
if ((ifp->if_flags & IFF_UP) == 0)
|
if ((ifp->if_flags & IFF_UP) == 0)
|
return (0);
|
return (0);
|
}
|
}
|
ifr.ifr_flags = ifp->if_flags;
|
ifr.ifr_flags = ifp->if_flags;
|
return ((*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr));
|
return ((*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr));
|
}
|
}
|
|
|
|
|