@@ -54,6 +54,7 @@ enum {
/* Extended flags under NDA_FLAGS_EXT: */
#define NTF_EXT_MANAGED (1 << 0)
#define NTF_EXT_LOCKED (1 << 1)
+#define NTF_EXT_BLACKHOLE (1 << 2)
/*
* Neighbor Cache Entry States.
@@ -91,6 +92,9 @@ enum {
* NTF_EXT_LOCKED flagged FDB entries are placeholder entries used with the
* locked port feature, that ensures that an entry exists while at the same
* time dropping packets on ingress with src MAC and VID matching the entry.
+ *
+ * NTF_EXT_BLACKHOLE flagged FDB entries ensure that no forwarding is allowed
+ * from any port to the destination MAC, VID pair associated with it.
*/
struct nda_cacheinfo {
@@ -128,6 +128,8 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
ndm->ndm_flags |= NTF_STICKY;
if (test_bit(BR_FDB_LOCKED, &fdb->flags))
ext_flags |= NTF_EXT_LOCKED;
+ if (test_bit(BR_FDB_BLACKHOLE, &fdb->flags))
+ ext_flags |= NTF_EXT_BLACKHOLE;
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->key.addr))
goto nla_put_failure;
@@ -1018,8 +1020,9 @@ static bool fdb_handle_notify(struct net_bridge_fdb_entry *fdb, u8 notify)
/* Update (create or replace) forwarding database entry */
static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
const u8 *addr, struct ndmsg *ndm, u16 flags, u16 vid,
- struct nlattr *nfea_tb[])
+ u32 ext_flags, struct nlattr *nfea_tb[])
{
+ bool blackhole = !!(ext_flags & NTF_EXT_BLACKHOLE);
bool is_sticky = !!(ndm->ndm_flags & NTF_STICKY);
bool refresh = !nfea_tb[NFEA_DONT_REFRESH];
struct net_bridge_fdb_entry *fdb;
@@ -1092,6 +1095,14 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
modified = true;
}
+ if (blackhole != test_bit(BR_FDB_BLACKHOLE, &fdb->flags)) {
+ change_bit(BR_FDB_BLACKHOLE, &fdb->flags);
+ modified = true;
+ }
+
+ if (blackhole)
+ set_bit(BR_FDB_LOCAL, &fdb->flags);
+
if (test_and_clear_bit(BR_FDB_LOCKED, &fdb->flags))
modified = true;
@@ -1113,7 +1124,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
struct net_bridge_port *p, const unsigned char *addr,
u16 nlh_flags, u16 vid, struct nlattr *nfea_tb[],
- struct netlink_ext_ack *extack)
+ u32 ext_flags, struct netlink_ext_ack *extack)
{
int err = 0;
@@ -1138,9 +1149,12 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
return -EINVAL;
}
err = br_fdb_external_learn_add(br, p, addr, vid, true);
+ } else if ((ext_flags & NTF_EXT_BLACKHOLE) && p) {
+ NL_SET_ERR_MSG_MOD(extack, "Blackhole FDB entry cannot be applied on a port");
+ return -EINVAL;
} else {
spin_lock_bh(&br->hash_lock);
- err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb);
+ err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, ext_flags, nfea_tb);
spin_unlock_bh(&br->hash_lock);
}
@@ -1219,10 +1233,10 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
/* VID was specified, so use it. */
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid, nfea_tb,
- extack);
+ ext_flags, extack);
} else {
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0, nfea_tb,
- extack);
+ ext_flags, extack);
if (err || !vg || !vg->num_vlans)
goto out;
@@ -1234,7 +1248,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
if (!br_vlan_should_use(v))
continue;
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid,
- nfea_tb, extack);
+ nfea_tb, ext_flags, extack);
if (err)
goto out;
}
@@ -193,8 +193,11 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
if (dst) {
unsigned long now = jiffies;
- if (test_bit(BR_FDB_LOCAL, &dst->flags))
+ if (test_bit(BR_FDB_LOCAL, &dst->flags)) {
+ if (unlikely(test_bit(BR_FDB_BLACKHOLE, &dst->flags)))
+ goto drop;
return br_pass_frame_up(skb);
+ }
if (now != dst->used)
dst->used = now;
@@ -253,6 +253,7 @@ enum {
BR_FDB_NOTIFY,
BR_FDB_NOTIFY_INACTIVE,
BR_FDB_LOCKED,
+ BR_FDB_BLACKHOLE,
};
struct net_bridge_fdb_key {
@@ -4054,7 +4054,7 @@ int ndo_dflt_fdb_add(struct ndmsg *ndm,
if (tb[NDA_FLAGS_EXT])
ext_flags = nla_get_u32(tb[NDA_FLAGS_EXT]);
- if (ext_flags & NTF_EXT_LOCKED) {
+ if (ext_flags & (NTF_EXT_LOCKED | NTF_EXT_BLACKHOLE)) {
netdev_info(dev, "invalid flags given to default FDB implementation\n");
return err;
}