18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/err.h> 38c2ecf20Sopenharmony_ci#include <linux/igmp.h> 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 68c2ecf20Sopenharmony_ci#include <linux/rculist.h> 78c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 88c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 98c2ecf20Sopenharmony_ci#include <net/ip.h> 108c2ecf20Sopenharmony_ci#include <net/netlink.h> 118c2ecf20Sopenharmony_ci#include <net/switchdev.h> 128c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 138c2ecf20Sopenharmony_ci#include <net/ipv6.h> 148c2ecf20Sopenharmony_ci#include <net/addrconf.h> 158c2ecf20Sopenharmony_ci#endif 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "br_private.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb, 208c2ecf20Sopenharmony_ci struct net_device *dev) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct net_bridge *br = netdev_priv(dev); 238c2ecf20Sopenharmony_ci struct net_bridge_port *p; 248c2ecf20Sopenharmony_ci struct nlattr *nest, *port_nest; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (!br->multicast_router || hlist_empty(&br->router_list)) 278c2ecf20Sopenharmony_ci return 0; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, MDBA_ROUTER); 308c2ecf20Sopenharmony_ci if (nest == NULL) 318c2ecf20Sopenharmony_ci return -EMSGSIZE; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(p, &br->router_list, rlist) { 348c2ecf20Sopenharmony_ci if (!p) 358c2ecf20Sopenharmony_ci continue; 368c2ecf20Sopenharmony_ci port_nest = nla_nest_start_noflag(skb, MDBA_ROUTER_PORT); 378c2ecf20Sopenharmony_ci if (!port_nest) 388c2ecf20Sopenharmony_ci goto fail; 398c2ecf20Sopenharmony_ci if (nla_put_nohdr(skb, sizeof(u32), &p->dev->ifindex) || 408c2ecf20Sopenharmony_ci nla_put_u32(skb, MDBA_ROUTER_PATTR_TIMER, 418c2ecf20Sopenharmony_ci br_timer_value(&p->multicast_router_timer)) || 428c2ecf20Sopenharmony_ci nla_put_u8(skb, MDBA_ROUTER_PATTR_TYPE, 438c2ecf20Sopenharmony_ci p->multicast_router)) { 448c2ecf20Sopenharmony_ci nla_nest_cancel(skb, port_nest); 458c2ecf20Sopenharmony_ci goto fail; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci nla_nest_end(skb, port_nest); 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_cifail: 538c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 548c2ecf20Sopenharmony_ci return -EMSGSIZE; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void __mdb_entry_fill_flags(struct br_mdb_entry *e, unsigned char flags) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci e->state = flags & MDB_PG_FLAGS_PERMANENT; 608c2ecf20Sopenharmony_ci e->flags = 0; 618c2ecf20Sopenharmony_ci if (flags & MDB_PG_FLAGS_OFFLOAD) 628c2ecf20Sopenharmony_ci e->flags |= MDB_FLAGS_OFFLOAD; 638c2ecf20Sopenharmony_ci if (flags & MDB_PG_FLAGS_FAST_LEAVE) 648c2ecf20Sopenharmony_ci e->flags |= MDB_FLAGS_FAST_LEAVE; 658c2ecf20Sopenharmony_ci if (flags & MDB_PG_FLAGS_STAR_EXCL) 668c2ecf20Sopenharmony_ci e->flags |= MDB_FLAGS_STAR_EXCL; 678c2ecf20Sopenharmony_ci if (flags & MDB_PG_FLAGS_BLOCKED) 688c2ecf20Sopenharmony_ci e->flags |= MDB_FLAGS_BLOCKED; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip, 728c2ecf20Sopenharmony_ci struct nlattr **mdb_attrs) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci memset(ip, 0, sizeof(struct br_ip)); 758c2ecf20Sopenharmony_ci ip->vid = entry->vid; 768c2ecf20Sopenharmony_ci ip->proto = entry->addr.proto; 778c2ecf20Sopenharmony_ci switch (ip->proto) { 788c2ecf20Sopenharmony_ci case htons(ETH_P_IP): 798c2ecf20Sopenharmony_ci ip->dst.ip4 = entry->addr.u.ip4; 808c2ecf20Sopenharmony_ci if (mdb_attrs && mdb_attrs[MDBE_ATTR_SOURCE]) 818c2ecf20Sopenharmony_ci ip->src.ip4 = nla_get_in_addr(mdb_attrs[MDBE_ATTR_SOURCE]); 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 848c2ecf20Sopenharmony_ci case htons(ETH_P_IPV6): 858c2ecf20Sopenharmony_ci ip->dst.ip6 = entry->addr.u.ip6; 868c2ecf20Sopenharmony_ci if (mdb_attrs && mdb_attrs[MDBE_ATTR_SOURCE]) 878c2ecf20Sopenharmony_ci ip->src.ip6 = nla_get_in6_addr(mdb_attrs[MDBE_ATTR_SOURCE]); 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci#endif 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int __mdb_fill_srcs(struct sk_buff *skb, 958c2ecf20Sopenharmony_ci struct net_bridge_port_group *p) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct net_bridge_group_src *ent; 988c2ecf20Sopenharmony_ci struct nlattr *nest, *nest_ent; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (hlist_empty(&p->src_list)) 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci nest = nla_nest_start(skb, MDBA_MDB_EATTR_SRC_LIST); 1048c2ecf20Sopenharmony_ci if (!nest) 1058c2ecf20Sopenharmony_ci return -EMSGSIZE; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(ent, &p->src_list, node, 1088c2ecf20Sopenharmony_ci lockdep_is_held(&p->key.port->br->multicast_lock)) { 1098c2ecf20Sopenharmony_ci nest_ent = nla_nest_start(skb, MDBA_MDB_SRCLIST_ENTRY); 1108c2ecf20Sopenharmony_ci if (!nest_ent) 1118c2ecf20Sopenharmony_ci goto out_cancel_err; 1128c2ecf20Sopenharmony_ci switch (ent->addr.proto) { 1138c2ecf20Sopenharmony_ci case htons(ETH_P_IP): 1148c2ecf20Sopenharmony_ci if (nla_put_in_addr(skb, MDBA_MDB_SRCATTR_ADDRESS, 1158c2ecf20Sopenharmony_ci ent->addr.src.ip4)) { 1168c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest_ent); 1178c2ecf20Sopenharmony_ci goto out_cancel_err; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 1218c2ecf20Sopenharmony_ci case htons(ETH_P_IPV6): 1228c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, MDBA_MDB_SRCATTR_ADDRESS, 1238c2ecf20Sopenharmony_ci &ent->addr.src.ip6)) { 1248c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest_ent); 1258c2ecf20Sopenharmony_ci goto out_cancel_err; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci#endif 1298c2ecf20Sopenharmony_ci default: 1308c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest_ent); 1318c2ecf20Sopenharmony_ci continue; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci if (nla_put_u32(skb, MDBA_MDB_SRCATTR_TIMER, 1348c2ecf20Sopenharmony_ci br_timer_value(&ent->timer))) { 1358c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest_ent); 1368c2ecf20Sopenharmony_ci goto out_cancel_err; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci nla_nest_end(skb, nest_ent); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciout_cancel_err: 1468c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 1478c2ecf20Sopenharmony_ci return -EMSGSIZE; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int __mdb_fill_info(struct sk_buff *skb, 1518c2ecf20Sopenharmony_ci struct net_bridge_mdb_entry *mp, 1528c2ecf20Sopenharmony_ci struct net_bridge_port_group *p) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci bool dump_srcs_mode = false; 1558c2ecf20Sopenharmony_ci struct timer_list *mtimer; 1568c2ecf20Sopenharmony_ci struct nlattr *nest_ent; 1578c2ecf20Sopenharmony_ci struct br_mdb_entry e; 1588c2ecf20Sopenharmony_ci u8 flags = 0; 1598c2ecf20Sopenharmony_ci int ifindex; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci memset(&e, 0, sizeof(e)); 1628c2ecf20Sopenharmony_ci if (p) { 1638c2ecf20Sopenharmony_ci ifindex = p->key.port->dev->ifindex; 1648c2ecf20Sopenharmony_ci mtimer = &p->timer; 1658c2ecf20Sopenharmony_ci flags = p->flags; 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci ifindex = mp->br->dev->ifindex; 1688c2ecf20Sopenharmony_ci mtimer = &mp->timer; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci __mdb_entry_fill_flags(&e, flags); 1728c2ecf20Sopenharmony_ci e.ifindex = ifindex; 1738c2ecf20Sopenharmony_ci e.vid = mp->addr.vid; 1748c2ecf20Sopenharmony_ci if (mp->addr.proto == htons(ETH_P_IP)) 1758c2ecf20Sopenharmony_ci e.addr.u.ip4 = mp->addr.dst.ip4; 1768c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 1778c2ecf20Sopenharmony_ci if (mp->addr.proto == htons(ETH_P_IPV6)) 1788c2ecf20Sopenharmony_ci e.addr.u.ip6 = mp->addr.dst.ip6; 1798c2ecf20Sopenharmony_ci#endif 1808c2ecf20Sopenharmony_ci e.addr.proto = mp->addr.proto; 1818c2ecf20Sopenharmony_ci nest_ent = nla_nest_start_noflag(skb, 1828c2ecf20Sopenharmony_ci MDBA_MDB_ENTRY_INFO); 1838c2ecf20Sopenharmony_ci if (!nest_ent) 1848c2ecf20Sopenharmony_ci return -EMSGSIZE; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (nla_put_nohdr(skb, sizeof(e), &e) || 1878c2ecf20Sopenharmony_ci nla_put_u32(skb, 1888c2ecf20Sopenharmony_ci MDBA_MDB_EATTR_TIMER, 1898c2ecf20Sopenharmony_ci br_timer_value(mtimer))) 1908c2ecf20Sopenharmony_ci goto nest_err; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci switch (mp->addr.proto) { 1938c2ecf20Sopenharmony_ci case htons(ETH_P_IP): 1948c2ecf20Sopenharmony_ci dump_srcs_mode = !!(mp->br->multicast_igmp_version == 3); 1958c2ecf20Sopenharmony_ci if (mp->addr.src.ip4) { 1968c2ecf20Sopenharmony_ci if (nla_put_in_addr(skb, MDBA_MDB_EATTR_SOURCE, 1978c2ecf20Sopenharmony_ci mp->addr.src.ip4)) 1988c2ecf20Sopenharmony_ci goto nest_err; 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 2038c2ecf20Sopenharmony_ci case htons(ETH_P_IPV6): 2048c2ecf20Sopenharmony_ci dump_srcs_mode = !!(mp->br->multicast_mld_version == 2); 2058c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&mp->addr.src.ip6)) { 2068c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, MDBA_MDB_EATTR_SOURCE, 2078c2ecf20Sopenharmony_ci &mp->addr.src.ip6)) 2088c2ecf20Sopenharmony_ci goto nest_err; 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci#endif 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci if (p) { 2158c2ecf20Sopenharmony_ci if (nla_put_u8(skb, MDBA_MDB_EATTR_RTPROT, p->rt_protocol)) 2168c2ecf20Sopenharmony_ci goto nest_err; 2178c2ecf20Sopenharmony_ci if (dump_srcs_mode && 2188c2ecf20Sopenharmony_ci (__mdb_fill_srcs(skb, p) || 2198c2ecf20Sopenharmony_ci nla_put_u8(skb, MDBA_MDB_EATTR_GROUP_MODE, 2208c2ecf20Sopenharmony_ci p->filter_mode))) 2218c2ecf20Sopenharmony_ci goto nest_err; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci nla_nest_end(skb, nest_ent); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cinest_err: 2288c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest_ent); 2298c2ecf20Sopenharmony_ci return -EMSGSIZE; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb, 2338c2ecf20Sopenharmony_ci struct net_device *dev) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci int idx = 0, s_idx = cb->args[1], err = 0, pidx = 0, s_pidx = cb->args[2]; 2368c2ecf20Sopenharmony_ci struct net_bridge *br = netdev_priv(dev); 2378c2ecf20Sopenharmony_ci struct net_bridge_mdb_entry *mp; 2388c2ecf20Sopenharmony_ci struct nlattr *nest, *nest2; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, MDBA_MDB); 2448c2ecf20Sopenharmony_ci if (nest == NULL) 2458c2ecf20Sopenharmony_ci return -EMSGSIZE; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) { 2488c2ecf20Sopenharmony_ci struct net_bridge_port_group *p; 2498c2ecf20Sopenharmony_ci struct net_bridge_port_group __rcu **pp; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (idx < s_idx) 2528c2ecf20Sopenharmony_ci goto skip; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci nest2 = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY); 2558c2ecf20Sopenharmony_ci if (!nest2) { 2568c2ecf20Sopenharmony_ci err = -EMSGSIZE; 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (!s_pidx && mp->host_joined) { 2618c2ecf20Sopenharmony_ci err = __mdb_fill_info(skb, mp, NULL); 2628c2ecf20Sopenharmony_ci if (err) { 2638c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest2); 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL; 2698c2ecf20Sopenharmony_ci pp = &p->next) { 2708c2ecf20Sopenharmony_ci if (!p->key.port) 2718c2ecf20Sopenharmony_ci continue; 2728c2ecf20Sopenharmony_ci if (pidx < s_pidx) 2738c2ecf20Sopenharmony_ci goto skip_pg; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci err = __mdb_fill_info(skb, mp, p); 2768c2ecf20Sopenharmony_ci if (err) { 2778c2ecf20Sopenharmony_ci nla_nest_end(skb, nest2); 2788c2ecf20Sopenharmony_ci goto out; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ciskip_pg: 2818c2ecf20Sopenharmony_ci pidx++; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci pidx = 0; 2848c2ecf20Sopenharmony_ci s_pidx = 0; 2858c2ecf20Sopenharmony_ci nla_nest_end(skb, nest2); 2868c2ecf20Sopenharmony_ciskip: 2878c2ecf20Sopenharmony_ci idx++; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ciout: 2918c2ecf20Sopenharmony_ci cb->args[1] = idx; 2928c2ecf20Sopenharmony_ci cb->args[2] = pidx; 2938c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 2948c2ecf20Sopenharmony_ci return err; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int br_mdb_valid_dump_req(const struct nlmsghdr *nlh, 2988c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct br_port_msg *bpm; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*bpm))) { 3038c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for mdb dump request"); 3048c2ecf20Sopenharmony_ci return -EINVAL; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci bpm = nlmsg_data(nlh); 3088c2ecf20Sopenharmony_ci if (bpm->ifindex) { 3098c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Filtering by device index is not supported for mdb dump request"); 3108c2ecf20Sopenharmony_ci return -EINVAL; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci if (nlmsg_attrlen(nlh, sizeof(*bpm))) { 3138c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid data after header in mdb dump request"); 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct net_device *dev; 3238c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 3248c2ecf20Sopenharmony_ci struct nlmsghdr *nlh = NULL; 3258c2ecf20Sopenharmony_ci int idx = 0, s_idx; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (cb->strict_check) { 3288c2ecf20Sopenharmony_ci int err = br_mdb_valid_dump_req(cb->nlh, cb->extack); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (err < 0) 3318c2ecf20Sopenharmony_ci return err; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci s_idx = cb->args[0]; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci rcu_read_lock(); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci cb->seq = net->dev_base_seq; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci for_each_netdev_rcu(net, dev) { 3418c2ecf20Sopenharmony_ci if (dev->priv_flags & IFF_EBRIDGE) { 3428c2ecf20Sopenharmony_ci struct br_port_msg *bpm; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (idx < s_idx) 3458c2ecf20Sopenharmony_ci goto skip; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, 3488c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, RTM_GETMDB, 3498c2ecf20Sopenharmony_ci sizeof(*bpm), NLM_F_MULTI); 3508c2ecf20Sopenharmony_ci if (nlh == NULL) 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci bpm = nlmsg_data(nlh); 3548c2ecf20Sopenharmony_ci memset(bpm, 0, sizeof(*bpm)); 3558c2ecf20Sopenharmony_ci bpm->ifindex = dev->ifindex; 3568c2ecf20Sopenharmony_ci if (br_mdb_fill_info(skb, cb, dev) < 0) 3578c2ecf20Sopenharmony_ci goto out; 3588c2ecf20Sopenharmony_ci if (br_rports_fill_info(skb, cb, dev) < 0) 3598c2ecf20Sopenharmony_ci goto out; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci cb->args[1] = 0; 3628c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 3638c2ecf20Sopenharmony_ci skip: 3648c2ecf20Sopenharmony_ci idx++; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ciout: 3698c2ecf20Sopenharmony_ci if (nlh) 3708c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 3718c2ecf20Sopenharmony_ci rcu_read_unlock(); 3728c2ecf20Sopenharmony_ci cb->args[0] = idx; 3738c2ecf20Sopenharmony_ci return skb->len; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int nlmsg_populate_mdb_fill(struct sk_buff *skb, 3778c2ecf20Sopenharmony_ci struct net_device *dev, 3788c2ecf20Sopenharmony_ci struct net_bridge_mdb_entry *mp, 3798c2ecf20Sopenharmony_ci struct net_bridge_port_group *pg, 3808c2ecf20Sopenharmony_ci int type) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 3838c2ecf20Sopenharmony_ci struct br_port_msg *bpm; 3848c2ecf20Sopenharmony_ci struct nlattr *nest, *nest2; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, type, sizeof(*bpm), 0); 3878c2ecf20Sopenharmony_ci if (!nlh) 3888c2ecf20Sopenharmony_ci return -EMSGSIZE; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci bpm = nlmsg_data(nlh); 3918c2ecf20Sopenharmony_ci memset(bpm, 0, sizeof(*bpm)); 3928c2ecf20Sopenharmony_ci bpm->family = AF_BRIDGE; 3938c2ecf20Sopenharmony_ci bpm->ifindex = dev->ifindex; 3948c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, MDBA_MDB); 3958c2ecf20Sopenharmony_ci if (nest == NULL) 3968c2ecf20Sopenharmony_ci goto cancel; 3978c2ecf20Sopenharmony_ci nest2 = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY); 3988c2ecf20Sopenharmony_ci if (nest2 == NULL) 3998c2ecf20Sopenharmony_ci goto end; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (__mdb_fill_info(skb, mp, pg)) 4028c2ecf20Sopenharmony_ci goto end; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci nla_nest_end(skb, nest2); 4058c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 4068c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ciend: 4108c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 4118c2ecf20Sopenharmony_cicancel: 4128c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 4138c2ecf20Sopenharmony_ci return -EMSGSIZE; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic size_t rtnl_mdb_nlmsg_size(struct net_bridge_port_group *pg) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci size_t nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) + 4198c2ecf20Sopenharmony_ci nla_total_size(sizeof(struct br_mdb_entry)) + 4208c2ecf20Sopenharmony_ci nla_total_size(sizeof(u32)); 4218c2ecf20Sopenharmony_ci struct net_bridge_group_src *ent; 4228c2ecf20Sopenharmony_ci size_t addr_size = 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (!pg) 4258c2ecf20Sopenharmony_ci goto out; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* MDBA_MDB_EATTR_RTPROT */ 4288c2ecf20Sopenharmony_ci nlmsg_size += nla_total_size(sizeof(u8)); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci switch (pg->key.addr.proto) { 4318c2ecf20Sopenharmony_ci case htons(ETH_P_IP): 4328c2ecf20Sopenharmony_ci /* MDBA_MDB_EATTR_SOURCE */ 4338c2ecf20Sopenharmony_ci if (pg->key.addr.src.ip4) 4348c2ecf20Sopenharmony_ci nlmsg_size += nla_total_size(sizeof(__be32)); 4358c2ecf20Sopenharmony_ci if (pg->key.port->br->multicast_igmp_version == 2) 4368c2ecf20Sopenharmony_ci goto out; 4378c2ecf20Sopenharmony_ci addr_size = sizeof(__be32); 4388c2ecf20Sopenharmony_ci break; 4398c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 4408c2ecf20Sopenharmony_ci case htons(ETH_P_IPV6): 4418c2ecf20Sopenharmony_ci /* MDBA_MDB_EATTR_SOURCE */ 4428c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&pg->key.addr.src.ip6)) 4438c2ecf20Sopenharmony_ci nlmsg_size += nla_total_size(sizeof(struct in6_addr)); 4448c2ecf20Sopenharmony_ci if (pg->key.port->br->multicast_mld_version == 1) 4458c2ecf20Sopenharmony_ci goto out; 4468c2ecf20Sopenharmony_ci addr_size = sizeof(struct in6_addr); 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci#endif 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* MDBA_MDB_EATTR_GROUP_MODE */ 4528c2ecf20Sopenharmony_ci nlmsg_size += nla_total_size(sizeof(u8)); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* MDBA_MDB_EATTR_SRC_LIST nested attr */ 4558c2ecf20Sopenharmony_ci if (!hlist_empty(&pg->src_list)) 4568c2ecf20Sopenharmony_ci nlmsg_size += nla_total_size(0); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci hlist_for_each_entry(ent, &pg->src_list, node) { 4598c2ecf20Sopenharmony_ci /* MDBA_MDB_SRCLIST_ENTRY nested attr + 4608c2ecf20Sopenharmony_ci * MDBA_MDB_SRCATTR_ADDRESS + MDBA_MDB_SRCATTR_TIMER 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci nlmsg_size += nla_total_size(0) + 4638c2ecf20Sopenharmony_ci nla_total_size(addr_size) + 4648c2ecf20Sopenharmony_ci nla_total_size(sizeof(u32)); 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ciout: 4678c2ecf20Sopenharmony_ci return nlmsg_size; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistruct br_mdb_complete_info { 4718c2ecf20Sopenharmony_ci struct net_bridge_port *port; 4728c2ecf20Sopenharmony_ci struct br_ip ip; 4738c2ecf20Sopenharmony_ci}; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void br_mdb_complete(struct net_device *dev, int err, void *priv) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct br_mdb_complete_info *data = priv; 4788c2ecf20Sopenharmony_ci struct net_bridge_port_group __rcu **pp; 4798c2ecf20Sopenharmony_ci struct net_bridge_port_group *p; 4808c2ecf20Sopenharmony_ci struct net_bridge_mdb_entry *mp; 4818c2ecf20Sopenharmony_ci struct net_bridge_port *port = data->port; 4828c2ecf20Sopenharmony_ci struct net_bridge *br = port->br; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (err) 4858c2ecf20Sopenharmony_ci goto err; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci spin_lock_bh(&br->multicast_lock); 4888c2ecf20Sopenharmony_ci mp = br_mdb_ip_get(br, &data->ip); 4898c2ecf20Sopenharmony_ci if (!mp) 4908c2ecf20Sopenharmony_ci goto out; 4918c2ecf20Sopenharmony_ci for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL; 4928c2ecf20Sopenharmony_ci pp = &p->next) { 4938c2ecf20Sopenharmony_ci if (p->key.port != port) 4948c2ecf20Sopenharmony_ci continue; 4958c2ecf20Sopenharmony_ci p->flags |= MDB_PG_FLAGS_OFFLOAD; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ciout: 4988c2ecf20Sopenharmony_ci spin_unlock_bh(&br->multicast_lock); 4998c2ecf20Sopenharmony_cierr: 5008c2ecf20Sopenharmony_ci kfree(priv); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic void br_mdb_switchdev_host_port(struct net_device *dev, 5048c2ecf20Sopenharmony_ci struct net_device *lower_dev, 5058c2ecf20Sopenharmony_ci struct net_bridge_mdb_entry *mp, 5068c2ecf20Sopenharmony_ci int type) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct switchdev_obj_port_mdb mdb = { 5098c2ecf20Sopenharmony_ci .obj = { 5108c2ecf20Sopenharmony_ci .id = SWITCHDEV_OBJ_ID_HOST_MDB, 5118c2ecf20Sopenharmony_ci .flags = SWITCHDEV_F_DEFER, 5128c2ecf20Sopenharmony_ci }, 5138c2ecf20Sopenharmony_ci .vid = mp->addr.vid, 5148c2ecf20Sopenharmony_ci }; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (mp->addr.proto == htons(ETH_P_IP)) 5178c2ecf20Sopenharmony_ci ip_eth_mc_map(mp->addr.dst.ip4, mdb.addr); 5188c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 5198c2ecf20Sopenharmony_ci else 5208c2ecf20Sopenharmony_ci ipv6_eth_mc_map(&mp->addr.dst.ip6, mdb.addr); 5218c2ecf20Sopenharmony_ci#endif 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci mdb.obj.orig_dev = dev; 5248c2ecf20Sopenharmony_ci switch (type) { 5258c2ecf20Sopenharmony_ci case RTM_NEWMDB: 5268c2ecf20Sopenharmony_ci switchdev_port_obj_add(lower_dev, &mdb.obj, NULL); 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci case RTM_DELMDB: 5298c2ecf20Sopenharmony_ci switchdev_port_obj_del(lower_dev, &mdb.obj); 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic void br_mdb_switchdev_host(struct net_device *dev, 5358c2ecf20Sopenharmony_ci struct net_bridge_mdb_entry *mp, int type) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct net_device *lower_dev; 5388c2ecf20Sopenharmony_ci struct list_head *iter; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci netdev_for_each_lower_dev(dev, lower_dev, iter) 5418c2ecf20Sopenharmony_ci br_mdb_switchdev_host_port(dev, lower_dev, mp, type); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_civoid br_mdb_notify(struct net_device *dev, 5458c2ecf20Sopenharmony_ci struct net_bridge_mdb_entry *mp, 5468c2ecf20Sopenharmony_ci struct net_bridge_port_group *pg, 5478c2ecf20Sopenharmony_ci int type) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct br_mdb_complete_info *complete_info; 5508c2ecf20Sopenharmony_ci struct switchdev_obj_port_mdb mdb = { 5518c2ecf20Sopenharmony_ci .obj = { 5528c2ecf20Sopenharmony_ci .id = SWITCHDEV_OBJ_ID_PORT_MDB, 5538c2ecf20Sopenharmony_ci .flags = SWITCHDEV_F_DEFER, 5548c2ecf20Sopenharmony_ci }, 5558c2ecf20Sopenharmony_ci .vid = mp->addr.vid, 5568c2ecf20Sopenharmony_ci }; 5578c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 5588c2ecf20Sopenharmony_ci struct sk_buff *skb; 5598c2ecf20Sopenharmony_ci int err = -ENOBUFS; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (pg) { 5628c2ecf20Sopenharmony_ci if (mp->addr.proto == htons(ETH_P_IP)) 5638c2ecf20Sopenharmony_ci ip_eth_mc_map(mp->addr.dst.ip4, mdb.addr); 5648c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 5658c2ecf20Sopenharmony_ci else 5668c2ecf20Sopenharmony_ci ipv6_eth_mc_map(&mp->addr.dst.ip6, mdb.addr); 5678c2ecf20Sopenharmony_ci#endif 5688c2ecf20Sopenharmony_ci mdb.obj.orig_dev = pg->key.port->dev; 5698c2ecf20Sopenharmony_ci switch (type) { 5708c2ecf20Sopenharmony_ci case RTM_NEWMDB: 5718c2ecf20Sopenharmony_ci complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC); 5728c2ecf20Sopenharmony_ci if (!complete_info) 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci complete_info->port = pg->key.port; 5758c2ecf20Sopenharmony_ci complete_info->ip = mp->addr; 5768c2ecf20Sopenharmony_ci mdb.obj.complete_priv = complete_info; 5778c2ecf20Sopenharmony_ci mdb.obj.complete = br_mdb_complete; 5788c2ecf20Sopenharmony_ci if (switchdev_port_obj_add(pg->key.port->dev, &mdb.obj, NULL)) 5798c2ecf20Sopenharmony_ci kfree(complete_info); 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci case RTM_DELMDB: 5828c2ecf20Sopenharmony_ci switchdev_port_obj_del(pg->key.port->dev, &mdb.obj); 5838c2ecf20Sopenharmony_ci break; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci } else { 5868c2ecf20Sopenharmony_ci br_mdb_switchdev_host(dev, mp, type); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci skb = nlmsg_new(rtnl_mdb_nlmsg_size(pg), GFP_ATOMIC); 5908c2ecf20Sopenharmony_ci if (!skb) 5918c2ecf20Sopenharmony_ci goto errout; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci err = nlmsg_populate_mdb_fill(skb, dev, mp, pg, type); 5948c2ecf20Sopenharmony_ci if (err < 0) { 5958c2ecf20Sopenharmony_ci kfree_skb(skb); 5968c2ecf20Sopenharmony_ci goto errout; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC); 6008c2ecf20Sopenharmony_ci return; 6018c2ecf20Sopenharmony_cierrout: 6028c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_MDB, err); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic int nlmsg_populate_rtr_fill(struct sk_buff *skb, 6068c2ecf20Sopenharmony_ci struct net_device *dev, 6078c2ecf20Sopenharmony_ci int ifindex, u32 pid, 6088c2ecf20Sopenharmony_ci u32 seq, int type, unsigned int flags) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct br_port_msg *bpm; 6118c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 6128c2ecf20Sopenharmony_ci struct nlattr *nest; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, pid, seq, type, sizeof(*bpm), 0); 6158c2ecf20Sopenharmony_ci if (!nlh) 6168c2ecf20Sopenharmony_ci return -EMSGSIZE; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci bpm = nlmsg_data(nlh); 6198c2ecf20Sopenharmony_ci memset(bpm, 0, sizeof(*bpm)); 6208c2ecf20Sopenharmony_ci bpm->family = AF_BRIDGE; 6218c2ecf20Sopenharmony_ci bpm->ifindex = dev->ifindex; 6228c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, MDBA_ROUTER); 6238c2ecf20Sopenharmony_ci if (!nest) 6248c2ecf20Sopenharmony_ci goto cancel; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (nla_put_u32(skb, MDBA_ROUTER_PORT, ifindex)) 6278c2ecf20Sopenharmony_ci goto end; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 6308c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 6318c2ecf20Sopenharmony_ci return 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ciend: 6348c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 6358c2ecf20Sopenharmony_cicancel: 6368c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 6378c2ecf20Sopenharmony_ci return -EMSGSIZE; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic inline size_t rtnl_rtr_nlmsg_size(void) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct br_port_msg)) 6438c2ecf20Sopenharmony_ci + nla_total_size(sizeof(__u32)); 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_civoid br_rtr_notify(struct net_device *dev, struct net_bridge_port *port, 6478c2ecf20Sopenharmony_ci int type) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 6508c2ecf20Sopenharmony_ci struct sk_buff *skb; 6518c2ecf20Sopenharmony_ci int err = -ENOBUFS; 6528c2ecf20Sopenharmony_ci int ifindex; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ifindex = port ? port->dev->ifindex : 0; 6558c2ecf20Sopenharmony_ci skb = nlmsg_new(rtnl_rtr_nlmsg_size(), GFP_ATOMIC); 6568c2ecf20Sopenharmony_ci if (!skb) 6578c2ecf20Sopenharmony_ci goto errout; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci err = nlmsg_populate_rtr_fill(skb, dev, ifindex, 0, 0, type, NTF_SELF); 6608c2ecf20Sopenharmony_ci if (err < 0) { 6618c2ecf20Sopenharmony_ci kfree_skb(skb); 6628c2ecf20Sopenharmony_ci goto errout; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC); 6668c2ecf20Sopenharmony_ci return; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cierrout: 6698c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_MDB, err); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic bool is_valid_mdb_entry(struct br_mdb_entry *entry, 6738c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci if (entry->ifindex == 0) { 6768c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Zero entry ifindex is not allowed"); 6778c2ecf20Sopenharmony_ci return false; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (entry->addr.proto == htons(ETH_P_IP)) { 6818c2ecf20Sopenharmony_ci if (!ipv4_is_multicast(entry->addr.u.ip4)) { 6828c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv4 entry group address is not multicast"); 6838c2ecf20Sopenharmony_ci return false; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(entry->addr.u.ip4)) { 6868c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv4 entry group address is local multicast"); 6878c2ecf20Sopenharmony_ci return false; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 6908c2ecf20Sopenharmony_ci } else if (entry->addr.proto == htons(ETH_P_IPV6)) { 6918c2ecf20Sopenharmony_ci if (ipv6_addr_is_ll_all_nodes(&entry->addr.u.ip6)) { 6928c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv6 entry group address is link-local all nodes"); 6938c2ecf20Sopenharmony_ci return false; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci#endif 6968c2ecf20Sopenharmony_ci } else { 6978c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unknown entry protocol"); 6988c2ecf20Sopenharmony_ci return false; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (entry->state != MDB_PERMANENT && entry->state != MDB_TEMPORARY) { 7028c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unknown entry state"); 7038c2ecf20Sopenharmony_ci return false; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci if (entry->vid >= VLAN_VID_MASK) { 7068c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid entry VLAN id"); 7078c2ecf20Sopenharmony_ci return false; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return true; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic bool is_valid_mdb_source(struct nlattr *attr, __be16 proto, 7148c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci switch (proto) { 7178c2ecf20Sopenharmony_ci case htons(ETH_P_IP): 7188c2ecf20Sopenharmony_ci if (nla_len(attr) != sizeof(struct in_addr)) { 7198c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv4 invalid source address length"); 7208c2ecf20Sopenharmony_ci return false; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci if (ipv4_is_multicast(nla_get_in_addr(attr))) { 7238c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed"); 7248c2ecf20Sopenharmony_ci return false; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci break; 7278c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 7288c2ecf20Sopenharmony_ci case htons(ETH_P_IPV6): { 7298c2ecf20Sopenharmony_ci struct in6_addr src; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (nla_len(attr) != sizeof(struct in6_addr)) { 7328c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv6 invalid source address length"); 7338c2ecf20Sopenharmony_ci return false; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci src = nla_get_in6_addr(attr); 7368c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(&src)) { 7378c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed"); 7388c2ecf20Sopenharmony_ci return false; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci break; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci#endif 7438c2ecf20Sopenharmony_ci default: 7448c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid protocol used with source address"); 7458c2ecf20Sopenharmony_ci return false; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return true; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = { 7528c2ecf20Sopenharmony_ci [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY, 7538c2ecf20Sopenharmony_ci sizeof(struct in_addr), 7548c2ecf20Sopenharmony_ci sizeof(struct in6_addr)), 7558c2ecf20Sopenharmony_ci}; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh, 7588c2ecf20Sopenharmony_ci struct net_device **pdev, struct br_mdb_entry **pentry, 7598c2ecf20Sopenharmony_ci struct nlattr **mdb_attrs, struct netlink_ext_ack *extack) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 7628c2ecf20Sopenharmony_ci struct br_mdb_entry *entry; 7638c2ecf20Sopenharmony_ci struct br_port_msg *bpm; 7648c2ecf20Sopenharmony_ci struct nlattr *tb[MDBA_SET_ENTRY_MAX+1]; 7658c2ecf20Sopenharmony_ci struct net_device *dev; 7668c2ecf20Sopenharmony_ci int err; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb, 7698c2ecf20Sopenharmony_ci MDBA_SET_ENTRY_MAX, NULL, NULL); 7708c2ecf20Sopenharmony_ci if (err < 0) 7718c2ecf20Sopenharmony_ci return err; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci bpm = nlmsg_data(nlh); 7748c2ecf20Sopenharmony_ci if (bpm->ifindex == 0) { 7758c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid bridge ifindex"); 7768c2ecf20Sopenharmony_ci return -EINVAL; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci dev = __dev_get_by_index(net, bpm->ifindex); 7808c2ecf20Sopenharmony_ci if (dev == NULL) { 7818c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Bridge device doesn't exist"); 7828c2ecf20Sopenharmony_ci return -ENODEV; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (!(dev->priv_flags & IFF_EBRIDGE)) { 7868c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge"); 7878c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci *pdev = dev; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (!tb[MDBA_SET_ENTRY]) { 7938c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY attribute"); 7948c2ecf20Sopenharmony_ci return -EINVAL; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci if (nla_len(tb[MDBA_SET_ENTRY]) != sizeof(struct br_mdb_entry)) { 7978c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid MDBA_SET_ENTRY attribute length"); 7988c2ecf20Sopenharmony_ci return -EINVAL; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci entry = nla_data(tb[MDBA_SET_ENTRY]); 8028c2ecf20Sopenharmony_ci if (!is_valid_mdb_entry(entry, extack)) 8038c2ecf20Sopenharmony_ci return -EINVAL; 8048c2ecf20Sopenharmony_ci *pentry = entry; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (tb[MDBA_SET_ENTRY_ATTRS]) { 8078c2ecf20Sopenharmony_ci err = nla_parse_nested(mdb_attrs, MDBE_ATTR_MAX, 8088c2ecf20Sopenharmony_ci tb[MDBA_SET_ENTRY_ATTRS], 8098c2ecf20Sopenharmony_ci br_mdbe_attrs_pol, extack); 8108c2ecf20Sopenharmony_ci if (err) 8118c2ecf20Sopenharmony_ci return err; 8128c2ecf20Sopenharmony_ci if (mdb_attrs[MDBE_ATTR_SOURCE] && 8138c2ecf20Sopenharmony_ci !is_valid_mdb_source(mdb_attrs[MDBE_ATTR_SOURCE], 8148c2ecf20Sopenharmony_ci entry->addr.proto, extack)) 8158c2ecf20Sopenharmony_ci return -EINVAL; 8168c2ecf20Sopenharmony_ci } else { 8178c2ecf20Sopenharmony_ci memset(mdb_attrs, 0, 8188c2ecf20Sopenharmony_ci sizeof(struct nlattr *) * (MDBE_ATTR_MAX + 1)); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, 8258c2ecf20Sopenharmony_ci struct br_mdb_entry *entry, 8268c2ecf20Sopenharmony_ci struct nlattr **mdb_attrs, 8278c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci struct net_bridge_mdb_entry *mp, *star_mp; 8308c2ecf20Sopenharmony_ci struct net_bridge_port_group *p; 8318c2ecf20Sopenharmony_ci struct net_bridge_port_group __rcu **pp; 8328c2ecf20Sopenharmony_ci struct br_ip group, star_group; 8338c2ecf20Sopenharmony_ci unsigned long now = jiffies; 8348c2ecf20Sopenharmony_ci u8 filter_mode; 8358c2ecf20Sopenharmony_ci int err; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci __mdb_entry_to_br_ip(entry, &group, mdb_attrs); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* host join errors which can happen before creating the group */ 8408c2ecf20Sopenharmony_ci if (!port) { 8418c2ecf20Sopenharmony_ci /* don't allow any flags for host-joined groups */ 8428c2ecf20Sopenharmony_ci if (entry->state) { 8438c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups"); 8448c2ecf20Sopenharmony_ci return -EINVAL; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci if (!br_multicast_is_star_g(&group)) { 8478c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined"); 8488c2ecf20Sopenharmony_ci return -EINVAL; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci mp = br_mdb_ip_get(br, &group); 8538c2ecf20Sopenharmony_ci if (!mp) { 8548c2ecf20Sopenharmony_ci mp = br_multicast_new_group(br, &group); 8558c2ecf20Sopenharmony_ci err = PTR_ERR_OR_ZERO(mp); 8568c2ecf20Sopenharmony_ci if (err) 8578c2ecf20Sopenharmony_ci return err; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* host join */ 8618c2ecf20Sopenharmony_ci if (!port) { 8628c2ecf20Sopenharmony_ci if (mp->host_joined) { 8638c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Group is already joined by host"); 8648c2ecf20Sopenharmony_ci return -EEXIST; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci br_multicast_host_join(mp, false); 8688c2ecf20Sopenharmony_ci br_mdb_notify(br->dev, mp, NULL, RTM_NEWMDB); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return 0; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci for (pp = &mp->ports; 8748c2ecf20Sopenharmony_ci (p = mlock_dereference(*pp, br)) != NULL; 8758c2ecf20Sopenharmony_ci pp = &p->next) { 8768c2ecf20Sopenharmony_ci if (p->key.port == port) { 8778c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Group is already joined by port"); 8788c2ecf20Sopenharmony_ci return -EEXIST; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci if ((unsigned long)p->key.port < (unsigned long)port) 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci filter_mode = br_multicast_is_star_g(&group) ? MCAST_EXCLUDE : 8858c2ecf20Sopenharmony_ci MCAST_INCLUDE; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci p = br_multicast_new_port_group(port, &group, *pp, entry->state, NULL, 8888c2ecf20Sopenharmony_ci filter_mode, RTPROT_STATIC); 8898c2ecf20Sopenharmony_ci if (unlikely(!p)) { 8908c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new port group"); 8918c2ecf20Sopenharmony_ci return -ENOMEM; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci rcu_assign_pointer(*pp, p); 8948c2ecf20Sopenharmony_ci if (entry->state == MDB_TEMPORARY) 8958c2ecf20Sopenharmony_ci mod_timer(&p->timer, now + br->multicast_membership_interval); 8968c2ecf20Sopenharmony_ci br_mdb_notify(br->dev, mp, p, RTM_NEWMDB); 8978c2ecf20Sopenharmony_ci /* if we are adding a new EXCLUDE port group (*,G) it needs to be also 8988c2ecf20Sopenharmony_ci * added to all S,G entries for proper replication, if we are adding 8998c2ecf20Sopenharmony_ci * a new INCLUDE port (S,G) then all of *,G EXCLUDE ports need to be 9008c2ecf20Sopenharmony_ci * added to it for proper replication 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_ci if (br_multicast_should_handle_mode(br, group.proto)) { 9038c2ecf20Sopenharmony_ci switch (filter_mode) { 9048c2ecf20Sopenharmony_ci case MCAST_EXCLUDE: 9058c2ecf20Sopenharmony_ci br_multicast_star_g_handle_mode(p, MCAST_EXCLUDE); 9068c2ecf20Sopenharmony_ci break; 9078c2ecf20Sopenharmony_ci case MCAST_INCLUDE: 9088c2ecf20Sopenharmony_ci star_group = p->key.addr; 9098c2ecf20Sopenharmony_ci memset(&star_group.src, 0, sizeof(star_group.src)); 9108c2ecf20Sopenharmony_ci star_mp = br_mdb_ip_get(br, &star_group); 9118c2ecf20Sopenharmony_ci if (star_mp) 9128c2ecf20Sopenharmony_ci br_multicast_sg_add_exclude_ports(star_mp, p); 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci return 0; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic int __br_mdb_add(struct net *net, struct net_bridge *br, 9218c2ecf20Sopenharmony_ci struct net_bridge_port *p, 9228c2ecf20Sopenharmony_ci struct br_mdb_entry *entry, 9238c2ecf20Sopenharmony_ci struct nlattr **mdb_attrs, 9248c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci int ret; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci spin_lock_bh(&br->multicast_lock); 9298c2ecf20Sopenharmony_ci ret = br_mdb_add_group(br, p, entry, mdb_attrs, extack); 9308c2ecf20Sopenharmony_ci spin_unlock_bh(&br->multicast_lock); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci return ret; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, 9368c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1]; 9398c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 9408c2ecf20Sopenharmony_ci struct net_bridge_vlan_group *vg; 9418c2ecf20Sopenharmony_ci struct net_bridge_port *p = NULL; 9428c2ecf20Sopenharmony_ci struct net_device *dev, *pdev; 9438c2ecf20Sopenharmony_ci struct br_mdb_entry *entry; 9448c2ecf20Sopenharmony_ci struct net_bridge_vlan *v; 9458c2ecf20Sopenharmony_ci struct net_bridge *br; 9468c2ecf20Sopenharmony_ci int err; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); 9498c2ecf20Sopenharmony_ci if (err < 0) 9508c2ecf20Sopenharmony_ci return err; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci br = netdev_priv(dev); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (!netif_running(br->dev)) { 9558c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Bridge device is not running"); 9568c2ecf20Sopenharmony_ci return -EINVAL; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { 9608c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Bridge's multicast processing is disabled"); 9618c2ecf20Sopenharmony_ci return -EINVAL; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (entry->ifindex != br->dev->ifindex) { 9658c2ecf20Sopenharmony_ci pdev = __dev_get_by_index(net, entry->ifindex); 9668c2ecf20Sopenharmony_ci if (!pdev) { 9678c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Port net device doesn't exist"); 9688c2ecf20Sopenharmony_ci return -ENODEV; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci p = br_port_get_rtnl(pdev); 9728c2ecf20Sopenharmony_ci if (!p) { 9738c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port"); 9748c2ecf20Sopenharmony_ci return -EINVAL; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (p->br != br) { 9788c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device"); 9798c2ecf20Sopenharmony_ci return -EINVAL; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci if (p->state == BR_STATE_DISABLED) { 9828c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state"); 9838c2ecf20Sopenharmony_ci return -EINVAL; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci vg = nbp_vlan_group(p); 9868c2ecf20Sopenharmony_ci } else { 9878c2ecf20Sopenharmony_ci vg = br_vlan_group(br); 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* If vlan filtering is enabled and VLAN is not specified 9918c2ecf20Sopenharmony_ci * install mdb entry on all vlans configured on the port. 9928c2ecf20Sopenharmony_ci */ 9938c2ecf20Sopenharmony_ci if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) { 9948c2ecf20Sopenharmony_ci list_for_each_entry(v, &vg->vlan_list, vlist) { 9958c2ecf20Sopenharmony_ci entry->vid = v->vid; 9968c2ecf20Sopenharmony_ci err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack); 9978c2ecf20Sopenharmony_ci if (err) 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci } else { 10018c2ecf20Sopenharmony_ci err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack); 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci return err; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry, 10088c2ecf20Sopenharmony_ci struct nlattr **mdb_attrs) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci struct net_bridge_mdb_entry *mp; 10118c2ecf20Sopenharmony_ci struct net_bridge_port_group *p; 10128c2ecf20Sopenharmony_ci struct net_bridge_port_group __rcu **pp; 10138c2ecf20Sopenharmony_ci struct br_ip ip; 10148c2ecf20Sopenharmony_ci int err = -EINVAL; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED)) 10178c2ecf20Sopenharmony_ci return -EINVAL; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci __mdb_entry_to_br_ip(entry, &ip, mdb_attrs); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci spin_lock_bh(&br->multicast_lock); 10228c2ecf20Sopenharmony_ci mp = br_mdb_ip_get(br, &ip); 10238c2ecf20Sopenharmony_ci if (!mp) 10248c2ecf20Sopenharmony_ci goto unlock; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* host leave */ 10278c2ecf20Sopenharmony_ci if (entry->ifindex == mp->br->dev->ifindex && mp->host_joined) { 10288c2ecf20Sopenharmony_ci br_multicast_host_leave(mp, false); 10298c2ecf20Sopenharmony_ci err = 0; 10308c2ecf20Sopenharmony_ci br_mdb_notify(br->dev, mp, NULL, RTM_DELMDB); 10318c2ecf20Sopenharmony_ci if (!mp->ports && netif_running(br->dev)) 10328c2ecf20Sopenharmony_ci mod_timer(&mp->timer, jiffies); 10338c2ecf20Sopenharmony_ci goto unlock; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci for (pp = &mp->ports; 10378c2ecf20Sopenharmony_ci (p = mlock_dereference(*pp, br)) != NULL; 10388c2ecf20Sopenharmony_ci pp = &p->next) { 10398c2ecf20Sopenharmony_ci if (!p->key.port || p->key.port->dev->ifindex != entry->ifindex) 10408c2ecf20Sopenharmony_ci continue; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (p->key.port->state == BR_STATE_DISABLED) 10438c2ecf20Sopenharmony_ci goto unlock; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci br_multicast_del_pg(mp, p, pp); 10468c2ecf20Sopenharmony_ci err = 0; 10478c2ecf20Sopenharmony_ci break; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ciunlock: 10518c2ecf20Sopenharmony_ci spin_unlock_bh(&br->multicast_lock); 10528c2ecf20Sopenharmony_ci return err; 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, 10568c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1]; 10598c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 10608c2ecf20Sopenharmony_ci struct net_bridge_vlan_group *vg; 10618c2ecf20Sopenharmony_ci struct net_bridge_port *p = NULL; 10628c2ecf20Sopenharmony_ci struct net_device *dev, *pdev; 10638c2ecf20Sopenharmony_ci struct br_mdb_entry *entry; 10648c2ecf20Sopenharmony_ci struct net_bridge_vlan *v; 10658c2ecf20Sopenharmony_ci struct net_bridge *br; 10668c2ecf20Sopenharmony_ci int err; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); 10698c2ecf20Sopenharmony_ci if (err < 0) 10708c2ecf20Sopenharmony_ci return err; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci br = netdev_priv(dev); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if (entry->ifindex != br->dev->ifindex) { 10758c2ecf20Sopenharmony_ci pdev = __dev_get_by_index(net, entry->ifindex); 10768c2ecf20Sopenharmony_ci if (!pdev) 10778c2ecf20Sopenharmony_ci return -ENODEV; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci p = br_port_get_rtnl(pdev); 10808c2ecf20Sopenharmony_ci if (!p || p->br != br || p->state == BR_STATE_DISABLED) 10818c2ecf20Sopenharmony_ci return -EINVAL; 10828c2ecf20Sopenharmony_ci vg = nbp_vlan_group(p); 10838c2ecf20Sopenharmony_ci } else { 10848c2ecf20Sopenharmony_ci vg = br_vlan_group(br); 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci /* If vlan filtering is enabled and VLAN is not specified 10888c2ecf20Sopenharmony_ci * delete mdb entry on all vlans configured on the port. 10898c2ecf20Sopenharmony_ci */ 10908c2ecf20Sopenharmony_ci if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) { 10918c2ecf20Sopenharmony_ci list_for_each_entry(v, &vg->vlan_list, vlist) { 10928c2ecf20Sopenharmony_ci entry->vid = v->vid; 10938c2ecf20Sopenharmony_ci err = __br_mdb_del(br, entry, mdb_attrs); 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci } else { 10968c2ecf20Sopenharmony_ci err = __br_mdb_del(br, entry, mdb_attrs); 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci return err; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_civoid br_mdb_init(void) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, 0); 11058c2ecf20Sopenharmony_ci rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_NEWMDB, br_mdb_add, NULL, 0); 11068c2ecf20Sopenharmony_ci rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_DELMDB, br_mdb_del, NULL, 0); 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_civoid br_mdb_uninit(void) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci rtnl_unregister(PF_BRIDGE, RTM_GETMDB); 11128c2ecf20Sopenharmony_ci rtnl_unregister(PF_BRIDGE, RTM_NEWMDB); 11138c2ecf20Sopenharmony_ci rtnl_unregister(PF_BRIDGE, RTM_DELMDB); 11148c2ecf20Sopenharmony_ci} 1115