summaryrefslogtreecommitdiff
path: root/net/bridge
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/Makefile6
-rw-r--r--net/bridge/br.c7
-rw-r--r--net/bridge/br_arp_nd_proxy.c135
-rw-r--r--net/bridge/br_cfm.c17
-rw-r--r--net/bridge/br_cfm_netlink.c4
-rw-r--r--net/bridge/br_device.c7
-rw-r--r--net/bridge/br_fdb.c66
-rw-r--r--net/bridge/br_forward.c28
-rw-r--r--net/bridge/br_if.c20
-rw-r--r--net/bridge/br_input.c53
-rw-r--r--net/bridge/br_ioctl.c10
-rw-r--r--net/bridge/br_mdb.c2
-rw-r--r--net/bridge/br_mrp.c41
-rw-r--r--net/bridge/br_mrp_netlink.c8
-rw-r--r--net/bridge/br_mst.c20
-rw-r--r--net/bridge/br_multicast.c189
-rw-r--r--net/bridge/br_netfilter_hooks.c6
-rw-r--r--net/bridge/br_netlink.c177
-rw-r--r--net/bridge/br_netlink_tunnel.c6
-rw-r--r--net/bridge/br_private.h47
-rw-r--r--net/bridge/br_stp.c54
-rw-r--r--net/bridge/br_stp_bpdu.c7
-rw-r--r--net/bridge/br_stp_if.c16
-rw-r--r--net/bridge/br_stp_timer.c2
-rw-r--r--net/bridge/br_switchdev.c9
-rw-r--r--net/bridge/br_sysfs_if.c102
-rw-r--r--net/bridge/br_vlan.c46
-rw-r--r--net/bridge/br_vlan_options.c29
-rw-r--r--net/bridge/netfilter/Makefile4
-rw-r--r--net/bridge/netfilter/ebt_dnat.c4
-rw-r--r--net/bridge/netfilter/ebt_nflog.c17
-rw-r--r--net/bridge/netfilter/ebt_redirect.c16
-rw-r--r--net/bridge/netfilter/ebt_snat.c3
-rw-r--r--net/bridge/netfilter/ebtable_broute.c14
-rw-r--r--net/bridge/netfilter/ebtable_filter.c14
-rw-r--r--net/bridge/netfilter/ebtable_nat.c12
-rw-r--r--net/bridge/netfilter/ebtables.c129
-rw-r--r--net/bridge/netfilter/nf_conntrack_bridge.c1
-rw-r--r--net/bridge/netfilter/nft_meta_bridge.c25
39 files changed, 824 insertions, 529 deletions
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 24bd1c0a9a5a..1203dc19e15c 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -29,3 +29,9 @@ obj-$(CONFIG_NETFILTER) += netfilter/
bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o
bridge-$(CONFIG_BRIDGE_CFM) += br_cfm.o br_cfm_netlink.o
+
+ifdef CONFIG_GCOV_PROFILE_NETFILTER
+GCOV_PROFILE_br_nf_core.o := y
+GCOV_PROFILE_br_netfilter_hooks.o := y
+GCOV_PROFILE_br_netfilter_ipv6.o := y
+endif
diff --git a/net/bridge/br.c b/net/bridge/br.c
index c37e52e2f29a..a5e5b2db110e 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -464,10 +464,6 @@ static int __init br_init(void)
brioctl_set(br_ioctl_stub);
-#if IS_ENABLED(CONFIG_ATM_LANE)
- br_fdb_test_addr_hook = br_fdb_test_addr;
-#endif
-
#if IS_MODULE(CONFIG_BRIDGE_NETFILTER)
pr_info("bridge: filtering via arp/ip/ip6tables is no longer available "
"by default. Update your scripts to load br_netfilter if you "
@@ -506,9 +502,6 @@ static void __exit br_deinit(void)
rcu_barrier(); /* Wait for completion of call_rcu()'s */
br_nf_core_fini();
-#if IS_ENABLED(CONFIG_ATM_LANE)
- br_fdb_test_addr_hook = NULL;
-#endif
br_fdb_fini();
}
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index 0c8a06cdd46f..87a40e2f1c50 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -19,6 +19,7 @@
#include <net/addrconf.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ip6_checksum.h>
+#include <net/ndisc.h>
#endif
#include "br_private.h"
@@ -29,7 +30,7 @@ void br_recalculate_neigh_suppress_enabled(struct net_bridge *br)
bool neigh_suppress = false;
list_for_each_entry(p, &br->port_list, list) {
- if (p->flags & (BR_NEIGH_SUPPRESS | BR_NEIGH_VLAN_SUPPRESS)) {
+ if (READ_ONCE(p->flags) & (BR_NEIGH_SUPPRESS | BR_NEIGH_VLAN_SUPPRESS)) {
neigh_suppress = true;
break;
}
@@ -132,6 +133,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
__be32 sip, tip;
BR_INPUT_SKB_CB(skb)->proxyarp_replied = 0;
+ BR_INPUT_SKB_CB(skb)->grat_arp = 0;
if ((dev->flags & IFF_NOARP) ||
!pskb_may_pull(skb, arp_hdr_len(dev)))
@@ -164,9 +166,10 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
return;
if (parp->ar_op != htons(ARPOP_RREQUEST) &&
parp->ar_op != htons(ARPOP_RREPLY) &&
- (ipv4_is_zeronet(sip) || sip == tip)) {
+ sip == tip) {
/* prevent flooding to neigh suppress ports */
BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1;
+ BR_INPUT_SKB_CB(skb)->grat_arp = 1;
return;
}
}
@@ -193,25 +196,28 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
n = neigh_lookup(&arp_tbl, &tip, vlandev);
if (n) {
struct net_bridge_fdb_entry *f;
+ u8 ha[ETH_ALEN] __aligned(2);
if (!(READ_ONCE(n->nud_state) & NUD_VALID)) {
neigh_release(n);
return;
}
- f = br_fdb_find_rcu(br, n->ha, vid);
+ neigh_ha_snapshot(ha, n, n->dev);
+ f = br_fdb_find_rcu(br, ha, vid);
if (f) {
+ const struct net_bridge_port *dst = READ_ONCE(f->dst);
bool replied = false;
- if ((p && (p->flags & BR_PROXYARP)) ||
- (f->dst && (f->dst->flags & BR_PROXYARP_WIFI)) ||
- br_is_neigh_suppress_enabled(f->dst, vid)) {
+ if ((p && test_bit(BR_PROXYARP_BIT, &p->flags)) ||
+ (dst && test_bit(BR_PROXYARP_WIFI_BIT, &dst->flags)) ||
+ br_is_neigh_suppress_enabled(dst, vid)) {
if (!vid)
br_arp_send(br, p, skb->dev, sip, tip,
- sha, n->ha, sha, 0, 0);
+ sha, ha, sha, 0, 0);
else
br_arp_send(br, p, skb->dev, sip, tip,
- sha, n->ha, sha,
+ sha, ha, sha,
skb->vlan_proto,
skb_vlan_tag_get(skb));
replied = true;
@@ -231,39 +237,38 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
#endif
#if IS_ENABLED(CONFIG_IPV6)
-struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *msg)
+/* Validate skb as an NS/NA and linearize it for br_nd_send()'s ND
+ * option parsing; returns the nd_msg, or NULL on failure.
+ */
+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb)
{
- struct nd_msg *m;
-
- m = skb_header_pointer(skb, skb_network_offset(skb) +
- sizeof(struct ipv6hdr), sizeof(*msg), msg);
- if (!m)
+ if (ndisc_check_ns_na(skb))
return NULL;
- if (m->icmph.icmp6_code != 0 ||
- (m->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION &&
- m->icmph.icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT))
+ if (skb_linearize(skb))
return NULL;
- return m;
+ return (struct nd_msg *)skb_transport_header(skb);
}
static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
- struct sk_buff *request, struct neighbour *n,
+ struct sk_buff *request, struct neighbour *n, u8 *ha,
__be16 vlan_proto, u16 vlan_tci)
{
struct net_device *dev = request->dev;
struct net_bridge_vlan_group *vg;
+ struct ndisc_options ndopts;
struct nd_msg *na, *ns;
struct sk_buff *reply;
struct ipv6hdr *pip6;
int na_olen = 8; /* opt hdr + ETH_ALEN for target */
int ns_olen;
- int i, len;
u8 *daddr;
+ bool dad;
u16 pvid;
+ int len;
- if (!dev || skb_linearize(request))
+ if (!dev)
return;
len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
@@ -280,28 +285,33 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
skb_set_mac_header(reply, 0);
daddr = eth_hdr(request)->h_source;
- ns = (struct nd_msg *)(skb_network_header(request) +
- sizeof(struct ipv6hdr));
-
- /* Do we need option processing ? */
- ns_olen = request->len - (skb_network_offset(request) +
- sizeof(struct ipv6hdr)) - sizeof(*ns);
- for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) {
- if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) {
- kfree_skb(reply);
- return;
- }
- if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
- if ((ns->opt[i + 1] << 3) >=
- sizeof(struct nd_opt_hdr) + ETH_ALEN)
- daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
- break;
- }
+ ns = (struct nd_msg *)skb_transport_header(request);
+
+ /* Derive the option length from the IPv6 payload length so that any
+ * trailing L2 padding in the skb is not parsed as ND options.
+ */
+ ns_olen = ntohs(ipv6_hdr(request)->payload_len) - sizeof(*ns);
+ if (!ndisc_parse_options(dev, ns->opt, ns_olen, &ndopts)) {
+ kfree_skb(reply);
+ return;
+ }
+
+ if (ndopts.nd_opts_src_lladdr) {
+ u8 *lladdr;
+
+ lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
+ if (lladdr)
+ daddr = lladdr;
}
+ dad = ipv6_addr_any(&ipv6_hdr(request)->saddr);
+
/* Ethernet header */
- ether_addr_copy(eth_hdr(reply)->h_dest, daddr);
- ether_addr_copy(eth_hdr(reply)->h_source, n->ha);
+ if (dad)
+ ipv6_eth_mc_map(&in6addr_linklocal_allnodes, eth_hdr(reply)->h_dest);
+ else
+ ether_addr_copy(eth_hdr(reply)->h_dest, daddr);
+ ether_addr_copy(eth_hdr(reply)->h_source, ha);
eth_hdr(reply)->h_proto = htons(ETH_P_IPV6);
reply->protocol = htons(ETH_P_IPV6);
@@ -316,7 +326,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
pip6->priority = ipv6_hdr(request)->priority;
pip6->nexthdr = IPPROTO_ICMPV6;
pip6->hop_limit = 255;
- pip6->daddr = ipv6_hdr(request)->saddr;
+ pip6->daddr = dad ? in6addr_linklocal_allnodes : ipv6_hdr(request)->saddr;
pip6->saddr = *(struct in6_addr *)n->primary_key;
skb_pull(reply, sizeof(struct ipv6hdr));
@@ -329,9 +339,9 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
na->icmph.icmp6_router = (n->flags & NTF_ROUTER) ? 1 : 0;
na->icmph.icmp6_override = 1;
- na->icmph.icmp6_solicited = 1;
+ na->icmph.icmp6_solicited = dad ? 0 : 1;
na->target = ns->target;
- ether_addr_copy(&na->opt[2], n->ha);
+ ether_addr_copy(&na->opt[2], ha);
na->opt[0] = ND_OPT_TARGET_LL_ADDR;
na->opt[1] = na_olen >> 3;
@@ -360,7 +370,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
__vlan_hwaccel_put_tag(reply, vlan_proto, vlan_tci);
netdev_dbg(dev, "nd send dev %s dst %pI6 dst_hw %pM src %pI6 src_hw %pM\n",
- dev->name, &pip6->daddr, daddr, &pip6->saddr, n->ha);
+ dev->name, &pip6->daddr, daddr, &pip6->saddr, ha);
if (p) {
dev_queue_xmit(reply);
@@ -412,6 +422,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
struct neighbour *n;
BR_INPUT_SKB_CB(skb)->proxyarp_replied = 0;
+ BR_INPUT_SKB_CB(skb)->grat_arp = 0;
if (br_is_neigh_suppress_enabled(p, vid))
return;
@@ -424,6 +435,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
!msg->icmph.icmp6_solicited) {
/* prevent flooding to neigh suppress ports */
BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1;
+ BR_INPUT_SKB_CB(skb)->grat_arp = 1;
return;
}
@@ -434,7 +446,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
saddr = &iphdr->saddr;
daddr = &iphdr->daddr;
- if (ipv6_addr_any(saddr) || !ipv6_addr_cmp(saddr, daddr)) {
+ if (!ipv6_addr_cmp(saddr, daddr)) {
/* prevent flooding to neigh suppress ports */
BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1;
return;
@@ -461,23 +473,26 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
n = neigh_lookup(&nd_tbl, &msg->target, vlandev);
if (n) {
struct net_bridge_fdb_entry *f;
+ u8 ha[ETH_ALEN] __aligned(2);
if (!(READ_ONCE(n->nud_state) & NUD_VALID)) {
neigh_release(n);
return;
}
- f = br_fdb_find_rcu(br, n->ha, vid);
+ neigh_ha_snapshot(ha, n, n->dev);
+ f = br_fdb_find_rcu(br, ha, vid);
if (f) {
+ const struct net_bridge_port *dst = READ_ONCE(f->dst);
bool replied = false;
- if (br_is_neigh_suppress_enabled(f->dst, vid)) {
+ if (br_is_neigh_suppress_enabled(dst, vid)) {
if (vid != 0)
- br_nd_send(br, p, skb, n,
+ br_nd_send(br, p, skb, n, ha,
skb->vlan_proto,
skb_vlan_tag_get(skb));
else
- br_nd_send(br, p, skb, n, 0, 0);
+ br_nd_send(br, p, skb, n, ha, 0, 0);
replied = true;
}
@@ -499,10 +514,7 @@ bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid)
if (!p)
return false;
- if (!vid)
- return !!(p->flags & BR_NEIGH_SUPPRESS);
-
- if (p->flags & BR_NEIGH_VLAN_SUPPRESS) {
+ if (vid && test_bit(BR_NEIGH_VLAN_SUPPRESS_BIT, &p->flags)) {
struct net_bridge_vlan_group *vg = nbp_vlan_group_rcu(p);
struct net_bridge_vlan *v;
@@ -510,7 +522,20 @@ bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid)
if (!v)
return false;
return !!(v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED);
- } else {
- return !!(p->flags & BR_NEIGH_SUPPRESS);
}
+ return test_bit(BR_NEIGH_SUPPRESS_BIT, &p->flags);
+}
+
+bool br_is_neigh_forward_grat_enabled(const struct net_bridge_port *p, u16 vid)
+{
+ if (vid && test_bit(BR_NEIGH_VLAN_SUPPRESS_BIT, &p->flags)) {
+ struct net_bridge_vlan_group *vg = nbp_vlan_group_rcu(p);
+ struct net_bridge_vlan *v;
+
+ v = br_vlan_find(vg, vid);
+ if (!v)
+ return false;
+ return !!(v->priv_flags & BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED);
+ }
+ return test_bit(BR_NEIGH_FORWARD_GRAT_BIT, &p->flags);
}
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index 118c7ea48c35..9dcc97d63a6f 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -367,7 +367,7 @@ static u32 ccm_tlv_extract(struct sk_buff *skb, u32 index,
}
/* note: already called with rcu_read_lock */
-static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
+int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
{
u32 mdlevel, interval, size, index, max;
const struct br_cfm_common_hdr *hdr;
@@ -489,11 +489,6 @@ static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
return 1;
}
-static struct br_frame_type cfm_frame_type __read_mostly = {
- .type = cpu_to_be16(ETH_P_CFM),
- .frame_handler = br_cfm_frame_rx,
-};
-
int br_cfm_mep_create(struct net_bridge *br,
const u32 instance,
struct br_cfm_mep_create *const create,
@@ -559,7 +554,7 @@ int br_cfm_mep_create(struct net_bridge *br,
INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
if (hlist_empty(&br->mep_list))
- br_add_frame(br, &cfm_frame_type);
+ br_opt_toggle(br, BROPT_CFM_ENABLED, true);
hlist_add_tail_rcu(&mep->head, &br->mep_list);
@@ -588,7 +583,7 @@ static void mep_delete_implementation(struct net_bridge *br,
kfree_rcu(mep, rcu);
if (hlist_empty(&br->mep_list))
- br_del_frame(br, &cfm_frame_type);
+ br_opt_toggle(br, BROPT_CFM_ENABLED, false);
}
int br_cfm_mep_delete(struct net_bridge *br,
@@ -805,6 +800,12 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
goto save;
}
+ if (!interval_to_us(mep->cc_config.exp_interval)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Invalid CCM interval");
+ return -EINVAL;
+ }
+
/* Start delayed work to transmit CCM frames. It is done with zero delay
* to send first frame immediately
*/
diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c
index 2faab44652e7..91b9922dc3f2 100644
--- a/net/bridge/br_cfm_netlink.c
+++ b/net/bridge/br_cfm_netlink.c
@@ -34,7 +34,9 @@ br_cfm_cc_config_policy[IFLA_BRIDGE_CFM_CC_CONFIG_MAX + 1] = {
[IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC] = { .type = NLA_REJECT },
[IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE] = { .type = NLA_U32 },
[IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE] = { .type = NLA_U32 },
- [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] =
+ NLA_POLICY_RANGE(NLA_U32, BR_CFM_CCM_INTERVAL_3_3_MS,
+ BR_CFM_CCM_INTERVAL_10_MIN),
[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID] = {
.type = NLA_BINARY, .len = CFM_MAID_LENGTH },
};
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index a35ceae0a6f2..e01c44a90d84 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -80,9 +80,9 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
pskb_may_pull(skb, sizeof(struct ipv6hdr) +
sizeof(struct nd_msg)) &&
ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
- struct nd_msg *msg, _msg;
+ struct nd_msg *msg;
- msg = br_is_nd_neigh_msg(skb, &_msg);
+ msg = br_is_nd_neigh_msg(skb);
if (msg)
br_do_suppress_nd(skb, br, vid, NULL, msg);
}
@@ -107,7 +107,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
else
br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid);
} else if ((dst = br_fdb_find_rcu(br, dest, vid)) != NULL) {
- br_forward(dst->dst, skb, false, true);
+ br_forward(READ_ONCE(dst->dst), skb, false, true);
} else {
br_flood(br, skb, BR_PKT_UNICAST, false, true, vid);
}
@@ -503,7 +503,6 @@ void br_dev_setup(struct net_device *dev)
spin_lock_init(&br->lock);
INIT_LIST_HEAD(&br->port_list);
INIT_HLIST_HEAD(&br->fdb_list);
- INIT_HLIST_HEAD(&br->frame_type_list);
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
INIT_HLIST_HEAD(&br->mrp_list);
#endif
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index e2c17f620f00..e4570bbed854 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -236,6 +236,7 @@ struct net_device *br_fdb_find_port(const struct net_device *br_dev,
const unsigned char *addr,
__u16 vid)
{
+ const struct net_bridge_port *dst;
struct net_bridge_fdb_entry *f;
struct net_device *dev = NULL;
struct net_bridge *br;
@@ -248,8 +249,11 @@ struct net_device *br_fdb_find_port(const struct net_device *br_dev,
br = netdev_priv(br_dev);
rcu_read_lock();
f = br_fdb_find_rcu(br, addr, vid);
- if (f && f->dst)
- dev = f->dst->dev;
+ if (f) {
+ dst = READ_ONCE(f->dst);
+ if (dst)
+ dev = dst->dev;
+ }
rcu_read_unlock();
return dev;
@@ -346,7 +350,7 @@ static void fdb_delete_local(struct net_bridge *br,
vg = nbp_vlan_group(op);
if (op != p && ether_addr_equal(op->dev->dev_addr, addr) &&
(!vid || br_vlan_find(vg, vid))) {
- f->dst = op;
+ WRITE_ONCE(f->dst, op);
clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
return;
}
@@ -357,7 +361,7 @@ static void fdb_delete_local(struct net_bridge *br,
/* Maybe bridge device has same hw addr? */
if (p && ether_addr_equal(br->dev->dev_addr, addr) &&
(!vid || (v && br_vlan_should_use(v)))) {
- f->dst = NULL;
+ WRITE_ONCE(f->dst, NULL);
clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
return;
}
@@ -466,7 +470,8 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
spin_lock_bh(&br->hash_lock);
vg = nbp_vlan_group(p);
hlist_for_each_entry(f, &br->fdb_list, fdb_node) {
- if (f->dst == p && test_bit(BR_FDB_LOCAL, &f->flags) &&
+ if (READ_ONCE(f->dst) == p &&
+ test_bit(BR_FDB_LOCAL, &f->flags) &&
!test_bit(BR_FDB_ADDED_BY_USER, &f->flags)) {
/* delete old one */
fdb_delete_local(br, p, f);
@@ -874,7 +879,7 @@ void br_fdb_delete_by_port(struct net_bridge *br,
spin_lock_bh(&br->hash_lock);
hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
- if (f->dst != p)
+ if (READ_ONCE(f->dst) != p)
continue;
if (!do_all)
@@ -892,35 +897,6 @@ void br_fdb_delete_by_port(struct net_bridge *br,
spin_unlock_bh(&br->hash_lock);
}
-#if IS_ENABLED(CONFIG_ATM_LANE)
-/* Interface used by ATM LANE hook to test
- * if an addr is on some other bridge port */
-int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
-{
- struct net_bridge_fdb_entry *fdb;
- struct net_bridge_port *port;
- int ret;
-
- rcu_read_lock();
- port = br_port_get_rcu(dev);
- if (!port)
- ret = 0;
- else {
- const struct net_bridge_port *dst = NULL;
-
- fdb = br_fdb_find_rcu(port->br, addr, 0);
- if (fdb)
- dst = READ_ONCE(fdb->dst);
-
- ret = dst && dst->dev != dev &&
- dst->state == BR_STATE_FORWARDING;
- }
- rcu_read_unlock();
-
- return ret;
-}
-#endif /* CONFIG_ATM_LANE */
-
/*
* Fill buffer with forwarding table records in
* the API format.
@@ -928,6 +904,7 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
int br_fdb_fillbuf(struct net_bridge *br, void *buf,
unsigned long maxnum, unsigned long skip)
{
+ const struct net_bridge_port *dst;
struct net_bridge_fdb_entry *f;
struct __fdb_entry *fe = buf;
unsigned long delta;
@@ -944,7 +921,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
continue;
/* ignore pseudo entry for local MAC address */
- if (!f->dst)
+ dst = READ_ONCE(f->dst);
+ if (!dst)
continue;
if (skip) {
@@ -956,8 +934,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
memcpy(fe->mac_addr, f->key.addr.addr, ETH_ALEN);
/* due to ABI compat need to split into hi/lo */
- fe->port_no = f->dst->port_no;
- fe->port_hi = f->dst->port_no >> 8;
+ fe->port_no = dst->port_no;
+ fe->port_hi = dst->port_no >> 8;
fe->is_local = test_bit(BR_FDB_LOCAL, &f->flags);
if (!test_bit(BR_FDB_STATIC, &f->flags)) {
@@ -1083,9 +1061,11 @@ int br_fdb_dump(struct sk_buff *skb,
rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
+ const struct net_bridge_port *dst = READ_ONCE(f->dst);
+
if (*idx < ctx->fdb_idx)
goto skip;
- if (filter_dev && (!f->dst || f->dst->dev != filter_dev)) {
+ if (filter_dev && (!dst || dst->dev != filter_dev)) {
if (filter_dev != dev)
goto skip;
/* !f->dst is a special case for bridge
@@ -1093,10 +1073,10 @@ int br_fdb_dump(struct sk_buff *skb,
* Therefore need a little more filtering
* we only want to dump the !f->dst case
*/
- if (f->dst)
+ if (dst)
goto skip;
}
- if (!filter_dev && f->dst)
+ if (!filter_dev && dst)
goto skip;
err = fdb_fill_info(skb, br, f,
@@ -1534,7 +1514,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
trace_br_fdb_external_learn_add(br, p, addr, vid);
- if (locked && (!p || !(p->flags & BR_PORT_MAB)))
+ if (locked && (!p || !test_bit(BR_PORT_MAB_BIT, &p->flags)))
return -EINVAL;
spin_lock_bh(&br->hash_lock);
@@ -1652,7 +1632,7 @@ void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
spin_lock_bh(&p->br->hash_lock);
hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) {
- if (f->dst == p && f->key.vlan_id == vid)
+ if (READ_ONCE(f->dst) == p && f->key.vlan_id == vid)
clear_bit(BR_FDB_OFFLOADED, &f->flags);
}
spin_unlock_bh(&p->br->hash_lock);
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index dea09096ad0f..46c762ca5177 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -24,7 +24,7 @@ static inline int should_deliver(const struct net_bridge_port *p,
struct net_bridge_vlan_group *vg;
vg = nbp_vlan_group_rcu(p);
- return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
+ return (test_bit(BR_HAIRPIN_MODE_BIT, &p->flags) || skb->dev != p->dev) &&
(br_mst_is_enabled(p) || p->state == BR_STATE_FORWARDING) &&
br_allowed_egress(vg, skb) && nbp_switchdev_allowed_egress(p, skb) &&
!br_skb_isolated(p, skb);
@@ -214,26 +214,33 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb,
*/
switch (pkt_type) {
case BR_PKT_UNICAST:
- if (!(p->flags & BR_FLOOD))
+ if (!test_bit(BR_FLOOD_BIT, &p->flags))
continue;
break;
case BR_PKT_MULTICAST:
- if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
+ if (!test_bit(BR_MCAST_FLOOD_BIT, &p->flags) && skb->dev != br->dev)
continue;
break;
case BR_PKT_BROADCAST:
- if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev)
+ if (!test_bit(BR_BCAST_FLOOD_BIT, &p->flags) && skb->dev != br->dev)
continue;
break;
}
/* Do not flood to ports that enable proxy ARP */
- if (p->flags & BR_PROXYARP)
- continue;
- if (BR_INPUT_SKB_CB(skb)->proxyarp_replied &&
- ((p->flags & BR_PROXYARP_WIFI) ||
- br_is_neigh_suppress_enabled(p, vid)))
+ if (test_bit(BR_PROXYARP_BIT, &p->flags))
continue;
+ if (BR_INPUT_SKB_CB(skb)->proxyarp_replied) {
+ if (test_bit(BR_PROXYARP_WIFI_BIT, &p->flags))
+ continue;
+ /* For gratuitous ARPs/NAs, check neigh_forward_grat.
+ * For regular ARPs/NDs, check only neigh_suppress.
+ */
+ if (br_is_neigh_suppress_enabled(p, vid) &&
+ (!BR_INPUT_SKB_CB(skb)->grat_arp ||
+ !br_is_neigh_forward_grat_enabled(p, vid)))
+ continue;
+ }
prev = maybe_deliver(prev, p, skb, local_orig);
if (IS_ERR(prev)) {
@@ -321,7 +328,8 @@ void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
if ((unsigned long)lport > (unsigned long)rport) {
port = lport;
- if (port->flags & BR_MULTICAST_TO_UNICAST) {
+ if (test_bit(BR_MULTICAST_TO_UNICAST_BIT,
+ &port->flags)) {
maybe_deliver_addr(lport, skb, p->eth_addr,
local_orig);
goto delivered;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index d39571e13744..c52613431f88 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -19,6 +19,7 @@
#include <linux/if_ether.h>
#include <linux/slab.h>
#include <net/dsa.h>
+#include <net/netdev_lock.h>
#include <net/sock.h>
#include <linux/if_vlan.h>
#include <net/switchdev.h>
@@ -30,13 +31,13 @@
* Determine initial path cost based on speed.
* using recommendations from 802.1d standard
*
- * Since driver might sleep need to not be holding any locks.
+ * Since driver might sleep, we need to not be holding any bridge spinlocks.
*/
static int port_cost(struct net_device *dev)
{
struct ethtool_link_ksettings ecmd;
- if (!__ethtool_get_link_ksettings(dev, &ecmd)) {
+ if (!netif_get_link_ksettings(dev, &ecmd)) {
switch (ecmd.base.speed) {
case SPEED_10000:
return 2;
@@ -75,9 +76,9 @@ void br_port_carrier_check(struct net_bridge_port *p, bool *notified)
struct net_device *dev = p->dev;
struct net_bridge *br = p->br;
- if (!(p->flags & BR_ADMIN_COST) &&
+ if (!test_bit(BR_ADMIN_COST_BIT, &p->flags) &&
netif_running(dev) && netif_oper_up(dev))
- p->path_cost = port_cost(dev);
+ WRITE_ONCE(p->path_cost, port_cost(dev));
*notified = false;
if (!netif_running(br->dev))
@@ -110,7 +111,7 @@ static void br_port_set_promisc(struct net_bridge_port *p)
return;
br_fdb_unsync_static(p->br, p);
- p->flags |= BR_PROMISC;
+ set_bit(BR_PROMISC_BIT, &p->flags);
}
static void br_port_clear_promisc(struct net_bridge_port *p)
@@ -133,7 +134,7 @@ static void br_port_clear_promisc(struct net_bridge_port *p)
return;
dev_set_promiscuity(p->dev, -1);
- p->flags &= ~BR_PROMISC;
+ clear_bit(BR_PROMISC_BIT, &p->flags);
}
/* When a port is added or removed or when certain port flags
@@ -391,6 +392,9 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)
br_fdb_delete_by_port(br, NULL, 0, 1);
+ timer_shutdown_sync(&br->hello_timer);
+ timer_shutdown_sync(&br->topology_change_timer);
+ timer_shutdown_sync(&br->tcn_timer);
cancel_delayed_work_sync(&br->gc_work);
br_sysfs_delbr(br->dev);
@@ -436,7 +440,9 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
p->br = br;
netdev_hold(dev, &p->dev_tracker, GFP_KERNEL);
p->dev = dev;
+ netdev_lock_ops(dev);
p->path_cost = port_cost(dev);
+ netdev_unlock_ops(dev);
p->priority = 0x8000 >> BR_PORT_BITS;
p->port_no = index;
p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
@@ -758,6 +764,6 @@ bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
if (!p)
return false;
- return p->flags & flag;
+ return READ_ONCE(p->flags) & flag;
}
EXPORT_SYMBOL_GPL(br_port_flag_is_set);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 2cbae0f9ae1f..8bed72baf161 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -111,7 +111,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
&state, &vlan))
goto out;
- if (p->flags & BR_PORT_LOCKED) {
+ if (test_bit(BR_PORT_LOCKED_BIT, &p->flags)) {
struct net_bridge_fdb_entry *fdb_src =
br_fdb_find_rcu(br, eth_hdr(skb)->h_source, vid);
@@ -119,7 +119,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
/* FDB miss. Create locked FDB entry if MAB is enabled
* and drop the packet.
*/
- if (p->flags & BR_PORT_MAB)
+ if (test_bit(BR_PORT_MAB_BIT, &p->flags))
br_fdb_update(br, p, eth_hdr(skb)->h_source,
vid, BIT(BR_FDB_LOCKED));
goto drop;
@@ -140,7 +140,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
nbp_switchdev_frame_mark(p, skb);
/* insert into forwarding database after filtering to avoid spoofing */
- if (p->flags & BR_LEARNING)
+ if (test_bit(BR_LEARNING_BIT, &p->flags))
br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, 0);
promisc = !!(br->dev->flags & IFF_PROMISC);
@@ -164,7 +164,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
}
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
- BR_INPUT_SKB_CB(skb)->src_port_isolated = !!(p->flags & BR_ISOLATED);
+ BR_INPUT_SKB_CB(skb)->src_port_isolated = test_bit(BR_ISOLATED_BIT, &p->flags);
if (IS_ENABLED(CONFIG_INET) &&
(skb->protocol == htons(ETH_P_ARP) ||
@@ -176,9 +176,9 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
pskb_may_pull(skb, sizeof(struct ipv6hdr) +
sizeof(struct nd_msg)) &&
ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
- struct nd_msg *msg, _msg;
+ struct nd_msg *msg;
- msg = br_is_nd_neigh_msg(skb, &_msg);
+ msg = br_is_nd_neigh_msg(skb);
if (msg)
br_do_suppress_nd(skb, br, vid, p, msg);
}
@@ -223,7 +223,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
if (now != READ_ONCE(dst->used))
WRITE_ONCE(dst->used, now);
- br_forward(dst->dst, skb, local_rcv, false);
+ br_forward(READ_ONCE(dst->dst), skb, local_rcv, false);
} else {
if (!mcast_hit)
br_flood(br, skb, pkt_type, local_rcv, false, vid);
@@ -248,7 +248,7 @@ static void __br_handle_local_finish(struct sk_buff *skb)
u16 vid = 0;
/* check if vlan is allowed, to avoid spoofing */
- if ((p->flags & BR_LEARNING) &&
+ if (test_bit(BR_LEARNING_BIT, &p->flags) &&
nbp_state_should_learn(p) &&
!br_opt_get(p->br, BROPT_NO_LL_LEARN) &&
br_should_learn(p, skb, &vid))
@@ -317,17 +317,25 @@ frame_finish:
return RX_HANDLER_CONSUMED;
}
+#define BR_CFM_MRP_OPTS \
+ ((IS_ENABLED(CONFIG_BRIDGE_CFM) ? BIT(BROPT_CFM_ENABLED) : 0UL) | \
+ (IS_ENABLED(CONFIG_BRIDGE_MRP) ? BIT(BROPT_MRP_ENABLED) : 0UL))
+
/* Return 0 if the frame was not processed otherwise 1
* note: already called with rcu_read_lock
*/
static int br_process_frame_type(struct net_bridge_port *p,
struct sk_buff *skb)
{
- struct br_frame_type *tmp;
+ struct net_bridge *br = p->br;
+
+ if (skb->protocol == htons(ETH_P_CFM) &&
+ br_opt_get(br, BROPT_CFM_ENABLED))
+ return br_cfm_frame_rx(p, skb);
- hlist_for_each_entry_rcu(tmp, &p->br->frame_type_list, list)
- if (unlikely(tmp->type == skb->protocol))
- return tmp->frame_handler(p, skb);
+ if (skb->protocol == htons(ETH_P_MRP) &&
+ br_opt_get(br, BROPT_MRP_ENABLED))
+ return br_mrp_process(p, skb);
return 0;
}
@@ -359,7 +367,7 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
br_tc_skb_miss_set(skb, false);
p = br_port_get_rcu(skb->dev);
- if (p->flags & BR_VLAN_TUNNEL)
+ if (test_bit(BR_VLAN_TUNNEL_BIT, &p->flags))
br_handle_ingress_vlan_tunnel(skb, p, nbp_vlan_group_rcu(p));
if (unlikely(is_link_local_ether_addr(dest))) {
@@ -425,7 +433,8 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
}
}
- if (unlikely(br_process_frame_type(p, skb)))
+ if (unlikely((READ_ONCE(p->br->options) & BR_CFM_MRP_OPTS) &&
+ br_process_frame_type(p, skb)))
return RX_HANDLER_PASS;
forward:
@@ -467,19 +476,3 @@ rx_handler_func_t *br_get_rx_handler(const struct net_device *dev)
return br_handle_frame;
}
-
-void br_add_frame(struct net_bridge *br, struct br_frame_type *ft)
-{
- hlist_add_head_rcu(&ft->list, &br->frame_type_list);
-}
-
-void br_del_frame(struct net_bridge *br, struct br_frame_type *ft)
-{
- struct br_frame_type *tmp;
-
- hlist_for_each_entry(tmp, &br->frame_type_list, list)
- if (ft == tmp) {
- hlist_del_rcu(&ft->list);
- return;
- }
-}
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index 766c43b327af..39f3ffcfa2d3 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -257,13 +257,13 @@ int br_dev_siocdevprivate(struct net_device *dev, struct ifreq *rq,
memset(&p, 0, sizeof(struct __port_info));
memcpy(&p.designated_root, &pt->designated_root, 8);
memcpy(&p.designated_bridge, &pt->designated_bridge, 8);
- p.port_id = pt->port_id;
- p.designated_port = pt->designated_port;
- p.path_cost = pt->path_cost;
- p.designated_cost = pt->designated_cost;
+ p.port_id = READ_ONCE(pt->port_id);
+ p.designated_port = READ_ONCE(pt->designated_port);
+ p.path_cost = READ_ONCE(pt->path_cost);
+ p.designated_cost = READ_ONCE(pt->designated_cost);
p.state = pt->state;
p.top_change_ack = pt->topology_change_ack;
- p.config_pending = pt->config_pending;
+ p.config_pending = READ_ONCE(pt->config_pending);
p.message_age_timer_value = br_timer_value(&pt->message_age_timer);
p.forward_delay_timer_value = br_timer_value(&pt->forward_delay_timer);
p.hold_timer_value = br_timer_value(&pt->hold_timer);
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index e0c7020b12f5..a01bd280c722 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -1523,6 +1523,8 @@ static void br_mdb_flush_pgs(struct net_bridge *br,
}
br_multicast_del_pg(mp, p, pp);
+ /* br_multicast_del_pg() can remove other groups from this list. */
+ pp = &mp->ports;
}
}
diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
index f1aa67f7a051..dce6efa96c4c 100644
--- a/net/bridge/br_mrp.c
+++ b/net/bridge/br_mrp.c
@@ -6,13 +6,6 @@
static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 };
static const u8 mrp_in_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x3 };
-static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb);
-
-static struct br_frame_type mrp_frame_type __read_mostly = {
- .type = cpu_to_be16(ETH_P_MRP),
- .frame_handler = br_mrp_process,
-};
-
static bool br_mrp_is_ring_port(struct net_bridge_port *p_port,
struct net_bridge_port *s_port,
struct net_bridge_port *port)
@@ -215,7 +208,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
struct br_mrp_oui_hdr *oui = NULL;
u8 length;
- length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) +
+ length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) +
MRP_OPT_PADDING;
br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length);
@@ -224,11 +217,9 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
sub_opt = skb_put(skb, sizeof(*sub_opt));
memset(sub_opt, 0x0, sizeof(*sub_opt));
- sub_tlv = skb_put(skb, sizeof(*sub_tlv));
- sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR;
-
/* 32 bit alligment shall be ensured therefore add 2 bytes */
- skb_put(skb, MRP_OPT_PADDING);
+ sub_tlv = skb_put_zero(skb, sizeof(*sub_tlv) + MRP_OPT_PADDING);
+ sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR;
}
br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0);
@@ -454,7 +445,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)
state = netif_running(br->dev) ?
BR_STATE_FORWARDING : BR_STATE_DISABLED;
p->state = state;
- p->flags &= ~BR_MRP_AWARE;
+ clear_bit(BR_MRP_AWARE_BIT, &p->flags);
spin_unlock_bh(&br->lock);
br_mrp_port_switchdev_set_state(p, state);
rcu_assign_pointer(mrp->p_port, NULL);
@@ -466,7 +457,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)
state = netif_running(br->dev) ?
BR_STATE_FORWARDING : BR_STATE_DISABLED;
p->state = state;
- p->flags &= ~BR_MRP_AWARE;
+ clear_bit(BR_MRP_AWARE_BIT, &p->flags);
spin_unlock_bh(&br->lock);
br_mrp_port_switchdev_set_state(p, state);
rcu_assign_pointer(mrp->s_port, NULL);
@@ -478,7 +469,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)
state = netif_running(br->dev) ?
BR_STATE_FORWARDING : BR_STATE_DISABLED;
p->state = state;
- p->flags &= ~BR_MRP_AWARE;
+ clear_bit(BR_MRP_AWARE_BIT, &p->flags);
spin_unlock_bh(&br->lock);
br_mrp_port_switchdev_set_state(p, state);
rcu_assign_pointer(mrp->i_port, NULL);
@@ -488,7 +479,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)
kfree_rcu(mrp, rcu);
if (hlist_empty(&br->mrp_list))
- br_del_frame(br, &mrp_frame_type);
+ br_opt_toggle(br, BROPT_MRP_ENABLED, false);
}
/* Adds a new MRP instance.
@@ -526,19 +517,19 @@ int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance)
p = br_mrp_get_port(br, instance->p_ifindex);
spin_lock_bh(&br->lock);
p->state = BR_STATE_FORWARDING;
- p->flags |= BR_MRP_AWARE;
+ set_bit(BR_MRP_AWARE_BIT, &p->flags);
spin_unlock_bh(&br->lock);
rcu_assign_pointer(mrp->p_port, p);
p = br_mrp_get_port(br, instance->s_ifindex);
spin_lock_bh(&br->lock);
p->state = BR_STATE_FORWARDING;
- p->flags |= BR_MRP_AWARE;
+ set_bit(BR_MRP_AWARE_BIT, &p->flags);
spin_unlock_bh(&br->lock);
rcu_assign_pointer(mrp->s_port, p);
if (hlist_empty(&br->mrp_list))
- br_add_frame(br, &mrp_frame_type);
+ br_opt_toggle(br, BROPT_MRP_ENABLED, true);
INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired);
INIT_DELAYED_WORK(&mrp->in_test_work, br_mrp_in_test_work_expired);
@@ -593,7 +584,7 @@ int br_mrp_set_port_state(struct net_bridge_port *p,
{
u32 port_state;
- if (!p || !(p->flags & BR_MRP_AWARE))
+ if (!p || !test_bit(BR_MRP_AWARE_BIT, &p->flags))
return -EINVAL;
spin_lock_bh(&p->br->lock);
@@ -619,7 +610,7 @@ int br_mrp_set_port_role(struct net_bridge_port *p,
{
struct br_mrp *mrp;
- if (!p || !(p->flags & BR_MRP_AWARE))
+ if (!p || !test_bit(BR_MRP_AWARE_BIT, &p->flags))
return -EINVAL;
mrp = br_mrp_find_port(p->br, p);
@@ -784,7 +775,7 @@ int br_mrp_set_in_role(struct net_bridge *br, struct br_mrp_in_role *role)
state = netif_running(br->dev) ?
BR_STATE_FORWARDING : BR_STATE_DISABLED;
p->state = state;
- p->flags &= ~BR_MRP_AWARE;
+ clear_bit(BR_MRP_AWARE_BIT, &p->flags);
spin_unlock_bh(&br->lock);
br_mrp_port_switchdev_set_state(p, state);
rcu_assign_pointer(mrp->i_port, NULL);
@@ -809,7 +800,7 @@ int br_mrp_set_in_role(struct net_bridge *br, struct br_mrp_in_role *role)
p = br_mrp_get_port(br, role->i_ifindex);
spin_lock_bh(&br->lock);
p->state = BR_STATE_FORWARDING;
- p->flags |= BR_MRP_AWARE;
+ set_bit(BR_MRP_AWARE_BIT, &p->flags);
spin_unlock_bh(&br->lock);
rcu_assign_pointer(mrp->i_port, p);
@@ -1243,10 +1234,10 @@ no_forward:
* normal forwarding.
* note: already called with rcu_read_lock
*/
-static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
+int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
{
/* If there is no MRP instance do normal forwarding */
- if (likely(!(p->flags & BR_MRP_AWARE)))
+ if (likely(!test_bit(BR_MRP_AWARE_BIT, &p->flags)))
goto out;
return br_mrp_rcv(p, skb, p->dev);
diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
index 86f0e75d6e34..39f07e13fba5 100644
--- a/net/bridge/br_mrp_netlink.c
+++ b/net/bridge/br_mrp_netlink.c
@@ -538,9 +538,9 @@ int br_mrp_ring_port_open(struct net_device *dev, u8 loc)
}
if (loc)
- p->flags |= BR_MRP_LOST_CONT;
+ set_bit(BR_MRP_LOST_CONT_BIT, &p->flags);
else
- p->flags &= ~BR_MRP_LOST_CONT;
+ clear_bit(BR_MRP_LOST_CONT_BIT, &p->flags);
br_ifinfo_notify(RTM_NEWLINK, NULL, p);
@@ -560,9 +560,9 @@ int br_mrp_in_port_open(struct net_device *dev, u8 loc)
}
if (loc)
- p->flags |= BR_MRP_LOST_IN_CONT;
+ set_bit(BR_MRP_LOST_IN_CONT_BIT, &p->flags);
else
- p->flags &= ~BR_MRP_LOST_IN_CONT;
+ clear_bit(BR_MRP_LOST_IN_CONT_BIT, &p->flags);
br_ifinfo_notify(RTM_NEWLINK, NULL, p);
diff --git a/net/bridge/br_mst.c b/net/bridge/br_mst.c
index 43a300ae6bfa..1654efd3045b 100644
--- a/net/bridge/br_mst.c
+++ b/net/bridge/br_mst.c
@@ -107,21 +107,24 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
struct net_bridge_vlan *v;
int err = 0;
- rcu_read_lock();
- vg = nbp_vlan_group_rcu(p);
- if (!vg)
- goto out;
-
/* MSTI 0 (CST) state changes are notified via the regular
- * SWITCHDEV_ATTR_ID_PORT_STP_STATE.
+ * SWITCHDEV_ATTR_ID_PORT_STP_STATE. All other MSTIs are handled via
+ * netlink with RTNL held
*/
if (msti) {
+ ASSERT_RTNL();
+
err = switchdev_port_attr_set(p->dev, &attr, extack);
if (err && err != -EOPNOTSUPP)
goto out;
+ err = 0;
}
- err = 0;
+ rcu_read_lock();
+ vg = nbp_vlan_group_rcu(p);
+ if (!vg)
+ goto out_rcu_unlock;
+
list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
if (v->brvlan->msti != msti)
continue;
@@ -129,8 +132,9 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
br_mst_vlan_set_state(vg, v, state);
}
-out:
+out_rcu_unlock:
rcu_read_unlock();
+out:
return err;
}
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 881d866d687a..2f9bb30e1a1f 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -281,7 +281,7 @@ static bool br_port_group_equal(struct net_bridge_port_group *p,
if (p->key.port != port)
return false;
- if (!(port->flags & BR_MULTICAST_TO_UNICAST))
+ if (!test_bit(BR_MULTICAST_TO_UNICAST_BIT, &port->flags))
return true;
return ether_addr_equal(src, p->eth_addr);
@@ -808,7 +808,11 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
struct hlist_node *tmp;
rcu_assign_pointer(*pp, pg->next);
- hlist_del_init(&pg->mglist);
+ /* Keep ->next (held under multicast_lock, freed later by the GC work):
+ * a port->mglist teardown walk may have latched this node as its next,
+ * and deleting other groups of the same port must not truncate it.
+ */
+ hlist_del_init_rcu(&pg->mglist);
br_multicast_eht_clean_sets(pg);
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
br_multicast_del_group_src(ent, false);
@@ -835,6 +839,13 @@ static void br_multicast_find_del_pg(struct net_bridge *br,
struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p;
+ /* A teardown walk over port->mglist can reach a group that an earlier
+ * iteration already deleted as a side effect. It is off mp->ports by
+ * now, so skip it instead of falling through to the WARN_ON() below.
+ */
+ if (hlist_unhashed(&pg->mglist))
+ return;
+
mp = br_mdb_ip_get(br, &pg->key.addr);
if (WARN_ON(!mp))
return;
@@ -926,7 +937,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
struct net_bridge_port_group *pg,
__be32 ip_dst, __be32 group,
bool with_srcs, bool over_lmqt,
- u8 sflag, u8 *igmp_type,
+ u8 sflag,
bool *need_rexmit)
{
struct net_bridge_port *p = pg ? pg->key.port : NULL;
@@ -934,12 +945,12 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
size_t pkt_size, igmp_hdr_size;
unsigned long now = jiffies;
struct igmpv3_query *ihv3;
+ unsigned long lmqt, mrt;
void *csum_start = NULL;
__sum16 *csum = NULL;
struct sk_buff *skb;
struct igmphdr *ih;
struct ethhdr *eth;
- unsigned long lmqt;
struct iphdr *iph;
u16 lmqt_srcs = 0;
@@ -1004,15 +1015,14 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
skb_put(skb, 24);
skb_set_transport_header(skb, skb->len);
- *igmp_type = IGMP_HOST_MEMBERSHIP_QUERY;
+ mrt = group ? brmctx->multicast_last_member_interval :
+ brmctx->multicast_query_response_interval;
switch (brmctx->multicast_igmp_version) {
case 2:
ih = igmp_hdr(skb);
ih->type = IGMP_HOST_MEMBERSHIP_QUERY;
- ih->code = (group ? brmctx->multicast_last_member_interval :
- brmctx->multicast_query_response_interval) /
- (HZ / IGMP_TIMER_SCALE);
+ ih->code = mrt / (HZ / IGMP_TIMER_SCALE);
ih->group = group;
ih->csum = 0;
csum = &ih->csum;
@@ -1021,11 +1031,9 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
case 3:
ihv3 = igmpv3_query_hdr(skb);
ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY;
- ihv3->code = (group ? brmctx->multicast_last_member_interval :
- brmctx->multicast_query_response_interval) /
- (HZ / IGMP_TIMER_SCALE);
+ ihv3->code = igmpv3_mrc(mrt / (HZ / IGMP_TIMER_SCALE));
ihv3->group = group;
- ihv3->qqic = brmctx->multicast_query_interval / HZ;
+ ihv3->qqic = igmpv3_qqic(brmctx->multicast_query_interval / HZ);
ihv3->nsrcs = htons(lmqt_srcs);
ihv3->resv = 0;
ihv3->suppress = sflag;
@@ -1074,7 +1082,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
const struct in6_addr *ip6_dst,
const struct in6_addr *group,
bool with_srcs, bool over_llqt,
- u8 sflag, u8 *igmp_type,
+ u8 sflag,
bool *need_rexmit)
{
struct net_bridge_port *p = pg ? pg->key.port : NULL;
@@ -1168,7 +1176,6 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
interval = ipv6_addr_any(group) ?
brmctx->multicast_query_response_interval :
brmctx->multicast_last_member_interval;
- *igmp_type = ICMPV6_MGM_QUERY;
switch (brmctx->multicast_mld_version) {
case 1:
mldq = (struct mld_msg *)icmp6_hdr(skb);
@@ -1183,7 +1190,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
break;
case 2:
mld2q = (struct mld2_query *)icmp6_hdr(skb);
- mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval));
+ mld2q->mld2q_mrc = htons(mldv2_mrc(jiffies_to_msecs(interval)));
mld2q->mld2q_type = ICMPV6_MGM_QUERY;
mld2q->mld2q_code = 0;
mld2q->mld2q_cksum = 0;
@@ -1192,7 +1199,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
mld2q->mld2q_suppress = sflag;
mld2q->mld2q_qrv = 2;
mld2q->mld2q_nsrcs = htons(llqt_srcs);
- mld2q->mld2q_qqic = brmctx->multicast_query_interval / HZ;
+ mld2q->mld2q_qqic = mldv2_qqic(brmctx->multicast_query_interval / HZ);
mld2q->mld2q_mca = *group;
csum = &mld2q->mld2q_cksum;
csum_start = (void *)mld2q;
@@ -1239,8 +1246,7 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge_mcast *brmctx,
struct br_ip *ip_dst,
struct br_ip *group,
bool with_srcs, bool over_lmqt,
- u8 sflag, u8 *igmp_type,
- bool *need_rexmit)
+ u8 sflag, bool *need_rexmit)
{
__be32 ip4_dst;
@@ -1250,8 +1256,7 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge_mcast *brmctx,
return br_ip4_multicast_alloc_query(brmctx, pmctx, pg,
ip4_dst, group->dst.ip4,
with_srcs, over_lmqt,
- sflag, igmp_type,
- need_rexmit);
+ sflag, need_rexmit);
#if IS_ENABLED(CONFIG_IPV6)
case htons(ETH_P_IPV6): {
struct in6_addr ip6_dst;
@@ -1265,8 +1270,7 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge_mcast *brmctx,
return br_ip6_multicast_alloc_query(brmctx, pmctx, pg,
&ip6_dst, &group->dst.ip6,
with_srcs, over_lmqt,
- sflag, igmp_type,
- need_rexmit);
+ sflag, need_rexmit);
}
#endif
}
@@ -1437,16 +1441,17 @@ struct net_bridge_port_group *br_multicast_new_port_group(
goto free_out;
}
- rcu_assign_pointer(p->next, next);
timer_setup(&p->timer, br_multicast_port_group_expired, 0);
timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0);
- hlist_add_head(&p->mglist, &port->mglist);
if (src)
memcpy(p->eth_addr, src, ETH_ALEN);
else
eth_broadcast_addr(p->eth_addr);
+ RCU_INIT_POINTER(p->next, next);
+ hlist_add_head_rcu(&p->mglist, &port->mglist);
+
return p;
free_out:
@@ -1461,11 +1466,11 @@ void br_multicast_del_port_group(struct net_bridge_port_group *p)
struct net_bridge_port *port = p->key.port;
__u16 vid = p->key.addr.vid;
- hlist_del_init(&p->mglist);
+ hlist_del_init_rcu(&p->mglist);
if (!br_multicast_is_star_g(&p->key.addr))
rhashtable_remove_fast(&port->br->sg_port_tbl, &p->rhnode,
br_sg_port_rht_params);
- kfree(p);
+ kfree_rcu(p, rcu);
br_multicast_port_ngroups_dec(port, vid);
}
@@ -1776,6 +1781,64 @@ static void br_multicast_select_own_querier(struct net_bridge_mcast *brmctx,
#endif
}
+static u8 br_multicast_query_type(const struct sk_buff *skb)
+{
+ return skb->protocol == htons(ETH_P_IP) ? IGMP_HOST_MEMBERSHIP_QUERY :
+ ICMPV6_MGM_QUERY;
+}
+
+static void br_multicast_port_query_queue_work(struct work_struct *work)
+{
+ struct net_bridge_mcast_port *pmctx;
+ struct sk_buff_head list;
+ struct sk_buff *skb;
+
+ pmctx = container_of(work, struct net_bridge_mcast_port,
+ query_queue_work);
+
+ __skb_queue_head_init(&list);
+ spin_lock_bh(&pmctx->query_queue.lock);
+ skb_queue_splice_tail_init(&pmctx->query_queue, &list);
+ spin_unlock_bh(&pmctx->query_queue.lock);
+
+ while ((skb = __skb_dequeue(&list))) {
+ u8 query_type = br_multicast_query_type(skb);
+
+ local_bh_disable();
+ br_multicast_count(pmctx->port->br, pmctx->port, skb,
+ query_type, BR_MCAST_DIR_TX);
+ NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, dev_net(skb->dev),
+ NULL, skb, NULL, skb->dev, br_dev_queue_push_xmit);
+ local_bh_enable();
+ }
+}
+
+static void br_multicast_query_queue_work(struct work_struct *work)
+{
+ struct net_bridge_mcast *brmctx;
+ struct sk_buff_head list;
+ struct sk_buff *skb;
+
+ brmctx = container_of(work, struct net_bridge_mcast, query_queue_work);
+
+ __skb_queue_head_init(&list);
+ spin_lock_bh(&brmctx->query_queue.lock);
+ skb_queue_splice_tail_init(&brmctx->query_queue, &list);
+ spin_unlock_bh(&brmctx->query_queue.lock);
+
+ while ((skb = __skb_dequeue(&list))) {
+ u8 query_type = br_multicast_query_type(skb);
+
+ local_bh_disable();
+ br_multicast_count(brmctx->br, NULL, skb, query_type,
+ BR_MCAST_DIR_RX);
+ netif_rx(skb);
+ local_bh_enable();
+ }
+}
+
+#define BR_MULTICAST_QUERY_QUEUE_LEN_MAX 1000
+
static void __br_multicast_send_query(struct net_bridge_mcast *brmctx,
struct net_bridge_mcast_port *pmctx,
struct net_bridge_port_group *pg,
@@ -1785,28 +1848,30 @@ static void __br_multicast_send_query(struct net_bridge_mcast *brmctx,
u8 sflag,
bool *need_rexmit)
{
+ struct sk_buff_head *queue;
bool over_lmqt = !!sflag;
struct sk_buff *skb;
- u8 igmp_type;
if (!br_multicast_ctx_should_use(brmctx, pmctx) ||
!br_multicast_ctx_matches_vlan_snooping(brmctx))
return;
+ queue = pmctx ? &pmctx->query_queue : &brmctx->query_queue;
+
again_under_lmqt:
+ if (skb_queue_len_lockless(queue) >= BR_MULTICAST_QUERY_QUEUE_LEN_MAX)
+ return;
+
skb = br_multicast_alloc_query(brmctx, pmctx, pg, ip_dst, group,
- with_srcs, over_lmqt, sflag, &igmp_type,
+ with_srcs, over_lmqt, sflag,
need_rexmit);
if (!skb)
return;
if (pmctx) {
skb->dev = pmctx->port->dev;
- br_multicast_count(brmctx->br, pmctx->port, skb, igmp_type,
- BR_MCAST_DIR_TX);
- NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
- dev_net(pmctx->port->dev), NULL, skb, NULL, skb->dev,
- br_dev_queue_push_xmit);
+ skb_queue_tail(queue, skb);
+ queue_work(system_highpri_wq, &pmctx->query_queue_work);
if (over_lmqt && with_srcs && sflag) {
over_lmqt = false;
@@ -1814,9 +1879,8 @@ again_under_lmqt:
}
} else {
br_multicast_select_own_querier(brmctx, group, skb);
- br_multicast_count(brmctx->br, NULL, skb, igmp_type,
- BR_MCAST_DIR_RX);
- netif_rx(skb);
+ skb_queue_tail(queue, skb);
+ queue_work(system_highpri_wq, &brmctx->query_queue_work);
}
}
@@ -1999,6 +2063,10 @@ void br_multicast_port_ctx_init(struct net_bridge_port *port,
pmctx->port = port;
pmctx->vlan = vlan;
pmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
+
+ skb_queue_head_init(&pmctx->query_queue);
+ INIT_WORK(&pmctx->query_queue_work, br_multicast_port_query_queue_work);
+
timer_setup(&pmctx->ip4_mc_router_timer,
br_ip4_multicast_router_expired, 0);
timer_setup(&pmctx->ip4_own_query.timer,
@@ -2029,15 +2097,19 @@ void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx)
bool del = false;
#if IS_ENABLED(CONFIG_IPV6)
- timer_delete_sync(&pmctx->ip6_mc_router_timer);
+ timer_shutdown_sync(&pmctx->ip6_own_query.timer);
+ timer_shutdown_sync(&pmctx->ip6_mc_router_timer);
#endif
- timer_delete_sync(&pmctx->ip4_mc_router_timer);
+ timer_shutdown_sync(&pmctx->ip4_own_query.timer);
+ timer_shutdown_sync(&pmctx->ip4_mc_router_timer);
spin_lock_bh(&br->multicast_lock);
del |= br_ip6_multicast_rport_del(pmctx);
del |= br_ip4_multicast_rport_del(pmctx);
br_multicast_rport_del_notify(pmctx, del);
spin_unlock_bh(&br->multicast_lock);
+ cancel_work_sync(&pmctx->query_queue_work);
+ __skb_queue_purge(&pmctx->query_queue);
}
int br_multicast_add_port(struct net_bridge_port *port)
@@ -3518,7 +3590,7 @@ static void br_ip4_multicast_query(struct net_bridge_mcast *brmctx,
goto out;
max_delay = ih3->code ?
- IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
+ igmpv3_mrt(ih3) * (HZ / IGMP_TIMER_SCALE) : 1;
} else {
goto out;
}
@@ -3606,7 +3678,7 @@ static int br_ip6_multicast_query(struct net_bridge_mcast *brmctx,
mld2q->mld2q_suppress)
goto out;
- max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL);
+ max_delay = max(msecs_to_jiffies(mldv2_mrd(mld2q)), 1UL);
}
is_general_query = group && ipv6_addr_any(group);
@@ -3672,7 +3744,8 @@ br_multicast_leave_group(struct net_bridge_mcast *brmctx,
if (!mp)
goto out;
- if (pmctx && (pmctx->port->flags & BR_MULTICAST_FAST_LEAVE)) {
+ if (pmctx &&
+ test_bit(BR_MULTICAST_FAST_LEAVE_BIT, &pmctx->port->flags)) {
struct net_bridge_port_group __rcu **pp;
for (pp = &mp->ports;
@@ -3686,6 +3759,7 @@ br_multicast_leave_group(struct net_bridge_mcast *brmctx,
p->flags |= MDB_PG_FLAGS_FAST_LEAVE;
br_multicast_del_pg(mp, p, pp);
+ break;
}
goto out;
}
@@ -4111,6 +4185,9 @@ void br_multicast_ctx_init(struct net_bridge *br,
seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock);
#endif
+ skb_queue_head_init(&brmctx->query_queue);
+ INIT_WORK(&brmctx->query_queue_work, br_multicast_query_queue_work);
+
timer_setup(&brmctx->ip4_mc_router_timer,
br_ip4_multicast_local_router_expired, 0);
timer_setup(&brmctx->ip4_other_query.timer,
@@ -4134,6 +4211,8 @@ void br_multicast_ctx_init(struct net_bridge *br,
void br_multicast_ctx_deinit(struct net_bridge_mcast *brmctx)
{
__br_multicast_stop(brmctx);
+ cancel_work_sync(&brmctx->query_queue_work);
+ __skb_queue_purge(&brmctx->query_queue);
}
void br_multicast_init(struct net_bridge *br)
@@ -4310,8 +4389,8 @@ void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
if (br_vlan_is_master(vlan)) {
br = vlan->br;
- if (!br_vlan_is_brentry(vlan) ||
- (on &&
+ if (on &&
+ (!br_vlan_is_brentry(vlan) ||
br_multicast_ctx_vlan_global_disabled(&vlan->br_mcast_ctx)))
return;
@@ -4455,8 +4534,6 @@ void br_multicast_dev_del(struct net_bridge *br)
br_multicast_ctx_deinit(&br->multicast_ctx);
br_multicast_gc(&deleted_head);
cancel_work_sync(&br->mcast_gc_work);
-
- rcu_barrier();
}
int br_multicast_set_router(struct net_bridge_mcast *brmctx, unsigned long val)
@@ -4640,10 +4717,24 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx,
rcu_read_unlock();
}
-static void br_multicast_del_grps(struct net_bridge *br)
+static void br_multicast_enable_all_ports(struct net_bridge *br)
{
struct net_bridge_port *port;
+ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED))
+ return;
+
+ list_for_each_entry(port, &br->port_list, list)
+ __br_multicast_enable_port_ctx(&port->multicast_ctx);
+}
+
+static void br_multicast_disable_all_ports(struct net_bridge *br)
+{
+ struct net_bridge_port *port;
+
+ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED))
+ return;
+
list_for_each_entry(port, &br->port_list, list)
__br_multicast_disable_port_ctx(&port->multicast_ctx);
}
@@ -4651,7 +4742,6 @@ static void br_multicast_del_grps(struct net_bridge *br)
int br_multicast_toggle(struct net_bridge *br, unsigned long val,
struct netlink_ext_ack *extack)
{
- struct net_bridge_port *port;
bool change_snoopers = false;
int err = 0;
@@ -4668,7 +4758,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val,
br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val);
if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) {
change_snoopers = true;
- br_multicast_del_grps(br);
+ br_multicast_disable_all_ports(br);
goto unlock;
}
@@ -4676,8 +4766,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val,
goto unlock;
br_multicast_open(br);
- list_for_each_entry(port, &br->port_list, list)
- __br_multicast_enable_port_ctx(&port->multicast_ctx);
+ br_multicast_enable_all_ports(br);
change_snoopers = true;
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 0ab1c94db4b9..0a394e5f4391 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -297,7 +297,11 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_
goto free_skb;
}
- neigh_hh_bridge(&neigh->hh, skb);
+ if (neigh_hh_bridge(&neigh->hh, skb)) {
+ neigh_release(neigh);
+ goto free_skb;
+ }
+
skb->dev = br_indev;
ret = br_handle_frame_finish(net, sk, skb);
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 6fd5386a1d64..b2cd4e39326d 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -113,7 +113,7 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask);
rcu_read_unlock();
- if (p && (p->flags & BR_VLAN_TUNNEL))
+ if (p && test_bit(BR_VLAN_TUNNEL_BIT, &p->flags))
vinfo_sz += br_get_vlan_tunnel_info_size(vg);
/* Each VLAN is returned in bridge_vlan_info along with flags */
@@ -190,6 +190,7 @@ static inline size_t br_port_info_size(void)
+ nla_total_size(1) /* IFLA_BRPORT_LOCKED */
+ nla_total_size(1) /* IFLA_BRPORT_MAB */
+ nla_total_size(1) /* IFLA_BRPORT_NEIGH_VLAN_SUPPRESS */
+ + nla_total_size(1) /* IFLA_BRPORT_NEIGH_FORWARD_GRAT */
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */
+ nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */
@@ -233,56 +234,61 @@ static inline size_t br_nlmsg_size(struct net_device *dev, u32 filter_mask)
static int br_port_fill_attrs(struct sk_buff *skb,
const struct net_bridge_port *p)
{
- u8 mode = !!(p->flags & BR_HAIRPIN_MODE);
+ unsigned long flags = READ_ONCE(p->flags);
+ u8 mode = !!(flags & BR_HAIRPIN_MODE);
struct net_bridge_port *backup_p;
u64 timerval;
if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) ||
- nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) ||
- nla_put_u32(skb, IFLA_BRPORT_COST, p->path_cost) ||
+ nla_put_u16(skb, IFLA_BRPORT_PRIORITY, READ_ONCE(p->priority)) ||
+ nla_put_u32(skb, IFLA_BRPORT_COST, READ_ONCE(p->path_cost)) ||
nla_put_u8(skb, IFLA_BRPORT_MODE, mode) ||
- nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD)) ||
+ nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(flags & BR_BPDU_GUARD)) ||
nla_put_u8(skb, IFLA_BRPORT_PROTECT,
- !!(p->flags & BR_ROOT_BLOCK)) ||
+ !!(flags & BR_ROOT_BLOCK)) ||
nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE,
- !!(p->flags & BR_MULTICAST_FAST_LEAVE)) ||
+ !!(flags & BR_MULTICAST_FAST_LEAVE)) ||
nla_put_u8(skb, IFLA_BRPORT_MCAST_TO_UCAST,
- !!(p->flags & BR_MULTICAST_TO_UNICAST)) ||
- nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) ||
+ !!(flags & BR_MULTICAST_TO_UNICAST)) ||
+ nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(flags & BR_LEARNING)) ||
nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD,
- !!(p->flags & BR_FLOOD)) ||
+ !!(flags & BR_FLOOD)) ||
nla_put_u8(skb, IFLA_BRPORT_MCAST_FLOOD,
- !!(p->flags & BR_MCAST_FLOOD)) ||
+ !!(flags & BR_MCAST_FLOOD)) ||
nla_put_u8(skb, IFLA_BRPORT_BCAST_FLOOD,
- !!(p->flags & BR_BCAST_FLOOD)) ||
- nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP)) ||
+ !!(flags & BR_BCAST_FLOOD)) ||
+ nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(flags & BR_PROXYARP)) ||
nla_put_u8(skb, IFLA_BRPORT_PROXYARP_WIFI,
- !!(p->flags & BR_PROXYARP_WIFI)) ||
+ !!(flags & BR_PROXYARP_WIFI)) ||
nla_put(skb, IFLA_BRPORT_ROOT_ID, sizeof(struct ifla_bridge_id),
&p->designated_root) ||
nla_put(skb, IFLA_BRPORT_BRIDGE_ID, sizeof(struct ifla_bridge_id),
&p->designated_bridge) ||
- nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT, p->designated_port) ||
- nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, p->designated_cost) ||
- nla_put_u16(skb, IFLA_BRPORT_ID, p->port_id) ||
+ nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT,
+ READ_ONCE(p->designated_port)) ||
+ nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST,
+ READ_ONCE(p->designated_cost)) ||
+ nla_put_u16(skb, IFLA_BRPORT_ID, READ_ONCE(p->port_id)) ||
nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) ||
nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
p->topology_change_ack) ||
- nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) ||
- nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(p->flags &
+ nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, READ_ONCE(p->config_pending)) ||
+ nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(flags &
BR_VLAN_TUNNEL)) ||
nla_put_u16(skb, IFLA_BRPORT_GROUP_FWD_MASK, p->group_fwd_mask) ||
nla_put_u8(skb, IFLA_BRPORT_NEIGH_SUPPRESS,
- !!(p->flags & BR_NEIGH_SUPPRESS)) ||
- nla_put_u8(skb, IFLA_BRPORT_MRP_RING_OPEN, !!(p->flags &
+ !!(flags & BR_NEIGH_SUPPRESS)) ||
+ nla_put_u8(skb, IFLA_BRPORT_MRP_RING_OPEN, !!(flags &
BR_MRP_LOST_CONT)) ||
nla_put_u8(skb, IFLA_BRPORT_MRP_IN_OPEN,
- !!(p->flags & BR_MRP_LOST_IN_CONT)) ||
- nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)) ||
- nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED)) ||
- nla_put_u8(skb, IFLA_BRPORT_MAB, !!(p->flags & BR_PORT_MAB)) ||
+ !!(flags & BR_MRP_LOST_IN_CONT)) ||
+ nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(flags & BR_ISOLATED)) ||
+ nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(flags & BR_PORT_LOCKED)) ||
+ nla_put_u8(skb, IFLA_BRPORT_MAB, !!(flags & BR_PORT_MAB)) ||
nla_put_u8(skb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
- !!(p->flags & BR_NEIGH_VLAN_SUPPRESS)))
+ !!(flags & BR_NEIGH_VLAN_SUPPRESS)) ||
+ nla_put_u8(skb, IFLA_BRPORT_NEIGH_FORWARD_GRAT,
+ !!(flags & BR_NEIGH_FORWARD_GRAT)))
return -EMSGSIZE;
timerval = br_timer_value(&p->message_age_timer);
@@ -534,7 +540,7 @@ static int br_fill_ifinfo(struct sk_buff *skb,
else
err = br_fill_ifvlaninfo(skb, vg);
- if (port && (port->flags & BR_VLAN_TUNNEL))
+ if (port && test_bit(BR_VLAN_TUNNEL_BIT, &port->flags))
err = br_fill_vlan_tunnel_info(skb, vg);
rcu_read_unlock();
if (err)
@@ -817,7 +823,7 @@ static int br_afspec(struct net_bridge *br,
err = 0;
switch (nla_type(attr)) {
case IFLA_BRIDGE_VLAN_TUNNEL_INFO:
- if (!p || !(p->flags & BR_VLAN_TUNNEL))
+ if (!p || !test_bit(BR_VLAN_TUNNEL_BIT, &p->flags))
return -EINVAL;
err = br_parse_vlan_tunnel_info(attr, &tinfo_curr);
if (err)
@@ -902,6 +908,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = {
[IFLA_BRPORT_MCAST_MAX_GROUPS] = { .type = NLA_U32 },
[IFLA_BRPORT_NEIGH_VLAN_SUPPRESS] = NLA_POLICY_MAX(NLA_U8, 1),
[IFLA_BRPORT_BACKUP_NHID] = { .type = NLA_U32 },
+ [IFLA_BRPORT_NEIGH_FORWARD_GRAT] = NLA_POLICY_MAX(NLA_U8, 1),
};
/* Change the state of the port and notify spanning tree */
@@ -927,56 +934,67 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state)
}
/* Set/clear or port flags based on attribute */
-static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
+static void br_set_port_flag(unsigned long *set_flags,
+ unsigned long *clear_flags,
+ struct nlattr *tb[],
int attrtype, unsigned long mask)
{
- if (!tb[attrtype])
- return;
-
- if (nla_get_u8(tb[attrtype]))
- p->flags |= mask;
- else
- p->flags &= ~mask;
+ if (tb[attrtype]) {
+ if (nla_get_u8(tb[attrtype]))
+ *set_flags |= mask;
+ else
+ *clear_flags |= mask;
+ }
}
/* Process bridge protocol info on port */
static int br_setport(struct net_bridge_port *p, struct nlattr *tb[],
struct netlink_ext_ack *extack)
{
- unsigned long old_flags, changed_mask;
+ unsigned long old_flags, flags, changed_mask;
+ unsigned long set = 0, clear = 0;
bool br_vlan_tunnel_old;
int err;
- old_flags = p->flags;
+ old_flags = READ_ONCE(p->flags);
br_vlan_tunnel_old = (old_flags & BR_VLAN_TUNNEL) ? true : false;
- br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
- br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
- br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE,
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_FAST_LEAVE,
BR_MULTICAST_FAST_LEAVE);
- br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK);
- br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
- br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
- br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD);
- br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST,
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_MCAST_FLOOD,
+ BR_MCAST_FLOOD);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_MCAST_TO_UCAST,
BR_MULTICAST_TO_UNICAST);
- br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD);
- br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
- br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI);
- br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL);
- br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, BR_NEIGH_SUPPRESS);
- br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED);
- br_set_port_flag(p, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED);
- br_set_port_flag(p, tb, IFLA_BRPORT_MAB, BR_PORT_MAB);
- br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_BCAST_FLOOD,
+ BR_BCAST_FLOOD);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_PROXYARP_WIFI,
+ BR_PROXYARP_WIFI);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_VLAN_TUNNEL,
+ BR_VLAN_TUNNEL);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_NEIGH_SUPPRESS,
+ BR_NEIGH_SUPPRESS);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_MAB, BR_PORT_MAB);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
BR_NEIGH_VLAN_SUPPRESS);
+ br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_NEIGH_FORWARD_GRAT,
+ BR_NEIGH_FORWARD_GRAT);
- if ((p->flags & BR_PORT_MAB) &&
- (!(p->flags & BR_PORT_LOCKED) || !(p->flags & BR_LEARNING))) {
+ flags = (old_flags | set) & ~clear;
+
+ if ((flags & BR_PORT_MAB) &&
+ (!(flags & BR_PORT_LOCKED) || !(flags & BR_LEARNING))) {
NL_SET_ERR_MSG(extack, "Bridge port must be locked and have learning enabled when MAB is enabled");
- p->flags = old_flags;
return -EINVAL;
- } else if (!(p->flags & BR_PORT_MAB) && (old_flags & BR_PORT_MAB)) {
+ }
+ if (!(flags & BR_PORT_MAB) && (old_flags & BR_PORT_MAB)) {
struct net_bridge_fdb_flush_desc desc = {
.flags = BIT(BR_FDB_LOCKED),
.flags_mask = BIT(BR_FDB_LOCKED),
@@ -986,33 +1004,41 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[],
br_fdb_flush(p->br, &desc);
}
- changed_mask = old_flags ^ p->flags;
+ changed_mask = old_flags ^ flags;
- err = br_switchdev_set_port_flag(p, p->flags, changed_mask, extack);
- if (err) {
- p->flags = old_flags;
+ err = br_switchdev_set_port_flag(p, flags, changed_mask, extack);
+ if (err)
return err;
- }
- if (br_vlan_tunnel_old && !(p->flags & BR_VLAN_TUNNEL))
+ do {
+ flags = (old_flags | set) & ~clear;
+ } while (!try_cmpxchg(&p->flags, &old_flags, flags));
+
+ if (br_vlan_tunnel_old && !(flags & BR_VLAN_TUNNEL))
nbp_vlan_tunnel_info_flush(p);
br_port_flags_change(p, changed_mask);
if (tb[IFLA_BRPORT_COST]) {
+ spin_lock_bh(&p->br->lock);
err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
+ spin_unlock_bh(&p->br->lock);
if (err)
return err;
}
if (tb[IFLA_BRPORT_PRIORITY]) {
+ spin_lock_bh(&p->br->lock);
err = br_stp_set_port_priority(p, nla_get_u16(tb[IFLA_BRPORT_PRIORITY]));
+ spin_unlock_bh(&p->br->lock);
if (err)
return err;
}
if (tb[IFLA_BRPORT_STATE]) {
+ spin_lock_bh(&p->br->lock);
err = br_set_port_state(p, nla_get_u8(tb[IFLA_BRPORT_STATE]));
+ spin_unlock_bh(&p->br->lock);
if (err)
return err;
}
@@ -1114,9 +1140,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags,
if (err)
return err;
- spin_lock_bh(&p->br->lock);
err = br_setport(p, tb, extack);
- spin_unlock_bh(&p->br->lock);
} else {
/* Binary compatibility with old RSTP */
if (nla_len(protinfo) < sizeof(u8))
@@ -1203,17 +1227,10 @@ static int br_port_slave_changelink(struct net_device *brdev,
struct nlattr *data[],
struct netlink_ext_ack *extack)
{
- struct net_bridge *br = netdev_priv(brdev);
- int ret;
-
if (!data)
return 0;
- spin_lock_bh(&br->lock);
- ret = br_setport(br_port_get_rtnl(dev), data, extack);
- spin_unlock_bh(&br->lock);
-
- return ret;
+ return br_setport(br_port_get_rtnl(dev), data, extack);
}
static int br_port_fill_slave_info(struct sk_buff *skb,
@@ -1824,6 +1841,7 @@ static int br_fill_linkxstats(struct sk_buff *skb,
const struct net_device *dev,
int *prividx, int attr)
{
+ unsigned int limit = U16_MAX - nla_total_size(0);
struct nlattr *nla __maybe_unused;
struct net_bridge_port *p = NULL;
struct net_bridge_vlan_group *vg;
@@ -1841,6 +1859,7 @@ static int br_fill_linkxstats(struct sk_buff *skb,
p = br_port_get_rtnl(dev);
if (!p)
return 0;
+ limit -= nla_total_size_64bit(sizeof(p->stp_xstats));
br = p->br;
vg = nbp_vlan_group(p);
break;
@@ -1855,6 +1874,9 @@ static int br_fill_linkxstats(struct sk_buff *skb,
if (vg) {
u16 pvid;
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ limit -= nla_total_size_64bit(sizeof(struct br_mcast_stats));
+#endif
pvid = br_get_pvid(vg);
list_for_each_entry(v, &vg->vlan_list, vlist) {
struct bridge_vlan_xstats vxi;
@@ -1862,6 +1884,11 @@ static int br_fill_linkxstats(struct sk_buff *skb,
if (++vl_idx < *prividx)
continue;
+
+ if (skb_tail_pointer(skb) - (unsigned char *)nest +
+ nla_total_size(sizeof(vxi)) >= limit)
+ goto nla_put_failure;
+
memset(&vxi, 0, sizeof(vxi));
vxi.vid = v->vid;
vxi.flags = v->flags;
diff --git a/net/bridge/br_netlink_tunnel.c b/net/bridge/br_netlink_tunnel.c
index 71a12da30004..e7eceab5b515 100644
--- a/net/bridge/br_netlink_tunnel.c
+++ b/net/bridge/br_netlink_tunnel.c
@@ -271,7 +271,8 @@ static void __vlan_tunnel_handle_range(const struct net_bridge_port *p,
if (!*v_start)
goto out_init;
- if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) {
+ if (v && curr_change &&
+ br_vlan_can_enter_range(v, *v_end, br_get_pvid(vg))) {
*v_end = v;
return;
}
@@ -301,7 +302,8 @@ int br_process_vlan_tunnel_info(const struct net_bridge *br,
if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
return -EINVAL;
- if ((tinfo_curr->vid - tinfo_last->vid) !=
+ if (tinfo_curr->vid < tinfo_last->vid ||
+ (tinfo_curr->vid - tinfo_last->vid) !=
(tinfo_curr->tunid - tinfo_last->tunid))
return -EINVAL;
t = tinfo_last->tunid;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 361a9b84451e..b01997ea9508 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -131,6 +131,8 @@ struct net_bridge_mcast_port {
unsigned char multicast_router;
u32 mdb_n_entries;
u32 mdb_max_entries;
+ struct sk_buff_head query_queue;
+ struct work_struct query_queue_work;
#endif /* CONFIG_BRIDGE_IGMP_SNOOPING */
};
@@ -167,6 +169,8 @@ struct net_bridge_mcast {
struct bridge_mcast_own_query ip6_own_query;
struct bridge_mcast_querier ip6_querier;
#endif /* IS_ENABLED(CONFIG_IPV6) */
+ struct sk_buff_head query_queue;
+ struct work_struct query_queue_work;
#endif /* CONFIG_BRIDGE_IGMP_SNOOPING */
};
@@ -183,6 +187,7 @@ enum {
BR_VLFLAG_GLOBAL_MCAST_ENABLED = BIT(3),
BR_VLFLAG_NEIGH_SUPPRESS_ENABLED = BIT(4),
BR_VLFLAG_TAGGING_BY_SWITCHDEV = BIT(5),
+ BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED = BIT(6),
};
/**
@@ -451,7 +456,7 @@ struct net_bridge_port {
#define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj)
#define br_auto_port(p) ((p)->flags & BR_AUTO_MASK)
-#define br_promisc_port(p) ((p)->flags & BR_PROMISC)
+#define br_promisc_port(p) test_bit(BR_PROMISC_BIT, &(p)->flags)
static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev)
{
@@ -490,12 +495,13 @@ enum net_bridge_opts {
BROPT_MST_ENABLED,
BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION,
BROPT_FDB_LOCAL_VLAN_0,
+ BROPT_CFM_ENABLED,
+ BROPT_MRP_ENABLED,
};
struct net_bridge {
spinlock_t lock;
spinlock_t hash_lock;
- struct hlist_head frame_type_list;
struct net_device *dev;
unsigned long options;
/* These fields are accessed on each packet */
@@ -600,6 +606,7 @@ struct br_input_skb_cb {
u8 proxyarp_replied:1;
u8 src_port_isolated:1;
u8 promisc:1;
+ u8 grat_arp:1;
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
u8 vlan_filtered:1;
#endif
@@ -855,7 +862,6 @@ void br_fdb_delete_by_port(struct net_bridge *br,
struct net_bridge_fdb_entry *br_fdb_find_rcu(struct net_bridge *br,
const unsigned char *addr,
__u16 vid);
-int br_fdb_test_addr(struct net_device *dev, unsigned char *addr);
int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count,
unsigned long off);
int br_fdb_add_local(struct net_bridge *br, struct net_bridge_port *source,
@@ -927,16 +933,6 @@ int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev);
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
rx_handler_func_t *br_get_rx_handler(const struct net_device *dev);
-struct br_frame_type {
- __be16 type;
- int (*frame_handler)(struct net_bridge_port *port,
- struct sk_buff *skb);
- struct hlist_node list;
-};
-
-void br_add_frame(struct net_bridge *br, struct br_frame_type *ft);
-void br_del_frame(struct net_bridge *br, struct br_frame_type *ft);
-
static inline bool br_rx_handler_check_rcu(const struct net_device *dev)
{
return rcu_dereference(dev->rx_handler) == br_get_rx_handler(dev);
@@ -1626,7 +1622,8 @@ void br_vlan_notify(const struct net_bridge *br,
u16 vid, u16 vid_range,
int cmd);
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
- const struct net_bridge_vlan *range_end);
+ const struct net_bridge_vlan *range_end,
+ u16 pvid);
void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
struct net_device_path_ctx *ctx,
@@ -1873,7 +1870,8 @@ static inline void br_vlan_notify(const struct net_bridge *br,
}
static inline bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
- const struct net_bridge_vlan *range_end)
+ const struct net_bridge_vlan *range_end,
+ u16 pvid)
{
return true;
}
@@ -2065,9 +2063,6 @@ void br_stp_port_timer_init(struct net_bridge_port *p);
unsigned long br_timer_value(const struct timer_list *timer);
/* br.c */
-#if IS_ENABLED(CONFIG_ATM_LANE)
-extern int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr);
-#endif
/* br_mrp.c */
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
@@ -2076,6 +2071,7 @@ int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
bool br_mrp_enabled(struct net_bridge *br);
void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p);
int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br);
+int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb);
#else
static inline int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
struct nlattr *attr, int cmd,
@@ -2099,6 +2095,11 @@ static inline int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br)
return 0;
}
+static inline int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
+{
+ return 0;
+}
+
#endif
/* br_cfm.c */
@@ -2107,6 +2108,7 @@ int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p,
struct nlattr *attr, int cmd, struct netlink_ext_ack *extack);
bool br_cfm_created(struct net_bridge *br);
void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *p);
+int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb);
int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br);
int br_cfm_status_fill_info(struct sk_buff *skb,
struct net_bridge *br,
@@ -2131,6 +2133,12 @@ static inline void br_cfm_port_del(struct net_bridge *br,
{
}
+static inline int br_cfm_frame_rx(struct net_bridge_port *port,
+ struct sk_buff *skb)
+{
+ return 0;
+}
+
static inline int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br)
{
return -EOPNOTSUPP;
@@ -2363,6 +2371,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
u16 vid, struct net_bridge_port *p);
void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
u16 vid, struct net_bridge_port *p, struct nd_msg *msg);
-struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *m);
+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb);
bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid);
+bool br_is_neigh_forward_grat_enabled(const struct net_bridge_port *p, u16 vid);
#endif
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 024210f95468..c7e7e924f155 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -39,7 +39,7 @@ void br_set_state(struct net_bridge_port *p, unsigned int state)
/* Don't change the state of the ports if they are driven by a different
* protocol.
*/
- if (p->flags & BR_MRP_AWARE)
+ if (test_bit(BR_MRP_AWARE_BIT, &p->flags))
return;
p->state = state;
@@ -102,8 +102,9 @@ struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no)
static int br_should_become_root_port(const struct net_bridge_port *p,
u16 root_port)
{
- struct net_bridge *br;
+ u32 p_path_cost, rp_path_cost, p_designated_cost, rp_designated_cost;
struct net_bridge_port *rp;
+ struct net_bridge *br;
int t;
br = p->br;
@@ -125,11 +126,16 @@ static int br_should_become_root_port(const struct net_bridge_port *p,
else if (t > 0)
return 0;
- if (p->designated_cost + p->path_cost <
- rp->designated_cost + rp->path_cost)
+ p_path_cost = READ_ONCE(p->path_cost);
+ rp_path_cost = READ_ONCE(rp->path_cost);
+ p_designated_cost = READ_ONCE(p->designated_cost);
+ rp_designated_cost = READ_ONCE(rp->designated_cost);
+
+ if (p_designated_cost + p_path_cost <
+ rp_designated_cost + rp_path_cost)
return 1;
- else if (p->designated_cost + p->path_cost >
- rp->designated_cost + rp->path_cost)
+ else if (p_designated_cost + p_path_cost >
+ rp_designated_cost + rp_path_cost)
return 0;
t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8);
@@ -173,7 +179,7 @@ static void br_root_selection(struct net_bridge *br)
if (!br_should_become_root_port(p, root_port))
continue;
- if (p->flags & BR_ROOT_BLOCK)
+ if (test_bit(BR_ROOT_BLOCK_BIT, &p->flags))
br_root_port_block(br, p);
else
root_port = p->port_no;
@@ -187,7 +193,8 @@ static void br_root_selection(struct net_bridge *br)
} else {
p = br_get_port(br, root_port);
br->designated_root = p->designated_root;
- br->root_path_cost = p->designated_cost + p->path_cost;
+ br->root_path_cost = READ_ONCE(p->designated_cost) +
+ READ_ONCE(p->path_cost);
}
}
@@ -213,7 +220,7 @@ void br_transmit_config(struct net_bridge_port *p)
struct net_bridge *br;
if (timer_pending(&p->hold_timer)) {
- p->config_pending = 1;
+ WRITE_ONCE(p->config_pending, 1);
return;
}
@@ -240,7 +247,7 @@ void br_transmit_config(struct net_bridge_port *p)
if (bpdu.message_age < br->max_age) {
br_send_config_bpdu(p, &bpdu);
p->topology_change_ack = 0;
- p->config_pending = 0;
+ WRITE_ONCE(p->config_pending, 0);
if (p->br->stp_enabled == BR_KERNEL_STP)
mod_timer(&p->hold_timer,
round_jiffies(jiffies + BR_HOLD_TIME));
@@ -252,9 +259,9 @@ static void br_record_config_information(struct net_bridge_port *p,
const struct br_config_bpdu *bpdu)
{
p->designated_root = bpdu->root;
- p->designated_cost = bpdu->root_path_cost;
+ WRITE_ONCE(p->designated_cost, bpdu->root_path_cost);
p->designated_bridge = bpdu->bridge_id;
- p->designated_port = bpdu->port_id;
+ WRITE_ONCE(p->designated_port, bpdu->port_id);
p->designated_age = jiffies - bpdu->message_age;
mod_timer(&p->message_age_timer, jiffies
@@ -288,6 +295,7 @@ void br_transmit_tcn(struct net_bridge *br)
static int br_should_become_designated_port(const struct net_bridge_port *p)
{
struct net_bridge *br;
+ u32 p_designated_cost;
int t;
br = p->br;
@@ -297,9 +305,10 @@ static int br_should_become_designated_port(const struct net_bridge_port *p)
if (memcmp(&p->designated_root, &br->designated_root, 8))
return 1;
- if (br->root_path_cost < p->designated_cost)
+ p_designated_cost = READ_ONCE(p->designated_cost);
+ if (br->root_path_cost < p_designated_cost)
return 1;
- else if (br->root_path_cost > p->designated_cost)
+ else if (br->root_path_cost > p_designated_cost)
return 0;
t = memcmp(&br->bridge_id, &p->designated_bridge, 8);
@@ -331,6 +340,7 @@ static void br_designated_port_selection(struct net_bridge *br)
static int br_supersedes_port_info(const struct net_bridge_port *p,
const struct br_config_bpdu *bpdu)
{
+ u32 p_designated_cost;
int t;
t = memcmp(&bpdu->root, &p->designated_root, 8);
@@ -339,9 +349,10 @@ static int br_supersedes_port_info(const struct net_bridge_port *p,
else if (t > 0)
return 0;
- if (bpdu->root_path_cost < p->designated_cost)
+ p_designated_cost = READ_ONCE(p->designated_cost);
+ if (bpdu->root_path_cost < p_designated_cost)
return 1;
- else if (bpdu->root_path_cost > p->designated_cost)
+ else if (bpdu->root_path_cost > p_designated_cost)
return 0;
t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8);
@@ -371,7 +382,8 @@ void br_topology_change_detection(struct net_bridge *br)
{
int isroot = br_is_root_bridge(br);
- if (br->stp_enabled != BR_KERNEL_STP)
+ if (br->stp_enabled != BR_KERNEL_STP ||
+ !(br->dev->flags & IFF_UP))
return;
br_info(br, "topology change detected, %s\n",
@@ -421,9 +433,9 @@ void br_become_designated_port(struct net_bridge_port *p)
br = p->br;
p->designated_root = br->designated_root;
- p->designated_cost = br->root_path_cost;
+ WRITE_ONCE(p->designated_cost, br->root_path_cost);
p->designated_bridge = br->bridge_id;
- p->designated_port = p->port_id;
+ WRITE_ONCE(p->designated_port, p->port_id);
}
@@ -479,14 +491,14 @@ void br_port_state_selection(struct net_bridge *br)
/* Don't change port states if userspace is handling STP */
if (br->stp_enabled != BR_USER_STP) {
if (p->port_no == br->root_port) {
- p->config_pending = 0;
+ WRITE_ONCE(p->config_pending, 0);
p->topology_change_ack = 0;
br_make_forwarding(p);
} else if (br_is_designated_port(p)) {
timer_delete(&p->message_age_timer);
br_make_forwarding(p);
} else {
- p->config_pending = 0;
+ WRITE_ONCE(p->config_pending, 0);
p->topology_change_ack = 0;
br_make_blocking(p);
}
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index 7895489ac6fe..21d092f5acbb 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -52,7 +52,10 @@ static void br_send_bpdu(struct net_bridge_port *p,
LLC_SAP_BSPAN, LLC_PDU_CMD);
llc_pdu_init_as_ui_cmd(skb);
- llc_mac_hdr_init(skb, p->dev->dev_addr, p->br->group_addr);
+ if (llc_mac_hdr_init(skb, p->dev->dev_addr, p->br->group_addr)) {
+ kfree_skb(skb);
+ return;
+ }
skb_reset_mac_header(skb);
@@ -178,7 +181,7 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
if (!ether_addr_equal(eth_hdr(skb)->h_dest, br->group_addr))
goto out;
- if (p->flags & BR_BPDU_GUARD) {
+ if (test_bit(BR_BPDU_GUARD_BIT, &p->flags)) {
br_notice(br, "BPDU received on blocked port %u(%s)\n",
(unsigned int) p->port_no, p->dev->name);
br_stp_disable_port(p);
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 28c1d3f7e22f..a7e5422eb5d1 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -34,11 +34,11 @@ void br_init_port(struct net_bridge_port *p)
{
int err;
- p->port_id = br_make_port_id(p->priority, p->port_no);
+ WRITE_ONCE(p->port_id, br_make_port_id(p->priority, p->port_no));
br_become_designated_port(p);
br_set_state(p, BR_STATE_BLOCKING);
p->topology_change_ack = 0;
- p->config_pending = 0;
+ WRITE_ONCE(p->config_pending, 0);
err = __set_ageing_time(p->dev, p->br->ageing_time);
if (err)
@@ -105,7 +105,7 @@ void br_stp_disable_port(struct net_bridge_port *p)
br_become_designated_port(p);
br_set_state(p, BR_STATE_DISABLED);
p->topology_change_ack = 0;
- p->config_pending = 0;
+ WRITE_ONCE(p->config_pending, 0);
br_ifinfo_notify(RTM_NEWLINK, NULL, p);
@@ -320,10 +320,10 @@ int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio)
new_port_id = br_make_port_id(newprio, p->port_no);
if (br_is_designated_port(p))
- p->designated_port = new_port_id;
+ WRITE_ONCE(p->designated_port, new_port_id);
- p->port_id = new_port_id;
- p->priority = newprio;
+ WRITE_ONCE(p->port_id, new_port_id);
+ WRITE_ONCE(p->priority, newprio);
if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) &&
p->port_id < p->designated_port) {
br_become_designated_port(p);
@@ -340,8 +340,8 @@ int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost)
path_cost > BR_MAX_PATH_COST)
return -ERANGE;
- p->flags |= BR_ADMIN_COST;
- p->path_cost = path_cost;
+ set_bit(BR_ADMIN_COST_BIT, &p->flags);
+ WRITE_ONCE(p->path_cost, path_cost);
br_configuration_update(p->br);
br_port_state_selection(p->br);
return 0;
diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c
index e5d453305381..83cc9c6a3943 100644
--- a/net/bridge/br_stp_timer.c
+++ b/net/bridge/br_stp_timer.c
@@ -160,5 +160,5 @@ void br_stp_port_timer_init(struct net_bridge_port *p)
unsigned long br_timer_value(const struct timer_list *timer)
{
return timer_pending(timer)
- ? jiffies_delta_to_clock_t(timer->expires - jiffies) : 0;
+ ? jiffies_delta_to_clock_t(READ_ONCE(timer->expires) - jiffies) : 0;
}
diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index 18b558a931ad..990c6b38fd39 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -20,7 +20,7 @@ static bool nbp_switchdev_can_offload_tx_fwd(const struct net_bridge_port *p,
if (br_multicast_igmp_type(skb))
return false;
- return (p->flags & BR_TX_FWD_OFFLOAD) &&
+ return test_bit(BR_TX_FWD_OFFLOAD_BIT, &p->flags) &&
(p->hwdom != BR_INPUT_SKB_CB(skb)->src_hwdom);
}
@@ -99,7 +99,6 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
attr.u.brport_flags.val = flags;
attr.u.brport_flags.mask = mask;
- /* We run from atomic context here */
err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
&info.info, extack);
err = notifier_to_errno(err);
@@ -288,7 +287,7 @@ static int nbp_switchdev_add(struct net_bridge_port *p,
return err;
if (tx_fwd_offload) {
- p->flags |= BR_TX_FWD_OFFLOAD;
+ set_bit(BR_TX_FWD_OFFLOAD_BIT, &p->flags);
static_branch_inc(&br_switchdev_tx_fwd_offload);
}
@@ -308,8 +307,8 @@ static void nbp_switchdev_del(struct net_bridge_port *p)
if (p->hwdom)
nbp_switchdev_hwdom_put(p);
- if (p->flags & BR_TX_FWD_OFFLOAD) {
- p->flags &= ~BR_TX_FWD_OFFLOAD;
+ if (test_bit(BR_TX_FWD_OFFLOAD_BIT, &p->flags)) {
+ clear_bit(BR_TX_FWD_OFFLOAD_BIT, &p->flags);
static_branch_dec(&br_switchdev_tx_fwd_offload);
}
}
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 1f57c36a7fc0..bde731c01e10 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -44,58 +44,80 @@ const struct brport_attribute brport_attr_##_name = { \
.store = _store, \
};
-#define BRPORT_ATTR_FLAG(_name, _mask) \
+#define BRPORT_ATTR_FLAG(_name, _bitnr) \
static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
{ \
- return sysfs_emit(buf, "%d\n", !!(p->flags & _mask)); \
+ return sysfs_emit(buf, "%d\n", test_bit(_bitnr, &p->flags)); \
} \
static int store_##_name(struct net_bridge_port *p, unsigned long v) \
{ \
- return store_flag(p, v, _mask); \
+ return store_flag(p, v, _bitnr); \
} \
static BRPORT_ATTR(_name, 0644, \
show_##_name, store_##_name)
static int store_flag(struct net_bridge_port *p, unsigned long v,
- unsigned long mask)
+ unsigned long bitnr)
{
+ unsigned long oflags, flags = READ_ONCE(p->flags);
struct netlink_ext_ack extack = {0};
- unsigned long flags = p->flags;
int err;
+ oflags = flags;
if (v)
- flags |= mask;
+ __set_bit(bitnr, &flags);
else
- flags &= ~mask;
+ __clear_bit(bitnr, &flags);
- if (flags != p->flags) {
- err = br_switchdev_set_port_flag(p, flags, mask, &extack);
- if (err) {
- netdev_err(p->dev, "%s\n", extack._msg);
- return err;
- }
+ if (flags == oflags)
+ return 0;
- p->flags = flags;
- br_port_flags_change(p, mask);
+ err = br_switchdev_set_port_flag(p, flags, BIT(bitnr), &extack);
+ if (err) {
+ netdev_err(p->dev, "%s\n", extack._msg);
+ return err;
}
+ if (v)
+ set_bit(bitnr, &p->flags);
+ else
+ clear_bit(bitnr, &p->flags);
+ br_port_flags_change(p, BIT(bitnr));
return 0;
}
static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
{
- return sysfs_emit(buf, "%d\n", p->path_cost);
+ return sysfs_emit(buf, "%d\n", READ_ONCE(p->path_cost));
+}
+
+static int store_path_cost(struct net_bridge_port *p, unsigned long v)
+{
+ int ret;
+
+ spin_lock_bh(&p->br->lock);
+ ret = br_stp_set_path_cost(p, v);
+ spin_unlock_bh(&p->br->lock);
+ return ret;
}
-static BRPORT_ATTR(path_cost, 0644,
- show_path_cost, br_stp_set_path_cost);
+static BRPORT_ATTR(path_cost, 0644, show_path_cost, store_path_cost);
static ssize_t show_priority(struct net_bridge_port *p, char *buf)
{
- return sysfs_emit(buf, "%d\n", p->priority);
+ return sysfs_emit(buf, "%d\n", READ_ONCE(p->priority));
+}
+
+static int store_priority(struct net_bridge_port *p, unsigned long v)
+{
+ int ret;
+
+ spin_lock_bh(&p->br->lock);
+ ret = br_stp_set_port_priority(p, v);
+ spin_unlock_bh(&p->br->lock);
+ return ret;
}
-static BRPORT_ATTR(priority, 0644,
- show_priority, br_stp_set_port_priority);
+static BRPORT_ATTR(priority, 0644, show_priority, store_priority);
static ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
{
@@ -111,19 +133,19 @@ static BRPORT_ATTR(designated_bridge, 0444, show_designated_bridge, NULL);
static ssize_t show_designated_port(struct net_bridge_port *p, char *buf)
{
- return sysfs_emit(buf, "%d\n", p->designated_port);
+ return sysfs_emit(buf, "%d\n", READ_ONCE(p->designated_port));
}
static BRPORT_ATTR(designated_port, 0444, show_designated_port, NULL);
static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf)
{
- return sysfs_emit(buf, "%d\n", p->designated_cost);
+ return sysfs_emit(buf, "%d\n", READ_ONCE(p->designated_cost));
}
static BRPORT_ATTR(designated_cost, 0444, show_designated_cost, NULL);
static ssize_t show_port_id(struct net_bridge_port *p, char *buf)
{
- return sysfs_emit(buf, "0x%x\n", p->port_id);
+ return sysfs_emit(buf, "0x%x\n", READ_ONCE(p->port_id));
}
static BRPORT_ATTR(port_id, 0444, show_port_id, NULL);
@@ -142,7 +164,7 @@ static BRPORT_ATTR(change_ack, 0444, show_change_ack, NULL);
static ssize_t show_config_pending(struct net_bridge_port *p, char *buf)
{
- return sysfs_emit(buf, "%d\n", p->config_pending);
+ return sysfs_emit(buf, "%d\n", READ_ONCE(p->config_pending));
}
static BRPORT_ATTR(config_pending, 0444, show_config_pending, NULL);
@@ -229,17 +251,17 @@ static int store_backup_port(struct net_bridge_port *p, char *buf)
}
static BRPORT_ATTR_RAW(backup_port, 0644, show_backup_port, store_backup_port);
-BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE);
-BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD);
-BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK);
-BRPORT_ATTR_FLAG(learning, BR_LEARNING);
-BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD);
-BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP);
-BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI);
-BRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD);
-BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD);
-BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
-BRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
+BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE_BIT);
+BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD_BIT);
+BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK_BIT);
+BRPORT_ATTR_FLAG(learning, BR_LEARNING_BIT);
+BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD_BIT);
+BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP_BIT);
+BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI_BIT);
+BRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD_BIT);
+BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD_BIT);
+BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS_BIT);
+BRPORT_ATTR_FLAG(isolated, BR_ISOLATED_BIT);
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
@@ -255,8 +277,8 @@ static int store_multicast_router(struct net_bridge_port *p,
static BRPORT_ATTR(multicast_router, 0644, show_multicast_router,
store_multicast_router);
-BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE);
-BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST);
+BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE_BIT);
+BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST_BIT);
#endif
static const struct brport_attribute *brport_attrs[] = {
@@ -334,17 +356,13 @@ static ssize_t brport_store(struct kobject *kobj,
ret = -ENOMEM;
goto out_unlock;
}
- spin_lock_bh(&p->br->lock);
ret = brport_attr->store_raw(p, buf_copy);
- spin_unlock_bh(&p->br->lock);
kfree(buf_copy);
} else if (brport_attr->store) {
val = simple_strtoul(buf, &endp, 0);
if (endp == buf)
goto out_unlock;
- spin_lock_bh(&p->br->lock);
ret = brport_attr->store(p, val);
- spin_unlock_bh(&p->br->lock);
}
if (!ret) {
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 84a180927eb7..92b3cb621a26 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -387,12 +387,12 @@ out_filt:
goto out;
}
-static int __vlan_del(struct net_bridge_vlan *v)
+static void __vlan_del(struct net_bridge_vlan *v)
{
struct net_bridge_vlan *masterv = v;
struct net_bridge_vlan_group *vg;
struct net_bridge_port *p = NULL;
- int err = 0;
+ int err;
if (br_vlan_is_master(v)) {
vg = br_vlan_group(v->br);
@@ -406,12 +406,16 @@ static int __vlan_del(struct net_bridge_vlan *v)
if (p) {
err = __vlan_vid_del(p->dev, p->br, v);
if (err)
- goto out;
+ br_warn(p->br,
+ "port %u(%s) failed to delete vlan %u from switchdev: %pe\n",
+ (unsigned int)p->port_no, p->dev->name,
+ v->vid, ERR_PTR(err));
} else {
err = br_switchdev_port_vlan_del(v->br->dev, v->vid);
if (err && err != -EOPNOTSUPP)
- goto out;
- err = 0;
+ br_warn(v->br,
+ "failed to delete bridge vlan %u from switchdev: %pe\n",
+ v->vid, ERR_PTR(err));
}
if (br_vlan_should_use(v)) {
@@ -431,8 +435,6 @@ static int __vlan_del(struct net_bridge_vlan *v)
}
br_vlan_put_master(masterv);
-out:
- return err;
}
static void __vlan_group_free(struct net_bridge_vlan_group *vg)
@@ -449,7 +451,6 @@ static void __vlan_flush(const struct net_bridge *br,
{
struct net_bridge_vlan *vlan, *tmp;
u16 v_start = 0, v_end = 0;
- int err;
__vlan_delete_pvid(vg, vg->pvid);
list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) {
@@ -463,13 +464,7 @@ static void __vlan_flush(const struct net_bridge *br,
}
v_end = vlan->vid;
- err = __vlan_del(vlan);
- if (err) {
- br_err(br,
- "port %u(%s) failed to delete vlan %d: %pe\n",
- (unsigned int) p->port_no, p->dev->name,
- vlan->vid, ERR_PTR(err));
- }
+ __vlan_del(vlan);
}
/* notify about the last/whole vlan range */
@@ -837,8 +832,9 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
br_fdb_delete_by_port(br, NULL, vid, 0);
vlan_tunnel_info_del(vg, v);
+ __vlan_del(v);
- return __vlan_del(v);
+ return 0;
}
void br_vlan_flush(struct net_bridge *br)
@@ -1136,7 +1132,7 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
if (err)
goto out;
- if (br_vlan_delete(br, old_pvid))
+ if (!br_vlan_delete(br, old_pvid))
br_vlan_notify(br, NULL, old_pvid, 0, RTM_DELVLAN);
br_vlan_notify(br, NULL, pvid, 0, RTM_NEWVLAN);
__set_bit(0, changed);
@@ -1158,7 +1154,7 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
&vlchange, extack);
if (err)
goto err_port;
- if (nbp_vlan_delete(p, old_pvid))
+ if (!nbp_vlan_delete(p, old_pvid))
br_vlan_notify(br, p, old_pvid, 0, RTM_DELVLAN);
br_vlan_notify(p->br, p, pvid, 0, RTM_NEWVLAN);
__set_bit(p->port_no, changed);
@@ -1368,8 +1364,9 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
return -ENOENT;
br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
br_fdb_delete_by_port(port->br, port, vid, 0);
+ __vlan_del(v);
- return __vlan_del(v);
+ return 0;
}
void nbp_vlan_flush(struct net_bridge_port *port)
@@ -1982,9 +1979,11 @@ out_kfree:
/* check if v_curr can enter a range ending in range_end */
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
- const struct net_bridge_vlan *range_end)
+ const struct net_bridge_vlan *range_end,
+ u16 pvid)
{
- return v_curr->vid - range_end->vid == 1 &&
+ return v_curr->vid != pvid && range_end->vid != pvid &&
+ v_curr->vid - range_end->vid == 1 &&
range_end->flags == v_curr->flags &&
br_vlan_opts_eq_range(v_curr, range_end);
}
@@ -2066,8 +2065,8 @@ static int br_vlan_dump_dev(const struct net_device *dev,
idx += range_end->vid - range_start->vid + 1;
range_start = v;
- } else if (dump_stats || v->vid == pvid ||
- !br_vlan_can_enter_range(v, range_end)) {
+ } else if (dump_stats ||
+ !br_vlan_can_enter_range(v, range_end, pvid)) {
u16 vlan_flags = br_vlan_flags(range_start, pvid);
if (!br_vlan_fill_vids(skb, range_start->vid,
@@ -2176,6 +2175,7 @@ static const struct nla_policy br_vlan_db_policy[BRIDGE_VLANDB_ENTRY_MAX + 1] =
[BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS] = { .type = NLA_REJECT },
[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS] = { .type = NLA_U32 },
[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS] = NLA_POLICY_MAX(NLA_U8, 1),
+ [BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT] = NLA_POLICY_MAX(NLA_U8, 1),
};
static int br_vlan_rtm_process_one(struct net_device *dev,
diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c
index 5514e1fc8d1f..cb0f556ff40d 100644
--- a/net/bridge/br_vlan_options.c
+++ b/net/bridge/br_vlan_options.c
@@ -54,7 +54,8 @@ bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr,
/* Check user-visible priv_flags that affect output */
if ((v_curr->priv_flags ^ range_end->priv_flags) &
- (BR_VLFLAG_NEIGH_SUPPRESS_ENABLED | BR_VLFLAG_MCAST_ENABLED))
+ (BR_VLFLAG_NEIGH_SUPPRESS_ENABLED | BR_VLFLAG_MCAST_ENABLED |
+ BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED))
return false;
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
@@ -74,7 +75,9 @@ bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v,
if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE, br_vlan_get_state(v)) ||
!__vlan_tun_put(skb, v) ||
nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS,
- !!(v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED)))
+ !!(v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED)) ||
+ nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT,
+ !!(v->priv_flags & BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED)))
return false;
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
@@ -103,6 +106,7 @@ size_t br_vlan_opts_nl_size(void)
+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS */
#endif
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS */
+ + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT */
+ 0;
}
@@ -168,7 +172,7 @@ static int br_vlan_modify_tunnel(const struct net_bridge_port *p,
NL_SET_ERR_MSG_MOD(extack, "Can't modify tunnel mapping of non-port vlans");
return -EINVAL;
}
- if (!(p->flags & BR_VLAN_TUNNEL)) {
+ if (!test_bit(BR_VLAN_TUNNEL_BIT, &p->flags)) {
NL_SET_ERR_MSG_MOD(extack, "Port doesn't have tunnel flag set");
return -EINVAL;
}
@@ -277,6 +281,22 @@ static int br_vlan_process_one_opts(const struct net_bridge *br,
}
}
+ if (tb[BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT]) {
+ bool enabled = v->priv_flags & BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED;
+ bool val = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT]);
+
+ if (!p) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can't set neigh_forward_grat for non-port vlans");
+ return -EINVAL;
+ }
+
+ if (val != enabled) {
+ v->priv_flags ^= BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED;
+ *changed = true;
+ }
+ }
+
return 0;
}
@@ -330,8 +350,7 @@ int br_vlan_process_options(const struct net_bridge *br,
continue;
}
- if (v->vid == pvid ||
- !br_vlan_can_enter_range(v, curr_end)) {
+ if (!br_vlan_can_enter_range(v, curr_end, pvid)) {
br_vlan_notify(br, p, curr_start->vid,
curr_end->vid, RTM_NEWVLAN);
curr_start = v;
diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile
index b9a1303da977..af0c903aa4ac 100644
--- a/net/bridge/netfilter/Makefile
+++ b/net/bridge/netfilter/Makefile
@@ -38,3 +38,7 @@ obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
# watchers
obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
obj-$(CONFIG_BRIDGE_EBT_NFLOG) += ebt_nflog.o
+
+ifdef CONFIG_GCOV_PROFILE_NETFILTER
+GCOV_PROFILE := y
+endif
diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c
index 3fda71a8579d..73f185cccd63 100644
--- a/net/bridge/netfilter/ebt_dnat.c
+++ b/net/bridge/netfilter/ebt_dnat.c
@@ -39,7 +39,9 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par)
dev = xt_in(par);
break;
case NF_BR_PRE_ROUTING:
- dev = br_port_get_rcu(xt_in(par))->br->dev;
+ dev = netdev_master_upper_dev_get_rcu(xt_in(par));
+ if (!dev) /* bridge port removed? */
+ return EBT_DROP;
break;
default:
dev = NULL;
diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c
index 61bf8f4465ab..426f8adc912c 100644
--- a/net/bridge/netfilter/ebt_nflog.c
+++ b/net/bridge/netfilter/ebt_nflog.c
@@ -41,11 +41,25 @@ ebt_nflog_tg(struct sk_buff *skb, const struct xt_action_param *par)
static int ebt_nflog_tg_check(const struct xt_tgchk_param *par)
{
struct ebt_nflog_info *info = par->targinfo;
+ int ret;
if (info->flags & ~EBT_NFLOG_MASK)
return -EINVAL;
info->prefix[EBT_NFLOG_PREFIX_SIZE - 1] = '\0';
- return 0;
+
+ ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG);
+ if (ret != 0 && !par->nft_compat) {
+ request_module("%s", "nfnetlink_log");
+
+ ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG);
+ }
+
+ return ret;
+}
+
+static void ebt_nflog_tg_destroy(const struct xt_tgdtor_param *par)
+{
+ nf_logger_put(par->family, NF_LOG_TYPE_ULOG);
}
static struct xt_target ebt_nflog_tg_reg __read_mostly = {
@@ -54,6 +68,7 @@ static struct xt_target ebt_nflog_tg_reg __read_mostly = {
.family = NFPROTO_BRIDGE,
.target = ebt_nflog_tg,
.checkentry = ebt_nflog_tg_check,
+ .destroy = ebt_nflog_tg_destroy,
.targetsize = sizeof(struct ebt_nflog_info),
.me = THIS_MODULE,
};
diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c
index 307790562b49..83486cd4d564 100644
--- a/net/bridge/netfilter/ebt_redirect.c
+++ b/net/bridge/netfilter/ebt_redirect.c
@@ -24,12 +24,18 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)
if (skb_ensure_writable(skb, 0))
return EBT_DROP;
- if (xt_hooknum(par) != NF_BR_BROUTING)
- /* rcu_read_lock()ed by nf_hook_thresh */
- ether_addr_copy(eth_hdr(skb)->h_dest,
- br_port_get_rcu(xt_in(par))->br->dev->dev_addr);
- else
+ if (xt_hooknum(par) != NF_BR_BROUTING) {
+ const struct net_device *dev;
+
+ dev = netdev_master_upper_dev_get_rcu(xt_in(par));
+ if (!dev)
+ return EBT_DROP;
+
+ ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
+ } else {
ether_addr_copy(eth_hdr(skb)->h_dest, xt_in(par)->dev_addr);
+ }
+
skb->pkt_type = PACKET_HOST;
return info->target;
}
diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c
index 7dfbcdfc30e5..c9e229af0366 100644
--- a/net/bridge/netfilter/ebt_snat.c
+++ b/net/bridge/netfilter/ebt_snat.c
@@ -31,6 +31,9 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par)
const struct arphdr *ap;
struct arphdr _ah;
+ if (skb_ensure_writable(skb, sizeof(_ah) + ETH_ALEN))
+ return EBT_DROP;
+
ap = skb_header_pointer(skb, 0, sizeof(_ah), &_ah);
if (ap == NULL)
return EBT_DROP;
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
index 741360219552..f05c79f215ea 100644
--- a/net/bridge/netfilter/ebtable_broute.c
+++ b/net/bridge/netfilter/ebtable_broute.c
@@ -112,24 +112,22 @@ static struct pernet_operations broute_net_ops = {
static int __init ebtable_broute_init(void)
{
- int ret = ebt_register_template(&broute_table, broute_table_init);
+ int ret = register_pernet_subsys(&broute_net_ops);
if (ret)
return ret;
- ret = register_pernet_subsys(&broute_net_ops);
- if (ret) {
- ebt_unregister_template(&broute_table);
- return ret;
- }
+ ret = ebt_register_template(&broute_table, broute_table_init);
+ if (ret)
+ unregister_pernet_subsys(&broute_net_ops);
- return 0;
+ return ret;
}
static void __exit ebtable_broute_fini(void)
{
- unregister_pernet_subsys(&broute_net_ops);
ebt_unregister_template(&broute_table);
+ unregister_pernet_subsys(&broute_net_ops);
}
module_init(ebtable_broute_init);
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index dacd81b12e62..0fc03b07e62a 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -93,24 +93,22 @@ static struct pernet_operations frame_filter_net_ops = {
static int __init ebtable_filter_init(void)
{
- int ret = ebt_register_template(&frame_filter, frame_filter_table_init);
+ int ret = register_pernet_subsys(&frame_filter_net_ops);
if (ret)
return ret;
- ret = register_pernet_subsys(&frame_filter_net_ops);
- if (ret) {
- ebt_unregister_template(&frame_filter);
- return ret;
- }
+ ret = ebt_register_template(&frame_filter, frame_filter_table_init);
+ if (ret)
+ unregister_pernet_subsys(&frame_filter_net_ops);
- return 0;
+ return ret;
}
static void __exit ebtable_filter_fini(void)
{
- unregister_pernet_subsys(&frame_filter_net_ops);
ebt_unregister_template(&frame_filter);
+ unregister_pernet_subsys(&frame_filter_net_ops);
}
module_init(ebtable_filter_init);
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index 0f2a8c6118d4..8a10375d8909 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -93,24 +93,22 @@ static struct pernet_operations frame_nat_net_ops = {
static int __init ebtable_nat_init(void)
{
- int ret = ebt_register_template(&frame_nat, frame_nat_table_init);
+ int ret = register_pernet_subsys(&frame_nat_net_ops);
if (ret)
return ret;
- ret = register_pernet_subsys(&frame_nat_net_ops);
- if (ret) {
- ebt_unregister_template(&frame_nat);
- return ret;
- }
+ ret = ebt_register_template(&frame_nat, frame_nat_table_init);
+ if (ret)
+ unregister_pernet_subsys(&frame_nat_net_ops);
return ret;
}
static void __exit ebtable_nat_fini(void)
{
- unregister_pernet_subsys(&frame_nat_net_ops);
ebt_unregister_template(&frame_nat);
+ unregister_pernet_subsys(&frame_nat_net_ops);
}
module_init(ebtable_nat_init);
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index aea3e19875c6..0bf541936f60 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -39,9 +39,12 @@
#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
COUNTER_OFFSET(n) * cpu))
+#define MAX_EBT_ENTRIES (((INT_MAX - sizeof(struct ebt_table_info)) / \
+ NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
struct ebt_pernet {
struct list_head tables;
+ struct list_head dead_tables;
};
struct ebt_template {
@@ -402,6 +405,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par,
left - sizeof(struct ebt_entry_match) < m->match_size)
return -EINVAL;
+ if (strnlen(m->u.name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
+ return -EINVAL;
+
match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision);
if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) {
if (!IS_ERR(match))
@@ -920,8 +926,7 @@ static int translate_table(struct net *net, const char *name,
* if an error occurs
*/
newinfo->chainstack =
- vmalloc_array(nr_cpu_ids,
- sizeof(*(newinfo->chainstack)));
+ vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack)));
if (!newinfo->chainstack)
return -ENOMEM;
for_each_possible_cpu(i) {
@@ -1123,10 +1128,9 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len)
return -EINVAL;
/* overflow check */
- if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) /
- NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
+ if (tmp.nentries >= MAX_EBT_ENTRIES)
return -ENOMEM;
- if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+ if (tmp.num_counters >= MAX_EBT_ENTRIES)
return -ENOMEM;
tmp.name[sizeof(tmp.name) - 1] = 0;
@@ -1162,11 +1166,6 @@ free_newinfo:
static void __ebt_unregister_table(struct net *net, struct ebt_table *table)
{
- mutex_lock(&ebt_mutex);
- list_del(&table->list);
- mutex_unlock(&ebt_mutex);
- audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries,
- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL);
EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size,
ebt_cleanup_entry, net, NULL);
if (table->private->nentries)
@@ -1267,13 +1266,15 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
for (i = 0; i < num_ops; i++)
ops[i].priv = table;
- list_add(&table->list, &ebt_net->tables);
- mutex_unlock(&ebt_mutex);
-
table->ops = ops;
ret = nf_register_net_hooks(net, ops, num_ops);
- if (ret)
+ if (ret) {
+ synchronize_rcu();
__ebt_unregister_table(net, table);
+ } else {
+ list_add(&table->list, &ebt_net->tables);
+ }
+ mutex_unlock(&ebt_mutex);
audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries,
AUDIT_XT_OP_REGISTER, GFP_KERNEL);
@@ -1339,7 +1340,7 @@ void ebt_unregister_template(const struct ebt_table *t)
}
EXPORT_SYMBOL(ebt_unregister_template);
-static struct ebt_table *__ebt_find_table(struct net *net, const char *name)
+void ebt_unregister_table_pre_exit(struct net *net, const char *name)
{
struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id);
struct ebt_table *t;
@@ -1348,30 +1349,36 @@ static struct ebt_table *__ebt_find_table(struct net *net, const char *name)
list_for_each_entry(t, &ebt_net->tables, list) {
if (strcmp(t->name, name) == 0) {
+ list_move(&t->list, &ebt_net->dead_tables);
mutex_unlock(&ebt_mutex);
- return t;
+ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks));
+ return;
}
}
mutex_unlock(&ebt_mutex);
- return NULL;
-}
-
-void ebt_unregister_table_pre_exit(struct net *net, const char *name)
-{
- struct ebt_table *table = __ebt_find_table(net, name);
-
- if (table)
- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks));
}
EXPORT_SYMBOL(ebt_unregister_table_pre_exit);
void ebt_unregister_table(struct net *net, const char *name)
{
- struct ebt_table *table = __ebt_find_table(net, name);
+ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id);
+ struct ebt_table *t;
- if (table)
- __ebt_unregister_table(net, table);
+ mutex_lock(&ebt_mutex);
+
+ list_for_each_entry(t, &ebt_net->dead_tables, list) {
+ if (strcmp(t->name, name) == 0) {
+ list_del(&t->list);
+ audit_log_nfcfg(t->name, AF_BRIDGE, t->private->nentries,
+ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL);
+ __ebt_unregister_table(net, t);
+ mutex_unlock(&ebt_mutex);
+ return;
+ }
+ }
+
+ mutex_unlock(&ebt_mutex);
}
/* userspace just supplied us with counters */
@@ -1430,6 +1437,8 @@ static int update_counters(struct net *net, sockptr_t arg, unsigned int len)
if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
return -EFAULT;
+ hlp.name[sizeof(hlp.name) - 1] = '\0';
+
if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
return -EINVAL;
@@ -1952,6 +1961,25 @@ enum compat_mwt {
EBT_COMPAT_TARGET,
};
+static bool match_size_ok(const struct xt_match *match, unsigned int match_size)
+{
+ u16 csize;
+
+ if (match->matchsize == -1) /* cannot validate ebt_among */
+ return true;
+
+ csize = match->compatsize ? : match->matchsize;
+
+ return match_size >= csize;
+}
+
+static bool tgt_size_ok(const struct xt_target *tgt, unsigned int tgt_size)
+{
+ u16 csize = tgt->compatsize ? : tgt->targetsize;
+
+ return tgt_size >= csize;
+}
+
static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
enum compat_mwt compat_mwt,
struct ebt_entries_buf_state *state,
@@ -1977,6 +2005,11 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
if (IS_ERR(match))
return PTR_ERR(match);
+ if (!match_size_ok(match, match_size)) {
+ module_put(match->me);
+ return -EINVAL;
+ }
+
off = ebt_compat_match_offset(match, match_size);
if (dst) {
if (match->compat_from_user)
@@ -1996,6 +2029,12 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
mwt->u.revision);
if (IS_ERR(wt))
return PTR_ERR(wt);
+
+ if (!tgt_size_ok(wt, match_size)) {
+ module_put(wt->me);
+ return -EINVAL;
+ }
+
off = xt_compat_target_offset(wt);
if (dst) {
@@ -2231,14 +2270,15 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl,
if (tmp.entries_size == 0)
return -EINVAL;
- if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) /
- NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
+ if (tmp.nentries >= MAX_EBT_ENTRIES)
return -ENOMEM;
- if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+ if (tmp.num_counters >= MAX_EBT_ENTRIES)
return -ENOMEM;
memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry));
+ repl->name[sizeof(repl->name) - 1] = '\0';
+
/* starting with hook_entry, 32 vs. 64 bit structures are different */
for (i = 0; i < NF_BR_NUMHOOKS; i++)
repl->hook_entry[i] = compat_ptr(tmp.hook_entry[i]);
@@ -2361,6 +2401,8 @@ static int compat_update_counters(struct net *net, sockptr_t arg,
if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
return -EFAULT;
+ hlp.name[sizeof(hlp.name) - 1] = '\0';
+
/* try real handler in case userland supplied needed padding */
if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
return update_counters(net, arg, len);
@@ -2451,6 +2493,8 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
+ if (!xt_compat_check())
+ return -EPERM;
#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
/* try real handler in case userland supplied needed padding */
@@ -2516,6 +2560,8 @@ static int do_ebt_set_ctl(struct sock *sk, int cmd, sockptr_t arg,
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
+ if (!xt_compat_check())
+ return -EPERM;
switch (cmd) {
case EBT_SO_SET_ENTRIES:
@@ -2556,11 +2602,21 @@ static int __net_init ebt_pernet_init(struct net *net)
struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id);
INIT_LIST_HEAD(&ebt_net->tables);
+ INIT_LIST_HEAD(&ebt_net->dead_tables);
return 0;
}
+static void __net_exit ebt_pernet_exit(struct net *net)
+{
+ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id);
+
+ WARN_ON_ONCE(!list_empty(&ebt_net->tables));
+ WARN_ON_ONCE(!list_empty(&ebt_net->dead_tables));
+}
+
static struct pernet_operations ebt_net_ops = {
.init = ebt_pernet_init,
+ .exit = ebt_pernet_exit,
.id = &ebt_pernet_id,
.size = sizeof(struct ebt_pernet),
};
@@ -2569,19 +2625,20 @@ static int __init ebtables_init(void)
{
int ret;
- ret = xt_register_target(&ebt_standard_target);
+ ret = register_pernet_subsys(&ebt_net_ops);
if (ret < 0)
return ret;
- ret = nf_register_sockopt(&ebt_sockopts);
+
+ ret = xt_register_target(&ebt_standard_target);
if (ret < 0) {
- xt_unregister_target(&ebt_standard_target);
+ unregister_pernet_subsys(&ebt_net_ops);
return ret;
}
- ret = register_pernet_subsys(&ebt_net_ops);
+ ret = nf_register_sockopt(&ebt_sockopts);
if (ret < 0) {
- nf_unregister_sockopt(&ebt_sockopts);
xt_unregister_target(&ebt_standard_target);
+ unregister_pernet_subsys(&ebt_net_ops);
return ret;
}
diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c
index 58a33d0380b0..7ecb8a26bfa3 100644
--- a/net/bridge/netfilter/nf_conntrack_bridge.c
+++ b/net/bridge/netfilter/nf_conntrack_bridge.c
@@ -280,6 +280,7 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb,
ret = nf_ct_br_defrag6(skb, &bridge_state);
break;
default:
+ nf_reset_ct(skb);
nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
return NF_ACCEPT;
}
diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c
index 7763e78abb00..e4c9aa1f64e2 100644
--- a/net/bridge/netfilter/nft_meta_bridge.c
+++ b/net/bridge/netfilter/nft_meta_bridge.c
@@ -44,7 +44,9 @@ static void nft_meta_bridge_get_eval(const struct nft_expr *expr,
if (!br_dev || !br_vlan_enabled(br_dev))
goto err;
- br_vlan_get_pvid_rcu(in, &p_pvid);
+ if (br_vlan_get_pvid_rcu(in, &p_pvid))
+ goto err;
+
nft_reg_store16(dest, p_pvid);
return;
}
@@ -64,6 +66,8 @@ static void nft_meta_bridge_get_eval(const struct nft_expr *expr,
if (!br_dev)
goto err;
+ /* ETH_ALEN (6) is shorter than the destination register span (8) */
+ dest[1] = 0;
memcpy(dest, br_dev->dev_addr, ETH_ALEN);
return;
default:
@@ -105,12 +109,30 @@ static int nft_meta_bridge_get_init(const struct nft_ctx *ctx,
NULL, NFT_DATA_VALUE, len);
}
+static int nft_meta_bridge_get_validate(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+{
+ struct nft_meta *priv = nft_expr_priv(expr);
+ unsigned int hooks;
+
+ switch (priv->key) {
+ case NFT_META_BRI_IIFHWADDR:
+ hooks = 1 << NF_BR_PRE_ROUTING;
+ break;
+ default:
+ return nft_meta_get_validate(ctx, expr);
+ }
+
+ return nft_chain_validate_hooks(ctx->chain, hooks);
+}
+
static struct nft_expr_type nft_meta_bridge_type;
static const struct nft_expr_ops nft_meta_bridge_get_ops = {
.type = &nft_meta_bridge_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_bridge_get_eval,
.init = nft_meta_bridge_get_init,
+ .validate = nft_meta_bridge_get_validate,
.dump = nft_meta_get_dump,
};
@@ -166,7 +188,6 @@ static int nft_meta_bridge_set_validate(const struct nft_ctx *ctx,
switch (priv->key) {
case NFT_META_BRI_BROUTE:
- case NFT_META_BRI_IIFHWADDR:
hooks = 1 << NF_BR_PRE_ROUTING;
break;
default: