18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Multicast support for IPv6 48c2ecf20Sopenharmony_ci * Linux INET6 implementation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* Changes: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * yoshfuji : fix format of router-alert option 158c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI: 168c2ecf20Sopenharmony_ci * Fixed source address for MLD message based on 178c2ecf20Sopenharmony_ci * <draft-ietf-magma-mld-source-05.txt>. 188c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI: 198c2ecf20Sopenharmony_ci * - Ignore Queries for invalid addresses. 208c2ecf20Sopenharmony_ci * - MLD for link-local addresses. 218c2ecf20Sopenharmony_ci * David L Stevens <dlstevens@us.ibm.com>: 228c2ecf20Sopenharmony_ci * - MLDv2 support 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/errno.h> 278c2ecf20Sopenharmony_ci#include <linux/types.h> 288c2ecf20Sopenharmony_ci#include <linux/string.h> 298c2ecf20Sopenharmony_ci#include <linux/socket.h> 308c2ecf20Sopenharmony_ci#include <linux/sockios.h> 318c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 328c2ecf20Sopenharmony_ci#include <linux/times.h> 338c2ecf20Sopenharmony_ci#include <linux/net.h> 348c2ecf20Sopenharmony_ci#include <linux/in.h> 358c2ecf20Sopenharmony_ci#include <linux/in6.h> 368c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 378c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 388c2ecf20Sopenharmony_ci#include <linux/route.h> 398c2ecf20Sopenharmony_ci#include <linux/init.h> 408c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 418c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 428c2ecf20Sopenharmony_ci#include <linux/slab.h> 438c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h> 448c2ecf20Sopenharmony_ci#include <net/mld.h> 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#include <linux/netfilter.h> 478c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv6.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 508c2ecf20Sopenharmony_ci#include <net/sock.h> 518c2ecf20Sopenharmony_ci#include <net/snmp.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include <net/ipv6.h> 548c2ecf20Sopenharmony_ci#include <net/protocol.h> 558c2ecf20Sopenharmony_ci#include <net/if_inet6.h> 568c2ecf20Sopenharmony_ci#include <net/ndisc.h> 578c2ecf20Sopenharmony_ci#include <net/addrconf.h> 588c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 598c2ecf20Sopenharmony_ci#include <net/inet_common.h> 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Ensure that we have struct in6_addr aligned on 32bit word. */ 648c2ecf20Sopenharmony_cistatic int __mld2_query_bugs[] __attribute__((__unused__)) = { 658c2ecf20Sopenharmony_ci BUILD_BUG_ON_ZERO(offsetof(struct mld2_query, mld2q_srcs) % 4), 668c2ecf20Sopenharmony_ci BUILD_BUG_ON_ZERO(offsetof(struct mld2_report, mld2r_grec) % 4), 678c2ecf20Sopenharmony_ci BUILD_BUG_ON_ZERO(offsetof(struct mld2_grec, grec_mca) % 4) 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void igmp6_join_group(struct ifmcaddr6 *ma); 738c2ecf20Sopenharmony_cistatic void igmp6_leave_group(struct ifmcaddr6 *ma); 748c2ecf20Sopenharmony_cistatic void igmp6_timer_handler(struct timer_list *t); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void mld_gq_timer_expire(struct timer_list *t); 778c2ecf20Sopenharmony_cistatic void mld_ifc_timer_expire(struct timer_list *t); 788c2ecf20Sopenharmony_cistatic void mld_ifc_event(struct inet6_dev *idev); 798c2ecf20Sopenharmony_cistatic void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc); 808c2ecf20Sopenharmony_cistatic void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc); 818c2ecf20Sopenharmony_cistatic void mld_clear_delrec(struct inet6_dev *idev); 828c2ecf20Sopenharmony_cistatic bool mld_in_v1_mode(const struct inet6_dev *idev); 838c2ecf20Sopenharmony_cistatic int sf_setstate(struct ifmcaddr6 *pmc); 848c2ecf20Sopenharmony_cistatic void sf_markstate(struct ifmcaddr6 *pmc); 858c2ecf20Sopenharmony_cistatic void ip6_mc_clear_src(struct ifmcaddr6 *pmc); 868c2ecf20Sopenharmony_cistatic int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca, 878c2ecf20Sopenharmony_ci int sfmode, int sfcount, const struct in6_addr *psfsrc, 888c2ecf20Sopenharmony_ci int delta); 898c2ecf20Sopenharmony_cistatic int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca, 908c2ecf20Sopenharmony_ci int sfmode, int sfcount, const struct in6_addr *psfsrc, 918c2ecf20Sopenharmony_ci int delta); 928c2ecf20Sopenharmony_cistatic int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, 938c2ecf20Sopenharmony_ci struct inet6_dev *idev); 948c2ecf20Sopenharmony_cistatic int __ipv6_dev_mc_inc(struct net_device *dev, 958c2ecf20Sopenharmony_ci const struct in6_addr *addr, unsigned int mode); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define MLD_QRV_DEFAULT 2 988c2ecf20Sopenharmony_ci/* RFC3810, 9.2. Query Interval */ 998c2ecf20Sopenharmony_ci#define MLD_QI_DEFAULT (125 * HZ) 1008c2ecf20Sopenharmony_ci/* RFC3810, 9.3. Query Response Interval */ 1018c2ecf20Sopenharmony_ci#define MLD_QRI_DEFAULT (10 * HZ) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* RFC3810, 8.1 Query Version Distinctions */ 1048c2ecf20Sopenharmony_ci#define MLD_V1_QUERY_LEN 24 1058c2ecf20Sopenharmony_ci#define MLD_V2_QUERY_LEN_MIN 28 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define IPV6_MLD_MAX_MSF 64 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ciint sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF; 1108c2ecf20Sopenharmony_ciint sysctl_mld_qrv __read_mostly = MLD_QRV_DEFAULT; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* 1138c2ecf20Sopenharmony_ci * socket join on multicast group 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define for_each_pmc_rcu(np, pmc) \ 1178c2ecf20Sopenharmony_ci for (pmc = rcu_dereference(np->ipv6_mc_list); \ 1188c2ecf20Sopenharmony_ci pmc != NULL; \ 1198c2ecf20Sopenharmony_ci pmc = rcu_dereference(pmc->next)) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int unsolicited_report_interval(struct inet6_dev *idev) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int iv; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (mld_in_v1_mode(idev)) 1268c2ecf20Sopenharmony_ci iv = idev->cnf.mldv1_unsolicited_report_interval; 1278c2ecf20Sopenharmony_ci else 1288c2ecf20Sopenharmony_ci iv = idev->cnf.mldv2_unsolicited_report_interval; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return iv > 0 ? iv : 1; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int __ipv6_sock_mc_join(struct sock *sk, int ifindex, 1348c2ecf20Sopenharmony_ci const struct in6_addr *addr, unsigned int mode) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 1378c2ecf20Sopenharmony_ci struct ipv6_mc_socklist *mc_lst; 1388c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 1398c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 1408c2ecf20Sopenharmony_ci int err; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!ipv6_addr_is_multicast(addr)) 1458c2ecf20Sopenharmony_ci return -EINVAL; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci rcu_read_lock(); 1488c2ecf20Sopenharmony_ci for_each_pmc_rcu(np, mc_lst) { 1498c2ecf20Sopenharmony_ci if ((ifindex == 0 || mc_lst->ifindex == ifindex) && 1508c2ecf20Sopenharmony_ci ipv6_addr_equal(&mc_lst->addr, addr)) { 1518c2ecf20Sopenharmony_ci rcu_read_unlock(); 1528c2ecf20Sopenharmony_ci return -EADDRINUSE; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci rcu_read_unlock(); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (!mc_lst) 1608c2ecf20Sopenharmony_ci return -ENOMEM; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci mc_lst->next = NULL; 1638c2ecf20Sopenharmony_ci mc_lst->addr = *addr; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (ifindex == 0) { 1668c2ecf20Sopenharmony_ci struct rt6_info *rt; 1678c2ecf20Sopenharmony_ci rt = rt6_lookup(net, addr, NULL, 0, NULL, 0); 1688c2ecf20Sopenharmony_ci if (rt) { 1698c2ecf20Sopenharmony_ci dev = rt->dst.dev; 1708c2ecf20Sopenharmony_ci ip6_rt_put(rt); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } else 1738c2ecf20Sopenharmony_ci dev = __dev_get_by_index(net, ifindex); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (!dev) { 1768c2ecf20Sopenharmony_ci sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); 1778c2ecf20Sopenharmony_ci return -ENODEV; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci mc_lst->ifindex = dev->ifindex; 1818c2ecf20Sopenharmony_ci mc_lst->sfmode = mode; 1828c2ecf20Sopenharmony_ci rwlock_init(&mc_lst->sflock); 1838c2ecf20Sopenharmony_ci mc_lst->sflist = NULL; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci * now add/increase the group membership on the device 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci err = __ipv6_dev_mc_inc(dev, addr, mode); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (err) { 1928c2ecf20Sopenharmony_ci sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); 1938c2ecf20Sopenharmony_ci return err; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci mc_lst->next = np->ipv6_mc_list; 1978c2ecf20Sopenharmony_ci rcu_assign_pointer(np->ipv6_mc_list, mc_lst); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ciint ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return __ipv6_sock_mc_join(sk, ifindex, addr, MCAST_EXCLUDE); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_sock_mc_join); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciint ipv6_sock_mc_join_ssm(struct sock *sk, int ifindex, 2098c2ecf20Sopenharmony_ci const struct in6_addr *addr, unsigned int mode) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci return __ipv6_sock_mc_join(sk, ifindex, addr, mode); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* 2158c2ecf20Sopenharmony_ci * socket leave on multicast group 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ciint ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 2208c2ecf20Sopenharmony_ci struct ipv6_mc_socklist *mc_lst; 2218c2ecf20Sopenharmony_ci struct ipv6_mc_socklist __rcu **lnk; 2228c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ASSERT_RTNL(); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!ipv6_addr_is_multicast(addr)) 2278c2ecf20Sopenharmony_ci return -EINVAL; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci for (lnk = &np->ipv6_mc_list; 2308c2ecf20Sopenharmony_ci (mc_lst = rtnl_dereference(*lnk)) != NULL; 2318c2ecf20Sopenharmony_ci lnk = &mc_lst->next) { 2328c2ecf20Sopenharmony_ci if ((ifindex == 0 || mc_lst->ifindex == ifindex) && 2338c2ecf20Sopenharmony_ci ipv6_addr_equal(&mc_lst->addr, addr)) { 2348c2ecf20Sopenharmony_ci struct net_device *dev; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci *lnk = mc_lst->next; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci dev = __dev_get_by_index(net, mc_lst->ifindex); 2398c2ecf20Sopenharmony_ci if (dev) { 2408c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci (void) ip6_mc_leave_src(sk, mc_lst, idev); 2438c2ecf20Sopenharmony_ci if (idev) 2448c2ecf20Sopenharmony_ci __ipv6_dev_mc_dec(idev, &mc_lst->addr); 2458c2ecf20Sopenharmony_ci } else 2468c2ecf20Sopenharmony_ci (void) ip6_mc_leave_src(sk, mc_lst, NULL); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); 2498c2ecf20Sopenharmony_ci kfree_rcu(mc_lst, rcu); 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_sock_mc_drop); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* called with rcu_read_lock() */ 2598c2ecf20Sopenharmony_cistatic struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, 2608c2ecf20Sopenharmony_ci const struct in6_addr *group, 2618c2ecf20Sopenharmony_ci int ifindex) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 2648c2ecf20Sopenharmony_ci struct inet6_dev *idev = NULL; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (ifindex == 0) { 2678c2ecf20Sopenharmony_ci struct rt6_info *rt = rt6_lookup(net, group, NULL, 0, NULL, 0); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (rt) { 2708c2ecf20Sopenharmony_ci dev = rt->dst.dev; 2718c2ecf20Sopenharmony_ci ip6_rt_put(rt); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci } else 2748c2ecf20Sopenharmony_ci dev = dev_get_by_index_rcu(net, ifindex); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (!dev) 2778c2ecf20Sopenharmony_ci return NULL; 2788c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 2798c2ecf20Sopenharmony_ci if (!idev) 2808c2ecf20Sopenharmony_ci return NULL; 2818c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 2828c2ecf20Sopenharmony_ci if (idev->dead) { 2838c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 2848c2ecf20Sopenharmony_ci return NULL; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci return idev; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_civoid __ipv6_sock_mc_close(struct sock *sk) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 2928c2ecf20Sopenharmony_ci struct ipv6_mc_socklist *mc_lst; 2938c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ASSERT_RTNL(); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci while ((mc_lst = rtnl_dereference(np->ipv6_mc_list)) != NULL) { 2988c2ecf20Sopenharmony_ci struct net_device *dev; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci np->ipv6_mc_list = mc_lst->next; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci dev = __dev_get_by_index(net, mc_lst->ifindex); 3038c2ecf20Sopenharmony_ci if (dev) { 3048c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci (void) ip6_mc_leave_src(sk, mc_lst, idev); 3078c2ecf20Sopenharmony_ci if (idev) 3088c2ecf20Sopenharmony_ci __ipv6_dev_mc_dec(idev, &mc_lst->addr); 3098c2ecf20Sopenharmony_ci } else 3108c2ecf20Sopenharmony_ci (void) ip6_mc_leave_src(sk, mc_lst, NULL); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); 3138c2ecf20Sopenharmony_ci kfree_rcu(mc_lst, rcu); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_civoid ipv6_sock_mc_close(struct sock *sk) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (!rcu_access_pointer(np->ipv6_mc_list)) 3228c2ecf20Sopenharmony_ci return; 3238c2ecf20Sopenharmony_ci rtnl_lock(); 3248c2ecf20Sopenharmony_ci __ipv6_sock_mc_close(sk); 3258c2ecf20Sopenharmony_ci rtnl_unlock(); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ciint ip6_mc_source(int add, int omode, struct sock *sk, 3298c2ecf20Sopenharmony_ci struct group_source_req *pgsr) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct in6_addr *source, *group; 3328c2ecf20Sopenharmony_ci struct ipv6_mc_socklist *pmc; 3338c2ecf20Sopenharmony_ci struct inet6_dev *idev; 3348c2ecf20Sopenharmony_ci struct ipv6_pinfo *inet6 = inet6_sk(sk); 3358c2ecf20Sopenharmony_ci struct ip6_sf_socklist *psl; 3368c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 3378c2ecf20Sopenharmony_ci int i, j, rv; 3388c2ecf20Sopenharmony_ci int leavegroup = 0; 3398c2ecf20Sopenharmony_ci int pmclocked = 0; 3408c2ecf20Sopenharmony_ci int err; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr; 3438c2ecf20Sopenharmony_ci group = &((struct sockaddr_in6 *)&pgsr->gsr_group)->sin6_addr; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (!ipv6_addr_is_multicast(group)) 3468c2ecf20Sopenharmony_ci return -EINVAL; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci rcu_read_lock(); 3498c2ecf20Sopenharmony_ci idev = ip6_mc_find_dev_rcu(net, group, pgsr->gsr_interface); 3508c2ecf20Sopenharmony_ci if (!idev) { 3518c2ecf20Sopenharmony_ci rcu_read_unlock(); 3528c2ecf20Sopenharmony_ci return -ENODEV; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci err = -EADDRNOTAVAIL; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci for_each_pmc_rcu(inet6, pmc) { 3588c2ecf20Sopenharmony_ci if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface) 3598c2ecf20Sopenharmony_ci continue; 3608c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&pmc->addr, group)) 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci if (!pmc) { /* must have a prior join */ 3648c2ecf20Sopenharmony_ci err = -EINVAL; 3658c2ecf20Sopenharmony_ci goto done; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci /* if a source filter was set, must be the same mode as before */ 3688c2ecf20Sopenharmony_ci if (pmc->sflist) { 3698c2ecf20Sopenharmony_ci if (pmc->sfmode != omode) { 3708c2ecf20Sopenharmony_ci err = -EINVAL; 3718c2ecf20Sopenharmony_ci goto done; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci } else if (pmc->sfmode != omode) { 3748c2ecf20Sopenharmony_ci /* allow mode switches for empty-set filters */ 3758c2ecf20Sopenharmony_ci ip6_mc_add_src(idev, group, omode, 0, NULL, 0); 3768c2ecf20Sopenharmony_ci ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); 3778c2ecf20Sopenharmony_ci pmc->sfmode = omode; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci write_lock(&pmc->sflock); 3818c2ecf20Sopenharmony_ci pmclocked = 1; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci psl = pmc->sflist; 3848c2ecf20Sopenharmony_ci if (!add) { 3858c2ecf20Sopenharmony_ci if (!psl) 3868c2ecf20Sopenharmony_ci goto done; /* err = -EADDRNOTAVAIL */ 3878c2ecf20Sopenharmony_ci rv = !0; 3888c2ecf20Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) { 3898c2ecf20Sopenharmony_ci rv = !ipv6_addr_equal(&psl->sl_addr[i], source); 3908c2ecf20Sopenharmony_ci if (rv == 0) 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci if (rv) /* source not found */ 3948c2ecf20Sopenharmony_ci goto done; /* err = -EADDRNOTAVAIL */ 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 3978c2ecf20Sopenharmony_ci if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { 3988c2ecf20Sopenharmony_ci leavegroup = 1; 3998c2ecf20Sopenharmony_ci goto done; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* update the interface filter */ 4038c2ecf20Sopenharmony_ci ip6_mc_del_src(idev, group, omode, 1, source, 1); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci for (j = i+1; j < psl->sl_count; j++) 4068c2ecf20Sopenharmony_ci psl->sl_addr[j-1] = psl->sl_addr[j]; 4078c2ecf20Sopenharmony_ci psl->sl_count--; 4088c2ecf20Sopenharmony_ci err = 0; 4098c2ecf20Sopenharmony_ci goto done; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci /* else, add a new source to the filter */ 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (psl && psl->sl_count >= sysctl_mld_max_msf) { 4148c2ecf20Sopenharmony_ci err = -ENOBUFS; 4158c2ecf20Sopenharmony_ci goto done; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci if (!psl || psl->sl_count == psl->sl_max) { 4188c2ecf20Sopenharmony_ci struct ip6_sf_socklist *newpsl; 4198c2ecf20Sopenharmony_ci int count = IP6_SFBLOCK; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (psl) 4228c2ecf20Sopenharmony_ci count += psl->sl_max; 4238c2ecf20Sopenharmony_ci newpsl = sock_kmalloc(sk, IP6_SFLSIZE(count), GFP_ATOMIC); 4248c2ecf20Sopenharmony_ci if (!newpsl) { 4258c2ecf20Sopenharmony_ci err = -ENOBUFS; 4268c2ecf20Sopenharmony_ci goto done; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci newpsl->sl_max = count; 4298c2ecf20Sopenharmony_ci newpsl->sl_count = count - IP6_SFBLOCK; 4308c2ecf20Sopenharmony_ci if (psl) { 4318c2ecf20Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) 4328c2ecf20Sopenharmony_ci newpsl->sl_addr[i] = psl->sl_addr[i]; 4338c2ecf20Sopenharmony_ci sock_kfree_s(sk, psl, IP6_SFLSIZE(psl->sl_max)); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci pmc->sflist = psl = newpsl; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci rv = 1; /* > 0 for insert logic below if sl_count is 0 */ 4388c2ecf20Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) { 4398c2ecf20Sopenharmony_ci rv = !ipv6_addr_equal(&psl->sl_addr[i], source); 4408c2ecf20Sopenharmony_ci if (rv == 0) /* There is an error in the address. */ 4418c2ecf20Sopenharmony_ci goto done; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci for (j = psl->sl_count-1; j >= i; j--) 4448c2ecf20Sopenharmony_ci psl->sl_addr[j+1] = psl->sl_addr[j]; 4458c2ecf20Sopenharmony_ci psl->sl_addr[i] = *source; 4468c2ecf20Sopenharmony_ci psl->sl_count++; 4478c2ecf20Sopenharmony_ci err = 0; 4488c2ecf20Sopenharmony_ci /* update the interface list */ 4498c2ecf20Sopenharmony_ci ip6_mc_add_src(idev, group, omode, 1, source, 1); 4508c2ecf20Sopenharmony_cidone: 4518c2ecf20Sopenharmony_ci if (pmclocked) 4528c2ecf20Sopenharmony_ci write_unlock(&pmc->sflock); 4538c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 4548c2ecf20Sopenharmony_ci rcu_read_unlock(); 4558c2ecf20Sopenharmony_ci if (leavegroup) 4568c2ecf20Sopenharmony_ci err = ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group); 4578c2ecf20Sopenharmony_ci return err; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ciint ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf, 4618c2ecf20Sopenharmony_ci struct sockaddr_storage *list) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci const struct in6_addr *group; 4648c2ecf20Sopenharmony_ci struct ipv6_mc_socklist *pmc; 4658c2ecf20Sopenharmony_ci struct inet6_dev *idev; 4668c2ecf20Sopenharmony_ci struct ipv6_pinfo *inet6 = inet6_sk(sk); 4678c2ecf20Sopenharmony_ci struct ip6_sf_socklist *newpsl, *psl; 4688c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 4698c2ecf20Sopenharmony_ci int leavegroup = 0; 4708c2ecf20Sopenharmony_ci int i, err; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (!ipv6_addr_is_multicast(group)) 4758c2ecf20Sopenharmony_ci return -EINVAL; 4768c2ecf20Sopenharmony_ci if (gsf->gf_fmode != MCAST_INCLUDE && 4778c2ecf20Sopenharmony_ci gsf->gf_fmode != MCAST_EXCLUDE) 4788c2ecf20Sopenharmony_ci return -EINVAL; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci rcu_read_lock(); 4818c2ecf20Sopenharmony_ci idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!idev) { 4848c2ecf20Sopenharmony_ci rcu_read_unlock(); 4858c2ecf20Sopenharmony_ci return -ENODEV; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci err = 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) { 4918c2ecf20Sopenharmony_ci leavegroup = 1; 4928c2ecf20Sopenharmony_ci goto done; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci for_each_pmc_rcu(inet6, pmc) { 4968c2ecf20Sopenharmony_ci if (pmc->ifindex != gsf->gf_interface) 4978c2ecf20Sopenharmony_ci continue; 4988c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&pmc->addr, group)) 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci if (!pmc) { /* must have a prior join */ 5028c2ecf20Sopenharmony_ci err = -EINVAL; 5038c2ecf20Sopenharmony_ci goto done; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci if (gsf->gf_numsrc) { 5068c2ecf20Sopenharmony_ci newpsl = sock_kmalloc(sk, IP6_SFLSIZE(gsf->gf_numsrc), 5078c2ecf20Sopenharmony_ci GFP_ATOMIC); 5088c2ecf20Sopenharmony_ci if (!newpsl) { 5098c2ecf20Sopenharmony_ci err = -ENOBUFS; 5108c2ecf20Sopenharmony_ci goto done; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci newpsl->sl_max = newpsl->sl_count = gsf->gf_numsrc; 5138c2ecf20Sopenharmony_ci for (i = 0; i < newpsl->sl_count; ++i, ++list) { 5148c2ecf20Sopenharmony_ci struct sockaddr_in6 *psin6; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci psin6 = (struct sockaddr_in6 *)list; 5178c2ecf20Sopenharmony_ci newpsl->sl_addr[i] = psin6->sin6_addr; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci err = ip6_mc_add_src(idev, group, gsf->gf_fmode, 5208c2ecf20Sopenharmony_ci newpsl->sl_count, newpsl->sl_addr, 0); 5218c2ecf20Sopenharmony_ci if (err) { 5228c2ecf20Sopenharmony_ci sock_kfree_s(sk, newpsl, IP6_SFLSIZE(newpsl->sl_max)); 5238c2ecf20Sopenharmony_ci goto done; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci } else { 5268c2ecf20Sopenharmony_ci newpsl = NULL; 5278c2ecf20Sopenharmony_ci (void) ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0); 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci write_lock(&pmc->sflock); 5318c2ecf20Sopenharmony_ci psl = pmc->sflist; 5328c2ecf20Sopenharmony_ci if (psl) { 5338c2ecf20Sopenharmony_ci (void) ip6_mc_del_src(idev, group, pmc->sfmode, 5348c2ecf20Sopenharmony_ci psl->sl_count, psl->sl_addr, 0); 5358c2ecf20Sopenharmony_ci sock_kfree_s(sk, psl, IP6_SFLSIZE(psl->sl_max)); 5368c2ecf20Sopenharmony_ci } else 5378c2ecf20Sopenharmony_ci (void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); 5388c2ecf20Sopenharmony_ci pmc->sflist = newpsl; 5398c2ecf20Sopenharmony_ci pmc->sfmode = gsf->gf_fmode; 5408c2ecf20Sopenharmony_ci write_unlock(&pmc->sflock); 5418c2ecf20Sopenharmony_ci err = 0; 5428c2ecf20Sopenharmony_cidone: 5438c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 5448c2ecf20Sopenharmony_ci rcu_read_unlock(); 5458c2ecf20Sopenharmony_ci if (leavegroup) 5468c2ecf20Sopenharmony_ci err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group); 5478c2ecf20Sopenharmony_ci return err; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ciint ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, 5518c2ecf20Sopenharmony_ci struct sockaddr_storage *p) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci int err, i, count, copycount; 5548c2ecf20Sopenharmony_ci const struct in6_addr *group; 5558c2ecf20Sopenharmony_ci struct ipv6_mc_socklist *pmc; 5568c2ecf20Sopenharmony_ci struct inet6_dev *idev; 5578c2ecf20Sopenharmony_ci struct ipv6_pinfo *inet6 = inet6_sk(sk); 5588c2ecf20Sopenharmony_ci struct ip6_sf_socklist *psl; 5598c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (!ipv6_addr_is_multicast(group)) 5648c2ecf20Sopenharmony_ci return -EINVAL; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci rcu_read_lock(); 5678c2ecf20Sopenharmony_ci idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!idev) { 5708c2ecf20Sopenharmony_ci rcu_read_unlock(); 5718c2ecf20Sopenharmony_ci return -ENODEV; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci err = -EADDRNOTAVAIL; 5758c2ecf20Sopenharmony_ci /* changes to the ipv6_mc_list require the socket lock and 5768c2ecf20Sopenharmony_ci * rtnl lock. We have the socket lock and rcu read lock, 5778c2ecf20Sopenharmony_ci * so reading the list is safe. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci for_each_pmc_rcu(inet6, pmc) { 5818c2ecf20Sopenharmony_ci if (pmc->ifindex != gsf->gf_interface) 5828c2ecf20Sopenharmony_ci continue; 5838c2ecf20Sopenharmony_ci if (ipv6_addr_equal(group, &pmc->addr)) 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci if (!pmc) /* must have a prior join */ 5878c2ecf20Sopenharmony_ci goto done; 5888c2ecf20Sopenharmony_ci gsf->gf_fmode = pmc->sfmode; 5898c2ecf20Sopenharmony_ci psl = pmc->sflist; 5908c2ecf20Sopenharmony_ci count = psl ? psl->sl_count : 0; 5918c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 5928c2ecf20Sopenharmony_ci rcu_read_unlock(); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; 5958c2ecf20Sopenharmony_ci gsf->gf_numsrc = count; 5968c2ecf20Sopenharmony_ci /* changes to psl require the socket lock, and a write lock 5978c2ecf20Sopenharmony_ci * on pmc->sflock. We have the socket lock so reading here is safe. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci for (i = 0; i < copycount; i++, p++) { 6008c2ecf20Sopenharmony_ci struct sockaddr_in6 *psin6; 6018c2ecf20Sopenharmony_ci struct sockaddr_storage ss; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci psin6 = (struct sockaddr_in6 *)&ss; 6048c2ecf20Sopenharmony_ci memset(&ss, 0, sizeof(ss)); 6058c2ecf20Sopenharmony_ci psin6->sin6_family = AF_INET6; 6068c2ecf20Sopenharmony_ci psin6->sin6_addr = psl->sl_addr[i]; 6078c2ecf20Sopenharmony_ci if (copy_to_user(p, &ss, sizeof(ss))) 6088c2ecf20Sopenharmony_ci return -EFAULT; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_cidone: 6128c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 6138c2ecf20Sopenharmony_ci rcu_read_unlock(); 6148c2ecf20Sopenharmony_ci return err; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cibool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr, 6188c2ecf20Sopenharmony_ci const struct in6_addr *src_addr) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 6218c2ecf20Sopenharmony_ci struct ipv6_mc_socklist *mc; 6228c2ecf20Sopenharmony_ci struct ip6_sf_socklist *psl; 6238c2ecf20Sopenharmony_ci bool rv = true; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci rcu_read_lock(); 6268c2ecf20Sopenharmony_ci for_each_pmc_rcu(np, mc) { 6278c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&mc->addr, mc_addr)) 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci if (!mc) { 6318c2ecf20Sopenharmony_ci rcu_read_unlock(); 6328c2ecf20Sopenharmony_ci return np->mc_all; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci read_lock(&mc->sflock); 6358c2ecf20Sopenharmony_ci psl = mc->sflist; 6368c2ecf20Sopenharmony_ci if (!psl) { 6378c2ecf20Sopenharmony_ci rv = mc->sfmode == MCAST_EXCLUDE; 6388c2ecf20Sopenharmony_ci } else { 6398c2ecf20Sopenharmony_ci int i; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) { 6428c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&psl->sl_addr[i], src_addr)) 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci if (mc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) 6468c2ecf20Sopenharmony_ci rv = false; 6478c2ecf20Sopenharmony_ci if (mc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) 6488c2ecf20Sopenharmony_ci rv = false; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci read_unlock(&mc->sflock); 6518c2ecf20Sopenharmony_ci rcu_read_unlock(); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci return rv; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic void igmp6_group_added(struct ifmcaddr6 *mc) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct net_device *dev = mc->idev->dev; 6598c2ecf20Sopenharmony_ci char buf[MAX_ADDR_LEN]; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < 6628c2ecf20Sopenharmony_ci IPV6_ADDR_SCOPE_LINKLOCAL) 6638c2ecf20Sopenharmony_ci return; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci spin_lock_bh(&mc->mca_lock); 6668c2ecf20Sopenharmony_ci if (!(mc->mca_flags&MAF_LOADED)) { 6678c2ecf20Sopenharmony_ci mc->mca_flags |= MAF_LOADED; 6688c2ecf20Sopenharmony_ci if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) 6698c2ecf20Sopenharmony_ci dev_mc_add(dev, buf); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci spin_unlock_bh(&mc->mca_lock); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (!(dev->flags & IFF_UP) || (mc->mca_flags & MAF_NOREPORT)) 6748c2ecf20Sopenharmony_ci return; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (mld_in_v1_mode(mc->idev)) { 6778c2ecf20Sopenharmony_ci igmp6_join_group(mc); 6788c2ecf20Sopenharmony_ci return; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci /* else v2 */ 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* Based on RFC3810 6.1, for newly added INCLUDE SSM, we 6838c2ecf20Sopenharmony_ci * should not send filter-mode change record as the mode 6848c2ecf20Sopenharmony_ci * should be from IN() to IN(A). 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci if (mc->mca_sfmode == MCAST_EXCLUDE) 6878c2ecf20Sopenharmony_ci mc->mca_crcount = mc->idev->mc_qrv; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci mld_ifc_event(mc->idev); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic void igmp6_group_dropped(struct ifmcaddr6 *mc) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct net_device *dev = mc->idev->dev; 6958c2ecf20Sopenharmony_ci char buf[MAX_ADDR_LEN]; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < 6988c2ecf20Sopenharmony_ci IPV6_ADDR_SCOPE_LINKLOCAL) 6998c2ecf20Sopenharmony_ci return; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci spin_lock_bh(&mc->mca_lock); 7028c2ecf20Sopenharmony_ci if (mc->mca_flags&MAF_LOADED) { 7038c2ecf20Sopenharmony_ci mc->mca_flags &= ~MAF_LOADED; 7048c2ecf20Sopenharmony_ci if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) 7058c2ecf20Sopenharmony_ci dev_mc_del(dev, buf); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci spin_unlock_bh(&mc->mca_lock); 7098c2ecf20Sopenharmony_ci if (mc->mca_flags & MAF_NOREPORT) 7108c2ecf20Sopenharmony_ci return; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (!mc->idev->dead) 7138c2ecf20Sopenharmony_ci igmp6_leave_group(mc); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci spin_lock_bh(&mc->mca_lock); 7168c2ecf20Sopenharmony_ci if (del_timer(&mc->mca_timer)) 7178c2ecf20Sopenharmony_ci refcount_dec(&mc->mca_refcnt); 7188c2ecf20Sopenharmony_ci spin_unlock_bh(&mc->mca_lock); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/* 7228c2ecf20Sopenharmony_ci * deleted ifmcaddr6 manipulation 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_cistatic void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct ifmcaddr6 *pmc; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* this is an "ifmcaddr6" for convenience; only the fields below 7298c2ecf20Sopenharmony_ci * are actually used. In particular, the refcnt and users are not 7308c2ecf20Sopenharmony_ci * used for management of the delete list. Using the same structure 7318c2ecf20Sopenharmony_ci * for deleted items allows change reports to use common code with 7328c2ecf20Sopenharmony_ci * non-deleted or query-response MCA's. 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_ci pmc = kzalloc(sizeof(*pmc), GFP_ATOMIC); 7358c2ecf20Sopenharmony_ci if (!pmc) 7368c2ecf20Sopenharmony_ci return; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci spin_lock_bh(&im->mca_lock); 7398c2ecf20Sopenharmony_ci spin_lock_init(&pmc->mca_lock); 7408c2ecf20Sopenharmony_ci pmc->idev = im->idev; 7418c2ecf20Sopenharmony_ci in6_dev_hold(idev); 7428c2ecf20Sopenharmony_ci pmc->mca_addr = im->mca_addr; 7438c2ecf20Sopenharmony_ci pmc->mca_crcount = idev->mc_qrv; 7448c2ecf20Sopenharmony_ci pmc->mca_sfmode = im->mca_sfmode; 7458c2ecf20Sopenharmony_ci if (pmc->mca_sfmode == MCAST_INCLUDE) { 7468c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci pmc->mca_tomb = im->mca_tomb; 7498c2ecf20Sopenharmony_ci pmc->mca_sources = im->mca_sources; 7508c2ecf20Sopenharmony_ci im->mca_tomb = im->mca_sources = NULL; 7518c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) 7528c2ecf20Sopenharmony_ci psf->sf_crcount = pmc->mca_crcount; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci spin_unlock_bh(&im->mca_lock); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci spin_lock_bh(&idev->mc_lock); 7578c2ecf20Sopenharmony_ci pmc->next = idev->mc_tomb; 7588c2ecf20Sopenharmony_ci idev->mc_tomb = pmc; 7598c2ecf20Sopenharmony_ci spin_unlock_bh(&idev->mc_lock); 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci struct ifmcaddr6 *pmc, *pmc_prev; 7658c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 7668c2ecf20Sopenharmony_ci struct in6_addr *pmca = &im->mca_addr; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci spin_lock_bh(&idev->mc_lock); 7698c2ecf20Sopenharmony_ci pmc_prev = NULL; 7708c2ecf20Sopenharmony_ci for (pmc = idev->mc_tomb; pmc; pmc = pmc->next) { 7718c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&pmc->mca_addr, pmca)) 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci pmc_prev = pmc; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci if (pmc) { 7768c2ecf20Sopenharmony_ci if (pmc_prev) 7778c2ecf20Sopenharmony_ci pmc_prev->next = pmc->next; 7788c2ecf20Sopenharmony_ci else 7798c2ecf20Sopenharmony_ci idev->mc_tomb = pmc->next; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci spin_unlock_bh(&idev->mc_lock); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci spin_lock_bh(&im->mca_lock); 7848c2ecf20Sopenharmony_ci if (pmc) { 7858c2ecf20Sopenharmony_ci im->idev = pmc->idev; 7868c2ecf20Sopenharmony_ci if (im->mca_sfmode == MCAST_INCLUDE) { 7878c2ecf20Sopenharmony_ci swap(im->mca_tomb, pmc->mca_tomb); 7888c2ecf20Sopenharmony_ci swap(im->mca_sources, pmc->mca_sources); 7898c2ecf20Sopenharmony_ci for (psf = im->mca_sources; psf; psf = psf->sf_next) 7908c2ecf20Sopenharmony_ci psf->sf_crcount = idev->mc_qrv; 7918c2ecf20Sopenharmony_ci } else { 7928c2ecf20Sopenharmony_ci im->mca_crcount = idev->mc_qrv; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci in6_dev_put(pmc->idev); 7958c2ecf20Sopenharmony_ci ip6_mc_clear_src(pmc); 7968c2ecf20Sopenharmony_ci kfree(pmc); 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci spin_unlock_bh(&im->mca_lock); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic void mld_clear_delrec(struct inet6_dev *idev) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci struct ifmcaddr6 *pmc, *nextpmc; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci spin_lock_bh(&idev->mc_lock); 8068c2ecf20Sopenharmony_ci pmc = idev->mc_tomb; 8078c2ecf20Sopenharmony_ci idev->mc_tomb = NULL; 8088c2ecf20Sopenharmony_ci spin_unlock_bh(&idev->mc_lock); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci for (; pmc; pmc = nextpmc) { 8118c2ecf20Sopenharmony_ci nextpmc = pmc->next; 8128c2ecf20Sopenharmony_ci ip6_mc_clear_src(pmc); 8138c2ecf20Sopenharmony_ci in6_dev_put(pmc->idev); 8148c2ecf20Sopenharmony_ci kfree(pmc); 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* clear dead sources, too */ 8188c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 8198c2ecf20Sopenharmony_ci for (pmc = idev->mc_list; pmc; pmc = pmc->next) { 8208c2ecf20Sopenharmony_ci struct ip6_sf_list *psf, *psf_next; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->mca_lock); 8238c2ecf20Sopenharmony_ci psf = pmc->mca_tomb; 8248c2ecf20Sopenharmony_ci pmc->mca_tomb = NULL; 8258c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->mca_lock); 8268c2ecf20Sopenharmony_ci for (; psf; psf = psf_next) { 8278c2ecf20Sopenharmony_ci psf_next = psf->sf_next; 8288c2ecf20Sopenharmony_ci kfree(psf); 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic void mca_get(struct ifmcaddr6 *mc) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci refcount_inc(&mc->mca_refcnt); 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic void ma_put(struct ifmcaddr6 *mc) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&mc->mca_refcnt)) { 8428c2ecf20Sopenharmony_ci in6_dev_put(mc->idev); 8438c2ecf20Sopenharmony_ci kfree(mc); 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev, 8488c2ecf20Sopenharmony_ci const struct in6_addr *addr, 8498c2ecf20Sopenharmony_ci unsigned int mode) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct ifmcaddr6 *mc; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci mc = kzalloc(sizeof(*mc), GFP_ATOMIC); 8548c2ecf20Sopenharmony_ci if (!mc) 8558c2ecf20Sopenharmony_ci return NULL; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci timer_setup(&mc->mca_timer, igmp6_timer_handler, 0); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci mc->mca_addr = *addr; 8608c2ecf20Sopenharmony_ci mc->idev = idev; /* reference taken by caller */ 8618c2ecf20Sopenharmony_ci mc->mca_users = 1; 8628c2ecf20Sopenharmony_ci /* mca_stamp should be updated upon changes */ 8638c2ecf20Sopenharmony_ci mc->mca_cstamp = mc->mca_tstamp = jiffies; 8648c2ecf20Sopenharmony_ci refcount_set(&mc->mca_refcnt, 1); 8658c2ecf20Sopenharmony_ci spin_lock_init(&mc->mca_lock); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci mc->mca_sfmode = mode; 8688c2ecf20Sopenharmony_ci mc->mca_sfcount[mode] = 1; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (ipv6_addr_is_ll_all_nodes(&mc->mca_addr) || 8718c2ecf20Sopenharmony_ci IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) 8728c2ecf20Sopenharmony_ci mc->mca_flags |= MAF_NOREPORT; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return mc; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci/* 8788c2ecf20Sopenharmony_ci * device multicast group inc (add if not found) 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_cistatic int __ipv6_dev_mc_inc(struct net_device *dev, 8818c2ecf20Sopenharmony_ci const struct in6_addr *addr, unsigned int mode) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct ifmcaddr6 *mc; 8848c2ecf20Sopenharmony_ci struct inet6_dev *idev; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci ASSERT_RTNL(); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci /* we need to take a reference on idev */ 8898c2ecf20Sopenharmony_ci idev = in6_dev_get(dev); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (!idev) 8928c2ecf20Sopenharmony_ci return -EINVAL; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 8958c2ecf20Sopenharmony_ci if (idev->dead) { 8968c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 8978c2ecf20Sopenharmony_ci in6_dev_put(idev); 8988c2ecf20Sopenharmony_ci return -ENODEV; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci for (mc = idev->mc_list; mc; mc = mc->next) { 9028c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&mc->mca_addr, addr)) { 9038c2ecf20Sopenharmony_ci mc->mca_users++; 9048c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 9058c2ecf20Sopenharmony_ci ip6_mc_add_src(idev, &mc->mca_addr, mode, 0, NULL, 0); 9068c2ecf20Sopenharmony_ci in6_dev_put(idev); 9078c2ecf20Sopenharmony_ci return 0; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci mc = mca_alloc(idev, addr, mode); 9128c2ecf20Sopenharmony_ci if (!mc) { 9138c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 9148c2ecf20Sopenharmony_ci in6_dev_put(idev); 9158c2ecf20Sopenharmony_ci return -ENOMEM; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci mc->next = idev->mc_list; 9198c2ecf20Sopenharmony_ci idev->mc_list = mc; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* Hold this for the code below before we unlock, 9228c2ecf20Sopenharmony_ci * it is already exposed via idev->mc_list. 9238c2ecf20Sopenharmony_ci */ 9248c2ecf20Sopenharmony_ci mca_get(mc); 9258c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci mld_del_delrec(idev, mc); 9288c2ecf20Sopenharmony_ci igmp6_group_added(mc); 9298c2ecf20Sopenharmony_ci ma_put(mc); 9308c2ecf20Sopenharmony_ci return 0; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ciint ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci return __ipv6_dev_mc_inc(dev, addr, MCAST_EXCLUDE); 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_mc_inc); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci/* 9408c2ecf20Sopenharmony_ci * device multicast group del 9418c2ecf20Sopenharmony_ci */ 9428c2ecf20Sopenharmony_ciint __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci struct ifmcaddr6 *ma, **map; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci ASSERT_RTNL(); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 9498c2ecf20Sopenharmony_ci for (map = &idev->mc_list; (ma = *map) != NULL; map = &ma->next) { 9508c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&ma->mca_addr, addr)) { 9518c2ecf20Sopenharmony_ci if (--ma->mca_users == 0) { 9528c2ecf20Sopenharmony_ci *map = ma->next; 9538c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci igmp6_group_dropped(ma); 9568c2ecf20Sopenharmony_ci ip6_mc_clear_src(ma); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci ma_put(ma); 9598c2ecf20Sopenharmony_ci return 0; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 9628c2ecf20Sopenharmony_ci return 0; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return -ENOENT; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ciint ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr) 9718c2ecf20Sopenharmony_ci{ 9728c2ecf20Sopenharmony_ci struct inet6_dev *idev; 9738c2ecf20Sopenharmony_ci int err; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci ASSERT_RTNL(); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 9788c2ecf20Sopenharmony_ci if (!idev) 9798c2ecf20Sopenharmony_ci err = -ENODEV; 9808c2ecf20Sopenharmony_ci else 9818c2ecf20Sopenharmony_ci err = __ipv6_dev_mc_dec(idev, addr); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci return err; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_mc_dec); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci/* 9888c2ecf20Sopenharmony_ci * check if the interface/address pair is valid 9898c2ecf20Sopenharmony_ci */ 9908c2ecf20Sopenharmony_cibool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, 9918c2ecf20Sopenharmony_ci const struct in6_addr *src_addr) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci struct inet6_dev *idev; 9948c2ecf20Sopenharmony_ci struct ifmcaddr6 *mc; 9958c2ecf20Sopenharmony_ci bool rv = false; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci rcu_read_lock(); 9988c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 9998c2ecf20Sopenharmony_ci if (idev) { 10008c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 10018c2ecf20Sopenharmony_ci for (mc = idev->mc_list; mc; mc = mc->next) { 10028c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&mc->mca_addr, group)) 10038c2ecf20Sopenharmony_ci break; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci if (mc) { 10068c2ecf20Sopenharmony_ci if (src_addr && !ipv6_addr_any(src_addr)) { 10078c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci spin_lock_bh(&mc->mca_lock); 10108c2ecf20Sopenharmony_ci for (psf = mc->mca_sources; psf; psf = psf->sf_next) { 10118c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&psf->sf_addr, src_addr)) 10128c2ecf20Sopenharmony_ci break; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci if (psf) 10158c2ecf20Sopenharmony_ci rv = psf->sf_count[MCAST_INCLUDE] || 10168c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE] != 10178c2ecf20Sopenharmony_ci mc->mca_sfcount[MCAST_EXCLUDE]; 10188c2ecf20Sopenharmony_ci else 10198c2ecf20Sopenharmony_ci rv = mc->mca_sfcount[MCAST_EXCLUDE] != 0; 10208c2ecf20Sopenharmony_ci spin_unlock_bh(&mc->mca_lock); 10218c2ecf20Sopenharmony_ci } else 10228c2ecf20Sopenharmony_ci rv = true; /* don't filter unspecified source */ 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci rcu_read_unlock(); 10278c2ecf20Sopenharmony_ci return rv; 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic void mld_gq_start_timer(struct inet6_dev *idev) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci unsigned long tv = prandom_u32() % idev->mc_maxdelay; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci idev->mc_gq_running = 1; 10358c2ecf20Sopenharmony_ci if (!mod_timer(&idev->mc_gq_timer, jiffies+tv+2)) 10368c2ecf20Sopenharmony_ci in6_dev_hold(idev); 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic void mld_gq_stop_timer(struct inet6_dev *idev) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci idev->mc_gq_running = 0; 10428c2ecf20Sopenharmony_ci if (del_timer(&idev->mc_gq_timer)) 10438c2ecf20Sopenharmony_ci __in6_dev_put(idev); 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic void mld_ifc_start_timer(struct inet6_dev *idev, unsigned long delay) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci unsigned long tv = prandom_u32() % delay; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (!mod_timer(&idev->mc_ifc_timer, jiffies+tv+2)) 10518c2ecf20Sopenharmony_ci in6_dev_hold(idev); 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_cistatic void mld_ifc_stop_timer(struct inet6_dev *idev) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci idev->mc_ifc_count = 0; 10578c2ecf20Sopenharmony_ci if (del_timer(&idev->mc_ifc_timer)) 10588c2ecf20Sopenharmony_ci __in6_dev_put(idev); 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cistatic void mld_dad_start_timer(struct inet6_dev *idev, unsigned long delay) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci unsigned long tv = prandom_u32() % delay; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2)) 10668c2ecf20Sopenharmony_ci in6_dev_hold(idev); 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic void mld_dad_stop_timer(struct inet6_dev *idev) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci if (del_timer(&idev->mc_dad_timer)) 10728c2ecf20Sopenharmony_ci __in6_dev_put(idev); 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci/* 10768c2ecf20Sopenharmony_ci * IGMP handling (alias multicast ICMPv6 messages) 10778c2ecf20Sopenharmony_ci */ 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_cistatic void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci unsigned long delay = resptime; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* Do not start timer for these addresses */ 10848c2ecf20Sopenharmony_ci if (ipv6_addr_is_ll_all_nodes(&ma->mca_addr) || 10858c2ecf20Sopenharmony_ci IPV6_ADDR_MC_SCOPE(&ma->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) 10868c2ecf20Sopenharmony_ci return; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (del_timer(&ma->mca_timer)) { 10898c2ecf20Sopenharmony_ci refcount_dec(&ma->mca_refcnt); 10908c2ecf20Sopenharmony_ci delay = ma->mca_timer.expires - jiffies; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (delay >= resptime) 10948c2ecf20Sopenharmony_ci delay = prandom_u32() % resptime; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci ma->mca_timer.expires = jiffies + delay; 10978c2ecf20Sopenharmony_ci if (!mod_timer(&ma->mca_timer, jiffies + delay)) 10988c2ecf20Sopenharmony_ci refcount_inc(&ma->mca_refcnt); 10998c2ecf20Sopenharmony_ci ma->mca_flags |= MAF_TIMER_RUNNING; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci/* mark EXCLUDE-mode sources */ 11038c2ecf20Sopenharmony_cistatic bool mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs, 11048c2ecf20Sopenharmony_ci const struct in6_addr *srcs) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 11078c2ecf20Sopenharmony_ci int i, scount; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci scount = 0; 11108c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) { 11118c2ecf20Sopenharmony_ci if (scount == nsrcs) 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci for (i = 0; i < nsrcs; i++) { 11148c2ecf20Sopenharmony_ci /* skip inactive filters */ 11158c2ecf20Sopenharmony_ci if (psf->sf_count[MCAST_INCLUDE] || 11168c2ecf20Sopenharmony_ci pmc->mca_sfcount[MCAST_EXCLUDE] != 11178c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]) 11188c2ecf20Sopenharmony_ci break; 11198c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&srcs[i], &psf->sf_addr)) { 11208c2ecf20Sopenharmony_ci scount++; 11218c2ecf20Sopenharmony_ci break; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci pmc->mca_flags &= ~MAF_GSQUERY; 11268c2ecf20Sopenharmony_ci if (scount == nsrcs) /* all sources excluded */ 11278c2ecf20Sopenharmony_ci return false; 11288c2ecf20Sopenharmony_ci return true; 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic bool mld_marksources(struct ifmcaddr6 *pmc, int nsrcs, 11328c2ecf20Sopenharmony_ci const struct in6_addr *srcs) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 11358c2ecf20Sopenharmony_ci int i, scount; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (pmc->mca_sfmode == MCAST_EXCLUDE) 11388c2ecf20Sopenharmony_ci return mld_xmarksources(pmc, nsrcs, srcs); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci /* mark INCLUDE-mode sources */ 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci scount = 0; 11438c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) { 11448c2ecf20Sopenharmony_ci if (scount == nsrcs) 11458c2ecf20Sopenharmony_ci break; 11468c2ecf20Sopenharmony_ci for (i = 0; i < nsrcs; i++) { 11478c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&srcs[i], &psf->sf_addr)) { 11488c2ecf20Sopenharmony_ci psf->sf_gsresp = 1; 11498c2ecf20Sopenharmony_ci scount++; 11508c2ecf20Sopenharmony_ci break; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci if (!scount) { 11558c2ecf20Sopenharmony_ci pmc->mca_flags &= ~MAF_GSQUERY; 11568c2ecf20Sopenharmony_ci return false; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci pmc->mca_flags |= MAF_GSQUERY; 11598c2ecf20Sopenharmony_ci return true; 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic int mld_force_mld_version(const struct inet6_dev *idev) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci /* Normally, both are 0 here. If enforcement to a particular is 11658c2ecf20Sopenharmony_ci * being used, individual device enforcement will have a lower 11668c2ecf20Sopenharmony_ci * precedence over 'all' device (.../conf/all/force_mld_version). 11678c2ecf20Sopenharmony_ci */ 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (dev_net(idev->dev)->ipv6.devconf_all->force_mld_version != 0) 11708c2ecf20Sopenharmony_ci return dev_net(idev->dev)->ipv6.devconf_all->force_mld_version; 11718c2ecf20Sopenharmony_ci else 11728c2ecf20Sopenharmony_ci return idev->cnf.force_mld_version; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic bool mld_in_v2_mode_only(const struct inet6_dev *idev) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci return mld_force_mld_version(idev) == 2; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic bool mld_in_v1_mode_only(const struct inet6_dev *idev) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci return mld_force_mld_version(idev) == 1; 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic bool mld_in_v1_mode(const struct inet6_dev *idev) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci if (mld_in_v2_mode_only(idev)) 11888c2ecf20Sopenharmony_ci return false; 11898c2ecf20Sopenharmony_ci if (mld_in_v1_mode_only(idev)) 11908c2ecf20Sopenharmony_ci return true; 11918c2ecf20Sopenharmony_ci if (idev->mc_v1_seen && time_before(jiffies, idev->mc_v1_seen)) 11928c2ecf20Sopenharmony_ci return true; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci return false; 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_cistatic void mld_set_v1_mode(struct inet6_dev *idev) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci /* RFC3810, relevant sections: 12008c2ecf20Sopenharmony_ci * - 9.1. Robustness Variable 12018c2ecf20Sopenharmony_ci * - 9.2. Query Interval 12028c2ecf20Sopenharmony_ci * - 9.3. Query Response Interval 12038c2ecf20Sopenharmony_ci * - 9.12. Older Version Querier Present Timeout 12048c2ecf20Sopenharmony_ci */ 12058c2ecf20Sopenharmony_ci unsigned long switchback; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci switchback = (idev->mc_qrv * idev->mc_qi) + idev->mc_qri; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci idev->mc_v1_seen = jiffies + switchback; 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_cistatic void mld_update_qrv(struct inet6_dev *idev, 12138c2ecf20Sopenharmony_ci const struct mld2_query *mlh2) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci /* RFC3810, relevant sections: 12168c2ecf20Sopenharmony_ci * - 5.1.8. QRV (Querier's Robustness Variable) 12178c2ecf20Sopenharmony_ci * - 9.1. Robustness Variable 12188c2ecf20Sopenharmony_ci */ 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* The value of the Robustness Variable MUST NOT be zero, 12218c2ecf20Sopenharmony_ci * and SHOULD NOT be one. Catch this here if we ever run 12228c2ecf20Sopenharmony_ci * into such a case in future. 12238c2ecf20Sopenharmony_ci */ 12248c2ecf20Sopenharmony_ci const int min_qrv = min(MLD_QRV_DEFAULT, sysctl_mld_qrv); 12258c2ecf20Sopenharmony_ci WARN_ON(idev->mc_qrv == 0); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (mlh2->mld2q_qrv > 0) 12288c2ecf20Sopenharmony_ci idev->mc_qrv = mlh2->mld2q_qrv; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci if (unlikely(idev->mc_qrv < min_qrv)) { 12318c2ecf20Sopenharmony_ci net_warn_ratelimited("IPv6: MLD: clamping QRV from %u to %u!\n", 12328c2ecf20Sopenharmony_ci idev->mc_qrv, min_qrv); 12338c2ecf20Sopenharmony_ci idev->mc_qrv = min_qrv; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic void mld_update_qi(struct inet6_dev *idev, 12388c2ecf20Sopenharmony_ci const struct mld2_query *mlh2) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci /* RFC3810, relevant sections: 12418c2ecf20Sopenharmony_ci * - 5.1.9. QQIC (Querier's Query Interval Code) 12428c2ecf20Sopenharmony_ci * - 9.2. Query Interval 12438c2ecf20Sopenharmony_ci * - 9.12. Older Version Querier Present Timeout 12448c2ecf20Sopenharmony_ci * (the [Query Interval] in the last Query received) 12458c2ecf20Sopenharmony_ci */ 12468c2ecf20Sopenharmony_ci unsigned long mc_qqi; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci if (mlh2->mld2q_qqic < 128) { 12498c2ecf20Sopenharmony_ci mc_qqi = mlh2->mld2q_qqic; 12508c2ecf20Sopenharmony_ci } else { 12518c2ecf20Sopenharmony_ci unsigned long mc_man, mc_exp; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci mc_exp = MLDV2_QQIC_EXP(mlh2->mld2q_qqic); 12548c2ecf20Sopenharmony_ci mc_man = MLDV2_QQIC_MAN(mlh2->mld2q_qqic); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci mc_qqi = (mc_man | 0x10) << (mc_exp + 3); 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci idev->mc_qi = mc_qqi * HZ; 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic void mld_update_qri(struct inet6_dev *idev, 12638c2ecf20Sopenharmony_ci const struct mld2_query *mlh2) 12648c2ecf20Sopenharmony_ci{ 12658c2ecf20Sopenharmony_ci /* RFC3810, relevant sections: 12668c2ecf20Sopenharmony_ci * - 5.1.3. Maximum Response Code 12678c2ecf20Sopenharmony_ci * - 9.3. Query Response Interval 12688c2ecf20Sopenharmony_ci */ 12698c2ecf20Sopenharmony_ci idev->mc_qri = msecs_to_jiffies(mldv2_mrc(mlh2)); 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld, 12738c2ecf20Sopenharmony_ci unsigned long *max_delay, bool v1_query) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci unsigned long mldv1_md; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci /* Ignore v1 queries */ 12788c2ecf20Sopenharmony_ci if (mld_in_v2_mode_only(idev)) 12798c2ecf20Sopenharmony_ci return -EINVAL; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci mldv1_md = ntohs(mld->mld_maxdelay); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci /* When in MLDv1 fallback and a MLDv2 router start-up being 12848c2ecf20Sopenharmony_ci * unaware of current MLDv1 operation, the MRC == MRD mapping 12858c2ecf20Sopenharmony_ci * only works when the exponential algorithm is not being 12868c2ecf20Sopenharmony_ci * used (as MLDv1 is unaware of such things). 12878c2ecf20Sopenharmony_ci * 12888c2ecf20Sopenharmony_ci * According to the RFC author, the MLDv2 implementations 12898c2ecf20Sopenharmony_ci * he's aware of all use a MRC < 32768 on start up queries. 12908c2ecf20Sopenharmony_ci * 12918c2ecf20Sopenharmony_ci * Thus, should we *ever* encounter something else larger 12928c2ecf20Sopenharmony_ci * than that, just assume the maximum possible within our 12938c2ecf20Sopenharmony_ci * reach. 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_ci if (!v1_query) 12968c2ecf20Sopenharmony_ci mldv1_md = min(mldv1_md, MLDV1_MRD_MAX_COMPAT); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci *max_delay = max(msecs_to_jiffies(mldv1_md), 1UL); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci /* MLDv1 router present: we need to go into v1 mode *only* 13018c2ecf20Sopenharmony_ci * when an MLDv1 query is received as per section 9.12. of 13028c2ecf20Sopenharmony_ci * RFC3810! And we know from RFC2710 section 3.7 that MLDv1 13038c2ecf20Sopenharmony_ci * queries MUST be of exactly 24 octets. 13048c2ecf20Sopenharmony_ci */ 13058c2ecf20Sopenharmony_ci if (v1_query) 13068c2ecf20Sopenharmony_ci mld_set_v1_mode(idev); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* cancel MLDv2 report timer */ 13098c2ecf20Sopenharmony_ci mld_gq_stop_timer(idev); 13108c2ecf20Sopenharmony_ci /* cancel the interface change timer */ 13118c2ecf20Sopenharmony_ci mld_ifc_stop_timer(idev); 13128c2ecf20Sopenharmony_ci /* clear deleted report items */ 13138c2ecf20Sopenharmony_ci mld_clear_delrec(idev); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci return 0; 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_cistatic int mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld, 13198c2ecf20Sopenharmony_ci unsigned long *max_delay) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci *max_delay = max(msecs_to_jiffies(mldv2_mrc(mld)), 1UL); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci mld_update_qrv(idev, mld); 13248c2ecf20Sopenharmony_ci mld_update_qi(idev, mld); 13258c2ecf20Sopenharmony_ci mld_update_qri(idev, mld); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci idev->mc_maxdelay = *max_delay; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci return 0; 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci/* called with rcu_read_lock() */ 13338c2ecf20Sopenharmony_ciint igmp6_event_query(struct sk_buff *skb) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci struct mld2_query *mlh2 = NULL; 13368c2ecf20Sopenharmony_ci struct ifmcaddr6 *ma; 13378c2ecf20Sopenharmony_ci const struct in6_addr *group; 13388c2ecf20Sopenharmony_ci unsigned long max_delay; 13398c2ecf20Sopenharmony_ci struct inet6_dev *idev; 13408c2ecf20Sopenharmony_ci struct mld_msg *mld; 13418c2ecf20Sopenharmony_ci int group_type; 13428c2ecf20Sopenharmony_ci int mark = 0; 13438c2ecf20Sopenharmony_ci int len, err; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct in6_addr))) 13468c2ecf20Sopenharmony_ci return -EINVAL; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* compute payload length excluding extension headers */ 13498c2ecf20Sopenharmony_ci len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr); 13508c2ecf20Sopenharmony_ci len -= skb_network_header_len(skb); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* RFC3810 6.2 13538c2ecf20Sopenharmony_ci * Upon reception of an MLD message that contains a Query, the node 13548c2ecf20Sopenharmony_ci * checks if the source address of the message is a valid link-local 13558c2ecf20Sopenharmony_ci * address, if the Hop Limit is set to 1, and if the Router Alert 13568c2ecf20Sopenharmony_ci * option is present in the Hop-By-Hop Options header of the IPv6 13578c2ecf20Sopenharmony_ci * packet. If any of these checks fails, the packet is dropped. 13588c2ecf20Sopenharmony_ci */ 13598c2ecf20Sopenharmony_ci if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL) || 13608c2ecf20Sopenharmony_ci ipv6_hdr(skb)->hop_limit != 1 || 13618c2ecf20Sopenharmony_ci !(IP6CB(skb)->flags & IP6SKB_ROUTERALERT) || 13628c2ecf20Sopenharmony_ci IP6CB(skb)->ra != htons(IPV6_OPT_ROUTERALERT_MLD)) 13638c2ecf20Sopenharmony_ci return -EINVAL; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 13668c2ecf20Sopenharmony_ci if (!idev) 13678c2ecf20Sopenharmony_ci return 0; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci mld = (struct mld_msg *)icmp6_hdr(skb); 13708c2ecf20Sopenharmony_ci group = &mld->mld_mca; 13718c2ecf20Sopenharmony_ci group_type = ipv6_addr_type(group); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (group_type != IPV6_ADDR_ANY && 13748c2ecf20Sopenharmony_ci !(group_type&IPV6_ADDR_MULTICAST)) 13758c2ecf20Sopenharmony_ci return -EINVAL; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if (len < MLD_V1_QUERY_LEN) { 13788c2ecf20Sopenharmony_ci return -EINVAL; 13798c2ecf20Sopenharmony_ci } else if (len == MLD_V1_QUERY_LEN || mld_in_v1_mode(idev)) { 13808c2ecf20Sopenharmony_ci err = mld_process_v1(idev, mld, &max_delay, 13818c2ecf20Sopenharmony_ci len == MLD_V1_QUERY_LEN); 13828c2ecf20Sopenharmony_ci if (err < 0) 13838c2ecf20Sopenharmony_ci return err; 13848c2ecf20Sopenharmony_ci } else if (len >= MLD_V2_QUERY_LEN_MIN) { 13858c2ecf20Sopenharmony_ci int srcs_offset = sizeof(struct mld2_query) - 13868c2ecf20Sopenharmony_ci sizeof(struct icmp6hdr); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, srcs_offset)) 13898c2ecf20Sopenharmony_ci return -EINVAL; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci mlh2 = (struct mld2_query *)skb_transport_header(skb); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci err = mld_process_v2(idev, mlh2, &max_delay); 13948c2ecf20Sopenharmony_ci if (err < 0) 13958c2ecf20Sopenharmony_ci return err; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (group_type == IPV6_ADDR_ANY) { /* general query */ 13988c2ecf20Sopenharmony_ci if (mlh2->mld2q_nsrcs) 13998c2ecf20Sopenharmony_ci return -EINVAL; /* no sources allowed */ 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci mld_gq_start_timer(idev); 14028c2ecf20Sopenharmony_ci return 0; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci /* mark sources to include, if group & source-specific */ 14058c2ecf20Sopenharmony_ci if (mlh2->mld2q_nsrcs != 0) { 14068c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, srcs_offset + 14078c2ecf20Sopenharmony_ci ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr))) 14088c2ecf20Sopenharmony_ci return -EINVAL; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci mlh2 = (struct mld2_query *)skb_transport_header(skb); 14118c2ecf20Sopenharmony_ci mark = 1; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci } else { 14148c2ecf20Sopenharmony_ci return -EINVAL; 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 14188c2ecf20Sopenharmony_ci if (group_type == IPV6_ADDR_ANY) { 14198c2ecf20Sopenharmony_ci for (ma = idev->mc_list; ma; ma = ma->next) { 14208c2ecf20Sopenharmony_ci spin_lock_bh(&ma->mca_lock); 14218c2ecf20Sopenharmony_ci igmp6_group_queried(ma, max_delay); 14228c2ecf20Sopenharmony_ci spin_unlock_bh(&ma->mca_lock); 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci } else { 14258c2ecf20Sopenharmony_ci for (ma = idev->mc_list; ma; ma = ma->next) { 14268c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(group, &ma->mca_addr)) 14278c2ecf20Sopenharmony_ci continue; 14288c2ecf20Sopenharmony_ci spin_lock_bh(&ma->mca_lock); 14298c2ecf20Sopenharmony_ci if (ma->mca_flags & MAF_TIMER_RUNNING) { 14308c2ecf20Sopenharmony_ci /* gsquery <- gsquery && mark */ 14318c2ecf20Sopenharmony_ci if (!mark) 14328c2ecf20Sopenharmony_ci ma->mca_flags &= ~MAF_GSQUERY; 14338c2ecf20Sopenharmony_ci } else { 14348c2ecf20Sopenharmony_ci /* gsquery <- mark */ 14358c2ecf20Sopenharmony_ci if (mark) 14368c2ecf20Sopenharmony_ci ma->mca_flags |= MAF_GSQUERY; 14378c2ecf20Sopenharmony_ci else 14388c2ecf20Sopenharmony_ci ma->mca_flags &= ~MAF_GSQUERY; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci if (!(ma->mca_flags & MAF_GSQUERY) || 14418c2ecf20Sopenharmony_ci mld_marksources(ma, ntohs(mlh2->mld2q_nsrcs), mlh2->mld2q_srcs)) 14428c2ecf20Sopenharmony_ci igmp6_group_queried(ma, max_delay); 14438c2ecf20Sopenharmony_ci spin_unlock_bh(&ma->mca_lock); 14448c2ecf20Sopenharmony_ci break; 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci return 0; 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci/* called with rcu_read_lock() */ 14538c2ecf20Sopenharmony_ciint igmp6_event_report(struct sk_buff *skb) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct ifmcaddr6 *ma; 14568c2ecf20Sopenharmony_ci struct inet6_dev *idev; 14578c2ecf20Sopenharmony_ci struct mld_msg *mld; 14588c2ecf20Sopenharmony_ci int addr_type; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci /* Our own report looped back. Ignore it. */ 14618c2ecf20Sopenharmony_ci if (skb->pkt_type == PACKET_LOOPBACK) 14628c2ecf20Sopenharmony_ci return 0; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci /* send our report if the MC router may not have heard this report */ 14658c2ecf20Sopenharmony_ci if (skb->pkt_type != PACKET_MULTICAST && 14668c2ecf20Sopenharmony_ci skb->pkt_type != PACKET_BROADCAST) 14678c2ecf20Sopenharmony_ci return 0; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*mld) - sizeof(struct icmp6hdr))) 14708c2ecf20Sopenharmony_ci return -EINVAL; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci mld = (struct mld_msg *)icmp6_hdr(skb); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci /* Drop reports with not link local source */ 14758c2ecf20Sopenharmony_ci addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr); 14768c2ecf20Sopenharmony_ci if (addr_type != IPV6_ADDR_ANY && 14778c2ecf20Sopenharmony_ci !(addr_type&IPV6_ADDR_LINKLOCAL)) 14788c2ecf20Sopenharmony_ci return -EINVAL; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 14818c2ecf20Sopenharmony_ci if (!idev) 14828c2ecf20Sopenharmony_ci return -ENODEV; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci /* 14858c2ecf20Sopenharmony_ci * Cancel the timer for this group 14868c2ecf20Sopenharmony_ci */ 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 14898c2ecf20Sopenharmony_ci for (ma = idev->mc_list; ma; ma = ma->next) { 14908c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) { 14918c2ecf20Sopenharmony_ci spin_lock(&ma->mca_lock); 14928c2ecf20Sopenharmony_ci if (del_timer(&ma->mca_timer)) 14938c2ecf20Sopenharmony_ci refcount_dec(&ma->mca_refcnt); 14948c2ecf20Sopenharmony_ci ma->mca_flags &= ~(MAF_LAST_REPORTER|MAF_TIMER_RUNNING); 14958c2ecf20Sopenharmony_ci spin_unlock(&ma->mca_lock); 14968c2ecf20Sopenharmony_ci break; 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 15008c2ecf20Sopenharmony_ci return 0; 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cistatic bool is_in(struct ifmcaddr6 *pmc, struct ip6_sf_list *psf, int type, 15048c2ecf20Sopenharmony_ci int gdeleted, int sdeleted) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci switch (type) { 15078c2ecf20Sopenharmony_ci case MLD2_MODE_IS_INCLUDE: 15088c2ecf20Sopenharmony_ci case MLD2_MODE_IS_EXCLUDE: 15098c2ecf20Sopenharmony_ci if (gdeleted || sdeleted) 15108c2ecf20Sopenharmony_ci return false; 15118c2ecf20Sopenharmony_ci if (!((pmc->mca_flags & MAF_GSQUERY) && !psf->sf_gsresp)) { 15128c2ecf20Sopenharmony_ci if (pmc->mca_sfmode == MCAST_INCLUDE) 15138c2ecf20Sopenharmony_ci return true; 15148c2ecf20Sopenharmony_ci /* don't include if this source is excluded 15158c2ecf20Sopenharmony_ci * in all filters 15168c2ecf20Sopenharmony_ci */ 15178c2ecf20Sopenharmony_ci if (psf->sf_count[MCAST_INCLUDE]) 15188c2ecf20Sopenharmony_ci return type == MLD2_MODE_IS_INCLUDE; 15198c2ecf20Sopenharmony_ci return pmc->mca_sfcount[MCAST_EXCLUDE] == 15208c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]; 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci return false; 15238c2ecf20Sopenharmony_ci case MLD2_CHANGE_TO_INCLUDE: 15248c2ecf20Sopenharmony_ci if (gdeleted || sdeleted) 15258c2ecf20Sopenharmony_ci return false; 15268c2ecf20Sopenharmony_ci return psf->sf_count[MCAST_INCLUDE] != 0; 15278c2ecf20Sopenharmony_ci case MLD2_CHANGE_TO_EXCLUDE: 15288c2ecf20Sopenharmony_ci if (gdeleted || sdeleted) 15298c2ecf20Sopenharmony_ci return false; 15308c2ecf20Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE] == 0 || 15318c2ecf20Sopenharmony_ci psf->sf_count[MCAST_INCLUDE]) 15328c2ecf20Sopenharmony_ci return false; 15338c2ecf20Sopenharmony_ci return pmc->mca_sfcount[MCAST_EXCLUDE] == 15348c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]; 15358c2ecf20Sopenharmony_ci case MLD2_ALLOW_NEW_SOURCES: 15368c2ecf20Sopenharmony_ci if (gdeleted || !psf->sf_crcount) 15378c2ecf20Sopenharmony_ci return false; 15388c2ecf20Sopenharmony_ci return (pmc->mca_sfmode == MCAST_INCLUDE) ^ sdeleted; 15398c2ecf20Sopenharmony_ci case MLD2_BLOCK_OLD_SOURCES: 15408c2ecf20Sopenharmony_ci if (pmc->mca_sfmode == MCAST_INCLUDE) 15418c2ecf20Sopenharmony_ci return gdeleted || (psf->sf_crcount && sdeleted); 15428c2ecf20Sopenharmony_ci return psf->sf_crcount && !gdeleted && !sdeleted; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci return false; 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic int 15488c2ecf20Sopenharmony_cimld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 15518c2ecf20Sopenharmony_ci int scount = 0; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) { 15548c2ecf20Sopenharmony_ci if (!is_in(pmc, psf, type, gdeleted, sdeleted)) 15558c2ecf20Sopenharmony_ci continue; 15568c2ecf20Sopenharmony_ci scount++; 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci return scount; 15598c2ecf20Sopenharmony_ci} 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_cistatic void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb, 15628c2ecf20Sopenharmony_ci struct net_device *dev, 15638c2ecf20Sopenharmony_ci const struct in6_addr *saddr, 15648c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 15658c2ecf20Sopenharmony_ci int proto, int len) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct ipv6hdr *hdr; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 15708c2ecf20Sopenharmony_ci skb->dev = dev; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 15738c2ecf20Sopenharmony_ci skb_put(skb, sizeof(struct ipv6hdr)); 15748c2ecf20Sopenharmony_ci hdr = ipv6_hdr(skb); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci ip6_flow_hdr(hdr, 0, 0); 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci hdr->payload_len = htons(len); 15798c2ecf20Sopenharmony_ci hdr->nexthdr = proto; 15808c2ecf20Sopenharmony_ci hdr->hop_limit = inet6_sk(sk)->hop_limit; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci hdr->saddr = *saddr; 15838c2ecf20Sopenharmony_ci hdr->daddr = *daddr; 15848c2ecf20Sopenharmony_ci} 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_cistatic struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci struct net_device *dev = idev->dev; 15898c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 15908c2ecf20Sopenharmony_ci struct sock *sk = net->ipv6.igmp_sk; 15918c2ecf20Sopenharmony_ci struct sk_buff *skb; 15928c2ecf20Sopenharmony_ci struct mld2_report *pmr; 15938c2ecf20Sopenharmony_ci struct in6_addr addr_buf; 15948c2ecf20Sopenharmony_ci const struct in6_addr *saddr; 15958c2ecf20Sopenharmony_ci int hlen = LL_RESERVED_SPACE(dev); 15968c2ecf20Sopenharmony_ci int tlen = dev->needed_tailroom; 15978c2ecf20Sopenharmony_ci unsigned int size = mtu + hlen + tlen; 15988c2ecf20Sopenharmony_ci int err; 15998c2ecf20Sopenharmony_ci u8 ra[8] = { IPPROTO_ICMPV6, 0, 16008c2ecf20Sopenharmony_ci IPV6_TLV_ROUTERALERT, 2, 0, 0, 16018c2ecf20Sopenharmony_ci IPV6_TLV_PADN, 0 }; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci /* we assume size > sizeof(ra) here */ 16048c2ecf20Sopenharmony_ci skb = sock_alloc_send_skb(sk, size, 1, &err); 16058c2ecf20Sopenharmony_ci if (!skb) 16068c2ecf20Sopenharmony_ci return NULL; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci skb->priority = TC_PRIO_CONTROL; 16098c2ecf20Sopenharmony_ci skb_reserve(skb, hlen); 16108c2ecf20Sopenharmony_ci skb_tailroom_reserve(skb, mtu, tlen); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) { 16138c2ecf20Sopenharmony_ci /* <draft-ietf-magma-mld-source-05.txt>: 16148c2ecf20Sopenharmony_ci * use unspecified address as the source address 16158c2ecf20Sopenharmony_ci * when a valid link-local address is not available. 16168c2ecf20Sopenharmony_ci */ 16178c2ecf20Sopenharmony_ci saddr = &in6addr_any; 16188c2ecf20Sopenharmony_ci } else 16198c2ecf20Sopenharmony_ci saddr = &addr_buf; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci ip6_mc_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci skb_put_data(skb, ra, sizeof(ra)); 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci skb_set_transport_header(skb, skb_tail_pointer(skb) - skb->data); 16268c2ecf20Sopenharmony_ci skb_put(skb, sizeof(*pmr)); 16278c2ecf20Sopenharmony_ci pmr = (struct mld2_report *)skb_transport_header(skb); 16288c2ecf20Sopenharmony_ci pmr->mld2r_type = ICMPV6_MLD2_REPORT; 16298c2ecf20Sopenharmony_ci pmr->mld2r_resv1 = 0; 16308c2ecf20Sopenharmony_ci pmr->mld2r_cksum = 0; 16318c2ecf20Sopenharmony_ci pmr->mld2r_resv2 = 0; 16328c2ecf20Sopenharmony_ci pmr->mld2r_ngrec = 0; 16338c2ecf20Sopenharmony_ci return skb; 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_cistatic void mld_sendpack(struct sk_buff *skb) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci struct ipv6hdr *pip6 = ipv6_hdr(skb); 16398c2ecf20Sopenharmony_ci struct mld2_report *pmr = 16408c2ecf20Sopenharmony_ci (struct mld2_report *)skb_transport_header(skb); 16418c2ecf20Sopenharmony_ci int payload_len, mldlen; 16428c2ecf20Sopenharmony_ci struct inet6_dev *idev; 16438c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 16448c2ecf20Sopenharmony_ci int err; 16458c2ecf20Sopenharmony_ci struct flowi6 fl6; 16468c2ecf20Sopenharmony_ci struct dst_entry *dst; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci rcu_read_lock(); 16498c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 16508c2ecf20Sopenharmony_ci IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci payload_len = (skb_tail_pointer(skb) - skb_network_header(skb)) - 16538c2ecf20Sopenharmony_ci sizeof(*pip6); 16548c2ecf20Sopenharmony_ci mldlen = skb_tail_pointer(skb) - skb_transport_header(skb); 16558c2ecf20Sopenharmony_ci pip6->payload_len = htons(payload_len); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci pmr->mld2r_cksum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, 16588c2ecf20Sopenharmony_ci IPPROTO_ICMPV6, 16598c2ecf20Sopenharmony_ci csum_partial(skb_transport_header(skb), 16608c2ecf20Sopenharmony_ci mldlen, 0)); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci icmpv6_flow_init(net->ipv6.igmp_sk, &fl6, ICMPV6_MLD2_REPORT, 16638c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 16648c2ecf20Sopenharmony_ci skb->dev->ifindex); 16658c2ecf20Sopenharmony_ci dst = icmp6_dst_alloc(skb->dev, &fl6); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci err = 0; 16688c2ecf20Sopenharmony_ci if (IS_ERR(dst)) { 16698c2ecf20Sopenharmony_ci err = PTR_ERR(dst); 16708c2ecf20Sopenharmony_ci dst = NULL; 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 16738c2ecf20Sopenharmony_ci if (err) 16748c2ecf20Sopenharmony_ci goto err_out; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, 16778c2ecf20Sopenharmony_ci net, net->ipv6.igmp_sk, skb, NULL, skb->dev, 16788c2ecf20Sopenharmony_ci dst_output); 16798c2ecf20Sopenharmony_ciout: 16808c2ecf20Sopenharmony_ci if (!err) { 16818c2ecf20Sopenharmony_ci ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT); 16828c2ecf20Sopenharmony_ci ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); 16838c2ecf20Sopenharmony_ci } else { 16848c2ecf20Sopenharmony_ci IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); 16858c2ecf20Sopenharmony_ci } 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci rcu_read_unlock(); 16888c2ecf20Sopenharmony_ci return; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_cierr_out: 16918c2ecf20Sopenharmony_ci kfree_skb(skb); 16928c2ecf20Sopenharmony_ci goto out; 16938c2ecf20Sopenharmony_ci} 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_cistatic int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel) 16968c2ecf20Sopenharmony_ci{ 16978c2ecf20Sopenharmony_ci return sizeof(struct mld2_grec) + 16 * mld_scount(pmc,type,gdel,sdel); 16988c2ecf20Sopenharmony_ci} 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_cistatic struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, 17018c2ecf20Sopenharmony_ci int type, struct mld2_grec **ppgr, unsigned int mtu) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci struct mld2_report *pmr; 17048c2ecf20Sopenharmony_ci struct mld2_grec *pgr; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if (!skb) { 17078c2ecf20Sopenharmony_ci skb = mld_newpack(pmc->idev, mtu); 17088c2ecf20Sopenharmony_ci if (!skb) 17098c2ecf20Sopenharmony_ci return NULL; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci pgr = skb_put(skb, sizeof(struct mld2_grec)); 17128c2ecf20Sopenharmony_ci pgr->grec_type = type; 17138c2ecf20Sopenharmony_ci pgr->grec_auxwords = 0; 17148c2ecf20Sopenharmony_ci pgr->grec_nsrcs = 0; 17158c2ecf20Sopenharmony_ci pgr->grec_mca = pmc->mca_addr; /* structure copy */ 17168c2ecf20Sopenharmony_ci pmr = (struct mld2_report *)skb_transport_header(skb); 17178c2ecf20Sopenharmony_ci pmr->mld2r_ngrec = htons(ntohs(pmr->mld2r_ngrec)+1); 17188c2ecf20Sopenharmony_ci *ppgr = pgr; 17198c2ecf20Sopenharmony_ci return skb; 17208c2ecf20Sopenharmony_ci} 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0) 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_cistatic struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, 17258c2ecf20Sopenharmony_ci int type, int gdeleted, int sdeleted, int crsend) 17268c2ecf20Sopenharmony_ci{ 17278c2ecf20Sopenharmony_ci struct inet6_dev *idev = pmc->idev; 17288c2ecf20Sopenharmony_ci struct net_device *dev = idev->dev; 17298c2ecf20Sopenharmony_ci struct mld2_report *pmr; 17308c2ecf20Sopenharmony_ci struct mld2_grec *pgr = NULL; 17318c2ecf20Sopenharmony_ci struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list; 17328c2ecf20Sopenharmony_ci int scount, stotal, first, isquery, truncate; 17338c2ecf20Sopenharmony_ci unsigned int mtu; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci if (pmc->mca_flags & MAF_NOREPORT) 17368c2ecf20Sopenharmony_ci return skb; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci mtu = READ_ONCE(dev->mtu); 17398c2ecf20Sopenharmony_ci if (mtu < IPV6_MIN_MTU) 17408c2ecf20Sopenharmony_ci return skb; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci isquery = type == MLD2_MODE_IS_INCLUDE || 17438c2ecf20Sopenharmony_ci type == MLD2_MODE_IS_EXCLUDE; 17448c2ecf20Sopenharmony_ci truncate = type == MLD2_MODE_IS_EXCLUDE || 17458c2ecf20Sopenharmony_ci type == MLD2_CHANGE_TO_EXCLUDE; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci stotal = scount = 0; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci psf_list = sdeleted ? &pmc->mca_tomb : &pmc->mca_sources; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci if (!*psf_list) 17528c2ecf20Sopenharmony_ci goto empty_source; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci pmr = skb ? (struct mld2_report *)skb_transport_header(skb) : NULL; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci /* EX and TO_EX get a fresh packet, if needed */ 17578c2ecf20Sopenharmony_ci if (truncate) { 17588c2ecf20Sopenharmony_ci if (pmr && pmr->mld2r_ngrec && 17598c2ecf20Sopenharmony_ci AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { 17608c2ecf20Sopenharmony_ci if (skb) 17618c2ecf20Sopenharmony_ci mld_sendpack(skb); 17628c2ecf20Sopenharmony_ci skb = mld_newpack(idev, mtu); 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci first = 1; 17668c2ecf20Sopenharmony_ci psf_prev = NULL; 17678c2ecf20Sopenharmony_ci for (psf = *psf_list; psf; psf = psf_next) { 17688c2ecf20Sopenharmony_ci struct in6_addr *psrc; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci psf_next = psf->sf_next; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if (!is_in(pmc, psf, type, gdeleted, sdeleted) && !crsend) { 17738c2ecf20Sopenharmony_ci psf_prev = psf; 17748c2ecf20Sopenharmony_ci continue; 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci /* Based on RFC3810 6.1. Should not send source-list change 17788c2ecf20Sopenharmony_ci * records when there is a filter mode change. 17798c2ecf20Sopenharmony_ci */ 17808c2ecf20Sopenharmony_ci if (((gdeleted && pmc->mca_sfmode == MCAST_EXCLUDE) || 17818c2ecf20Sopenharmony_ci (!gdeleted && pmc->mca_crcount)) && 17828c2ecf20Sopenharmony_ci (type == MLD2_ALLOW_NEW_SOURCES || 17838c2ecf20Sopenharmony_ci type == MLD2_BLOCK_OLD_SOURCES) && psf->sf_crcount) 17848c2ecf20Sopenharmony_ci goto decrease_sf_crcount; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci /* clear marks on query responses */ 17878c2ecf20Sopenharmony_ci if (isquery) 17888c2ecf20Sopenharmony_ci psf->sf_gsresp = 0; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci if (AVAILABLE(skb) < sizeof(*psrc) + 17918c2ecf20Sopenharmony_ci first*sizeof(struct mld2_grec)) { 17928c2ecf20Sopenharmony_ci if (truncate && !first) 17938c2ecf20Sopenharmony_ci break; /* truncate these */ 17948c2ecf20Sopenharmony_ci if (pgr) 17958c2ecf20Sopenharmony_ci pgr->grec_nsrcs = htons(scount); 17968c2ecf20Sopenharmony_ci if (skb) 17978c2ecf20Sopenharmony_ci mld_sendpack(skb); 17988c2ecf20Sopenharmony_ci skb = mld_newpack(idev, mtu); 17998c2ecf20Sopenharmony_ci first = 1; 18008c2ecf20Sopenharmony_ci scount = 0; 18018c2ecf20Sopenharmony_ci } 18028c2ecf20Sopenharmony_ci if (first) { 18038c2ecf20Sopenharmony_ci skb = add_grhead(skb, pmc, type, &pgr, mtu); 18048c2ecf20Sopenharmony_ci first = 0; 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci if (!skb) 18078c2ecf20Sopenharmony_ci return NULL; 18088c2ecf20Sopenharmony_ci psrc = skb_put(skb, sizeof(*psrc)); 18098c2ecf20Sopenharmony_ci *psrc = psf->sf_addr; 18108c2ecf20Sopenharmony_ci scount++; stotal++; 18118c2ecf20Sopenharmony_ci if ((type == MLD2_ALLOW_NEW_SOURCES || 18128c2ecf20Sopenharmony_ci type == MLD2_BLOCK_OLD_SOURCES) && psf->sf_crcount) { 18138c2ecf20Sopenharmony_cidecrease_sf_crcount: 18148c2ecf20Sopenharmony_ci psf->sf_crcount--; 18158c2ecf20Sopenharmony_ci if ((sdeleted || gdeleted) && psf->sf_crcount == 0) { 18168c2ecf20Sopenharmony_ci if (psf_prev) 18178c2ecf20Sopenharmony_ci psf_prev->sf_next = psf->sf_next; 18188c2ecf20Sopenharmony_ci else 18198c2ecf20Sopenharmony_ci *psf_list = psf->sf_next; 18208c2ecf20Sopenharmony_ci kfree(psf); 18218c2ecf20Sopenharmony_ci continue; 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci psf_prev = psf; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ciempty_source: 18288c2ecf20Sopenharmony_ci if (!stotal) { 18298c2ecf20Sopenharmony_ci if (type == MLD2_ALLOW_NEW_SOURCES || 18308c2ecf20Sopenharmony_ci type == MLD2_BLOCK_OLD_SOURCES) 18318c2ecf20Sopenharmony_ci return skb; 18328c2ecf20Sopenharmony_ci if (pmc->mca_crcount || isquery || crsend) { 18338c2ecf20Sopenharmony_ci /* make sure we have room for group header */ 18348c2ecf20Sopenharmony_ci if (skb && AVAILABLE(skb) < sizeof(struct mld2_grec)) { 18358c2ecf20Sopenharmony_ci mld_sendpack(skb); 18368c2ecf20Sopenharmony_ci skb = NULL; /* add_grhead will get a new one */ 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci skb = add_grhead(skb, pmc, type, &pgr, mtu); 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci if (pgr) 18428c2ecf20Sopenharmony_ci pgr->grec_nsrcs = htons(scount); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci if (isquery) 18458c2ecf20Sopenharmony_ci pmc->mca_flags &= ~MAF_GSQUERY; /* clear query state */ 18468c2ecf20Sopenharmony_ci return skb; 18478c2ecf20Sopenharmony_ci} 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_cistatic void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) 18508c2ecf20Sopenharmony_ci{ 18518c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 18528c2ecf20Sopenharmony_ci int type; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 18558c2ecf20Sopenharmony_ci if (!pmc) { 18568c2ecf20Sopenharmony_ci for (pmc = idev->mc_list; pmc; pmc = pmc->next) { 18578c2ecf20Sopenharmony_ci if (pmc->mca_flags & MAF_NOREPORT) 18588c2ecf20Sopenharmony_ci continue; 18598c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->mca_lock); 18608c2ecf20Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) 18618c2ecf20Sopenharmony_ci type = MLD2_MODE_IS_EXCLUDE; 18628c2ecf20Sopenharmony_ci else 18638c2ecf20Sopenharmony_ci type = MLD2_MODE_IS_INCLUDE; 18648c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 0); 18658c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->mca_lock); 18668c2ecf20Sopenharmony_ci } 18678c2ecf20Sopenharmony_ci } else { 18688c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->mca_lock); 18698c2ecf20Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) 18708c2ecf20Sopenharmony_ci type = MLD2_MODE_IS_EXCLUDE; 18718c2ecf20Sopenharmony_ci else 18728c2ecf20Sopenharmony_ci type = MLD2_MODE_IS_INCLUDE; 18738c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 0); 18748c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->mca_lock); 18758c2ecf20Sopenharmony_ci } 18768c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 18778c2ecf20Sopenharmony_ci if (skb) 18788c2ecf20Sopenharmony_ci mld_sendpack(skb); 18798c2ecf20Sopenharmony_ci} 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci/* 18828c2ecf20Sopenharmony_ci * remove zero-count source records from a source filter list 18838c2ecf20Sopenharmony_ci */ 18848c2ecf20Sopenharmony_cistatic void mld_clear_zeros(struct ip6_sf_list **ppsf) 18858c2ecf20Sopenharmony_ci{ 18868c2ecf20Sopenharmony_ci struct ip6_sf_list *psf_prev, *psf_next, *psf; 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci psf_prev = NULL; 18898c2ecf20Sopenharmony_ci for (psf = *ppsf; psf; psf = psf_next) { 18908c2ecf20Sopenharmony_ci psf_next = psf->sf_next; 18918c2ecf20Sopenharmony_ci if (psf->sf_crcount == 0) { 18928c2ecf20Sopenharmony_ci if (psf_prev) 18938c2ecf20Sopenharmony_ci psf_prev->sf_next = psf->sf_next; 18948c2ecf20Sopenharmony_ci else 18958c2ecf20Sopenharmony_ci *ppsf = psf->sf_next; 18968c2ecf20Sopenharmony_ci kfree(psf); 18978c2ecf20Sopenharmony_ci } else 18988c2ecf20Sopenharmony_ci psf_prev = psf; 18998c2ecf20Sopenharmony_ci } 19008c2ecf20Sopenharmony_ci} 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_cistatic void mld_send_cr(struct inet6_dev *idev) 19038c2ecf20Sopenharmony_ci{ 19048c2ecf20Sopenharmony_ci struct ifmcaddr6 *pmc, *pmc_prev, *pmc_next; 19058c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 19068c2ecf20Sopenharmony_ci int type, dtype; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 19098c2ecf20Sopenharmony_ci spin_lock(&idev->mc_lock); 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci /* deleted MCA's */ 19128c2ecf20Sopenharmony_ci pmc_prev = NULL; 19138c2ecf20Sopenharmony_ci for (pmc = idev->mc_tomb; pmc; pmc = pmc_next) { 19148c2ecf20Sopenharmony_ci pmc_next = pmc->next; 19158c2ecf20Sopenharmony_ci if (pmc->mca_sfmode == MCAST_INCLUDE) { 19168c2ecf20Sopenharmony_ci type = MLD2_BLOCK_OLD_SOURCES; 19178c2ecf20Sopenharmony_ci dtype = MLD2_BLOCK_OLD_SOURCES; 19188c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 1, 0, 0); 19198c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, dtype, 1, 1, 0); 19208c2ecf20Sopenharmony_ci } 19218c2ecf20Sopenharmony_ci if (pmc->mca_crcount) { 19228c2ecf20Sopenharmony_ci if (pmc->mca_sfmode == MCAST_EXCLUDE) { 19238c2ecf20Sopenharmony_ci type = MLD2_CHANGE_TO_INCLUDE; 19248c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 1, 0, 0); 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci pmc->mca_crcount--; 19278c2ecf20Sopenharmony_ci if (pmc->mca_crcount == 0) { 19288c2ecf20Sopenharmony_ci mld_clear_zeros(&pmc->mca_tomb); 19298c2ecf20Sopenharmony_ci mld_clear_zeros(&pmc->mca_sources); 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci } 19328c2ecf20Sopenharmony_ci if (pmc->mca_crcount == 0 && !pmc->mca_tomb && 19338c2ecf20Sopenharmony_ci !pmc->mca_sources) { 19348c2ecf20Sopenharmony_ci if (pmc_prev) 19358c2ecf20Sopenharmony_ci pmc_prev->next = pmc_next; 19368c2ecf20Sopenharmony_ci else 19378c2ecf20Sopenharmony_ci idev->mc_tomb = pmc_next; 19388c2ecf20Sopenharmony_ci in6_dev_put(pmc->idev); 19398c2ecf20Sopenharmony_ci kfree(pmc); 19408c2ecf20Sopenharmony_ci } else 19418c2ecf20Sopenharmony_ci pmc_prev = pmc; 19428c2ecf20Sopenharmony_ci } 19438c2ecf20Sopenharmony_ci spin_unlock(&idev->mc_lock); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci /* change recs */ 19468c2ecf20Sopenharmony_ci for (pmc = idev->mc_list; pmc; pmc = pmc->next) { 19478c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->mca_lock); 19488c2ecf20Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) { 19498c2ecf20Sopenharmony_ci type = MLD2_BLOCK_OLD_SOURCES; 19508c2ecf20Sopenharmony_ci dtype = MLD2_ALLOW_NEW_SOURCES; 19518c2ecf20Sopenharmony_ci } else { 19528c2ecf20Sopenharmony_ci type = MLD2_ALLOW_NEW_SOURCES; 19538c2ecf20Sopenharmony_ci dtype = MLD2_BLOCK_OLD_SOURCES; 19548c2ecf20Sopenharmony_ci } 19558c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 0); 19568c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, dtype, 0, 1, 0); /* deleted sources */ 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci /* filter mode changes */ 19598c2ecf20Sopenharmony_ci if (pmc->mca_crcount) { 19608c2ecf20Sopenharmony_ci if (pmc->mca_sfmode == MCAST_EXCLUDE) 19618c2ecf20Sopenharmony_ci type = MLD2_CHANGE_TO_EXCLUDE; 19628c2ecf20Sopenharmony_ci else 19638c2ecf20Sopenharmony_ci type = MLD2_CHANGE_TO_INCLUDE; 19648c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 0); 19658c2ecf20Sopenharmony_ci pmc->mca_crcount--; 19668c2ecf20Sopenharmony_ci } 19678c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->mca_lock); 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 19708c2ecf20Sopenharmony_ci if (!skb) 19718c2ecf20Sopenharmony_ci return; 19728c2ecf20Sopenharmony_ci (void) mld_sendpack(skb); 19738c2ecf20Sopenharmony_ci} 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_cistatic void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) 19768c2ecf20Sopenharmony_ci{ 19778c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 19788c2ecf20Sopenharmony_ci struct sock *sk = net->ipv6.igmp_sk; 19798c2ecf20Sopenharmony_ci struct inet6_dev *idev; 19808c2ecf20Sopenharmony_ci struct sk_buff *skb; 19818c2ecf20Sopenharmony_ci struct mld_msg *hdr; 19828c2ecf20Sopenharmony_ci const struct in6_addr *snd_addr, *saddr; 19838c2ecf20Sopenharmony_ci struct in6_addr addr_buf; 19848c2ecf20Sopenharmony_ci int hlen = LL_RESERVED_SPACE(dev); 19858c2ecf20Sopenharmony_ci int tlen = dev->needed_tailroom; 19868c2ecf20Sopenharmony_ci int err, len, payload_len, full_len; 19878c2ecf20Sopenharmony_ci u8 ra[8] = { IPPROTO_ICMPV6, 0, 19888c2ecf20Sopenharmony_ci IPV6_TLV_ROUTERALERT, 2, 0, 0, 19898c2ecf20Sopenharmony_ci IPV6_TLV_PADN, 0 }; 19908c2ecf20Sopenharmony_ci struct flowi6 fl6; 19918c2ecf20Sopenharmony_ci struct dst_entry *dst; 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci if (type == ICMPV6_MGM_REDUCTION) 19948c2ecf20Sopenharmony_ci snd_addr = &in6addr_linklocal_allrouters; 19958c2ecf20Sopenharmony_ci else 19968c2ecf20Sopenharmony_ci snd_addr = addr; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); 19998c2ecf20Sopenharmony_ci payload_len = len + sizeof(ra); 20008c2ecf20Sopenharmony_ci full_len = sizeof(struct ipv6hdr) + payload_len; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci rcu_read_lock(); 20038c2ecf20Sopenharmony_ci IP6_UPD_PO_STATS(net, __in6_dev_get(dev), 20048c2ecf20Sopenharmony_ci IPSTATS_MIB_OUT, full_len); 20058c2ecf20Sopenharmony_ci rcu_read_unlock(); 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci skb = sock_alloc_send_skb(sk, hlen + tlen + full_len, 1, &err); 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci if (!skb) { 20108c2ecf20Sopenharmony_ci rcu_read_lock(); 20118c2ecf20Sopenharmony_ci IP6_INC_STATS(net, __in6_dev_get(dev), 20128c2ecf20Sopenharmony_ci IPSTATS_MIB_OUTDISCARDS); 20138c2ecf20Sopenharmony_ci rcu_read_unlock(); 20148c2ecf20Sopenharmony_ci return; 20158c2ecf20Sopenharmony_ci } 20168c2ecf20Sopenharmony_ci skb->priority = TC_PRIO_CONTROL; 20178c2ecf20Sopenharmony_ci skb_reserve(skb, hlen); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) { 20208c2ecf20Sopenharmony_ci /* <draft-ietf-magma-mld-source-05.txt>: 20218c2ecf20Sopenharmony_ci * use unspecified address as the source address 20228c2ecf20Sopenharmony_ci * when a valid link-local address is not available. 20238c2ecf20Sopenharmony_ci */ 20248c2ecf20Sopenharmony_ci saddr = &in6addr_any; 20258c2ecf20Sopenharmony_ci } else 20268c2ecf20Sopenharmony_ci saddr = &addr_buf; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci ip6_mc_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len); 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci skb_put_data(skb, ra, sizeof(ra)); 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci hdr = skb_put_zero(skb, sizeof(struct mld_msg)); 20338c2ecf20Sopenharmony_ci hdr->mld_type = type; 20348c2ecf20Sopenharmony_ci hdr->mld_mca = *addr; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci hdr->mld_cksum = csum_ipv6_magic(saddr, snd_addr, len, 20378c2ecf20Sopenharmony_ci IPPROTO_ICMPV6, 20388c2ecf20Sopenharmony_ci csum_partial(hdr, len, 0)); 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci rcu_read_lock(); 20418c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci icmpv6_flow_init(sk, &fl6, type, 20448c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 20458c2ecf20Sopenharmony_ci skb->dev->ifindex); 20468c2ecf20Sopenharmony_ci dst = icmp6_dst_alloc(skb->dev, &fl6); 20478c2ecf20Sopenharmony_ci if (IS_ERR(dst)) { 20488c2ecf20Sopenharmony_ci err = PTR_ERR(dst); 20498c2ecf20Sopenharmony_ci goto err_out; 20508c2ecf20Sopenharmony_ci } 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 20538c2ecf20Sopenharmony_ci err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, 20548c2ecf20Sopenharmony_ci net, sk, skb, NULL, skb->dev, 20558c2ecf20Sopenharmony_ci dst_output); 20568c2ecf20Sopenharmony_ciout: 20578c2ecf20Sopenharmony_ci if (!err) { 20588c2ecf20Sopenharmony_ci ICMP6MSGOUT_INC_STATS(net, idev, type); 20598c2ecf20Sopenharmony_ci ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); 20608c2ecf20Sopenharmony_ci } else 20618c2ecf20Sopenharmony_ci IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci rcu_read_unlock(); 20648c2ecf20Sopenharmony_ci return; 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_cierr_out: 20678c2ecf20Sopenharmony_ci kfree_skb(skb); 20688c2ecf20Sopenharmony_ci goto out; 20698c2ecf20Sopenharmony_ci} 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_cistatic void mld_send_initial_cr(struct inet6_dev *idev) 20728c2ecf20Sopenharmony_ci{ 20738c2ecf20Sopenharmony_ci struct sk_buff *skb; 20748c2ecf20Sopenharmony_ci struct ifmcaddr6 *pmc; 20758c2ecf20Sopenharmony_ci int type; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci if (mld_in_v1_mode(idev)) 20788c2ecf20Sopenharmony_ci return; 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci skb = NULL; 20818c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 20828c2ecf20Sopenharmony_ci for (pmc = idev->mc_list; pmc; pmc = pmc->next) { 20838c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->mca_lock); 20848c2ecf20Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) 20858c2ecf20Sopenharmony_ci type = MLD2_CHANGE_TO_EXCLUDE; 20868c2ecf20Sopenharmony_ci else 20878c2ecf20Sopenharmony_ci type = MLD2_ALLOW_NEW_SOURCES; 20888c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 1); 20898c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->mca_lock); 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 20928c2ecf20Sopenharmony_ci if (skb) 20938c2ecf20Sopenharmony_ci mld_sendpack(skb); 20948c2ecf20Sopenharmony_ci} 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_civoid ipv6_mc_dad_complete(struct inet6_dev *idev) 20978c2ecf20Sopenharmony_ci{ 20988c2ecf20Sopenharmony_ci idev->mc_dad_count = idev->mc_qrv; 20998c2ecf20Sopenharmony_ci if (idev->mc_dad_count) { 21008c2ecf20Sopenharmony_ci mld_send_initial_cr(idev); 21018c2ecf20Sopenharmony_ci idev->mc_dad_count--; 21028c2ecf20Sopenharmony_ci if (idev->mc_dad_count) 21038c2ecf20Sopenharmony_ci mld_dad_start_timer(idev, 21048c2ecf20Sopenharmony_ci unsolicited_report_interval(idev)); 21058c2ecf20Sopenharmony_ci } 21068c2ecf20Sopenharmony_ci} 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_cistatic void mld_dad_timer_expire(struct timer_list *t) 21098c2ecf20Sopenharmony_ci{ 21108c2ecf20Sopenharmony_ci struct inet6_dev *idev = from_timer(idev, t, mc_dad_timer); 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci mld_send_initial_cr(idev); 21138c2ecf20Sopenharmony_ci if (idev->mc_dad_count) { 21148c2ecf20Sopenharmony_ci idev->mc_dad_count--; 21158c2ecf20Sopenharmony_ci if (idev->mc_dad_count) 21168c2ecf20Sopenharmony_ci mld_dad_start_timer(idev, 21178c2ecf20Sopenharmony_ci unsolicited_report_interval(idev)); 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci in6_dev_put(idev); 21208c2ecf20Sopenharmony_ci} 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_cistatic int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, 21238c2ecf20Sopenharmony_ci const struct in6_addr *psfsrc) 21248c2ecf20Sopenharmony_ci{ 21258c2ecf20Sopenharmony_ci struct ip6_sf_list *psf, *psf_prev; 21268c2ecf20Sopenharmony_ci int rv = 0; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci psf_prev = NULL; 21298c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) { 21308c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&psf->sf_addr, psfsrc)) 21318c2ecf20Sopenharmony_ci break; 21328c2ecf20Sopenharmony_ci psf_prev = psf; 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci if (!psf || psf->sf_count[sfmode] == 0) { 21358c2ecf20Sopenharmony_ci /* source filter not found, or count wrong => bug */ 21368c2ecf20Sopenharmony_ci return -ESRCH; 21378c2ecf20Sopenharmony_ci } 21388c2ecf20Sopenharmony_ci psf->sf_count[sfmode]--; 21398c2ecf20Sopenharmony_ci if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) { 21408c2ecf20Sopenharmony_ci struct inet6_dev *idev = pmc->idev; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci /* no more filters for this source */ 21438c2ecf20Sopenharmony_ci if (psf_prev) 21448c2ecf20Sopenharmony_ci psf_prev->sf_next = psf->sf_next; 21458c2ecf20Sopenharmony_ci else 21468c2ecf20Sopenharmony_ci pmc->mca_sources = psf->sf_next; 21478c2ecf20Sopenharmony_ci if (psf->sf_oldin && !(pmc->mca_flags & MAF_NOREPORT) && 21488c2ecf20Sopenharmony_ci !mld_in_v1_mode(idev)) { 21498c2ecf20Sopenharmony_ci psf->sf_crcount = idev->mc_qrv; 21508c2ecf20Sopenharmony_ci psf->sf_next = pmc->mca_tomb; 21518c2ecf20Sopenharmony_ci pmc->mca_tomb = psf; 21528c2ecf20Sopenharmony_ci rv = 1; 21538c2ecf20Sopenharmony_ci } else 21548c2ecf20Sopenharmony_ci kfree(psf); 21558c2ecf20Sopenharmony_ci } 21568c2ecf20Sopenharmony_ci return rv; 21578c2ecf20Sopenharmony_ci} 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_cistatic int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca, 21608c2ecf20Sopenharmony_ci int sfmode, int sfcount, const struct in6_addr *psfsrc, 21618c2ecf20Sopenharmony_ci int delta) 21628c2ecf20Sopenharmony_ci{ 21638c2ecf20Sopenharmony_ci struct ifmcaddr6 *pmc; 21648c2ecf20Sopenharmony_ci int changerec = 0; 21658c2ecf20Sopenharmony_ci int i, err; 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci if (!idev) 21688c2ecf20Sopenharmony_ci return -ENODEV; 21698c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 21708c2ecf20Sopenharmony_ci for (pmc = idev->mc_list; pmc; pmc = pmc->next) { 21718c2ecf20Sopenharmony_ci if (ipv6_addr_equal(pmca, &pmc->mca_addr)) 21728c2ecf20Sopenharmony_ci break; 21738c2ecf20Sopenharmony_ci } 21748c2ecf20Sopenharmony_ci if (!pmc) { 21758c2ecf20Sopenharmony_ci /* MCA not found?? bug */ 21768c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 21778c2ecf20Sopenharmony_ci return -ESRCH; 21788c2ecf20Sopenharmony_ci } 21798c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->mca_lock); 21808c2ecf20Sopenharmony_ci sf_markstate(pmc); 21818c2ecf20Sopenharmony_ci if (!delta) { 21828c2ecf20Sopenharmony_ci if (!pmc->mca_sfcount[sfmode]) { 21838c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->mca_lock); 21848c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 21858c2ecf20Sopenharmony_ci return -EINVAL; 21868c2ecf20Sopenharmony_ci } 21878c2ecf20Sopenharmony_ci pmc->mca_sfcount[sfmode]--; 21888c2ecf20Sopenharmony_ci } 21898c2ecf20Sopenharmony_ci err = 0; 21908c2ecf20Sopenharmony_ci for (i = 0; i < sfcount; i++) { 21918c2ecf20Sopenharmony_ci int rv = ip6_mc_del1_src(pmc, sfmode, &psfsrc[i]); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci changerec |= rv > 0; 21948c2ecf20Sopenharmony_ci if (!err && rv < 0) 21958c2ecf20Sopenharmony_ci err = rv; 21968c2ecf20Sopenharmony_ci } 21978c2ecf20Sopenharmony_ci if (pmc->mca_sfmode == MCAST_EXCLUDE && 21988c2ecf20Sopenharmony_ci pmc->mca_sfcount[MCAST_EXCLUDE] == 0 && 21998c2ecf20Sopenharmony_ci pmc->mca_sfcount[MCAST_INCLUDE]) { 22008c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci /* filter mode change */ 22038c2ecf20Sopenharmony_ci pmc->mca_sfmode = MCAST_INCLUDE; 22048c2ecf20Sopenharmony_ci pmc->mca_crcount = idev->mc_qrv; 22058c2ecf20Sopenharmony_ci idev->mc_ifc_count = pmc->mca_crcount; 22068c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) 22078c2ecf20Sopenharmony_ci psf->sf_crcount = 0; 22088c2ecf20Sopenharmony_ci mld_ifc_event(pmc->idev); 22098c2ecf20Sopenharmony_ci } else if (sf_setstate(pmc) || changerec) 22108c2ecf20Sopenharmony_ci mld_ifc_event(pmc->idev); 22118c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->mca_lock); 22128c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 22138c2ecf20Sopenharmony_ci return err; 22148c2ecf20Sopenharmony_ci} 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci/* 22178c2ecf20Sopenharmony_ci * Add multicast single-source filter to the interface list 22188c2ecf20Sopenharmony_ci */ 22198c2ecf20Sopenharmony_cistatic int ip6_mc_add1_src(struct ifmcaddr6 *pmc, int sfmode, 22208c2ecf20Sopenharmony_ci const struct in6_addr *psfsrc) 22218c2ecf20Sopenharmony_ci{ 22228c2ecf20Sopenharmony_ci struct ip6_sf_list *psf, *psf_prev; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci psf_prev = NULL; 22258c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) { 22268c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&psf->sf_addr, psfsrc)) 22278c2ecf20Sopenharmony_ci break; 22288c2ecf20Sopenharmony_ci psf_prev = psf; 22298c2ecf20Sopenharmony_ci } 22308c2ecf20Sopenharmony_ci if (!psf) { 22318c2ecf20Sopenharmony_ci psf = kzalloc(sizeof(*psf), GFP_ATOMIC); 22328c2ecf20Sopenharmony_ci if (!psf) 22338c2ecf20Sopenharmony_ci return -ENOBUFS; 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci psf->sf_addr = *psfsrc; 22368c2ecf20Sopenharmony_ci if (psf_prev) { 22378c2ecf20Sopenharmony_ci psf_prev->sf_next = psf; 22388c2ecf20Sopenharmony_ci } else 22398c2ecf20Sopenharmony_ci pmc->mca_sources = psf; 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci psf->sf_count[sfmode]++; 22428c2ecf20Sopenharmony_ci return 0; 22438c2ecf20Sopenharmony_ci} 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_cistatic void sf_markstate(struct ifmcaddr6 *pmc) 22468c2ecf20Sopenharmony_ci{ 22478c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 22488c2ecf20Sopenharmony_ci int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE]; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) 22518c2ecf20Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) { 22528c2ecf20Sopenharmony_ci psf->sf_oldin = mca_xcount == 22538c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE] && 22548c2ecf20Sopenharmony_ci !psf->sf_count[MCAST_INCLUDE]; 22558c2ecf20Sopenharmony_ci } else 22568c2ecf20Sopenharmony_ci psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0; 22578c2ecf20Sopenharmony_ci} 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_cistatic int sf_setstate(struct ifmcaddr6 *pmc) 22608c2ecf20Sopenharmony_ci{ 22618c2ecf20Sopenharmony_ci struct ip6_sf_list *psf, *dpsf; 22628c2ecf20Sopenharmony_ci int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE]; 22638c2ecf20Sopenharmony_ci int qrv = pmc->idev->mc_qrv; 22648c2ecf20Sopenharmony_ci int new_in, rv; 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci rv = 0; 22678c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) { 22688c2ecf20Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) { 22698c2ecf20Sopenharmony_ci new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && 22708c2ecf20Sopenharmony_ci !psf->sf_count[MCAST_INCLUDE]; 22718c2ecf20Sopenharmony_ci } else 22728c2ecf20Sopenharmony_ci new_in = psf->sf_count[MCAST_INCLUDE] != 0; 22738c2ecf20Sopenharmony_ci if (new_in) { 22748c2ecf20Sopenharmony_ci if (!psf->sf_oldin) { 22758c2ecf20Sopenharmony_ci struct ip6_sf_list *prev = NULL; 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci for (dpsf = pmc->mca_tomb; dpsf; 22788c2ecf20Sopenharmony_ci dpsf = dpsf->sf_next) { 22798c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&dpsf->sf_addr, 22808c2ecf20Sopenharmony_ci &psf->sf_addr)) 22818c2ecf20Sopenharmony_ci break; 22828c2ecf20Sopenharmony_ci prev = dpsf; 22838c2ecf20Sopenharmony_ci } 22848c2ecf20Sopenharmony_ci if (dpsf) { 22858c2ecf20Sopenharmony_ci if (prev) 22868c2ecf20Sopenharmony_ci prev->sf_next = dpsf->sf_next; 22878c2ecf20Sopenharmony_ci else 22888c2ecf20Sopenharmony_ci pmc->mca_tomb = dpsf->sf_next; 22898c2ecf20Sopenharmony_ci kfree(dpsf); 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci psf->sf_crcount = qrv; 22928c2ecf20Sopenharmony_ci rv++; 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci } else if (psf->sf_oldin) { 22958c2ecf20Sopenharmony_ci psf->sf_crcount = 0; 22968c2ecf20Sopenharmony_ci /* 22978c2ecf20Sopenharmony_ci * add or update "delete" records if an active filter 22988c2ecf20Sopenharmony_ci * is now inactive 22998c2ecf20Sopenharmony_ci */ 23008c2ecf20Sopenharmony_ci for (dpsf = pmc->mca_tomb; dpsf; dpsf = dpsf->sf_next) 23018c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&dpsf->sf_addr, 23028c2ecf20Sopenharmony_ci &psf->sf_addr)) 23038c2ecf20Sopenharmony_ci break; 23048c2ecf20Sopenharmony_ci if (!dpsf) { 23058c2ecf20Sopenharmony_ci dpsf = kmalloc(sizeof(*dpsf), GFP_ATOMIC); 23068c2ecf20Sopenharmony_ci if (!dpsf) 23078c2ecf20Sopenharmony_ci continue; 23088c2ecf20Sopenharmony_ci *dpsf = *psf; 23098c2ecf20Sopenharmony_ci /* pmc->mca_lock held by callers */ 23108c2ecf20Sopenharmony_ci dpsf->sf_next = pmc->mca_tomb; 23118c2ecf20Sopenharmony_ci pmc->mca_tomb = dpsf; 23128c2ecf20Sopenharmony_ci } 23138c2ecf20Sopenharmony_ci dpsf->sf_crcount = qrv; 23148c2ecf20Sopenharmony_ci rv++; 23158c2ecf20Sopenharmony_ci } 23168c2ecf20Sopenharmony_ci } 23178c2ecf20Sopenharmony_ci return rv; 23188c2ecf20Sopenharmony_ci} 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci/* 23218c2ecf20Sopenharmony_ci * Add multicast source filter list to the interface list 23228c2ecf20Sopenharmony_ci */ 23238c2ecf20Sopenharmony_cistatic int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca, 23248c2ecf20Sopenharmony_ci int sfmode, int sfcount, const struct in6_addr *psfsrc, 23258c2ecf20Sopenharmony_ci int delta) 23268c2ecf20Sopenharmony_ci{ 23278c2ecf20Sopenharmony_ci struct ifmcaddr6 *pmc; 23288c2ecf20Sopenharmony_ci int isexclude; 23298c2ecf20Sopenharmony_ci int i, err; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (!idev) 23328c2ecf20Sopenharmony_ci return -ENODEV; 23338c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 23348c2ecf20Sopenharmony_ci for (pmc = idev->mc_list; pmc; pmc = pmc->next) { 23358c2ecf20Sopenharmony_ci if (ipv6_addr_equal(pmca, &pmc->mca_addr)) 23368c2ecf20Sopenharmony_ci break; 23378c2ecf20Sopenharmony_ci } 23388c2ecf20Sopenharmony_ci if (!pmc) { 23398c2ecf20Sopenharmony_ci /* MCA not found?? bug */ 23408c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 23418c2ecf20Sopenharmony_ci return -ESRCH; 23428c2ecf20Sopenharmony_ci } 23438c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->mca_lock); 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci sf_markstate(pmc); 23468c2ecf20Sopenharmony_ci isexclude = pmc->mca_sfmode == MCAST_EXCLUDE; 23478c2ecf20Sopenharmony_ci if (!delta) 23488c2ecf20Sopenharmony_ci pmc->mca_sfcount[sfmode]++; 23498c2ecf20Sopenharmony_ci err = 0; 23508c2ecf20Sopenharmony_ci for (i = 0; i < sfcount; i++) { 23518c2ecf20Sopenharmony_ci err = ip6_mc_add1_src(pmc, sfmode, &psfsrc[i]); 23528c2ecf20Sopenharmony_ci if (err) 23538c2ecf20Sopenharmony_ci break; 23548c2ecf20Sopenharmony_ci } 23558c2ecf20Sopenharmony_ci if (err) { 23568c2ecf20Sopenharmony_ci int j; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci if (!delta) 23598c2ecf20Sopenharmony_ci pmc->mca_sfcount[sfmode]--; 23608c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 23618c2ecf20Sopenharmony_ci ip6_mc_del1_src(pmc, sfmode, &psfsrc[j]); 23628c2ecf20Sopenharmony_ci } else if (isexclude != (pmc->mca_sfcount[MCAST_EXCLUDE] != 0)) { 23638c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci /* filter mode change */ 23668c2ecf20Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) 23678c2ecf20Sopenharmony_ci pmc->mca_sfmode = MCAST_EXCLUDE; 23688c2ecf20Sopenharmony_ci else if (pmc->mca_sfcount[MCAST_INCLUDE]) 23698c2ecf20Sopenharmony_ci pmc->mca_sfmode = MCAST_INCLUDE; 23708c2ecf20Sopenharmony_ci /* else no filters; keep old mode for reports */ 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci pmc->mca_crcount = idev->mc_qrv; 23738c2ecf20Sopenharmony_ci idev->mc_ifc_count = pmc->mca_crcount; 23748c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = psf->sf_next) 23758c2ecf20Sopenharmony_ci psf->sf_crcount = 0; 23768c2ecf20Sopenharmony_ci mld_ifc_event(idev); 23778c2ecf20Sopenharmony_ci } else if (sf_setstate(pmc)) 23788c2ecf20Sopenharmony_ci mld_ifc_event(idev); 23798c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->mca_lock); 23808c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 23818c2ecf20Sopenharmony_ci return err; 23828c2ecf20Sopenharmony_ci} 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_cistatic void ip6_mc_clear_src(struct ifmcaddr6 *pmc) 23858c2ecf20Sopenharmony_ci{ 23868c2ecf20Sopenharmony_ci struct ip6_sf_list *psf, *nextpsf; 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci for (psf = pmc->mca_tomb; psf; psf = nextpsf) { 23898c2ecf20Sopenharmony_ci nextpsf = psf->sf_next; 23908c2ecf20Sopenharmony_ci kfree(psf); 23918c2ecf20Sopenharmony_ci } 23928c2ecf20Sopenharmony_ci pmc->mca_tomb = NULL; 23938c2ecf20Sopenharmony_ci for (psf = pmc->mca_sources; psf; psf = nextpsf) { 23948c2ecf20Sopenharmony_ci nextpsf = psf->sf_next; 23958c2ecf20Sopenharmony_ci kfree(psf); 23968c2ecf20Sopenharmony_ci } 23978c2ecf20Sopenharmony_ci pmc->mca_sources = NULL; 23988c2ecf20Sopenharmony_ci pmc->mca_sfmode = MCAST_EXCLUDE; 23998c2ecf20Sopenharmony_ci pmc->mca_sfcount[MCAST_INCLUDE] = 0; 24008c2ecf20Sopenharmony_ci pmc->mca_sfcount[MCAST_EXCLUDE] = 1; 24018c2ecf20Sopenharmony_ci} 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_cistatic void igmp6_join_group(struct ifmcaddr6 *ma) 24058c2ecf20Sopenharmony_ci{ 24068c2ecf20Sopenharmony_ci unsigned long delay; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci if (ma->mca_flags & MAF_NOREPORT) 24098c2ecf20Sopenharmony_ci return; 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci delay = prandom_u32() % unsolicited_report_interval(ma->idev); 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci spin_lock_bh(&ma->mca_lock); 24168c2ecf20Sopenharmony_ci if (del_timer(&ma->mca_timer)) { 24178c2ecf20Sopenharmony_ci refcount_dec(&ma->mca_refcnt); 24188c2ecf20Sopenharmony_ci delay = ma->mca_timer.expires - jiffies; 24198c2ecf20Sopenharmony_ci } 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci if (!mod_timer(&ma->mca_timer, jiffies + delay)) 24228c2ecf20Sopenharmony_ci refcount_inc(&ma->mca_refcnt); 24238c2ecf20Sopenharmony_ci ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER; 24248c2ecf20Sopenharmony_ci spin_unlock_bh(&ma->mca_lock); 24258c2ecf20Sopenharmony_ci} 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_cistatic int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, 24288c2ecf20Sopenharmony_ci struct inet6_dev *idev) 24298c2ecf20Sopenharmony_ci{ 24308c2ecf20Sopenharmony_ci int err; 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci write_lock_bh(&iml->sflock); 24338c2ecf20Sopenharmony_ci if (!iml->sflist) { 24348c2ecf20Sopenharmony_ci /* any-source empty exclude case */ 24358c2ecf20Sopenharmony_ci err = ip6_mc_del_src(idev, &iml->addr, iml->sfmode, 0, NULL, 0); 24368c2ecf20Sopenharmony_ci } else { 24378c2ecf20Sopenharmony_ci err = ip6_mc_del_src(idev, &iml->addr, iml->sfmode, 24388c2ecf20Sopenharmony_ci iml->sflist->sl_count, iml->sflist->sl_addr, 0); 24398c2ecf20Sopenharmony_ci sock_kfree_s(sk, iml->sflist, IP6_SFLSIZE(iml->sflist->sl_max)); 24408c2ecf20Sopenharmony_ci iml->sflist = NULL; 24418c2ecf20Sopenharmony_ci } 24428c2ecf20Sopenharmony_ci write_unlock_bh(&iml->sflock); 24438c2ecf20Sopenharmony_ci return err; 24448c2ecf20Sopenharmony_ci} 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_cistatic void igmp6_leave_group(struct ifmcaddr6 *ma) 24478c2ecf20Sopenharmony_ci{ 24488c2ecf20Sopenharmony_ci if (mld_in_v1_mode(ma->idev)) { 24498c2ecf20Sopenharmony_ci if (ma->mca_flags & MAF_LAST_REPORTER) 24508c2ecf20Sopenharmony_ci igmp6_send(&ma->mca_addr, ma->idev->dev, 24518c2ecf20Sopenharmony_ci ICMPV6_MGM_REDUCTION); 24528c2ecf20Sopenharmony_ci } else { 24538c2ecf20Sopenharmony_ci mld_add_delrec(ma->idev, ma); 24548c2ecf20Sopenharmony_ci mld_ifc_event(ma->idev); 24558c2ecf20Sopenharmony_ci } 24568c2ecf20Sopenharmony_ci} 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_cistatic void mld_gq_timer_expire(struct timer_list *t) 24598c2ecf20Sopenharmony_ci{ 24608c2ecf20Sopenharmony_ci struct inet6_dev *idev = from_timer(idev, t, mc_gq_timer); 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci idev->mc_gq_running = 0; 24638c2ecf20Sopenharmony_ci mld_send_report(idev, NULL); 24648c2ecf20Sopenharmony_ci in6_dev_put(idev); 24658c2ecf20Sopenharmony_ci} 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_cistatic void mld_ifc_timer_expire(struct timer_list *t) 24688c2ecf20Sopenharmony_ci{ 24698c2ecf20Sopenharmony_ci struct inet6_dev *idev = from_timer(idev, t, mc_ifc_timer); 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci mld_send_cr(idev); 24728c2ecf20Sopenharmony_ci if (idev->mc_ifc_count) { 24738c2ecf20Sopenharmony_ci idev->mc_ifc_count--; 24748c2ecf20Sopenharmony_ci if (idev->mc_ifc_count) 24758c2ecf20Sopenharmony_ci mld_ifc_start_timer(idev, 24768c2ecf20Sopenharmony_ci unsolicited_report_interval(idev)); 24778c2ecf20Sopenharmony_ci } 24788c2ecf20Sopenharmony_ci in6_dev_put(idev); 24798c2ecf20Sopenharmony_ci} 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_cistatic void mld_ifc_event(struct inet6_dev *idev) 24828c2ecf20Sopenharmony_ci{ 24838c2ecf20Sopenharmony_ci if (mld_in_v1_mode(idev)) 24848c2ecf20Sopenharmony_ci return; 24858c2ecf20Sopenharmony_ci idev->mc_ifc_count = idev->mc_qrv; 24868c2ecf20Sopenharmony_ci mld_ifc_start_timer(idev, 1); 24878c2ecf20Sopenharmony_ci} 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_cistatic void igmp6_timer_handler(struct timer_list *t) 24908c2ecf20Sopenharmony_ci{ 24918c2ecf20Sopenharmony_ci struct ifmcaddr6 *ma = from_timer(ma, t, mca_timer); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci if (mld_in_v1_mode(ma->idev)) 24948c2ecf20Sopenharmony_ci igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); 24958c2ecf20Sopenharmony_ci else 24968c2ecf20Sopenharmony_ci mld_send_report(ma->idev, ma); 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci spin_lock(&ma->mca_lock); 24998c2ecf20Sopenharmony_ci ma->mca_flags |= MAF_LAST_REPORTER; 25008c2ecf20Sopenharmony_ci ma->mca_flags &= ~MAF_TIMER_RUNNING; 25018c2ecf20Sopenharmony_ci spin_unlock(&ma->mca_lock); 25028c2ecf20Sopenharmony_ci ma_put(ma); 25038c2ecf20Sopenharmony_ci} 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci/* Device changing type */ 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_civoid ipv6_mc_unmap(struct inet6_dev *idev) 25088c2ecf20Sopenharmony_ci{ 25098c2ecf20Sopenharmony_ci struct ifmcaddr6 *i; 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci /* Install multicast list, except for all-nodes (already installed) */ 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 25148c2ecf20Sopenharmony_ci for (i = idev->mc_list; i; i = i->next) 25158c2ecf20Sopenharmony_ci igmp6_group_dropped(i); 25168c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 25178c2ecf20Sopenharmony_ci} 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_civoid ipv6_mc_remap(struct inet6_dev *idev) 25208c2ecf20Sopenharmony_ci{ 25218c2ecf20Sopenharmony_ci ipv6_mc_up(idev); 25228c2ecf20Sopenharmony_ci} 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci/* Device going down */ 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_civoid ipv6_mc_down(struct inet6_dev *idev) 25278c2ecf20Sopenharmony_ci{ 25288c2ecf20Sopenharmony_ci struct ifmcaddr6 *i; 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci /* Withdraw multicast list */ 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_ci for (i = idev->mc_list; i; i = i->next) 25358c2ecf20Sopenharmony_ci igmp6_group_dropped(i); 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci /* Should stop timer after group drop. or we will 25388c2ecf20Sopenharmony_ci * start timer again in mld_ifc_event() 25398c2ecf20Sopenharmony_ci */ 25408c2ecf20Sopenharmony_ci mld_ifc_stop_timer(idev); 25418c2ecf20Sopenharmony_ci mld_gq_stop_timer(idev); 25428c2ecf20Sopenharmony_ci mld_dad_stop_timer(idev); 25438c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 25448c2ecf20Sopenharmony_ci} 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_cistatic void ipv6_mc_reset(struct inet6_dev *idev) 25478c2ecf20Sopenharmony_ci{ 25488c2ecf20Sopenharmony_ci idev->mc_qrv = sysctl_mld_qrv; 25498c2ecf20Sopenharmony_ci idev->mc_qi = MLD_QI_DEFAULT; 25508c2ecf20Sopenharmony_ci idev->mc_qri = MLD_QRI_DEFAULT; 25518c2ecf20Sopenharmony_ci idev->mc_v1_seen = 0; 25528c2ecf20Sopenharmony_ci idev->mc_maxdelay = unsolicited_report_interval(idev); 25538c2ecf20Sopenharmony_ci} 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci/* Device going up */ 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_civoid ipv6_mc_up(struct inet6_dev *idev) 25588c2ecf20Sopenharmony_ci{ 25598c2ecf20Sopenharmony_ci struct ifmcaddr6 *i; 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci /* Install multicast list, except for all-nodes (already installed) */ 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 25648c2ecf20Sopenharmony_ci ipv6_mc_reset(idev); 25658c2ecf20Sopenharmony_ci for (i = idev->mc_list; i; i = i->next) { 25668c2ecf20Sopenharmony_ci mld_del_delrec(idev, i); 25678c2ecf20Sopenharmony_ci igmp6_group_added(i); 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 25708c2ecf20Sopenharmony_ci} 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci/* IPv6 device initialization. */ 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_civoid ipv6_mc_init_dev(struct inet6_dev *idev) 25758c2ecf20Sopenharmony_ci{ 25768c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 25778c2ecf20Sopenharmony_ci spin_lock_init(&idev->mc_lock); 25788c2ecf20Sopenharmony_ci idev->mc_gq_running = 0; 25798c2ecf20Sopenharmony_ci timer_setup(&idev->mc_gq_timer, mld_gq_timer_expire, 0); 25808c2ecf20Sopenharmony_ci idev->mc_tomb = NULL; 25818c2ecf20Sopenharmony_ci idev->mc_ifc_count = 0; 25828c2ecf20Sopenharmony_ci timer_setup(&idev->mc_ifc_timer, mld_ifc_timer_expire, 0); 25838c2ecf20Sopenharmony_ci timer_setup(&idev->mc_dad_timer, mld_dad_timer_expire, 0); 25848c2ecf20Sopenharmony_ci ipv6_mc_reset(idev); 25858c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 25868c2ecf20Sopenharmony_ci} 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_ci/* 25898c2ecf20Sopenharmony_ci * Device is about to be destroyed: clean up. 25908c2ecf20Sopenharmony_ci */ 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_civoid ipv6_mc_destroy_dev(struct inet6_dev *idev) 25938c2ecf20Sopenharmony_ci{ 25948c2ecf20Sopenharmony_ci struct ifmcaddr6 *i; 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci /* Deactivate timers */ 25978c2ecf20Sopenharmony_ci ipv6_mc_down(idev); 25988c2ecf20Sopenharmony_ci mld_clear_delrec(idev); 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci /* Delete all-nodes address. */ 26018c2ecf20Sopenharmony_ci /* We cannot call ipv6_dev_mc_dec() directly, our caller in 26028c2ecf20Sopenharmony_ci * addrconf.c has NULL'd out dev->ip6_ptr so in6_dev_get() will 26038c2ecf20Sopenharmony_ci * fail. 26048c2ecf20Sopenharmony_ci */ 26058c2ecf20Sopenharmony_ci __ipv6_dev_mc_dec(idev, &in6addr_linklocal_allnodes); 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci if (idev->cnf.forwarding) 26088c2ecf20Sopenharmony_ci __ipv6_dev_mc_dec(idev, &in6addr_linklocal_allrouters); 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 26118c2ecf20Sopenharmony_ci while ((i = idev->mc_list) != NULL) { 26128c2ecf20Sopenharmony_ci idev->mc_list = i->next; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 26158c2ecf20Sopenharmony_ci ip6_mc_clear_src(i); 26168c2ecf20Sopenharmony_ci ma_put(i); 26178c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 26208c2ecf20Sopenharmony_ci} 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_cistatic void ipv6_mc_rejoin_groups(struct inet6_dev *idev) 26238c2ecf20Sopenharmony_ci{ 26248c2ecf20Sopenharmony_ci struct ifmcaddr6 *pmc; 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci ASSERT_RTNL(); 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci if (mld_in_v1_mode(idev)) { 26298c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 26308c2ecf20Sopenharmony_ci for (pmc = idev->mc_list; pmc; pmc = pmc->next) 26318c2ecf20Sopenharmony_ci igmp6_join_group(pmc); 26328c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 26338c2ecf20Sopenharmony_ci } else 26348c2ecf20Sopenharmony_ci mld_send_report(idev, NULL); 26358c2ecf20Sopenharmony_ci} 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_cistatic int ipv6_mc_netdev_event(struct notifier_block *this, 26388c2ecf20Sopenharmony_ci unsigned long event, 26398c2ecf20Sopenharmony_ci void *ptr) 26408c2ecf20Sopenharmony_ci{ 26418c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 26428c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci switch (event) { 26458c2ecf20Sopenharmony_ci case NETDEV_RESEND_IGMP: 26468c2ecf20Sopenharmony_ci if (idev) 26478c2ecf20Sopenharmony_ci ipv6_mc_rejoin_groups(idev); 26488c2ecf20Sopenharmony_ci break; 26498c2ecf20Sopenharmony_ci default: 26508c2ecf20Sopenharmony_ci break; 26518c2ecf20Sopenharmony_ci } 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci return NOTIFY_DONE; 26548c2ecf20Sopenharmony_ci} 26558c2ecf20Sopenharmony_ci 26568c2ecf20Sopenharmony_cistatic struct notifier_block igmp6_netdev_notifier = { 26578c2ecf20Sopenharmony_ci .notifier_call = ipv6_mc_netdev_event, 26588c2ecf20Sopenharmony_ci}; 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 26618c2ecf20Sopenharmony_cistruct igmp6_mc_iter_state { 26628c2ecf20Sopenharmony_ci struct seq_net_private p; 26638c2ecf20Sopenharmony_ci struct net_device *dev; 26648c2ecf20Sopenharmony_ci struct inet6_dev *idev; 26658c2ecf20Sopenharmony_ci}; 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci#define igmp6_mc_seq_private(seq) ((struct igmp6_mc_iter_state *)(seq)->private) 26688c2ecf20Sopenharmony_ci 26698c2ecf20Sopenharmony_cistatic inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq) 26708c2ecf20Sopenharmony_ci{ 26718c2ecf20Sopenharmony_ci struct ifmcaddr6 *im = NULL; 26728c2ecf20Sopenharmony_ci struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); 26738c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci state->idev = NULL; 26768c2ecf20Sopenharmony_ci for_each_netdev_rcu(net, state->dev) { 26778c2ecf20Sopenharmony_ci struct inet6_dev *idev; 26788c2ecf20Sopenharmony_ci idev = __in6_dev_get(state->dev); 26798c2ecf20Sopenharmony_ci if (!idev) 26808c2ecf20Sopenharmony_ci continue; 26818c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 26828c2ecf20Sopenharmony_ci im = idev->mc_list; 26838c2ecf20Sopenharmony_ci if (im) { 26848c2ecf20Sopenharmony_ci state->idev = idev; 26858c2ecf20Sopenharmony_ci break; 26868c2ecf20Sopenharmony_ci } 26878c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 26888c2ecf20Sopenharmony_ci } 26898c2ecf20Sopenharmony_ci return im; 26908c2ecf20Sopenharmony_ci} 26918c2ecf20Sopenharmony_ci 26928c2ecf20Sopenharmony_cistatic struct ifmcaddr6 *igmp6_mc_get_next(struct seq_file *seq, struct ifmcaddr6 *im) 26938c2ecf20Sopenharmony_ci{ 26948c2ecf20Sopenharmony_ci struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci im = im->next; 26978c2ecf20Sopenharmony_ci while (!im) { 26988c2ecf20Sopenharmony_ci if (likely(state->idev)) 26998c2ecf20Sopenharmony_ci read_unlock_bh(&state->idev->lock); 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci state->dev = next_net_device_rcu(state->dev); 27028c2ecf20Sopenharmony_ci if (!state->dev) { 27038c2ecf20Sopenharmony_ci state->idev = NULL; 27048c2ecf20Sopenharmony_ci break; 27058c2ecf20Sopenharmony_ci } 27068c2ecf20Sopenharmony_ci state->idev = __in6_dev_get(state->dev); 27078c2ecf20Sopenharmony_ci if (!state->idev) 27088c2ecf20Sopenharmony_ci continue; 27098c2ecf20Sopenharmony_ci read_lock_bh(&state->idev->lock); 27108c2ecf20Sopenharmony_ci im = state->idev->mc_list; 27118c2ecf20Sopenharmony_ci } 27128c2ecf20Sopenharmony_ci return im; 27138c2ecf20Sopenharmony_ci} 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_cistatic struct ifmcaddr6 *igmp6_mc_get_idx(struct seq_file *seq, loff_t pos) 27168c2ecf20Sopenharmony_ci{ 27178c2ecf20Sopenharmony_ci struct ifmcaddr6 *im = igmp6_mc_get_first(seq); 27188c2ecf20Sopenharmony_ci if (im) 27198c2ecf20Sopenharmony_ci while (pos && (im = igmp6_mc_get_next(seq, im)) != NULL) 27208c2ecf20Sopenharmony_ci --pos; 27218c2ecf20Sopenharmony_ci return pos ? NULL : im; 27228c2ecf20Sopenharmony_ci} 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_cistatic void *igmp6_mc_seq_start(struct seq_file *seq, loff_t *pos) 27258c2ecf20Sopenharmony_ci __acquires(RCU) 27268c2ecf20Sopenharmony_ci{ 27278c2ecf20Sopenharmony_ci rcu_read_lock(); 27288c2ecf20Sopenharmony_ci return igmp6_mc_get_idx(seq, *pos); 27298c2ecf20Sopenharmony_ci} 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_cistatic void *igmp6_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 27328c2ecf20Sopenharmony_ci{ 27338c2ecf20Sopenharmony_ci struct ifmcaddr6 *im = igmp6_mc_get_next(seq, v); 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci ++*pos; 27368c2ecf20Sopenharmony_ci return im; 27378c2ecf20Sopenharmony_ci} 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_cistatic void igmp6_mc_seq_stop(struct seq_file *seq, void *v) 27408c2ecf20Sopenharmony_ci __releases(RCU) 27418c2ecf20Sopenharmony_ci{ 27428c2ecf20Sopenharmony_ci struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci if (likely(state->idev)) { 27458c2ecf20Sopenharmony_ci read_unlock_bh(&state->idev->lock); 27468c2ecf20Sopenharmony_ci state->idev = NULL; 27478c2ecf20Sopenharmony_ci } 27488c2ecf20Sopenharmony_ci state->dev = NULL; 27498c2ecf20Sopenharmony_ci rcu_read_unlock(); 27508c2ecf20Sopenharmony_ci} 27518c2ecf20Sopenharmony_ci 27528c2ecf20Sopenharmony_cistatic int igmp6_mc_seq_show(struct seq_file *seq, void *v) 27538c2ecf20Sopenharmony_ci{ 27548c2ecf20Sopenharmony_ci struct ifmcaddr6 *im = (struct ifmcaddr6 *)v; 27558c2ecf20Sopenharmony_ci struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci seq_printf(seq, 27588c2ecf20Sopenharmony_ci "%-4d %-15s %pi6 %5d %08X %ld\n", 27598c2ecf20Sopenharmony_ci state->dev->ifindex, state->dev->name, 27608c2ecf20Sopenharmony_ci &im->mca_addr, 27618c2ecf20Sopenharmony_ci im->mca_users, im->mca_flags, 27628c2ecf20Sopenharmony_ci (im->mca_flags&MAF_TIMER_RUNNING) ? 27638c2ecf20Sopenharmony_ci jiffies_to_clock_t(im->mca_timer.expires-jiffies) : 0); 27648c2ecf20Sopenharmony_ci return 0; 27658c2ecf20Sopenharmony_ci} 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_cistatic const struct seq_operations igmp6_mc_seq_ops = { 27688c2ecf20Sopenharmony_ci .start = igmp6_mc_seq_start, 27698c2ecf20Sopenharmony_ci .next = igmp6_mc_seq_next, 27708c2ecf20Sopenharmony_ci .stop = igmp6_mc_seq_stop, 27718c2ecf20Sopenharmony_ci .show = igmp6_mc_seq_show, 27728c2ecf20Sopenharmony_ci}; 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_cistruct igmp6_mcf_iter_state { 27758c2ecf20Sopenharmony_ci struct seq_net_private p; 27768c2ecf20Sopenharmony_ci struct net_device *dev; 27778c2ecf20Sopenharmony_ci struct inet6_dev *idev; 27788c2ecf20Sopenharmony_ci struct ifmcaddr6 *im; 27798c2ecf20Sopenharmony_ci}; 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci#define igmp6_mcf_seq_private(seq) ((struct igmp6_mcf_iter_state *)(seq)->private) 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_cistatic inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq) 27848c2ecf20Sopenharmony_ci{ 27858c2ecf20Sopenharmony_ci struct ip6_sf_list *psf = NULL; 27868c2ecf20Sopenharmony_ci struct ifmcaddr6 *im = NULL; 27878c2ecf20Sopenharmony_ci struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); 27888c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci state->idev = NULL; 27918c2ecf20Sopenharmony_ci state->im = NULL; 27928c2ecf20Sopenharmony_ci for_each_netdev_rcu(net, state->dev) { 27938c2ecf20Sopenharmony_ci struct inet6_dev *idev; 27948c2ecf20Sopenharmony_ci idev = __in6_dev_get(state->dev); 27958c2ecf20Sopenharmony_ci if (unlikely(idev == NULL)) 27968c2ecf20Sopenharmony_ci continue; 27978c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 27988c2ecf20Sopenharmony_ci im = idev->mc_list; 27998c2ecf20Sopenharmony_ci if (likely(im)) { 28008c2ecf20Sopenharmony_ci spin_lock_bh(&im->mca_lock); 28018c2ecf20Sopenharmony_ci psf = im->mca_sources; 28028c2ecf20Sopenharmony_ci if (likely(psf)) { 28038c2ecf20Sopenharmony_ci state->im = im; 28048c2ecf20Sopenharmony_ci state->idev = idev; 28058c2ecf20Sopenharmony_ci break; 28068c2ecf20Sopenharmony_ci } 28078c2ecf20Sopenharmony_ci spin_unlock_bh(&im->mca_lock); 28088c2ecf20Sopenharmony_ci } 28098c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 28108c2ecf20Sopenharmony_ci } 28118c2ecf20Sopenharmony_ci return psf; 28128c2ecf20Sopenharmony_ci} 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_cistatic struct ip6_sf_list *igmp6_mcf_get_next(struct seq_file *seq, struct ip6_sf_list *psf) 28158c2ecf20Sopenharmony_ci{ 28168c2ecf20Sopenharmony_ci struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); 28178c2ecf20Sopenharmony_ci 28188c2ecf20Sopenharmony_ci psf = psf->sf_next; 28198c2ecf20Sopenharmony_ci while (!psf) { 28208c2ecf20Sopenharmony_ci spin_unlock_bh(&state->im->mca_lock); 28218c2ecf20Sopenharmony_ci state->im = state->im->next; 28228c2ecf20Sopenharmony_ci while (!state->im) { 28238c2ecf20Sopenharmony_ci if (likely(state->idev)) 28248c2ecf20Sopenharmony_ci read_unlock_bh(&state->idev->lock); 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci state->dev = next_net_device_rcu(state->dev); 28278c2ecf20Sopenharmony_ci if (!state->dev) { 28288c2ecf20Sopenharmony_ci state->idev = NULL; 28298c2ecf20Sopenharmony_ci goto out; 28308c2ecf20Sopenharmony_ci } 28318c2ecf20Sopenharmony_ci state->idev = __in6_dev_get(state->dev); 28328c2ecf20Sopenharmony_ci if (!state->idev) 28338c2ecf20Sopenharmony_ci continue; 28348c2ecf20Sopenharmony_ci read_lock_bh(&state->idev->lock); 28358c2ecf20Sopenharmony_ci state->im = state->idev->mc_list; 28368c2ecf20Sopenharmony_ci } 28378c2ecf20Sopenharmony_ci if (!state->im) 28388c2ecf20Sopenharmony_ci break; 28398c2ecf20Sopenharmony_ci spin_lock_bh(&state->im->mca_lock); 28408c2ecf20Sopenharmony_ci psf = state->im->mca_sources; 28418c2ecf20Sopenharmony_ci } 28428c2ecf20Sopenharmony_ciout: 28438c2ecf20Sopenharmony_ci return psf; 28448c2ecf20Sopenharmony_ci} 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_cistatic struct ip6_sf_list *igmp6_mcf_get_idx(struct seq_file *seq, loff_t pos) 28478c2ecf20Sopenharmony_ci{ 28488c2ecf20Sopenharmony_ci struct ip6_sf_list *psf = igmp6_mcf_get_first(seq); 28498c2ecf20Sopenharmony_ci if (psf) 28508c2ecf20Sopenharmony_ci while (pos && (psf = igmp6_mcf_get_next(seq, psf)) != NULL) 28518c2ecf20Sopenharmony_ci --pos; 28528c2ecf20Sopenharmony_ci return pos ? NULL : psf; 28538c2ecf20Sopenharmony_ci} 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_cistatic void *igmp6_mcf_seq_start(struct seq_file *seq, loff_t *pos) 28568c2ecf20Sopenharmony_ci __acquires(RCU) 28578c2ecf20Sopenharmony_ci{ 28588c2ecf20Sopenharmony_ci rcu_read_lock(); 28598c2ecf20Sopenharmony_ci return *pos ? igmp6_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 28608c2ecf20Sopenharmony_ci} 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_cistatic void *igmp6_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos) 28638c2ecf20Sopenharmony_ci{ 28648c2ecf20Sopenharmony_ci struct ip6_sf_list *psf; 28658c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 28668c2ecf20Sopenharmony_ci psf = igmp6_mcf_get_first(seq); 28678c2ecf20Sopenharmony_ci else 28688c2ecf20Sopenharmony_ci psf = igmp6_mcf_get_next(seq, v); 28698c2ecf20Sopenharmony_ci ++*pos; 28708c2ecf20Sopenharmony_ci return psf; 28718c2ecf20Sopenharmony_ci} 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_cistatic void igmp6_mcf_seq_stop(struct seq_file *seq, void *v) 28748c2ecf20Sopenharmony_ci __releases(RCU) 28758c2ecf20Sopenharmony_ci{ 28768c2ecf20Sopenharmony_ci struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); 28778c2ecf20Sopenharmony_ci if (likely(state->im)) { 28788c2ecf20Sopenharmony_ci spin_unlock_bh(&state->im->mca_lock); 28798c2ecf20Sopenharmony_ci state->im = NULL; 28808c2ecf20Sopenharmony_ci } 28818c2ecf20Sopenharmony_ci if (likely(state->idev)) { 28828c2ecf20Sopenharmony_ci read_unlock_bh(&state->idev->lock); 28838c2ecf20Sopenharmony_ci state->idev = NULL; 28848c2ecf20Sopenharmony_ci } 28858c2ecf20Sopenharmony_ci state->dev = NULL; 28868c2ecf20Sopenharmony_ci rcu_read_unlock(); 28878c2ecf20Sopenharmony_ci} 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_cistatic int igmp6_mcf_seq_show(struct seq_file *seq, void *v) 28908c2ecf20Sopenharmony_ci{ 28918c2ecf20Sopenharmony_ci struct ip6_sf_list *psf = (struct ip6_sf_list *)v; 28928c2ecf20Sopenharmony_ci struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 28958c2ecf20Sopenharmony_ci seq_puts(seq, "Idx Device Multicast Address Source Address INC EXC\n"); 28968c2ecf20Sopenharmony_ci } else { 28978c2ecf20Sopenharmony_ci seq_printf(seq, 28988c2ecf20Sopenharmony_ci "%3d %6.6s %pi6 %pi6 %6lu %6lu\n", 28998c2ecf20Sopenharmony_ci state->dev->ifindex, state->dev->name, 29008c2ecf20Sopenharmony_ci &state->im->mca_addr, 29018c2ecf20Sopenharmony_ci &psf->sf_addr, 29028c2ecf20Sopenharmony_ci psf->sf_count[MCAST_INCLUDE], 29038c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]); 29048c2ecf20Sopenharmony_ci } 29058c2ecf20Sopenharmony_ci return 0; 29068c2ecf20Sopenharmony_ci} 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_cistatic const struct seq_operations igmp6_mcf_seq_ops = { 29098c2ecf20Sopenharmony_ci .start = igmp6_mcf_seq_start, 29108c2ecf20Sopenharmony_ci .next = igmp6_mcf_seq_next, 29118c2ecf20Sopenharmony_ci .stop = igmp6_mcf_seq_stop, 29128c2ecf20Sopenharmony_ci .show = igmp6_mcf_seq_show, 29138c2ecf20Sopenharmony_ci}; 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_cistatic int __net_init igmp6_proc_init(struct net *net) 29168c2ecf20Sopenharmony_ci{ 29178c2ecf20Sopenharmony_ci int err; 29188c2ecf20Sopenharmony_ci 29198c2ecf20Sopenharmony_ci err = -ENOMEM; 29208c2ecf20Sopenharmony_ci if (!proc_create_net("igmp6", 0444, net->proc_net, &igmp6_mc_seq_ops, 29218c2ecf20Sopenharmony_ci sizeof(struct igmp6_mc_iter_state))) 29228c2ecf20Sopenharmony_ci goto out; 29238c2ecf20Sopenharmony_ci if (!proc_create_net("mcfilter6", 0444, net->proc_net, 29248c2ecf20Sopenharmony_ci &igmp6_mcf_seq_ops, 29258c2ecf20Sopenharmony_ci sizeof(struct igmp6_mcf_iter_state))) 29268c2ecf20Sopenharmony_ci goto out_proc_net_igmp6; 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_ci err = 0; 29298c2ecf20Sopenharmony_ciout: 29308c2ecf20Sopenharmony_ci return err; 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ciout_proc_net_igmp6: 29338c2ecf20Sopenharmony_ci remove_proc_entry("igmp6", net->proc_net); 29348c2ecf20Sopenharmony_ci goto out; 29358c2ecf20Sopenharmony_ci} 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_cistatic void __net_exit igmp6_proc_exit(struct net *net) 29388c2ecf20Sopenharmony_ci{ 29398c2ecf20Sopenharmony_ci remove_proc_entry("mcfilter6", net->proc_net); 29408c2ecf20Sopenharmony_ci remove_proc_entry("igmp6", net->proc_net); 29418c2ecf20Sopenharmony_ci} 29428c2ecf20Sopenharmony_ci#else 29438c2ecf20Sopenharmony_cistatic inline int igmp6_proc_init(struct net *net) 29448c2ecf20Sopenharmony_ci{ 29458c2ecf20Sopenharmony_ci return 0; 29468c2ecf20Sopenharmony_ci} 29478c2ecf20Sopenharmony_cistatic inline void igmp6_proc_exit(struct net *net) 29488c2ecf20Sopenharmony_ci{ 29498c2ecf20Sopenharmony_ci} 29508c2ecf20Sopenharmony_ci#endif 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_cistatic int __net_init igmp6_net_init(struct net *net) 29538c2ecf20Sopenharmony_ci{ 29548c2ecf20Sopenharmony_ci int err; 29558c2ecf20Sopenharmony_ci 29568c2ecf20Sopenharmony_ci err = inet_ctl_sock_create(&net->ipv6.igmp_sk, PF_INET6, 29578c2ecf20Sopenharmony_ci SOCK_RAW, IPPROTO_ICMPV6, net); 29588c2ecf20Sopenharmony_ci if (err < 0) { 29598c2ecf20Sopenharmony_ci pr_err("Failed to initialize the IGMP6 control socket (err %d)\n", 29608c2ecf20Sopenharmony_ci err); 29618c2ecf20Sopenharmony_ci goto out; 29628c2ecf20Sopenharmony_ci } 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1; 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci err = inet_ctl_sock_create(&net->ipv6.mc_autojoin_sk, PF_INET6, 29678c2ecf20Sopenharmony_ci SOCK_RAW, IPPROTO_ICMPV6, net); 29688c2ecf20Sopenharmony_ci if (err < 0) { 29698c2ecf20Sopenharmony_ci pr_err("Failed to initialize the IGMP6 autojoin socket (err %d)\n", 29708c2ecf20Sopenharmony_ci err); 29718c2ecf20Sopenharmony_ci goto out_sock_create; 29728c2ecf20Sopenharmony_ci } 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci err = igmp6_proc_init(net); 29758c2ecf20Sopenharmony_ci if (err) 29768c2ecf20Sopenharmony_ci goto out_sock_create_autojoin; 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_ci return 0; 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_ciout_sock_create_autojoin: 29818c2ecf20Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk); 29828c2ecf20Sopenharmony_ciout_sock_create: 29838c2ecf20Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.igmp_sk); 29848c2ecf20Sopenharmony_ciout: 29858c2ecf20Sopenharmony_ci return err; 29868c2ecf20Sopenharmony_ci} 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_cistatic void __net_exit igmp6_net_exit(struct net *net) 29898c2ecf20Sopenharmony_ci{ 29908c2ecf20Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.igmp_sk); 29918c2ecf20Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk); 29928c2ecf20Sopenharmony_ci igmp6_proc_exit(net); 29938c2ecf20Sopenharmony_ci} 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_cistatic struct pernet_operations igmp6_net_ops = { 29968c2ecf20Sopenharmony_ci .init = igmp6_net_init, 29978c2ecf20Sopenharmony_ci .exit = igmp6_net_exit, 29988c2ecf20Sopenharmony_ci}; 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ciint __init igmp6_init(void) 30018c2ecf20Sopenharmony_ci{ 30028c2ecf20Sopenharmony_ci return register_pernet_subsys(&igmp6_net_ops); 30038c2ecf20Sopenharmony_ci} 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ciint __init igmp6_late_init(void) 30068c2ecf20Sopenharmony_ci{ 30078c2ecf20Sopenharmony_ci return register_netdevice_notifier(&igmp6_netdev_notifier); 30088c2ecf20Sopenharmony_ci} 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_civoid igmp6_cleanup(void) 30118c2ecf20Sopenharmony_ci{ 30128c2ecf20Sopenharmony_ci unregister_pernet_subsys(&igmp6_net_ops); 30138c2ecf20Sopenharmony_ci} 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_civoid igmp6_late_cleanup(void) 30168c2ecf20Sopenharmony_ci{ 30178c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&igmp6_netdev_notifier); 30188c2ecf20Sopenharmony_ci} 3019