Add an option to limit the number of source MACs that can be behind a bridge

interface.  Once the limit is reached packets with unknown source addresses are
dropped until an existing host cache entry expires or is removed.  Useful to
use with the STICKY cache option.

Sponsored by:	miniSuperHappyDevHouse NZ
This commit is contained in:
Andrew Thompson 2007-11-04 08:32:27 +00:00
parent 0ad277a852
commit 5f33ec7ba2
4 changed files with 124 additions and 29 deletions

View File

@ -177,12 +177,14 @@ bridge_interfaces(int s, const char *prefix)
printf("%s%s ", prefix, req->ifbr_ifsname);
printb("flags", req->ifbr_ifsflags, IFBIFBITS);
printf("\n");
printf("%s", pad);
printf("ifmaxaddr %u", req->ifbr_addrmax);
printf(" port %u priority %u", req->ifbr_portno,
req->ifbr_priority);
printf(" path cost %u", req->ifbr_path_cost);
if (req->ifbr_ifsflags & IFBIF_STP) {
printf("%s", pad);
printf("port %u priority %u",
req->ifbr_portno, req->ifbr_priority);
printf(" path cost %u", req->ifbr_path_cost);
if (req->ifbr_proto <
sizeof(stpproto) / sizeof(stpproto[0]))
printf(" proto %s", stpproto[req->ifbr_proto]);
@ -203,8 +205,8 @@ bridge_interfaces(int s, const char *prefix)
else
printf(" <unknown state %d>",
req->ifbr_state);
printf("\n");
}
printf("\n");
}
free(inbuf);
@ -650,6 +652,25 @@ setbridge_ifpathcost(const char *ifn, const char *cost, int s,
err(1, "BRDGSIFCOST %s", cost);
}
static void
setbridge_ifmaxaddr(const char *ifn, const char *arg, int s,
const struct afswtch *afp)
{
struct ifbreq req;
u_long val;
memset(&req, 0, sizeof(req));
if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0)
errx(1, "invalid value: %s", arg);
strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
req.ifbr_addrmax = val & 0xffffffff;
if (do_cmd(s, BRDGSIFAMAX, &req, sizeof(req), 1) < 0)
err(1, "BRDGSIFAMAX %s", arg);
}
static void
setbridge_timeout(const char *arg, int d, int s, const struct afswtch *afp)
{
@ -714,6 +735,7 @@ static struct cmd bridge_cmds[] = {
DEF_CMD_ARG("holdcnt", setbridge_holdcount),
DEF_CMD_ARG2("ifpriority", setbridge_ifpriority),
DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost),
DEF_CMD_ARG2("ifmaxaddr", setbridge_ifmaxaddr),
DEF_CMD_ARG("timeout", setbridge_timeout),
DEF_CMD_ARG("private", setbridge_private),
DEF_CMD_ARG("-private", unsetbridge_private),

View File

@ -1623,6 +1623,11 @@ The default is calculated from the link speed.
To change a previously selected path cost back to automatic, set the
cost to 0.
The minimum is 1 and the maximum is 200000000.
.It Cm ifmaxaddr Ar interface Ar size
Set the maximum number of hosts allowed from an interface, packets with unknown
source addresses are dropped until an existing host cache entry expires or is
removed.
Set to 0 to disable.
.El
.Pp
The following parameters are specific to lagg interfaces:

View File

@ -181,6 +181,9 @@ struct bridge_iflist {
struct bstp_port bif_stp; /* STP state */
uint32_t bif_flags; /* member if flags */
int bif_mutecap; /* member muted caps */
uint32_t bif_addrmax; /* max # of addresses */
uint32_t bif_addrcnt; /* cur. # of addresses */
uint32_t bif_addrexceeded;/* # of address violations */
};
/*
@ -189,12 +192,13 @@ struct bridge_iflist {
struct bridge_rtnode {
LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */
LIST_ENTRY(bridge_rtnode) brt_list; /* list linkage */
struct ifnet *brt_ifp; /* destination if */
struct bridge_iflist *brt_dst; /* destination if */
unsigned long brt_expire; /* expiration time */
uint8_t brt_flags; /* address flags */
uint8_t brt_addr[ETHER_ADDR_LEN];
uint16_t brt_vlan; /* vlan id */
};
#define brt_ifp brt_dst->bif_ifp
/*
* Software state for each bridge.
@ -307,6 +311,7 @@ static int bridge_ioctl_gma(struct bridge_softc *, void *);
static int bridge_ioctl_sma(struct bridge_softc *, void *);
static int bridge_ioctl_sifprio(struct bridge_softc *, void *);
static int bridge_ioctl_sifcost(struct bridge_softc *, void *);
static int bridge_ioctl_sifmaxaddr(struct bridge_softc *, void *);
static int bridge_ioctl_addspan(struct bridge_softc *, void *);
static int bridge_ioctl_delspan(struct bridge_softc *, void *);
static int bridge_ioctl_gbparam(struct bridge_softc *, void *);
@ -447,6 +452,10 @@ const struct bridge_control bridge_control_table[] = {
{ bridge_ioctl_stxhc, sizeof(struct ifbrparam),
BC_F_COPYIN|BC_F_SUSER },
{ bridge_ioctl_sifmaxaddr, sizeof(struct ifbreq),
BC_F_COPYIN|BC_F_SUSER },
};
const int bridge_control_table_size =
sizeof(bridge_control_table) / sizeof(bridge_control_table[0]);
@ -887,6 +896,8 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif,
BRIDGE_XDROP(sc);
bridge_rtdelete(sc, ifs, IFBF_FLUSHALL);
KASSERT(bif->bif_addrcnt == 0,
("%s: %d bridge routes referenced", __func__, bif->bif_addrcnt));
BRIDGE_UNLOCK(sc);
bstp_destroy(&bif->bif_stp); /* prepare to free */
@ -1025,6 +1036,9 @@ bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg)
req->ifbr_proto = bp->bp_protover;
req->ifbr_role = bp->bp_role;
req->ifbr_stpflags = bp->bp_flags;
req->ifbr_addrcnt = bif->bif_addrcnt;
req->ifbr_addrmax = bif->bif_addrmax;
req->ifbr_addrexceeded = bif->bif_addrexceeded;
/* Copy STP state options as flags */
if (bp->bp_operedge)
@ -1371,6 +1385,20 @@ bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg)
return (bstp_set_path_cost(&bif->bif_stp, req->ifbr_path_cost));
}
static int
bridge_ioctl_sifmaxaddr(struct bridge_softc *sc, void *arg)
{
struct ifbreq *req = arg;
struct bridge_iflist *bif;
bif = bridge_lookup_member(sc, req->ifbr_ifsname);
if (bif == NULL)
return (ENOENT);
bif->bif_addrmax = req->ifbr_addrmax;
return (0);
}
static int
bridge_ioctl_addspan(struct bridge_softc *sc, void *arg)
{
@ -1902,6 +1930,7 @@ bridge_forward(struct bridge_softc *sc, struct bridge_iflist *sbif,
struct ifnet *src_if, *dst_if, *ifp;
struct ether_header *eh;
uint16_t vlan;
int error;
src_if = m->m_pkthdr.rcvif;
ifp = sc->sc_ifp;
@ -1919,21 +1948,19 @@ bridge_forward(struct bridge_softc *sc, struct bridge_iflist *sbif,
eh = mtod(m, struct ether_header *);
/*
* If the interface is learning, and the source
* address is valid and not multicast, record
* the address.
*/
if ((sbif->bif_flags & IFBIF_LEARNING) != 0 &&
ETHER_IS_MULTICAST(eh->ether_shost) == 0 &&
(eh->ether_shost[0] == 0 &&
eh->ether_shost[1] == 0 &&
eh->ether_shost[2] == 0 &&
eh->ether_shost[3] == 0 &&
eh->ether_shost[4] == 0 &&
eh->ether_shost[5] == 0) == 0) {
(void) bridge_rtupdate(sc, eh->ether_shost, vlan,
/* If the interface is learning, record the address. */
if (sbif->bif_flags & IFBIF_LEARNING) {
error = bridge_rtupdate(sc, eh->ether_shost, vlan,
sbif, 0, IFBAF_DYNAMIC);
/*
* If the interface has addresses limits then deny any source
* that is not in the cache.
*/
if (error && sbif->bif_addrmax) {
BRIDGE_UNLOCK(sc);
m_freem(m);
return;
}
}
if ((sbif->bif_flags & IFBIF_STP) != 0 &&
@ -2058,6 +2085,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
struct ether_header *eh;
struct mbuf *mc, *mc2;
uint16_t vlan;
int error;
if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
return (m);
@ -2112,9 +2140,19 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
*/
/* Note where to send the reply to */
if (bif->bif_flags & IFBIF_LEARNING)
(void) bridge_rtupdate(sc,
if (bif->bif_flags & IFBIF_LEARNING) {
error = bridge_rtupdate(sc,
eh->ether_shost, vlan, bif, 0, IFBAF_DYNAMIC);
/*
* If the interface has addresses limits then deny any
* source that is not in the cache.
*/
if (error && bif->bif_addrmax) {
BRIDGE_UNLOCK(sc);
m_freem(m);
return (NULL);
}
}
/* Mark the packet as arriving on the bridge interface */
m->m_pkthdr.rcvif = bifp;
@ -2206,9 +2244,15 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
if (memcmp(IF_LLADDR((iface)), eh->ether_dhost, ETHER_ADDR_LEN) == 0 \
OR_CARP_CHECK_WE_ARE_DST((iface)) \
) { \
if (bif->bif_flags & IFBIF_LEARNING) \
(void) bridge_rtupdate(sc, eh->ether_shost, \
if (bif->bif_flags & IFBIF_LEARNING) { \
error = bridge_rtupdate(sc, eh->ether_shost, \
vlan, bif, 0, IFBAF_DYNAMIC); \
if (error && bif->bif_addrmax) { \
BRIDGE_UNLOCK(sc); \
m_freem(m); \
return (NULL); \
} \
} \
m->m_pkthdr.rcvif = iface; \
BRIDGE_UNLOCK(sc); \
return (m); \
@ -2394,11 +2438,16 @@ bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, uint16_t vlan,
struct bridge_iflist *bif, int setflags, uint8_t flags)
{
struct bridge_rtnode *brt;
struct ifnet *dst_if = bif->bif_ifp;
int error;
BRIDGE_LOCK_ASSERT(sc);
/* Check the source address is valid and not multicast. */
if (ETHER_IS_MULTICAST(dst) ||
(dst[0] == 0 && dst[1] == 0 && dst[2] == 0 &&
dst[3] == 0 && dst[4] == 0 && dst[5] == 0) != 0)
return (EINVAL);
/* 802.1p frames map to vlan 1 */
if (vlan == 0)
vlan = 1;
@ -2412,6 +2461,11 @@ bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, uint16_t vlan,
sc->sc_brtexceeded++;
return (ENOSPC);
}
/* Check per interface address limits (if enabled) */
if (bif->bif_addrmax && bif->bif_addrcnt >= bif->bif_addrmax) {
bif->bif_addrexceeded++;
return (ENOSPC);
}
/*
* Allocate a new bridge forwarding node, and
@ -2427,7 +2481,6 @@ bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, uint16_t vlan,
else
brt->brt_flags = IFBAF_DYNAMIC;
brt->brt_ifp = dst_if;
memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN);
brt->brt_vlan = vlan;
@ -2435,10 +2488,17 @@ bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, uint16_t vlan,
uma_zfree(bridge_rtnode_zone, brt);
return (error);
}
brt->brt_dst = bif;
bif->bif_addrcnt++;
}
if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC &&
brt->brt_dst != bif) {
brt->brt_dst->bif_addrcnt--;
brt->brt_dst = bif;
brt->brt_dst->bif_addrcnt++;
}
if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
brt->brt_ifp = dst_if;
if ((flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
brt->brt_expire = time_uptime + sc->sc_brttimeout;
if (setflags)
@ -2632,6 +2692,8 @@ static void
bridge_rtable_fini(struct bridge_softc *sc)
{
KASSERT(sc->sc_brtcnt == 0,
("%s: %d bridge routes referenced", __func__, sc->sc_brtcnt));
free(sc->sc_rthash, M_DEVBUF);
}
@ -2773,6 +2835,7 @@ bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt)
LIST_REMOVE(brt, brt_list);
sc->sc_brtcnt--;
brt->brt_dst->bif_addrcnt--;
uma_zfree(bridge_rtnode_zone, brt);
}

View File

@ -114,6 +114,7 @@
* (ifbpstpconf) */
#define BRDGSPROTO 28 /* set protocol (ifbrparam) */
#define BRDGSTXHC 29 /* set tx hold count (ifbrparam) */
#define BRDGSIFAMAX 30 /* set max interface addrs (ifbreq) */
/*
* Generic bridge control request.
@ -128,6 +129,10 @@ struct ifbreq {
uint8_t ifbr_proto; /* member if STP protocol */
uint8_t ifbr_role; /* member if STP role */
uint8_t ifbr_state; /* member if STP state */
uint32_t ifbr_addrcnt; /* member if addr number */
uint32_t ifbr_addrmax; /* member if addr max */
uint32_t ifbr_addrexceeded; /* member if addr violations */
uint8_t pad[32];
};
/* BRDGGIFFLAGS, BRDGSIFFLAGS */