diff options
Diffstat (limited to 'net/bridge')
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: |
