162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Multicast support for IPv6 462306a36Sopenharmony_ci * Linux INET6 implementation 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* Changes: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * yoshfuji : fix format of router-alert option 1562306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI: 1662306a36Sopenharmony_ci * Fixed source address for MLD message based on 1762306a36Sopenharmony_ci * <draft-ietf-magma-mld-source-05.txt>. 1862306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI: 1962306a36Sopenharmony_ci * - Ignore Queries for invalid addresses. 2062306a36Sopenharmony_ci * - MLD for link-local addresses. 2162306a36Sopenharmony_ci * David L Stevens <dlstevens@us.ibm.com>: 2262306a36Sopenharmony_ci * - MLDv2 support 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/errno.h> 2762306a36Sopenharmony_ci#include <linux/types.h> 2862306a36Sopenharmony_ci#include <linux/string.h> 2962306a36Sopenharmony_ci#include <linux/socket.h> 3062306a36Sopenharmony_ci#include <linux/sockios.h> 3162306a36Sopenharmony_ci#include <linux/jiffies.h> 3262306a36Sopenharmony_ci#include <linux/net.h> 3362306a36Sopenharmony_ci#include <linux/in.h> 3462306a36Sopenharmony_ci#include <linux/in6.h> 3562306a36Sopenharmony_ci#include <linux/netdevice.h> 3662306a36Sopenharmony_ci#include <linux/if_arp.h> 3762306a36Sopenharmony_ci#include <linux/route.h> 3862306a36Sopenharmony_ci#include <linux/init.h> 3962306a36Sopenharmony_ci#include <linux/proc_fs.h> 4062306a36Sopenharmony_ci#include <linux/seq_file.h> 4162306a36Sopenharmony_ci#include <linux/slab.h> 4262306a36Sopenharmony_ci#include <linux/pkt_sched.h> 4362306a36Sopenharmony_ci#include <net/mld.h> 4462306a36Sopenharmony_ci#include <linux/workqueue.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include <linux/netfilter.h> 4762306a36Sopenharmony_ci#include <linux/netfilter_ipv6.h> 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#include <net/net_namespace.h> 5062306a36Sopenharmony_ci#include <net/sock.h> 5162306a36Sopenharmony_ci#include <net/snmp.h> 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#include <net/ipv6.h> 5462306a36Sopenharmony_ci#include <net/protocol.h> 5562306a36Sopenharmony_ci#include <net/if_inet6.h> 5662306a36Sopenharmony_ci#include <net/ndisc.h> 5762306a36Sopenharmony_ci#include <net/addrconf.h> 5862306a36Sopenharmony_ci#include <net/ip6_route.h> 5962306a36Sopenharmony_ci#include <net/inet_common.h> 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#include <net/ip6_checksum.h> 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* Ensure that we have struct in6_addr aligned on 32bit word. */ 6462306a36Sopenharmony_cistatic int __mld2_query_bugs[] __attribute__((__unused__)) = { 6562306a36Sopenharmony_ci BUILD_BUG_ON_ZERO(offsetof(struct mld2_query, mld2q_srcs) % 4), 6662306a36Sopenharmony_ci BUILD_BUG_ON_ZERO(offsetof(struct mld2_report, mld2r_grec) % 4), 6762306a36Sopenharmony_ci BUILD_BUG_ON_ZERO(offsetof(struct mld2_grec, grec_mca) % 4) 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct workqueue_struct *mld_wq; 7162306a36Sopenharmony_cistatic struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void igmp6_join_group(struct ifmcaddr6 *ma); 7462306a36Sopenharmony_cistatic void igmp6_leave_group(struct ifmcaddr6 *ma); 7562306a36Sopenharmony_cistatic void mld_mca_work(struct work_struct *work); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void mld_ifc_event(struct inet6_dev *idev); 7862306a36Sopenharmony_cistatic bool mld_in_v1_mode(const struct inet6_dev *idev); 7962306a36Sopenharmony_cistatic int sf_setstate(struct ifmcaddr6 *pmc); 8062306a36Sopenharmony_cistatic void sf_markstate(struct ifmcaddr6 *pmc); 8162306a36Sopenharmony_cistatic void ip6_mc_clear_src(struct ifmcaddr6 *pmc); 8262306a36Sopenharmony_cistatic int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca, 8362306a36Sopenharmony_ci int sfmode, int sfcount, const struct in6_addr *psfsrc, 8462306a36Sopenharmony_ci int delta); 8562306a36Sopenharmony_cistatic int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca, 8662306a36Sopenharmony_ci int sfmode, int sfcount, const struct in6_addr *psfsrc, 8762306a36Sopenharmony_ci int delta); 8862306a36Sopenharmony_cistatic int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, 8962306a36Sopenharmony_ci struct inet6_dev *idev); 9062306a36Sopenharmony_cistatic int __ipv6_dev_mc_inc(struct net_device *dev, 9162306a36Sopenharmony_ci const struct in6_addr *addr, unsigned int mode); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define MLD_QRV_DEFAULT 2 9462306a36Sopenharmony_ci/* RFC3810, 9.2. Query Interval */ 9562306a36Sopenharmony_ci#define MLD_QI_DEFAULT (125 * HZ) 9662306a36Sopenharmony_ci/* RFC3810, 9.3. Query Response Interval */ 9762306a36Sopenharmony_ci#define MLD_QRI_DEFAULT (10 * HZ) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* RFC3810, 8.1 Query Version Distinctions */ 10062306a36Sopenharmony_ci#define MLD_V1_QUERY_LEN 24 10162306a36Sopenharmony_ci#define MLD_V2_QUERY_LEN_MIN 28 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define IPV6_MLD_MAX_MSF 64 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciint sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF; 10662306a36Sopenharmony_ciint sysctl_mld_qrv __read_mostly = MLD_QRV_DEFAULT; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* 10962306a36Sopenharmony_ci * socket join on multicast group 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci#define mc_dereference(e, idev) \ 11262306a36Sopenharmony_ci rcu_dereference_protected(e, lockdep_is_held(&(idev)->mc_lock)) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define sock_dereference(e, sk) \ 11562306a36Sopenharmony_ci rcu_dereference_protected(e, lockdep_sock_is_held(sk)) 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define for_each_pmc_socklock(np, sk, pmc) \ 11862306a36Sopenharmony_ci for (pmc = sock_dereference((np)->ipv6_mc_list, sk); \ 11962306a36Sopenharmony_ci pmc; \ 12062306a36Sopenharmony_ci pmc = sock_dereference(pmc->next, sk)) 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define for_each_pmc_rcu(np, pmc) \ 12362306a36Sopenharmony_ci for (pmc = rcu_dereference((np)->ipv6_mc_list); \ 12462306a36Sopenharmony_ci pmc; \ 12562306a36Sopenharmony_ci pmc = rcu_dereference(pmc->next)) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define for_each_psf_mclock(mc, psf) \ 12862306a36Sopenharmony_ci for (psf = mc_dereference((mc)->mca_sources, mc->idev); \ 12962306a36Sopenharmony_ci psf; \ 13062306a36Sopenharmony_ci psf = mc_dereference(psf->sf_next, mc->idev)) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define for_each_psf_rcu(mc, psf) \ 13362306a36Sopenharmony_ci for (psf = rcu_dereference((mc)->mca_sources); \ 13462306a36Sopenharmony_ci psf; \ 13562306a36Sopenharmony_ci psf = rcu_dereference(psf->sf_next)) 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#define for_each_psf_tomb(mc, psf) \ 13862306a36Sopenharmony_ci for (psf = mc_dereference((mc)->mca_tomb, mc->idev); \ 13962306a36Sopenharmony_ci psf; \ 14062306a36Sopenharmony_ci psf = mc_dereference(psf->sf_next, mc->idev)) 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#define for_each_mc_mclock(idev, mc) \ 14362306a36Sopenharmony_ci for (mc = mc_dereference((idev)->mc_list, idev); \ 14462306a36Sopenharmony_ci mc; \ 14562306a36Sopenharmony_ci mc = mc_dereference(mc->next, idev)) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define for_each_mc_rcu(idev, mc) \ 14862306a36Sopenharmony_ci for (mc = rcu_dereference((idev)->mc_list); \ 14962306a36Sopenharmony_ci mc; \ 15062306a36Sopenharmony_ci mc = rcu_dereference(mc->next)) 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#define for_each_mc_tomb(idev, mc) \ 15362306a36Sopenharmony_ci for (mc = mc_dereference((idev)->mc_tomb, idev); \ 15462306a36Sopenharmony_ci mc; \ 15562306a36Sopenharmony_ci mc = mc_dereference(mc->next, idev)) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int unsolicited_report_interval(struct inet6_dev *idev) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci int iv; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (mld_in_v1_mode(idev)) 16262306a36Sopenharmony_ci iv = idev->cnf.mldv1_unsolicited_report_interval; 16362306a36Sopenharmony_ci else 16462306a36Sopenharmony_ci iv = idev->cnf.mldv2_unsolicited_report_interval; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return iv > 0 ? iv : 1; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int __ipv6_sock_mc_join(struct sock *sk, int ifindex, 17062306a36Sopenharmony_ci const struct in6_addr *addr, unsigned int mode) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct net_device *dev = NULL; 17362306a36Sopenharmony_ci struct ipv6_mc_socklist *mc_lst; 17462306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 17562306a36Sopenharmony_ci struct net *net = sock_net(sk); 17662306a36Sopenharmony_ci int err; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ASSERT_RTNL(); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (!ipv6_addr_is_multicast(addr)) 18162306a36Sopenharmony_ci return -EINVAL; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci for_each_pmc_socklock(np, sk, mc_lst) { 18462306a36Sopenharmony_ci if ((ifindex == 0 || mc_lst->ifindex == ifindex) && 18562306a36Sopenharmony_ci ipv6_addr_equal(&mc_lst->addr, addr)) 18662306a36Sopenharmony_ci return -EADDRINUSE; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (!mc_lst) 19262306a36Sopenharmony_ci return -ENOMEM; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci mc_lst->next = NULL; 19562306a36Sopenharmony_ci mc_lst->addr = *addr; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (ifindex == 0) { 19862306a36Sopenharmony_ci struct rt6_info *rt; 19962306a36Sopenharmony_ci rt = rt6_lookup(net, addr, NULL, 0, NULL, 0); 20062306a36Sopenharmony_ci if (rt) { 20162306a36Sopenharmony_ci dev = rt->dst.dev; 20262306a36Sopenharmony_ci ip6_rt_put(rt); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci } else 20562306a36Sopenharmony_ci dev = __dev_get_by_index(net, ifindex); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!dev) { 20862306a36Sopenharmony_ci sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); 20962306a36Sopenharmony_ci return -ENODEV; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci mc_lst->ifindex = dev->ifindex; 21362306a36Sopenharmony_ci mc_lst->sfmode = mode; 21462306a36Sopenharmony_ci RCU_INIT_POINTER(mc_lst->sflist, NULL); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * now add/increase the group membership on the device 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci err = __ipv6_dev_mc_inc(dev, addr, mode); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (err) { 22362306a36Sopenharmony_ci sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); 22462306a36Sopenharmony_ci return err; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci mc_lst->next = np->ipv6_mc_list; 22862306a36Sopenharmony_ci rcu_assign_pointer(np->ipv6_mc_list, mc_lst); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciint ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci return __ipv6_sock_mc_join(sk, ifindex, addr, MCAST_EXCLUDE); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_sock_mc_join); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ciint ipv6_sock_mc_join_ssm(struct sock *sk, int ifindex, 24062306a36Sopenharmony_ci const struct in6_addr *addr, unsigned int mode) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci return __ipv6_sock_mc_join(sk, ifindex, addr, mode); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* 24662306a36Sopenharmony_ci * socket leave on multicast group 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ciint ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 25162306a36Sopenharmony_ci struct ipv6_mc_socklist *mc_lst; 25262306a36Sopenharmony_ci struct ipv6_mc_socklist __rcu **lnk; 25362306a36Sopenharmony_ci struct net *net = sock_net(sk); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ASSERT_RTNL(); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (!ipv6_addr_is_multicast(addr)) 25862306a36Sopenharmony_ci return -EINVAL; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci for (lnk = &np->ipv6_mc_list; 26162306a36Sopenharmony_ci (mc_lst = sock_dereference(*lnk, sk)) != NULL; 26262306a36Sopenharmony_ci lnk = &mc_lst->next) { 26362306a36Sopenharmony_ci if ((ifindex == 0 || mc_lst->ifindex == ifindex) && 26462306a36Sopenharmony_ci ipv6_addr_equal(&mc_lst->addr, addr)) { 26562306a36Sopenharmony_ci struct net_device *dev; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci *lnk = mc_lst->next; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci dev = __dev_get_by_index(net, mc_lst->ifindex); 27062306a36Sopenharmony_ci if (dev) { 27162306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ip6_mc_leave_src(sk, mc_lst, idev); 27462306a36Sopenharmony_ci if (idev) 27562306a36Sopenharmony_ci __ipv6_dev_mc_dec(idev, &mc_lst->addr); 27662306a36Sopenharmony_ci } else { 27762306a36Sopenharmony_ci ip6_mc_leave_src(sk, mc_lst, NULL); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); 28162306a36Sopenharmony_ci kfree_rcu(mc_lst, rcu); 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return -EADDRNOTAVAIL; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_sock_mc_drop); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic struct inet6_dev *ip6_mc_find_dev_rtnl(struct net *net, 29162306a36Sopenharmony_ci const struct in6_addr *group, 29262306a36Sopenharmony_ci int ifindex) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct net_device *dev = NULL; 29562306a36Sopenharmony_ci struct inet6_dev *idev = NULL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (ifindex == 0) { 29862306a36Sopenharmony_ci struct rt6_info *rt = rt6_lookup(net, group, NULL, 0, NULL, 0); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (rt) { 30162306a36Sopenharmony_ci dev = rt->dst.dev; 30262306a36Sopenharmony_ci ip6_rt_put(rt); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci } else { 30562306a36Sopenharmony_ci dev = __dev_get_by_index(net, ifindex); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (!dev) 30962306a36Sopenharmony_ci return NULL; 31062306a36Sopenharmony_ci idev = __in6_dev_get(dev); 31162306a36Sopenharmony_ci if (!idev) 31262306a36Sopenharmony_ci return NULL; 31362306a36Sopenharmony_ci if (idev->dead) 31462306a36Sopenharmony_ci return NULL; 31562306a36Sopenharmony_ci return idev; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_civoid __ipv6_sock_mc_close(struct sock *sk) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 32162306a36Sopenharmony_ci struct ipv6_mc_socklist *mc_lst; 32262306a36Sopenharmony_ci struct net *net = sock_net(sk); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ASSERT_RTNL(); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci while ((mc_lst = sock_dereference(np->ipv6_mc_list, sk)) != NULL) { 32762306a36Sopenharmony_ci struct net_device *dev; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci np->ipv6_mc_list = mc_lst->next; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci dev = __dev_get_by_index(net, mc_lst->ifindex); 33262306a36Sopenharmony_ci if (dev) { 33362306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ip6_mc_leave_src(sk, mc_lst, idev); 33662306a36Sopenharmony_ci if (idev) 33762306a36Sopenharmony_ci __ipv6_dev_mc_dec(idev, &mc_lst->addr); 33862306a36Sopenharmony_ci } else { 33962306a36Sopenharmony_ci ip6_mc_leave_src(sk, mc_lst, NULL); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); 34362306a36Sopenharmony_ci kfree_rcu(mc_lst, rcu); 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_civoid ipv6_sock_mc_close(struct sock *sk) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (!rcu_access_pointer(np->ipv6_mc_list)) 35262306a36Sopenharmony_ci return; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci rtnl_lock(); 35562306a36Sopenharmony_ci lock_sock(sk); 35662306a36Sopenharmony_ci __ipv6_sock_mc_close(sk); 35762306a36Sopenharmony_ci release_sock(sk); 35862306a36Sopenharmony_ci rtnl_unlock(); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ciint ip6_mc_source(int add, int omode, struct sock *sk, 36262306a36Sopenharmony_ci struct group_source_req *pgsr) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct in6_addr *source, *group; 36562306a36Sopenharmony_ci struct ipv6_mc_socklist *pmc; 36662306a36Sopenharmony_ci struct inet6_dev *idev; 36762306a36Sopenharmony_ci struct ipv6_pinfo *inet6 = inet6_sk(sk); 36862306a36Sopenharmony_ci struct ip6_sf_socklist *psl; 36962306a36Sopenharmony_ci struct net *net = sock_net(sk); 37062306a36Sopenharmony_ci int i, j, rv; 37162306a36Sopenharmony_ci int leavegroup = 0; 37262306a36Sopenharmony_ci int err; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr; 37562306a36Sopenharmony_ci group = &((struct sockaddr_in6 *)&pgsr->gsr_group)->sin6_addr; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (!ipv6_addr_is_multicast(group)) 37862306a36Sopenharmony_ci return -EINVAL; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci idev = ip6_mc_find_dev_rtnl(net, group, pgsr->gsr_interface); 38162306a36Sopenharmony_ci if (!idev) 38262306a36Sopenharmony_ci return -ENODEV; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci err = -EADDRNOTAVAIL; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 38762306a36Sopenharmony_ci for_each_pmc_socklock(inet6, sk, pmc) { 38862306a36Sopenharmony_ci if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface) 38962306a36Sopenharmony_ci continue; 39062306a36Sopenharmony_ci if (ipv6_addr_equal(&pmc->addr, group)) 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci if (!pmc) { /* must have a prior join */ 39462306a36Sopenharmony_ci err = -EINVAL; 39562306a36Sopenharmony_ci goto done; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci /* if a source filter was set, must be the same mode as before */ 39862306a36Sopenharmony_ci if (rcu_access_pointer(pmc->sflist)) { 39962306a36Sopenharmony_ci if (pmc->sfmode != omode) { 40062306a36Sopenharmony_ci err = -EINVAL; 40162306a36Sopenharmony_ci goto done; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci } else if (pmc->sfmode != omode) { 40462306a36Sopenharmony_ci /* allow mode switches for empty-set filters */ 40562306a36Sopenharmony_ci ip6_mc_add_src(idev, group, omode, 0, NULL, 0); 40662306a36Sopenharmony_ci ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); 40762306a36Sopenharmony_ci pmc->sfmode = omode; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci psl = sock_dereference(pmc->sflist, sk); 41162306a36Sopenharmony_ci if (!add) { 41262306a36Sopenharmony_ci if (!psl) 41362306a36Sopenharmony_ci goto done; /* err = -EADDRNOTAVAIL */ 41462306a36Sopenharmony_ci rv = !0; 41562306a36Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) { 41662306a36Sopenharmony_ci rv = !ipv6_addr_equal(&psl->sl_addr[i], source); 41762306a36Sopenharmony_ci if (rv == 0) 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci if (rv) /* source not found */ 42162306a36Sopenharmony_ci goto done; /* err = -EADDRNOTAVAIL */ 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 42462306a36Sopenharmony_ci if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { 42562306a36Sopenharmony_ci leavegroup = 1; 42662306a36Sopenharmony_ci goto done; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* update the interface filter */ 43062306a36Sopenharmony_ci ip6_mc_del_src(idev, group, omode, 1, source, 1); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci for (j = i+1; j < psl->sl_count; j++) 43362306a36Sopenharmony_ci psl->sl_addr[j-1] = psl->sl_addr[j]; 43462306a36Sopenharmony_ci psl->sl_count--; 43562306a36Sopenharmony_ci err = 0; 43662306a36Sopenharmony_ci goto done; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci /* else, add a new source to the filter */ 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (psl && psl->sl_count >= sysctl_mld_max_msf) { 44162306a36Sopenharmony_ci err = -ENOBUFS; 44262306a36Sopenharmony_ci goto done; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci if (!psl || psl->sl_count == psl->sl_max) { 44562306a36Sopenharmony_ci struct ip6_sf_socklist *newpsl; 44662306a36Sopenharmony_ci int count = IP6_SFBLOCK; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (psl) 44962306a36Sopenharmony_ci count += psl->sl_max; 45062306a36Sopenharmony_ci newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, count), 45162306a36Sopenharmony_ci GFP_KERNEL); 45262306a36Sopenharmony_ci if (!newpsl) { 45362306a36Sopenharmony_ci err = -ENOBUFS; 45462306a36Sopenharmony_ci goto done; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci newpsl->sl_max = count; 45762306a36Sopenharmony_ci newpsl->sl_count = count - IP6_SFBLOCK; 45862306a36Sopenharmony_ci if (psl) { 45962306a36Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) 46062306a36Sopenharmony_ci newpsl->sl_addr[i] = psl->sl_addr[i]; 46162306a36Sopenharmony_ci atomic_sub(struct_size(psl, sl_addr, psl->sl_max), 46262306a36Sopenharmony_ci &sk->sk_omem_alloc); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci rcu_assign_pointer(pmc->sflist, newpsl); 46562306a36Sopenharmony_ci kfree_rcu(psl, rcu); 46662306a36Sopenharmony_ci psl = newpsl; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci rv = 1; /* > 0 for insert logic below if sl_count is 0 */ 46962306a36Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) { 47062306a36Sopenharmony_ci rv = !ipv6_addr_equal(&psl->sl_addr[i], source); 47162306a36Sopenharmony_ci if (rv == 0) /* There is an error in the address. */ 47262306a36Sopenharmony_ci goto done; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci for (j = psl->sl_count-1; j >= i; j--) 47562306a36Sopenharmony_ci psl->sl_addr[j+1] = psl->sl_addr[j]; 47662306a36Sopenharmony_ci psl->sl_addr[i] = *source; 47762306a36Sopenharmony_ci psl->sl_count++; 47862306a36Sopenharmony_ci err = 0; 47962306a36Sopenharmony_ci /* update the interface list */ 48062306a36Sopenharmony_ci ip6_mc_add_src(idev, group, omode, 1, source, 1); 48162306a36Sopenharmony_cidone: 48262306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 48362306a36Sopenharmony_ci if (leavegroup) 48462306a36Sopenharmony_ci err = ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group); 48562306a36Sopenharmony_ci return err; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ciint ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf, 48962306a36Sopenharmony_ci struct sockaddr_storage *list) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci const struct in6_addr *group; 49262306a36Sopenharmony_ci struct ipv6_mc_socklist *pmc; 49362306a36Sopenharmony_ci struct inet6_dev *idev; 49462306a36Sopenharmony_ci struct ipv6_pinfo *inet6 = inet6_sk(sk); 49562306a36Sopenharmony_ci struct ip6_sf_socklist *newpsl, *psl; 49662306a36Sopenharmony_ci struct net *net = sock_net(sk); 49762306a36Sopenharmony_ci int leavegroup = 0; 49862306a36Sopenharmony_ci int i, err; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (!ipv6_addr_is_multicast(group)) 50362306a36Sopenharmony_ci return -EINVAL; 50462306a36Sopenharmony_ci if (gsf->gf_fmode != MCAST_INCLUDE && 50562306a36Sopenharmony_ci gsf->gf_fmode != MCAST_EXCLUDE) 50662306a36Sopenharmony_ci return -EINVAL; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci idev = ip6_mc_find_dev_rtnl(net, group, gsf->gf_interface); 50962306a36Sopenharmony_ci if (!idev) 51062306a36Sopenharmony_ci return -ENODEV; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci err = 0; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) { 51562306a36Sopenharmony_ci leavegroup = 1; 51662306a36Sopenharmony_ci goto done; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci for_each_pmc_socklock(inet6, sk, pmc) { 52062306a36Sopenharmony_ci if (pmc->ifindex != gsf->gf_interface) 52162306a36Sopenharmony_ci continue; 52262306a36Sopenharmony_ci if (ipv6_addr_equal(&pmc->addr, group)) 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci if (!pmc) { /* must have a prior join */ 52662306a36Sopenharmony_ci err = -EINVAL; 52762306a36Sopenharmony_ci goto done; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci if (gsf->gf_numsrc) { 53062306a36Sopenharmony_ci newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, 53162306a36Sopenharmony_ci gsf->gf_numsrc), 53262306a36Sopenharmony_ci GFP_KERNEL); 53362306a36Sopenharmony_ci if (!newpsl) { 53462306a36Sopenharmony_ci err = -ENOBUFS; 53562306a36Sopenharmony_ci goto done; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci newpsl->sl_max = newpsl->sl_count = gsf->gf_numsrc; 53862306a36Sopenharmony_ci for (i = 0; i < newpsl->sl_count; ++i, ++list) { 53962306a36Sopenharmony_ci struct sockaddr_in6 *psin6; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci psin6 = (struct sockaddr_in6 *)list; 54262306a36Sopenharmony_ci newpsl->sl_addr[i] = psin6->sin6_addr; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 54562306a36Sopenharmony_ci err = ip6_mc_add_src(idev, group, gsf->gf_fmode, 54662306a36Sopenharmony_ci newpsl->sl_count, newpsl->sl_addr, 0); 54762306a36Sopenharmony_ci if (err) { 54862306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 54962306a36Sopenharmony_ci sock_kfree_s(sk, newpsl, struct_size(newpsl, sl_addr, 55062306a36Sopenharmony_ci newpsl->sl_max)); 55162306a36Sopenharmony_ci goto done; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 55462306a36Sopenharmony_ci } else { 55562306a36Sopenharmony_ci newpsl = NULL; 55662306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 55762306a36Sopenharmony_ci ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0); 55862306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 56262306a36Sopenharmony_ci psl = sock_dereference(pmc->sflist, sk); 56362306a36Sopenharmony_ci if (psl) { 56462306a36Sopenharmony_ci ip6_mc_del_src(idev, group, pmc->sfmode, 56562306a36Sopenharmony_ci psl->sl_count, psl->sl_addr, 0); 56662306a36Sopenharmony_ci atomic_sub(struct_size(psl, sl_addr, psl->sl_max), 56762306a36Sopenharmony_ci &sk->sk_omem_alloc); 56862306a36Sopenharmony_ci } else { 56962306a36Sopenharmony_ci ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci rcu_assign_pointer(pmc->sflist, newpsl); 57262306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 57362306a36Sopenharmony_ci kfree_rcu(psl, rcu); 57462306a36Sopenharmony_ci pmc->sfmode = gsf->gf_fmode; 57562306a36Sopenharmony_ci err = 0; 57662306a36Sopenharmony_cidone: 57762306a36Sopenharmony_ci if (leavegroup) 57862306a36Sopenharmony_ci err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group); 57962306a36Sopenharmony_ci return err; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ciint ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, 58362306a36Sopenharmony_ci sockptr_t optval, size_t ss_offset) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct ipv6_pinfo *inet6 = inet6_sk(sk); 58662306a36Sopenharmony_ci const struct in6_addr *group; 58762306a36Sopenharmony_ci struct ipv6_mc_socklist *pmc; 58862306a36Sopenharmony_ci struct ip6_sf_socklist *psl; 58962306a36Sopenharmony_ci int i, count, copycount; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (!ipv6_addr_is_multicast(group)) 59462306a36Sopenharmony_ci return -EINVAL; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* changes to the ipv6_mc_list require the socket lock and 59762306a36Sopenharmony_ci * rtnl lock. We have the socket lock, so reading the list is safe. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci for_each_pmc_socklock(inet6, sk, pmc) { 60162306a36Sopenharmony_ci if (pmc->ifindex != gsf->gf_interface) 60262306a36Sopenharmony_ci continue; 60362306a36Sopenharmony_ci if (ipv6_addr_equal(group, &pmc->addr)) 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci if (!pmc) /* must have a prior join */ 60762306a36Sopenharmony_ci return -EADDRNOTAVAIL; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci gsf->gf_fmode = pmc->sfmode; 61062306a36Sopenharmony_ci psl = sock_dereference(pmc->sflist, sk); 61162306a36Sopenharmony_ci count = psl ? psl->sl_count : 0; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; 61462306a36Sopenharmony_ci gsf->gf_numsrc = count; 61562306a36Sopenharmony_ci for (i = 0; i < copycount; i++) { 61662306a36Sopenharmony_ci struct sockaddr_in6 *psin6; 61762306a36Sopenharmony_ci struct sockaddr_storage ss; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci psin6 = (struct sockaddr_in6 *)&ss; 62062306a36Sopenharmony_ci memset(&ss, 0, sizeof(ss)); 62162306a36Sopenharmony_ci psin6->sin6_family = AF_INET6; 62262306a36Sopenharmony_ci psin6->sin6_addr = psl->sl_addr[i]; 62362306a36Sopenharmony_ci if (copy_to_sockptr_offset(optval, ss_offset, &ss, sizeof(ss))) 62462306a36Sopenharmony_ci return -EFAULT; 62562306a36Sopenharmony_ci ss_offset += sizeof(ss); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cibool inet6_mc_check(const struct sock *sk, const struct in6_addr *mc_addr, 63162306a36Sopenharmony_ci const struct in6_addr *src_addr) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci const struct ipv6_pinfo *np = inet6_sk(sk); 63462306a36Sopenharmony_ci const struct ipv6_mc_socklist *mc; 63562306a36Sopenharmony_ci const struct ip6_sf_socklist *psl; 63662306a36Sopenharmony_ci bool rv = true; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci rcu_read_lock(); 63962306a36Sopenharmony_ci for_each_pmc_rcu(np, mc) { 64062306a36Sopenharmony_ci if (ipv6_addr_equal(&mc->addr, mc_addr)) 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci if (!mc) { 64462306a36Sopenharmony_ci rcu_read_unlock(); 64562306a36Sopenharmony_ci return np->mc_all; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci psl = rcu_dereference(mc->sflist); 64862306a36Sopenharmony_ci if (!psl) { 64962306a36Sopenharmony_ci rv = mc->sfmode == MCAST_EXCLUDE; 65062306a36Sopenharmony_ci } else { 65162306a36Sopenharmony_ci int i; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) { 65462306a36Sopenharmony_ci if (ipv6_addr_equal(&psl->sl_addr[i], src_addr)) 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci if (mc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) 65862306a36Sopenharmony_ci rv = false; 65962306a36Sopenharmony_ci if (mc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) 66062306a36Sopenharmony_ci rv = false; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci rcu_read_unlock(); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return rv; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* called with mc_lock */ 66862306a36Sopenharmony_cistatic void igmp6_group_added(struct ifmcaddr6 *mc) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct net_device *dev = mc->idev->dev; 67162306a36Sopenharmony_ci char buf[MAX_ADDR_LEN]; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < 67462306a36Sopenharmony_ci IPV6_ADDR_SCOPE_LINKLOCAL) 67562306a36Sopenharmony_ci return; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (!(mc->mca_flags&MAF_LOADED)) { 67862306a36Sopenharmony_ci mc->mca_flags |= MAF_LOADED; 67962306a36Sopenharmony_ci if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) 68062306a36Sopenharmony_ci dev_mc_add(dev, buf); 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!(dev->flags & IFF_UP) || (mc->mca_flags & MAF_NOREPORT)) 68462306a36Sopenharmony_ci return; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (mld_in_v1_mode(mc->idev)) { 68762306a36Sopenharmony_ci igmp6_join_group(mc); 68862306a36Sopenharmony_ci return; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci /* else v2 */ 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* Based on RFC3810 6.1, for newly added INCLUDE SSM, we 69362306a36Sopenharmony_ci * should not send filter-mode change record as the mode 69462306a36Sopenharmony_ci * should be from IN() to IN(A). 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_ci if (mc->mca_sfmode == MCAST_EXCLUDE) 69762306a36Sopenharmony_ci mc->mca_crcount = mc->idev->mc_qrv; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci mld_ifc_event(mc->idev); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci/* called with mc_lock */ 70362306a36Sopenharmony_cistatic void igmp6_group_dropped(struct ifmcaddr6 *mc) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct net_device *dev = mc->idev->dev; 70662306a36Sopenharmony_ci char buf[MAX_ADDR_LEN]; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < 70962306a36Sopenharmony_ci IPV6_ADDR_SCOPE_LINKLOCAL) 71062306a36Sopenharmony_ci return; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (mc->mca_flags&MAF_LOADED) { 71362306a36Sopenharmony_ci mc->mca_flags &= ~MAF_LOADED; 71462306a36Sopenharmony_ci if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) 71562306a36Sopenharmony_ci dev_mc_del(dev, buf); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (mc->mca_flags & MAF_NOREPORT) 71962306a36Sopenharmony_ci return; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (!mc->idev->dead) 72262306a36Sopenharmony_ci igmp6_leave_group(mc); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (cancel_delayed_work(&mc->mca_work)) 72562306a36Sopenharmony_ci refcount_dec(&mc->mca_refcnt); 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* 72962306a36Sopenharmony_ci * deleted ifmcaddr6 manipulation 73062306a36Sopenharmony_ci * called with mc_lock 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_cistatic void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct ifmcaddr6 *pmc; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* this is an "ifmcaddr6" for convenience; only the fields below 73762306a36Sopenharmony_ci * are actually used. In particular, the refcnt and users are not 73862306a36Sopenharmony_ci * used for management of the delete list. Using the same structure 73962306a36Sopenharmony_ci * for deleted items allows change reports to use common code with 74062306a36Sopenharmony_ci * non-deleted or query-response MCA's. 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_ci pmc = kzalloc(sizeof(*pmc), GFP_KERNEL); 74362306a36Sopenharmony_ci if (!pmc) 74462306a36Sopenharmony_ci return; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci pmc->idev = im->idev; 74762306a36Sopenharmony_ci in6_dev_hold(idev); 74862306a36Sopenharmony_ci pmc->mca_addr = im->mca_addr; 74962306a36Sopenharmony_ci pmc->mca_crcount = idev->mc_qrv; 75062306a36Sopenharmony_ci pmc->mca_sfmode = im->mca_sfmode; 75162306a36Sopenharmony_ci if (pmc->mca_sfmode == MCAST_INCLUDE) { 75262306a36Sopenharmony_ci struct ip6_sf_list *psf; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci rcu_assign_pointer(pmc->mca_tomb, 75562306a36Sopenharmony_ci mc_dereference(im->mca_tomb, idev)); 75662306a36Sopenharmony_ci rcu_assign_pointer(pmc->mca_sources, 75762306a36Sopenharmony_ci mc_dereference(im->mca_sources, idev)); 75862306a36Sopenharmony_ci RCU_INIT_POINTER(im->mca_tomb, NULL); 75962306a36Sopenharmony_ci RCU_INIT_POINTER(im->mca_sources, NULL); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) 76262306a36Sopenharmony_ci psf->sf_crcount = pmc->mca_crcount; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci rcu_assign_pointer(pmc->next, idev->mc_tomb); 76662306a36Sopenharmony_ci rcu_assign_pointer(idev->mc_tomb, pmc); 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/* called with mc_lock */ 77062306a36Sopenharmony_cistatic void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct ip6_sf_list *psf, *sources, *tomb; 77362306a36Sopenharmony_ci struct in6_addr *pmca = &im->mca_addr; 77462306a36Sopenharmony_ci struct ifmcaddr6 *pmc, *pmc_prev; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci pmc_prev = NULL; 77762306a36Sopenharmony_ci for_each_mc_tomb(idev, pmc) { 77862306a36Sopenharmony_ci if (ipv6_addr_equal(&pmc->mca_addr, pmca)) 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci pmc_prev = pmc; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci if (pmc) { 78362306a36Sopenharmony_ci if (pmc_prev) 78462306a36Sopenharmony_ci rcu_assign_pointer(pmc_prev->next, pmc->next); 78562306a36Sopenharmony_ci else 78662306a36Sopenharmony_ci rcu_assign_pointer(idev->mc_tomb, pmc->next); 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (pmc) { 79062306a36Sopenharmony_ci im->idev = pmc->idev; 79162306a36Sopenharmony_ci if (im->mca_sfmode == MCAST_INCLUDE) { 79262306a36Sopenharmony_ci tomb = rcu_replace_pointer(im->mca_tomb, 79362306a36Sopenharmony_ci mc_dereference(pmc->mca_tomb, pmc->idev), 79462306a36Sopenharmony_ci lockdep_is_held(&im->idev->mc_lock)); 79562306a36Sopenharmony_ci rcu_assign_pointer(pmc->mca_tomb, tomb); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci sources = rcu_replace_pointer(im->mca_sources, 79862306a36Sopenharmony_ci mc_dereference(pmc->mca_sources, pmc->idev), 79962306a36Sopenharmony_ci lockdep_is_held(&im->idev->mc_lock)); 80062306a36Sopenharmony_ci rcu_assign_pointer(pmc->mca_sources, sources); 80162306a36Sopenharmony_ci for_each_psf_mclock(im, psf) 80262306a36Sopenharmony_ci psf->sf_crcount = idev->mc_qrv; 80362306a36Sopenharmony_ci } else { 80462306a36Sopenharmony_ci im->mca_crcount = idev->mc_qrv; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci in6_dev_put(pmc->idev); 80762306a36Sopenharmony_ci ip6_mc_clear_src(pmc); 80862306a36Sopenharmony_ci kfree_rcu(pmc, rcu); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci/* called with mc_lock */ 81362306a36Sopenharmony_cistatic void mld_clear_delrec(struct inet6_dev *idev) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct ifmcaddr6 *pmc, *nextpmc; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci pmc = mc_dereference(idev->mc_tomb, idev); 81862306a36Sopenharmony_ci RCU_INIT_POINTER(idev->mc_tomb, NULL); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci for (; pmc; pmc = nextpmc) { 82162306a36Sopenharmony_ci nextpmc = mc_dereference(pmc->next, idev); 82262306a36Sopenharmony_ci ip6_mc_clear_src(pmc); 82362306a36Sopenharmony_ci in6_dev_put(pmc->idev); 82462306a36Sopenharmony_ci kfree_rcu(pmc, rcu); 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* clear dead sources, too */ 82862306a36Sopenharmony_ci for_each_mc_mclock(idev, pmc) { 82962306a36Sopenharmony_ci struct ip6_sf_list *psf, *psf_next; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci psf = mc_dereference(pmc->mca_tomb, idev); 83262306a36Sopenharmony_ci RCU_INIT_POINTER(pmc->mca_tomb, NULL); 83362306a36Sopenharmony_ci for (; psf; psf = psf_next) { 83462306a36Sopenharmony_ci psf_next = mc_dereference(psf->sf_next, idev); 83562306a36Sopenharmony_ci kfree_rcu(psf, rcu); 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic void mld_clear_query(struct inet6_dev *idev) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct sk_buff *skb; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci spin_lock_bh(&idev->mc_query_lock); 84562306a36Sopenharmony_ci while ((skb = __skb_dequeue(&idev->mc_query_queue))) 84662306a36Sopenharmony_ci kfree_skb(skb); 84762306a36Sopenharmony_ci spin_unlock_bh(&idev->mc_query_lock); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic void mld_clear_report(struct inet6_dev *idev) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct sk_buff *skb; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci spin_lock_bh(&idev->mc_report_lock); 85562306a36Sopenharmony_ci while ((skb = __skb_dequeue(&idev->mc_report_queue))) 85662306a36Sopenharmony_ci kfree_skb(skb); 85762306a36Sopenharmony_ci spin_unlock_bh(&idev->mc_report_lock); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void mca_get(struct ifmcaddr6 *mc) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci refcount_inc(&mc->mca_refcnt); 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic void ma_put(struct ifmcaddr6 *mc) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci if (refcount_dec_and_test(&mc->mca_refcnt)) { 86862306a36Sopenharmony_ci in6_dev_put(mc->idev); 86962306a36Sopenharmony_ci kfree_rcu(mc, rcu); 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/* called with mc_lock */ 87462306a36Sopenharmony_cistatic struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev, 87562306a36Sopenharmony_ci const struct in6_addr *addr, 87662306a36Sopenharmony_ci unsigned int mode) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct ifmcaddr6 *mc; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci mc = kzalloc(sizeof(*mc), GFP_KERNEL); 88162306a36Sopenharmony_ci if (!mc) 88262306a36Sopenharmony_ci return NULL; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci INIT_DELAYED_WORK(&mc->mca_work, mld_mca_work); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci mc->mca_addr = *addr; 88762306a36Sopenharmony_ci mc->idev = idev; /* reference taken by caller */ 88862306a36Sopenharmony_ci mc->mca_users = 1; 88962306a36Sopenharmony_ci /* mca_stamp should be updated upon changes */ 89062306a36Sopenharmony_ci mc->mca_cstamp = mc->mca_tstamp = jiffies; 89162306a36Sopenharmony_ci refcount_set(&mc->mca_refcnt, 1); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci mc->mca_sfmode = mode; 89462306a36Sopenharmony_ci mc->mca_sfcount[mode] = 1; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (ipv6_addr_is_ll_all_nodes(&mc->mca_addr) || 89762306a36Sopenharmony_ci IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) 89862306a36Sopenharmony_ci mc->mca_flags |= MAF_NOREPORT; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci return mc; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci/* 90462306a36Sopenharmony_ci * device multicast group inc (add if not found) 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_cistatic int __ipv6_dev_mc_inc(struct net_device *dev, 90762306a36Sopenharmony_ci const struct in6_addr *addr, unsigned int mode) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct ifmcaddr6 *mc; 91062306a36Sopenharmony_ci struct inet6_dev *idev; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci ASSERT_RTNL(); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* we need to take a reference on idev */ 91562306a36Sopenharmony_ci idev = in6_dev_get(dev); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (!idev) 91862306a36Sopenharmony_ci return -EINVAL; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (idev->dead) { 92162306a36Sopenharmony_ci in6_dev_put(idev); 92262306a36Sopenharmony_ci return -ENODEV; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 92662306a36Sopenharmony_ci for_each_mc_mclock(idev, mc) { 92762306a36Sopenharmony_ci if (ipv6_addr_equal(&mc->mca_addr, addr)) { 92862306a36Sopenharmony_ci mc->mca_users++; 92962306a36Sopenharmony_ci ip6_mc_add_src(idev, &mc->mca_addr, mode, 0, NULL, 0); 93062306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 93162306a36Sopenharmony_ci in6_dev_put(idev); 93262306a36Sopenharmony_ci return 0; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci mc = mca_alloc(idev, addr, mode); 93762306a36Sopenharmony_ci if (!mc) { 93862306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 93962306a36Sopenharmony_ci in6_dev_put(idev); 94062306a36Sopenharmony_ci return -ENOMEM; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci rcu_assign_pointer(mc->next, idev->mc_list); 94462306a36Sopenharmony_ci rcu_assign_pointer(idev->mc_list, mc); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci mca_get(mc); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci mld_del_delrec(idev, mc); 94962306a36Sopenharmony_ci igmp6_group_added(mc); 95062306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 95162306a36Sopenharmony_ci ma_put(mc); 95262306a36Sopenharmony_ci return 0; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ciint ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci return __ipv6_dev_mc_inc(dev, addr, MCAST_EXCLUDE); 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_mc_inc); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* 96262306a36Sopenharmony_ci * device multicast group del 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_ciint __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct ifmcaddr6 *ma, __rcu **map; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci ASSERT_RTNL(); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 97162306a36Sopenharmony_ci for (map = &idev->mc_list; 97262306a36Sopenharmony_ci (ma = mc_dereference(*map, idev)); 97362306a36Sopenharmony_ci map = &ma->next) { 97462306a36Sopenharmony_ci if (ipv6_addr_equal(&ma->mca_addr, addr)) { 97562306a36Sopenharmony_ci if (--ma->mca_users == 0) { 97662306a36Sopenharmony_ci *map = ma->next; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci igmp6_group_dropped(ma); 97962306a36Sopenharmony_ci ip6_mc_clear_src(ma); 98062306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci ma_put(ma); 98362306a36Sopenharmony_ci return 0; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 98662306a36Sopenharmony_ci return 0; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 99162306a36Sopenharmony_ci return -ENOENT; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ciint ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct inet6_dev *idev; 99762306a36Sopenharmony_ci int err; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci ASSERT_RTNL(); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci idev = __in6_dev_get(dev); 100262306a36Sopenharmony_ci if (!idev) 100362306a36Sopenharmony_ci err = -ENODEV; 100462306a36Sopenharmony_ci else 100562306a36Sopenharmony_ci err = __ipv6_dev_mc_dec(idev, addr); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci return err; 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_mc_dec); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci/* 101262306a36Sopenharmony_ci * check if the interface/address pair is valid 101362306a36Sopenharmony_ci */ 101462306a36Sopenharmony_cibool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, 101562306a36Sopenharmony_ci const struct in6_addr *src_addr) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct inet6_dev *idev; 101862306a36Sopenharmony_ci struct ifmcaddr6 *mc; 101962306a36Sopenharmony_ci bool rv = false; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci rcu_read_lock(); 102262306a36Sopenharmony_ci idev = __in6_dev_get(dev); 102362306a36Sopenharmony_ci if (idev) { 102462306a36Sopenharmony_ci for_each_mc_rcu(idev, mc) { 102562306a36Sopenharmony_ci if (ipv6_addr_equal(&mc->mca_addr, group)) 102662306a36Sopenharmony_ci break; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci if (mc) { 102962306a36Sopenharmony_ci if (src_addr && !ipv6_addr_any(src_addr)) { 103062306a36Sopenharmony_ci struct ip6_sf_list *psf; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci for_each_psf_rcu(mc, psf) { 103362306a36Sopenharmony_ci if (ipv6_addr_equal(&psf->sf_addr, src_addr)) 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci if (psf) 103762306a36Sopenharmony_ci rv = psf->sf_count[MCAST_INCLUDE] || 103862306a36Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE] != 103962306a36Sopenharmony_ci mc->mca_sfcount[MCAST_EXCLUDE]; 104062306a36Sopenharmony_ci else 104162306a36Sopenharmony_ci rv = mc->mca_sfcount[MCAST_EXCLUDE] != 0; 104262306a36Sopenharmony_ci } else 104362306a36Sopenharmony_ci rv = true; /* don't filter unspecified source */ 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci rcu_read_unlock(); 104762306a36Sopenharmony_ci return rv; 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci/* called with mc_lock */ 105162306a36Sopenharmony_cistatic void mld_gq_start_work(struct inet6_dev *idev) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci unsigned long tv = get_random_u32_below(idev->mc_maxdelay); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci idev->mc_gq_running = 1; 105662306a36Sopenharmony_ci if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) 105762306a36Sopenharmony_ci in6_dev_hold(idev); 105862306a36Sopenharmony_ci} 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci/* called with mc_lock */ 106162306a36Sopenharmony_cistatic void mld_gq_stop_work(struct inet6_dev *idev) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci idev->mc_gq_running = 0; 106462306a36Sopenharmony_ci if (cancel_delayed_work(&idev->mc_gq_work)) 106562306a36Sopenharmony_ci __in6_dev_put(idev); 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci/* called with mc_lock */ 106962306a36Sopenharmony_cistatic void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci unsigned long tv = get_random_u32_below(delay); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) 107462306a36Sopenharmony_ci in6_dev_hold(idev); 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci/* called with mc_lock */ 107862306a36Sopenharmony_cistatic void mld_ifc_stop_work(struct inet6_dev *idev) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci idev->mc_ifc_count = 0; 108162306a36Sopenharmony_ci if (cancel_delayed_work(&idev->mc_ifc_work)) 108262306a36Sopenharmony_ci __in6_dev_put(idev); 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci/* called with mc_lock */ 108662306a36Sopenharmony_cistatic void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci unsigned long tv = get_random_u32_below(delay); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) 109162306a36Sopenharmony_ci in6_dev_hold(idev); 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic void mld_dad_stop_work(struct inet6_dev *idev) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci if (cancel_delayed_work(&idev->mc_dad_work)) 109762306a36Sopenharmony_ci __in6_dev_put(idev); 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_cistatic void mld_query_stop_work(struct inet6_dev *idev) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci spin_lock_bh(&idev->mc_query_lock); 110362306a36Sopenharmony_ci if (cancel_delayed_work(&idev->mc_query_work)) 110462306a36Sopenharmony_ci __in6_dev_put(idev); 110562306a36Sopenharmony_ci spin_unlock_bh(&idev->mc_query_lock); 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_cistatic void mld_report_stop_work(struct inet6_dev *idev) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci if (cancel_delayed_work_sync(&idev->mc_report_work)) 111162306a36Sopenharmony_ci __in6_dev_put(idev); 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci/* 111562306a36Sopenharmony_ci * IGMP handling (alias multicast ICMPv6 messages) 111662306a36Sopenharmony_ci * called with mc_lock 111762306a36Sopenharmony_ci */ 111862306a36Sopenharmony_cistatic void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci unsigned long delay = resptime; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* Do not start work for these addresses */ 112362306a36Sopenharmony_ci if (ipv6_addr_is_ll_all_nodes(&ma->mca_addr) || 112462306a36Sopenharmony_ci IPV6_ADDR_MC_SCOPE(&ma->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) 112562306a36Sopenharmony_ci return; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (cancel_delayed_work(&ma->mca_work)) { 112862306a36Sopenharmony_ci refcount_dec(&ma->mca_refcnt); 112962306a36Sopenharmony_ci delay = ma->mca_work.timer.expires - jiffies; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (delay >= resptime) 113362306a36Sopenharmony_ci delay = get_random_u32_below(resptime); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (!mod_delayed_work(mld_wq, &ma->mca_work, delay)) 113662306a36Sopenharmony_ci refcount_inc(&ma->mca_refcnt); 113762306a36Sopenharmony_ci ma->mca_flags |= MAF_TIMER_RUNNING; 113862306a36Sopenharmony_ci} 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci/* mark EXCLUDE-mode sources 114162306a36Sopenharmony_ci * called with mc_lock 114262306a36Sopenharmony_ci */ 114362306a36Sopenharmony_cistatic bool mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs, 114462306a36Sopenharmony_ci const struct in6_addr *srcs) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci struct ip6_sf_list *psf; 114762306a36Sopenharmony_ci int i, scount; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci scount = 0; 115062306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) { 115162306a36Sopenharmony_ci if (scount == nsrcs) 115262306a36Sopenharmony_ci break; 115362306a36Sopenharmony_ci for (i = 0; i < nsrcs; i++) { 115462306a36Sopenharmony_ci /* skip inactive filters */ 115562306a36Sopenharmony_ci if (psf->sf_count[MCAST_INCLUDE] || 115662306a36Sopenharmony_ci pmc->mca_sfcount[MCAST_EXCLUDE] != 115762306a36Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]) 115862306a36Sopenharmony_ci break; 115962306a36Sopenharmony_ci if (ipv6_addr_equal(&srcs[i], &psf->sf_addr)) { 116062306a36Sopenharmony_ci scount++; 116162306a36Sopenharmony_ci break; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci pmc->mca_flags &= ~MAF_GSQUERY; 116662306a36Sopenharmony_ci if (scount == nsrcs) /* all sources excluded */ 116762306a36Sopenharmony_ci return false; 116862306a36Sopenharmony_ci return true; 116962306a36Sopenharmony_ci} 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci/* called with mc_lock */ 117262306a36Sopenharmony_cistatic bool mld_marksources(struct ifmcaddr6 *pmc, int nsrcs, 117362306a36Sopenharmony_ci const struct in6_addr *srcs) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci struct ip6_sf_list *psf; 117662306a36Sopenharmony_ci int i, scount; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (pmc->mca_sfmode == MCAST_EXCLUDE) 117962306a36Sopenharmony_ci return mld_xmarksources(pmc, nsrcs, srcs); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* mark INCLUDE-mode sources */ 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci scount = 0; 118462306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) { 118562306a36Sopenharmony_ci if (scount == nsrcs) 118662306a36Sopenharmony_ci break; 118762306a36Sopenharmony_ci for (i = 0; i < nsrcs; i++) { 118862306a36Sopenharmony_ci if (ipv6_addr_equal(&srcs[i], &psf->sf_addr)) { 118962306a36Sopenharmony_ci psf->sf_gsresp = 1; 119062306a36Sopenharmony_ci scount++; 119162306a36Sopenharmony_ci break; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci if (!scount) { 119662306a36Sopenharmony_ci pmc->mca_flags &= ~MAF_GSQUERY; 119762306a36Sopenharmony_ci return false; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci pmc->mca_flags |= MAF_GSQUERY; 120062306a36Sopenharmony_ci return true; 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_cistatic int mld_force_mld_version(const struct inet6_dev *idev) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci /* Normally, both are 0 here. If enforcement to a particular is 120662306a36Sopenharmony_ci * being used, individual device enforcement will have a lower 120762306a36Sopenharmony_ci * precedence over 'all' device (.../conf/all/force_mld_version). 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (dev_net(idev->dev)->ipv6.devconf_all->force_mld_version != 0) 121162306a36Sopenharmony_ci return dev_net(idev->dev)->ipv6.devconf_all->force_mld_version; 121262306a36Sopenharmony_ci else 121362306a36Sopenharmony_ci return idev->cnf.force_mld_version; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic bool mld_in_v2_mode_only(const struct inet6_dev *idev) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci return mld_force_mld_version(idev) == 2; 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic bool mld_in_v1_mode_only(const struct inet6_dev *idev) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci return mld_force_mld_version(idev) == 1; 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cistatic bool mld_in_v1_mode(const struct inet6_dev *idev) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci if (mld_in_v2_mode_only(idev)) 122962306a36Sopenharmony_ci return false; 123062306a36Sopenharmony_ci if (mld_in_v1_mode_only(idev)) 123162306a36Sopenharmony_ci return true; 123262306a36Sopenharmony_ci if (idev->mc_v1_seen && time_before(jiffies, idev->mc_v1_seen)) 123362306a36Sopenharmony_ci return true; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci return false; 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_cistatic void mld_set_v1_mode(struct inet6_dev *idev) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci /* RFC3810, relevant sections: 124162306a36Sopenharmony_ci * - 9.1. Robustness Variable 124262306a36Sopenharmony_ci * - 9.2. Query Interval 124362306a36Sopenharmony_ci * - 9.3. Query Response Interval 124462306a36Sopenharmony_ci * - 9.12. Older Version Querier Present Timeout 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_ci unsigned long switchback; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci switchback = (idev->mc_qrv * idev->mc_qi) + idev->mc_qri; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci idev->mc_v1_seen = jiffies + switchback; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_cistatic void mld_update_qrv(struct inet6_dev *idev, 125462306a36Sopenharmony_ci const struct mld2_query *mlh2) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci /* RFC3810, relevant sections: 125762306a36Sopenharmony_ci * - 5.1.8. QRV (Querier's Robustness Variable) 125862306a36Sopenharmony_ci * - 9.1. Robustness Variable 125962306a36Sopenharmony_ci */ 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* The value of the Robustness Variable MUST NOT be zero, 126262306a36Sopenharmony_ci * and SHOULD NOT be one. Catch this here if we ever run 126362306a36Sopenharmony_ci * into such a case in future. 126462306a36Sopenharmony_ci */ 126562306a36Sopenharmony_ci const int min_qrv = min(MLD_QRV_DEFAULT, sysctl_mld_qrv); 126662306a36Sopenharmony_ci WARN_ON(idev->mc_qrv == 0); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (mlh2->mld2q_qrv > 0) 126962306a36Sopenharmony_ci idev->mc_qrv = mlh2->mld2q_qrv; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (unlikely(idev->mc_qrv < min_qrv)) { 127262306a36Sopenharmony_ci net_warn_ratelimited("IPv6: MLD: clamping QRV from %u to %u!\n", 127362306a36Sopenharmony_ci idev->mc_qrv, min_qrv); 127462306a36Sopenharmony_ci idev->mc_qrv = min_qrv; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci} 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_cistatic void mld_update_qi(struct inet6_dev *idev, 127962306a36Sopenharmony_ci const struct mld2_query *mlh2) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci /* RFC3810, relevant sections: 128262306a36Sopenharmony_ci * - 5.1.9. QQIC (Querier's Query Interval Code) 128362306a36Sopenharmony_ci * - 9.2. Query Interval 128462306a36Sopenharmony_ci * - 9.12. Older Version Querier Present Timeout 128562306a36Sopenharmony_ci * (the [Query Interval] in the last Query received) 128662306a36Sopenharmony_ci */ 128762306a36Sopenharmony_ci unsigned long mc_qqi; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (mlh2->mld2q_qqic < 128) { 129062306a36Sopenharmony_ci mc_qqi = mlh2->mld2q_qqic; 129162306a36Sopenharmony_ci } else { 129262306a36Sopenharmony_ci unsigned long mc_man, mc_exp; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci mc_exp = MLDV2_QQIC_EXP(mlh2->mld2q_qqic); 129562306a36Sopenharmony_ci mc_man = MLDV2_QQIC_MAN(mlh2->mld2q_qqic); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci mc_qqi = (mc_man | 0x10) << (mc_exp + 3); 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci idev->mc_qi = mc_qqi * HZ; 130162306a36Sopenharmony_ci} 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cistatic void mld_update_qri(struct inet6_dev *idev, 130462306a36Sopenharmony_ci const struct mld2_query *mlh2) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci /* RFC3810, relevant sections: 130762306a36Sopenharmony_ci * - 5.1.3. Maximum Response Code 130862306a36Sopenharmony_ci * - 9.3. Query Response Interval 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_ci idev->mc_qri = msecs_to_jiffies(mldv2_mrc(mlh2)); 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cistatic int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld, 131462306a36Sopenharmony_ci unsigned long *max_delay, bool v1_query) 131562306a36Sopenharmony_ci{ 131662306a36Sopenharmony_ci unsigned long mldv1_md; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci /* Ignore v1 queries */ 131962306a36Sopenharmony_ci if (mld_in_v2_mode_only(idev)) 132062306a36Sopenharmony_ci return -EINVAL; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci mldv1_md = ntohs(mld->mld_maxdelay); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* When in MLDv1 fallback and a MLDv2 router start-up being 132562306a36Sopenharmony_ci * unaware of current MLDv1 operation, the MRC == MRD mapping 132662306a36Sopenharmony_ci * only works when the exponential algorithm is not being 132762306a36Sopenharmony_ci * used (as MLDv1 is unaware of such things). 132862306a36Sopenharmony_ci * 132962306a36Sopenharmony_ci * According to the RFC author, the MLDv2 implementations 133062306a36Sopenharmony_ci * he's aware of all use a MRC < 32768 on start up queries. 133162306a36Sopenharmony_ci * 133262306a36Sopenharmony_ci * Thus, should we *ever* encounter something else larger 133362306a36Sopenharmony_ci * than that, just assume the maximum possible within our 133462306a36Sopenharmony_ci * reach. 133562306a36Sopenharmony_ci */ 133662306a36Sopenharmony_ci if (!v1_query) 133762306a36Sopenharmony_ci mldv1_md = min(mldv1_md, MLDV1_MRD_MAX_COMPAT); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci *max_delay = max(msecs_to_jiffies(mldv1_md), 1UL); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /* MLDv1 router present: we need to go into v1 mode *only* 134262306a36Sopenharmony_ci * when an MLDv1 query is received as per section 9.12. of 134362306a36Sopenharmony_ci * RFC3810! And we know from RFC2710 section 3.7 that MLDv1 134462306a36Sopenharmony_ci * queries MUST be of exactly 24 octets. 134562306a36Sopenharmony_ci */ 134662306a36Sopenharmony_ci if (v1_query) 134762306a36Sopenharmony_ci mld_set_v1_mode(idev); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci /* cancel MLDv2 report work */ 135062306a36Sopenharmony_ci mld_gq_stop_work(idev); 135162306a36Sopenharmony_ci /* cancel the interface change work */ 135262306a36Sopenharmony_ci mld_ifc_stop_work(idev); 135362306a36Sopenharmony_ci /* clear deleted report items */ 135462306a36Sopenharmony_ci mld_clear_delrec(idev); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci return 0; 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cistatic void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld, 136062306a36Sopenharmony_ci unsigned long *max_delay) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci *max_delay = max(msecs_to_jiffies(mldv2_mrc(mld)), 1UL); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci mld_update_qrv(idev, mld); 136562306a36Sopenharmony_ci mld_update_qi(idev, mld); 136662306a36Sopenharmony_ci mld_update_qri(idev, mld); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci idev->mc_maxdelay = *max_delay; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci return; 137162306a36Sopenharmony_ci} 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci/* called with rcu_read_lock() */ 137462306a36Sopenharmony_civoid igmp6_event_query(struct sk_buff *skb) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(skb->dev); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (!idev || idev->dead) 137962306a36Sopenharmony_ci goto out; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci spin_lock_bh(&idev->mc_query_lock); 138262306a36Sopenharmony_ci if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) { 138362306a36Sopenharmony_ci __skb_queue_tail(&idev->mc_query_queue, skb); 138462306a36Sopenharmony_ci if (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) 138562306a36Sopenharmony_ci in6_dev_hold(idev); 138662306a36Sopenharmony_ci skb = NULL; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci spin_unlock_bh(&idev->mc_query_lock); 138962306a36Sopenharmony_ciout: 139062306a36Sopenharmony_ci kfree_skb(skb); 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic void __mld_query_work(struct sk_buff *skb) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci struct mld2_query *mlh2 = NULL; 139662306a36Sopenharmony_ci const struct in6_addr *group; 139762306a36Sopenharmony_ci unsigned long max_delay; 139862306a36Sopenharmony_ci struct inet6_dev *idev; 139962306a36Sopenharmony_ci struct ifmcaddr6 *ma; 140062306a36Sopenharmony_ci struct mld_msg *mld; 140162306a36Sopenharmony_ci int group_type; 140262306a36Sopenharmony_ci int mark = 0; 140362306a36Sopenharmony_ci int len, err; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct in6_addr))) 140662306a36Sopenharmony_ci goto kfree_skb; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci /* compute payload length excluding extension headers */ 140962306a36Sopenharmony_ci len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr); 141062306a36Sopenharmony_ci len -= skb_network_header_len(skb); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci /* RFC3810 6.2 141362306a36Sopenharmony_ci * Upon reception of an MLD message that contains a Query, the node 141462306a36Sopenharmony_ci * checks if the source address of the message is a valid link-local 141562306a36Sopenharmony_ci * address, if the Hop Limit is set to 1, and if the Router Alert 141662306a36Sopenharmony_ci * option is present in the Hop-By-Hop Options header of the IPv6 141762306a36Sopenharmony_ci * packet. If any of these checks fails, the packet is dropped. 141862306a36Sopenharmony_ci */ 141962306a36Sopenharmony_ci if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL) || 142062306a36Sopenharmony_ci ipv6_hdr(skb)->hop_limit != 1 || 142162306a36Sopenharmony_ci !(IP6CB(skb)->flags & IP6SKB_ROUTERALERT) || 142262306a36Sopenharmony_ci IP6CB(skb)->ra != htons(IPV6_OPT_ROUTERALERT_MLD)) 142362306a36Sopenharmony_ci goto kfree_skb; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci idev = in6_dev_get(skb->dev); 142662306a36Sopenharmony_ci if (!idev) 142762306a36Sopenharmony_ci goto kfree_skb; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci mld = (struct mld_msg *)icmp6_hdr(skb); 143062306a36Sopenharmony_ci group = &mld->mld_mca; 143162306a36Sopenharmony_ci group_type = ipv6_addr_type(group); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci if (group_type != IPV6_ADDR_ANY && 143462306a36Sopenharmony_ci !(group_type&IPV6_ADDR_MULTICAST)) 143562306a36Sopenharmony_ci goto out; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci if (len < MLD_V1_QUERY_LEN) { 143862306a36Sopenharmony_ci goto out; 143962306a36Sopenharmony_ci } else if (len == MLD_V1_QUERY_LEN || mld_in_v1_mode(idev)) { 144062306a36Sopenharmony_ci err = mld_process_v1(idev, mld, &max_delay, 144162306a36Sopenharmony_ci len == MLD_V1_QUERY_LEN); 144262306a36Sopenharmony_ci if (err < 0) 144362306a36Sopenharmony_ci goto out; 144462306a36Sopenharmony_ci } else if (len >= MLD_V2_QUERY_LEN_MIN) { 144562306a36Sopenharmony_ci int srcs_offset = sizeof(struct mld2_query) - 144662306a36Sopenharmony_ci sizeof(struct icmp6hdr); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if (!pskb_may_pull(skb, srcs_offset)) 144962306a36Sopenharmony_ci goto out; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci mlh2 = (struct mld2_query *)skb_transport_header(skb); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci mld_process_v2(idev, mlh2, &max_delay); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (group_type == IPV6_ADDR_ANY) { /* general query */ 145662306a36Sopenharmony_ci if (mlh2->mld2q_nsrcs) 145762306a36Sopenharmony_ci goto out; /* no sources allowed */ 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci mld_gq_start_work(idev); 146062306a36Sopenharmony_ci goto out; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci /* mark sources to include, if group & source-specific */ 146362306a36Sopenharmony_ci if (mlh2->mld2q_nsrcs != 0) { 146462306a36Sopenharmony_ci if (!pskb_may_pull(skb, srcs_offset + 146562306a36Sopenharmony_ci ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr))) 146662306a36Sopenharmony_ci goto out; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci mlh2 = (struct mld2_query *)skb_transport_header(skb); 146962306a36Sopenharmony_ci mark = 1; 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci } else { 147262306a36Sopenharmony_ci goto out; 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci if (group_type == IPV6_ADDR_ANY) { 147662306a36Sopenharmony_ci for_each_mc_mclock(idev, ma) { 147762306a36Sopenharmony_ci igmp6_group_queried(ma, max_delay); 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci } else { 148062306a36Sopenharmony_ci for_each_mc_mclock(idev, ma) { 148162306a36Sopenharmony_ci if (!ipv6_addr_equal(group, &ma->mca_addr)) 148262306a36Sopenharmony_ci continue; 148362306a36Sopenharmony_ci if (ma->mca_flags & MAF_TIMER_RUNNING) { 148462306a36Sopenharmony_ci /* gsquery <- gsquery && mark */ 148562306a36Sopenharmony_ci if (!mark) 148662306a36Sopenharmony_ci ma->mca_flags &= ~MAF_GSQUERY; 148762306a36Sopenharmony_ci } else { 148862306a36Sopenharmony_ci /* gsquery <- mark */ 148962306a36Sopenharmony_ci if (mark) 149062306a36Sopenharmony_ci ma->mca_flags |= MAF_GSQUERY; 149162306a36Sopenharmony_ci else 149262306a36Sopenharmony_ci ma->mca_flags &= ~MAF_GSQUERY; 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci if (!(ma->mca_flags & MAF_GSQUERY) || 149562306a36Sopenharmony_ci mld_marksources(ma, ntohs(mlh2->mld2q_nsrcs), mlh2->mld2q_srcs)) 149662306a36Sopenharmony_ci igmp6_group_queried(ma, max_delay); 149762306a36Sopenharmony_ci break; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ciout: 150262306a36Sopenharmony_ci in6_dev_put(idev); 150362306a36Sopenharmony_cikfree_skb: 150462306a36Sopenharmony_ci consume_skb(skb); 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_cistatic void mld_query_work(struct work_struct *work) 150862306a36Sopenharmony_ci{ 150962306a36Sopenharmony_ci struct inet6_dev *idev = container_of(to_delayed_work(work), 151062306a36Sopenharmony_ci struct inet6_dev, 151162306a36Sopenharmony_ci mc_query_work); 151262306a36Sopenharmony_ci struct sk_buff_head q; 151362306a36Sopenharmony_ci struct sk_buff *skb; 151462306a36Sopenharmony_ci bool rework = false; 151562306a36Sopenharmony_ci int cnt = 0; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci skb_queue_head_init(&q); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci spin_lock_bh(&idev->mc_query_lock); 152062306a36Sopenharmony_ci while ((skb = __skb_dequeue(&idev->mc_query_queue))) { 152162306a36Sopenharmony_ci __skb_queue_tail(&q, skb); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci if (++cnt >= MLD_MAX_QUEUE) { 152462306a36Sopenharmony_ci rework = true; 152562306a36Sopenharmony_ci break; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci spin_unlock_bh(&idev->mc_query_lock); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 153162306a36Sopenharmony_ci while ((skb = __skb_dequeue(&q))) 153262306a36Sopenharmony_ci __mld_query_work(skb); 153362306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci if (rework && queue_delayed_work(mld_wq, &idev->mc_query_work, 0)) 153662306a36Sopenharmony_ci return; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci in6_dev_put(idev); 153962306a36Sopenharmony_ci} 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci/* called with rcu_read_lock() */ 154262306a36Sopenharmony_civoid igmp6_event_report(struct sk_buff *skb) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(skb->dev); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci if (!idev || idev->dead) 154762306a36Sopenharmony_ci goto out; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci spin_lock_bh(&idev->mc_report_lock); 155062306a36Sopenharmony_ci if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) { 155162306a36Sopenharmony_ci __skb_queue_tail(&idev->mc_report_queue, skb); 155262306a36Sopenharmony_ci if (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) 155362306a36Sopenharmony_ci in6_dev_hold(idev); 155462306a36Sopenharmony_ci skb = NULL; 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci spin_unlock_bh(&idev->mc_report_lock); 155762306a36Sopenharmony_ciout: 155862306a36Sopenharmony_ci kfree_skb(skb); 155962306a36Sopenharmony_ci} 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_cistatic void __mld_report_work(struct sk_buff *skb) 156262306a36Sopenharmony_ci{ 156362306a36Sopenharmony_ci struct inet6_dev *idev; 156462306a36Sopenharmony_ci struct ifmcaddr6 *ma; 156562306a36Sopenharmony_ci struct mld_msg *mld; 156662306a36Sopenharmony_ci int addr_type; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci /* Our own report looped back. Ignore it. */ 156962306a36Sopenharmony_ci if (skb->pkt_type == PACKET_LOOPBACK) 157062306a36Sopenharmony_ci goto kfree_skb; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci /* send our report if the MC router may not have heard this report */ 157362306a36Sopenharmony_ci if (skb->pkt_type != PACKET_MULTICAST && 157462306a36Sopenharmony_ci skb->pkt_type != PACKET_BROADCAST) 157562306a36Sopenharmony_ci goto kfree_skb; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*mld) - sizeof(struct icmp6hdr))) 157862306a36Sopenharmony_ci goto kfree_skb; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci mld = (struct mld_msg *)icmp6_hdr(skb); 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci /* Drop reports with not link local source */ 158362306a36Sopenharmony_ci addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr); 158462306a36Sopenharmony_ci if (addr_type != IPV6_ADDR_ANY && 158562306a36Sopenharmony_ci !(addr_type&IPV6_ADDR_LINKLOCAL)) 158662306a36Sopenharmony_ci goto kfree_skb; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci idev = in6_dev_get(skb->dev); 158962306a36Sopenharmony_ci if (!idev) 159062306a36Sopenharmony_ci goto kfree_skb; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci /* 159362306a36Sopenharmony_ci * Cancel the work for this group 159462306a36Sopenharmony_ci */ 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci for_each_mc_mclock(idev, ma) { 159762306a36Sopenharmony_ci if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) { 159862306a36Sopenharmony_ci if (cancel_delayed_work(&ma->mca_work)) 159962306a36Sopenharmony_ci refcount_dec(&ma->mca_refcnt); 160062306a36Sopenharmony_ci ma->mca_flags &= ~(MAF_LAST_REPORTER | 160162306a36Sopenharmony_ci MAF_TIMER_RUNNING); 160262306a36Sopenharmony_ci break; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci in6_dev_put(idev); 160762306a36Sopenharmony_cikfree_skb: 160862306a36Sopenharmony_ci consume_skb(skb); 160962306a36Sopenharmony_ci} 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_cistatic void mld_report_work(struct work_struct *work) 161262306a36Sopenharmony_ci{ 161362306a36Sopenharmony_ci struct inet6_dev *idev = container_of(to_delayed_work(work), 161462306a36Sopenharmony_ci struct inet6_dev, 161562306a36Sopenharmony_ci mc_report_work); 161662306a36Sopenharmony_ci struct sk_buff_head q; 161762306a36Sopenharmony_ci struct sk_buff *skb; 161862306a36Sopenharmony_ci bool rework = false; 161962306a36Sopenharmony_ci int cnt = 0; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci skb_queue_head_init(&q); 162262306a36Sopenharmony_ci spin_lock_bh(&idev->mc_report_lock); 162362306a36Sopenharmony_ci while ((skb = __skb_dequeue(&idev->mc_report_queue))) { 162462306a36Sopenharmony_ci __skb_queue_tail(&q, skb); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (++cnt >= MLD_MAX_QUEUE) { 162762306a36Sopenharmony_ci rework = true; 162862306a36Sopenharmony_ci break; 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci spin_unlock_bh(&idev->mc_report_lock); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 163462306a36Sopenharmony_ci while ((skb = __skb_dequeue(&q))) 163562306a36Sopenharmony_ci __mld_report_work(skb); 163662306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci if (rework && queue_delayed_work(mld_wq, &idev->mc_report_work, 0)) 163962306a36Sopenharmony_ci return; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci in6_dev_put(idev); 164262306a36Sopenharmony_ci} 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_cistatic bool is_in(struct ifmcaddr6 *pmc, struct ip6_sf_list *psf, int type, 164562306a36Sopenharmony_ci int gdeleted, int sdeleted) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci switch (type) { 164862306a36Sopenharmony_ci case MLD2_MODE_IS_INCLUDE: 164962306a36Sopenharmony_ci case MLD2_MODE_IS_EXCLUDE: 165062306a36Sopenharmony_ci if (gdeleted || sdeleted) 165162306a36Sopenharmony_ci return false; 165262306a36Sopenharmony_ci if (!((pmc->mca_flags & MAF_GSQUERY) && !psf->sf_gsresp)) { 165362306a36Sopenharmony_ci if (pmc->mca_sfmode == MCAST_INCLUDE) 165462306a36Sopenharmony_ci return true; 165562306a36Sopenharmony_ci /* don't include if this source is excluded 165662306a36Sopenharmony_ci * in all filters 165762306a36Sopenharmony_ci */ 165862306a36Sopenharmony_ci if (psf->sf_count[MCAST_INCLUDE]) 165962306a36Sopenharmony_ci return type == MLD2_MODE_IS_INCLUDE; 166062306a36Sopenharmony_ci return pmc->mca_sfcount[MCAST_EXCLUDE] == 166162306a36Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci return false; 166462306a36Sopenharmony_ci case MLD2_CHANGE_TO_INCLUDE: 166562306a36Sopenharmony_ci if (gdeleted || sdeleted) 166662306a36Sopenharmony_ci return false; 166762306a36Sopenharmony_ci return psf->sf_count[MCAST_INCLUDE] != 0; 166862306a36Sopenharmony_ci case MLD2_CHANGE_TO_EXCLUDE: 166962306a36Sopenharmony_ci if (gdeleted || sdeleted) 167062306a36Sopenharmony_ci return false; 167162306a36Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE] == 0 || 167262306a36Sopenharmony_ci psf->sf_count[MCAST_INCLUDE]) 167362306a36Sopenharmony_ci return false; 167462306a36Sopenharmony_ci return pmc->mca_sfcount[MCAST_EXCLUDE] == 167562306a36Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]; 167662306a36Sopenharmony_ci case MLD2_ALLOW_NEW_SOURCES: 167762306a36Sopenharmony_ci if (gdeleted || !psf->sf_crcount) 167862306a36Sopenharmony_ci return false; 167962306a36Sopenharmony_ci return (pmc->mca_sfmode == MCAST_INCLUDE) ^ sdeleted; 168062306a36Sopenharmony_ci case MLD2_BLOCK_OLD_SOURCES: 168162306a36Sopenharmony_ci if (pmc->mca_sfmode == MCAST_INCLUDE) 168262306a36Sopenharmony_ci return gdeleted || (psf->sf_crcount && sdeleted); 168362306a36Sopenharmony_ci return psf->sf_crcount && !gdeleted && !sdeleted; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci return false; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cistatic int 168962306a36Sopenharmony_cimld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci struct ip6_sf_list *psf; 169262306a36Sopenharmony_ci int scount = 0; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) { 169562306a36Sopenharmony_ci if (!is_in(pmc, psf, type, gdeleted, sdeleted)) 169662306a36Sopenharmony_ci continue; 169762306a36Sopenharmony_ci scount++; 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci return scount; 170062306a36Sopenharmony_ci} 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_cistatic void ip6_mc_hdr(const struct sock *sk, struct sk_buff *skb, 170362306a36Sopenharmony_ci struct net_device *dev, const struct in6_addr *saddr, 170462306a36Sopenharmony_ci const struct in6_addr *daddr, int proto, int len) 170562306a36Sopenharmony_ci{ 170662306a36Sopenharmony_ci struct ipv6hdr *hdr; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 170962306a36Sopenharmony_ci skb->dev = dev; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci skb_reset_network_header(skb); 171262306a36Sopenharmony_ci skb_put(skb, sizeof(struct ipv6hdr)); 171362306a36Sopenharmony_ci hdr = ipv6_hdr(skb); 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci ip6_flow_hdr(hdr, 0, 0); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci hdr->payload_len = htons(len); 171862306a36Sopenharmony_ci hdr->nexthdr = proto; 171962306a36Sopenharmony_ci hdr->hop_limit = inet6_sk(sk)->hop_limit; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci hdr->saddr = *saddr; 172262306a36Sopenharmony_ci hdr->daddr = *daddr; 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cistatic struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu) 172662306a36Sopenharmony_ci{ 172762306a36Sopenharmony_ci u8 ra[8] = { IPPROTO_ICMPV6, 0, IPV6_TLV_ROUTERALERT, 172862306a36Sopenharmony_ci 2, 0, 0, IPV6_TLV_PADN, 0 }; 172962306a36Sopenharmony_ci struct net_device *dev = idev->dev; 173062306a36Sopenharmony_ci int hlen = LL_RESERVED_SPACE(dev); 173162306a36Sopenharmony_ci int tlen = dev->needed_tailroom; 173262306a36Sopenharmony_ci struct net *net = dev_net(dev); 173362306a36Sopenharmony_ci const struct in6_addr *saddr; 173462306a36Sopenharmony_ci struct in6_addr addr_buf; 173562306a36Sopenharmony_ci struct mld2_report *pmr; 173662306a36Sopenharmony_ci struct sk_buff *skb; 173762306a36Sopenharmony_ci unsigned int size; 173862306a36Sopenharmony_ci struct sock *sk; 173962306a36Sopenharmony_ci int err; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci sk = net->ipv6.igmp_sk; 174262306a36Sopenharmony_ci /* we assume size > sizeof(ra) here 174362306a36Sopenharmony_ci * Also try to not allocate high-order pages for big MTU 174462306a36Sopenharmony_ci */ 174562306a36Sopenharmony_ci size = min_t(int, mtu, PAGE_SIZE / 2) + hlen + tlen; 174662306a36Sopenharmony_ci skb = sock_alloc_send_skb(sk, size, 1, &err); 174762306a36Sopenharmony_ci if (!skb) 174862306a36Sopenharmony_ci return NULL; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci skb->priority = TC_PRIO_CONTROL; 175162306a36Sopenharmony_ci skb_reserve(skb, hlen); 175262306a36Sopenharmony_ci skb_tailroom_reserve(skb, mtu, tlen); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) { 175562306a36Sopenharmony_ci /* <draft-ietf-magma-mld-source-05.txt>: 175662306a36Sopenharmony_ci * use unspecified address as the source address 175762306a36Sopenharmony_ci * when a valid link-local address is not available. 175862306a36Sopenharmony_ci */ 175962306a36Sopenharmony_ci saddr = &in6addr_any; 176062306a36Sopenharmony_ci } else 176162306a36Sopenharmony_ci saddr = &addr_buf; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci ip6_mc_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0); 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci skb_put_data(skb, ra, sizeof(ra)); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci skb_set_transport_header(skb, skb_tail_pointer(skb) - skb->data); 176862306a36Sopenharmony_ci skb_put(skb, sizeof(*pmr)); 176962306a36Sopenharmony_ci pmr = (struct mld2_report *)skb_transport_header(skb); 177062306a36Sopenharmony_ci pmr->mld2r_type = ICMPV6_MLD2_REPORT; 177162306a36Sopenharmony_ci pmr->mld2r_resv1 = 0; 177262306a36Sopenharmony_ci pmr->mld2r_cksum = 0; 177362306a36Sopenharmony_ci pmr->mld2r_resv2 = 0; 177462306a36Sopenharmony_ci pmr->mld2r_ngrec = 0; 177562306a36Sopenharmony_ci return skb; 177662306a36Sopenharmony_ci} 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_cistatic void mld_sendpack(struct sk_buff *skb) 177962306a36Sopenharmony_ci{ 178062306a36Sopenharmony_ci struct ipv6hdr *pip6 = ipv6_hdr(skb); 178162306a36Sopenharmony_ci struct mld2_report *pmr = 178262306a36Sopenharmony_ci (struct mld2_report *)skb_transport_header(skb); 178362306a36Sopenharmony_ci int payload_len, mldlen; 178462306a36Sopenharmony_ci struct inet6_dev *idev; 178562306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 178662306a36Sopenharmony_ci int err; 178762306a36Sopenharmony_ci struct flowi6 fl6; 178862306a36Sopenharmony_ci struct dst_entry *dst; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci rcu_read_lock(); 179162306a36Sopenharmony_ci idev = __in6_dev_get(skb->dev); 179262306a36Sopenharmony_ci IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci payload_len = (skb_tail_pointer(skb) - skb_network_header(skb)) - 179562306a36Sopenharmony_ci sizeof(*pip6); 179662306a36Sopenharmony_ci mldlen = skb_tail_pointer(skb) - skb_transport_header(skb); 179762306a36Sopenharmony_ci pip6->payload_len = htons(payload_len); 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci pmr->mld2r_cksum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, 180062306a36Sopenharmony_ci IPPROTO_ICMPV6, 180162306a36Sopenharmony_ci csum_partial(skb_transport_header(skb), 180262306a36Sopenharmony_ci mldlen, 0)); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci icmpv6_flow_init(net->ipv6.igmp_sk, &fl6, ICMPV6_MLD2_REPORT, 180562306a36Sopenharmony_ci &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 180662306a36Sopenharmony_ci skb->dev->ifindex); 180762306a36Sopenharmony_ci dst = icmp6_dst_alloc(skb->dev, &fl6); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci err = 0; 181062306a36Sopenharmony_ci if (IS_ERR(dst)) { 181162306a36Sopenharmony_ci err = PTR_ERR(dst); 181262306a36Sopenharmony_ci dst = NULL; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci skb_dst_set(skb, dst); 181562306a36Sopenharmony_ci if (err) 181662306a36Sopenharmony_ci goto err_out; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, 181962306a36Sopenharmony_ci net, net->ipv6.igmp_sk, skb, NULL, skb->dev, 182062306a36Sopenharmony_ci dst_output); 182162306a36Sopenharmony_ciout: 182262306a36Sopenharmony_ci if (!err) { 182362306a36Sopenharmony_ci ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT); 182462306a36Sopenharmony_ci ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); 182562306a36Sopenharmony_ci } else { 182662306a36Sopenharmony_ci IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); 182762306a36Sopenharmony_ci } 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci rcu_read_unlock(); 183062306a36Sopenharmony_ci return; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_cierr_out: 183362306a36Sopenharmony_ci kfree_skb(skb); 183462306a36Sopenharmony_ci goto out; 183562306a36Sopenharmony_ci} 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_cistatic int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel) 183862306a36Sopenharmony_ci{ 183962306a36Sopenharmony_ci return sizeof(struct mld2_grec) + 16 * mld_scount(pmc,type,gdel,sdel); 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_cistatic struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, 184362306a36Sopenharmony_ci int type, struct mld2_grec **ppgr, unsigned int mtu) 184462306a36Sopenharmony_ci{ 184562306a36Sopenharmony_ci struct mld2_report *pmr; 184662306a36Sopenharmony_ci struct mld2_grec *pgr; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci if (!skb) { 184962306a36Sopenharmony_ci skb = mld_newpack(pmc->idev, mtu); 185062306a36Sopenharmony_ci if (!skb) 185162306a36Sopenharmony_ci return NULL; 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci pgr = skb_put(skb, sizeof(struct mld2_grec)); 185462306a36Sopenharmony_ci pgr->grec_type = type; 185562306a36Sopenharmony_ci pgr->grec_auxwords = 0; 185662306a36Sopenharmony_ci pgr->grec_nsrcs = 0; 185762306a36Sopenharmony_ci pgr->grec_mca = pmc->mca_addr; /* structure copy */ 185862306a36Sopenharmony_ci pmr = (struct mld2_report *)skb_transport_header(skb); 185962306a36Sopenharmony_ci pmr->mld2r_ngrec = htons(ntohs(pmr->mld2r_ngrec)+1); 186062306a36Sopenharmony_ci *ppgr = pgr; 186162306a36Sopenharmony_ci return skb; 186262306a36Sopenharmony_ci} 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0) 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci/* called with mc_lock */ 186762306a36Sopenharmony_cistatic struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, 186862306a36Sopenharmony_ci int type, int gdeleted, int sdeleted, 186962306a36Sopenharmony_ci int crsend) 187062306a36Sopenharmony_ci{ 187162306a36Sopenharmony_ci struct ip6_sf_list *psf, *psf_prev, *psf_next; 187262306a36Sopenharmony_ci int scount, stotal, first, isquery, truncate; 187362306a36Sopenharmony_ci struct ip6_sf_list __rcu **psf_list; 187462306a36Sopenharmony_ci struct inet6_dev *idev = pmc->idev; 187562306a36Sopenharmony_ci struct net_device *dev = idev->dev; 187662306a36Sopenharmony_ci struct mld2_grec *pgr = NULL; 187762306a36Sopenharmony_ci struct mld2_report *pmr; 187862306a36Sopenharmony_ci unsigned int mtu; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci if (pmc->mca_flags & MAF_NOREPORT) 188162306a36Sopenharmony_ci return skb; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci mtu = READ_ONCE(dev->mtu); 188462306a36Sopenharmony_ci if (mtu < IPV6_MIN_MTU) 188562306a36Sopenharmony_ci return skb; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci isquery = type == MLD2_MODE_IS_INCLUDE || 188862306a36Sopenharmony_ci type == MLD2_MODE_IS_EXCLUDE; 188962306a36Sopenharmony_ci truncate = type == MLD2_MODE_IS_EXCLUDE || 189062306a36Sopenharmony_ci type == MLD2_CHANGE_TO_EXCLUDE; 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci stotal = scount = 0; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci psf_list = sdeleted ? &pmc->mca_tomb : &pmc->mca_sources; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci if (!rcu_access_pointer(*psf_list)) 189762306a36Sopenharmony_ci goto empty_source; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci pmr = skb ? (struct mld2_report *)skb_transport_header(skb) : NULL; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci /* EX and TO_EX get a fresh packet, if needed */ 190262306a36Sopenharmony_ci if (truncate) { 190362306a36Sopenharmony_ci if (pmr && pmr->mld2r_ngrec && 190462306a36Sopenharmony_ci AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { 190562306a36Sopenharmony_ci if (skb) 190662306a36Sopenharmony_ci mld_sendpack(skb); 190762306a36Sopenharmony_ci skb = mld_newpack(idev, mtu); 190862306a36Sopenharmony_ci } 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci first = 1; 191162306a36Sopenharmony_ci psf_prev = NULL; 191262306a36Sopenharmony_ci for (psf = mc_dereference(*psf_list, idev); 191362306a36Sopenharmony_ci psf; 191462306a36Sopenharmony_ci psf = psf_next) { 191562306a36Sopenharmony_ci struct in6_addr *psrc; 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci psf_next = mc_dereference(psf->sf_next, idev); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci if (!is_in(pmc, psf, type, gdeleted, sdeleted) && !crsend) { 192062306a36Sopenharmony_ci psf_prev = psf; 192162306a36Sopenharmony_ci continue; 192262306a36Sopenharmony_ci } 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci /* Based on RFC3810 6.1. Should not send source-list change 192562306a36Sopenharmony_ci * records when there is a filter mode change. 192662306a36Sopenharmony_ci */ 192762306a36Sopenharmony_ci if (((gdeleted && pmc->mca_sfmode == MCAST_EXCLUDE) || 192862306a36Sopenharmony_ci (!gdeleted && pmc->mca_crcount)) && 192962306a36Sopenharmony_ci (type == MLD2_ALLOW_NEW_SOURCES || 193062306a36Sopenharmony_ci type == MLD2_BLOCK_OLD_SOURCES) && psf->sf_crcount) 193162306a36Sopenharmony_ci goto decrease_sf_crcount; 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci /* clear marks on query responses */ 193462306a36Sopenharmony_ci if (isquery) 193562306a36Sopenharmony_ci psf->sf_gsresp = 0; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci if (AVAILABLE(skb) < sizeof(*psrc) + 193862306a36Sopenharmony_ci first*sizeof(struct mld2_grec)) { 193962306a36Sopenharmony_ci if (truncate && !first) 194062306a36Sopenharmony_ci break; /* truncate these */ 194162306a36Sopenharmony_ci if (pgr) 194262306a36Sopenharmony_ci pgr->grec_nsrcs = htons(scount); 194362306a36Sopenharmony_ci if (skb) 194462306a36Sopenharmony_ci mld_sendpack(skb); 194562306a36Sopenharmony_ci skb = mld_newpack(idev, mtu); 194662306a36Sopenharmony_ci first = 1; 194762306a36Sopenharmony_ci scount = 0; 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci if (first) { 195062306a36Sopenharmony_ci skb = add_grhead(skb, pmc, type, &pgr, mtu); 195162306a36Sopenharmony_ci first = 0; 195262306a36Sopenharmony_ci } 195362306a36Sopenharmony_ci if (!skb) 195462306a36Sopenharmony_ci return NULL; 195562306a36Sopenharmony_ci psrc = skb_put(skb, sizeof(*psrc)); 195662306a36Sopenharmony_ci *psrc = psf->sf_addr; 195762306a36Sopenharmony_ci scount++; stotal++; 195862306a36Sopenharmony_ci if ((type == MLD2_ALLOW_NEW_SOURCES || 195962306a36Sopenharmony_ci type == MLD2_BLOCK_OLD_SOURCES) && psf->sf_crcount) { 196062306a36Sopenharmony_cidecrease_sf_crcount: 196162306a36Sopenharmony_ci psf->sf_crcount--; 196262306a36Sopenharmony_ci if ((sdeleted || gdeleted) && psf->sf_crcount == 0) { 196362306a36Sopenharmony_ci if (psf_prev) 196462306a36Sopenharmony_ci rcu_assign_pointer(psf_prev->sf_next, 196562306a36Sopenharmony_ci mc_dereference(psf->sf_next, idev)); 196662306a36Sopenharmony_ci else 196762306a36Sopenharmony_ci rcu_assign_pointer(*psf_list, 196862306a36Sopenharmony_ci mc_dereference(psf->sf_next, idev)); 196962306a36Sopenharmony_ci kfree_rcu(psf, rcu); 197062306a36Sopenharmony_ci continue; 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci psf_prev = psf; 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ciempty_source: 197762306a36Sopenharmony_ci if (!stotal) { 197862306a36Sopenharmony_ci if (type == MLD2_ALLOW_NEW_SOURCES || 197962306a36Sopenharmony_ci type == MLD2_BLOCK_OLD_SOURCES) 198062306a36Sopenharmony_ci return skb; 198162306a36Sopenharmony_ci if (pmc->mca_crcount || isquery || crsend) { 198262306a36Sopenharmony_ci /* make sure we have room for group header */ 198362306a36Sopenharmony_ci if (skb && AVAILABLE(skb) < sizeof(struct mld2_grec)) { 198462306a36Sopenharmony_ci mld_sendpack(skb); 198562306a36Sopenharmony_ci skb = NULL; /* add_grhead will get a new one */ 198662306a36Sopenharmony_ci } 198762306a36Sopenharmony_ci skb = add_grhead(skb, pmc, type, &pgr, mtu); 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci } 199062306a36Sopenharmony_ci if (pgr) 199162306a36Sopenharmony_ci pgr->grec_nsrcs = htons(scount); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci if (isquery) 199462306a36Sopenharmony_ci pmc->mca_flags &= ~MAF_GSQUERY; /* clear query state */ 199562306a36Sopenharmony_ci return skb; 199662306a36Sopenharmony_ci} 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci/* called with mc_lock */ 199962306a36Sopenharmony_cistatic void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) 200062306a36Sopenharmony_ci{ 200162306a36Sopenharmony_ci struct sk_buff *skb = NULL; 200262306a36Sopenharmony_ci int type; 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci if (!pmc) { 200562306a36Sopenharmony_ci for_each_mc_mclock(idev, pmc) { 200662306a36Sopenharmony_ci if (pmc->mca_flags & MAF_NOREPORT) 200762306a36Sopenharmony_ci continue; 200862306a36Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) 200962306a36Sopenharmony_ci type = MLD2_MODE_IS_EXCLUDE; 201062306a36Sopenharmony_ci else 201162306a36Sopenharmony_ci type = MLD2_MODE_IS_INCLUDE; 201262306a36Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 0); 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci } else { 201562306a36Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) 201662306a36Sopenharmony_ci type = MLD2_MODE_IS_EXCLUDE; 201762306a36Sopenharmony_ci else 201862306a36Sopenharmony_ci type = MLD2_MODE_IS_INCLUDE; 201962306a36Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 0); 202062306a36Sopenharmony_ci } 202162306a36Sopenharmony_ci if (skb) 202262306a36Sopenharmony_ci mld_sendpack(skb); 202362306a36Sopenharmony_ci} 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci/* 202662306a36Sopenharmony_ci * remove zero-count source records from a source filter list 202762306a36Sopenharmony_ci * called with mc_lock 202862306a36Sopenharmony_ci */ 202962306a36Sopenharmony_cistatic void mld_clear_zeros(struct ip6_sf_list __rcu **ppsf, struct inet6_dev *idev) 203062306a36Sopenharmony_ci{ 203162306a36Sopenharmony_ci struct ip6_sf_list *psf_prev, *psf_next, *psf; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci psf_prev = NULL; 203462306a36Sopenharmony_ci for (psf = mc_dereference(*ppsf, idev); 203562306a36Sopenharmony_ci psf; 203662306a36Sopenharmony_ci psf = psf_next) { 203762306a36Sopenharmony_ci psf_next = mc_dereference(psf->sf_next, idev); 203862306a36Sopenharmony_ci if (psf->sf_crcount == 0) { 203962306a36Sopenharmony_ci if (psf_prev) 204062306a36Sopenharmony_ci rcu_assign_pointer(psf_prev->sf_next, 204162306a36Sopenharmony_ci mc_dereference(psf->sf_next, idev)); 204262306a36Sopenharmony_ci else 204362306a36Sopenharmony_ci rcu_assign_pointer(*ppsf, 204462306a36Sopenharmony_ci mc_dereference(psf->sf_next, idev)); 204562306a36Sopenharmony_ci kfree_rcu(psf, rcu); 204662306a36Sopenharmony_ci } else { 204762306a36Sopenharmony_ci psf_prev = psf; 204862306a36Sopenharmony_ci } 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci} 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci/* called with mc_lock */ 205362306a36Sopenharmony_cistatic void mld_send_cr(struct inet6_dev *idev) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci struct ifmcaddr6 *pmc, *pmc_prev, *pmc_next; 205662306a36Sopenharmony_ci struct sk_buff *skb = NULL; 205762306a36Sopenharmony_ci int type, dtype; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci /* deleted MCA's */ 206062306a36Sopenharmony_ci pmc_prev = NULL; 206162306a36Sopenharmony_ci for (pmc = mc_dereference(idev->mc_tomb, idev); 206262306a36Sopenharmony_ci pmc; 206362306a36Sopenharmony_ci pmc = pmc_next) { 206462306a36Sopenharmony_ci pmc_next = mc_dereference(pmc->next, idev); 206562306a36Sopenharmony_ci if (pmc->mca_sfmode == MCAST_INCLUDE) { 206662306a36Sopenharmony_ci type = MLD2_BLOCK_OLD_SOURCES; 206762306a36Sopenharmony_ci dtype = MLD2_BLOCK_OLD_SOURCES; 206862306a36Sopenharmony_ci skb = add_grec(skb, pmc, type, 1, 0, 0); 206962306a36Sopenharmony_ci skb = add_grec(skb, pmc, dtype, 1, 1, 0); 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci if (pmc->mca_crcount) { 207262306a36Sopenharmony_ci if (pmc->mca_sfmode == MCAST_EXCLUDE) { 207362306a36Sopenharmony_ci type = MLD2_CHANGE_TO_INCLUDE; 207462306a36Sopenharmony_ci skb = add_grec(skb, pmc, type, 1, 0, 0); 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci pmc->mca_crcount--; 207762306a36Sopenharmony_ci if (pmc->mca_crcount == 0) { 207862306a36Sopenharmony_ci mld_clear_zeros(&pmc->mca_tomb, idev); 207962306a36Sopenharmony_ci mld_clear_zeros(&pmc->mca_sources, idev); 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci if (pmc->mca_crcount == 0 && 208362306a36Sopenharmony_ci !rcu_access_pointer(pmc->mca_tomb) && 208462306a36Sopenharmony_ci !rcu_access_pointer(pmc->mca_sources)) { 208562306a36Sopenharmony_ci if (pmc_prev) 208662306a36Sopenharmony_ci rcu_assign_pointer(pmc_prev->next, pmc_next); 208762306a36Sopenharmony_ci else 208862306a36Sopenharmony_ci rcu_assign_pointer(idev->mc_tomb, pmc_next); 208962306a36Sopenharmony_ci in6_dev_put(pmc->idev); 209062306a36Sopenharmony_ci kfree_rcu(pmc, rcu); 209162306a36Sopenharmony_ci } else 209262306a36Sopenharmony_ci pmc_prev = pmc; 209362306a36Sopenharmony_ci } 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci /* change recs */ 209662306a36Sopenharmony_ci for_each_mc_mclock(idev, pmc) { 209762306a36Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) { 209862306a36Sopenharmony_ci type = MLD2_BLOCK_OLD_SOURCES; 209962306a36Sopenharmony_ci dtype = MLD2_ALLOW_NEW_SOURCES; 210062306a36Sopenharmony_ci } else { 210162306a36Sopenharmony_ci type = MLD2_ALLOW_NEW_SOURCES; 210262306a36Sopenharmony_ci dtype = MLD2_BLOCK_OLD_SOURCES; 210362306a36Sopenharmony_ci } 210462306a36Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 0); 210562306a36Sopenharmony_ci skb = add_grec(skb, pmc, dtype, 0, 1, 0); /* deleted sources */ 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci /* filter mode changes */ 210862306a36Sopenharmony_ci if (pmc->mca_crcount) { 210962306a36Sopenharmony_ci if (pmc->mca_sfmode == MCAST_EXCLUDE) 211062306a36Sopenharmony_ci type = MLD2_CHANGE_TO_EXCLUDE; 211162306a36Sopenharmony_ci else 211262306a36Sopenharmony_ci type = MLD2_CHANGE_TO_INCLUDE; 211362306a36Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 0); 211462306a36Sopenharmony_ci pmc->mca_crcount--; 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci if (!skb) 211862306a36Sopenharmony_ci return; 211962306a36Sopenharmony_ci (void) mld_sendpack(skb); 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_cistatic void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) 212362306a36Sopenharmony_ci{ 212462306a36Sopenharmony_ci struct net *net = dev_net(dev); 212562306a36Sopenharmony_ci struct sock *sk = net->ipv6.igmp_sk; 212662306a36Sopenharmony_ci struct inet6_dev *idev; 212762306a36Sopenharmony_ci struct sk_buff *skb; 212862306a36Sopenharmony_ci struct mld_msg *hdr; 212962306a36Sopenharmony_ci const struct in6_addr *snd_addr, *saddr; 213062306a36Sopenharmony_ci struct in6_addr addr_buf; 213162306a36Sopenharmony_ci int hlen = LL_RESERVED_SPACE(dev); 213262306a36Sopenharmony_ci int tlen = dev->needed_tailroom; 213362306a36Sopenharmony_ci int err, len, payload_len, full_len; 213462306a36Sopenharmony_ci u8 ra[8] = { IPPROTO_ICMPV6, 0, 213562306a36Sopenharmony_ci IPV6_TLV_ROUTERALERT, 2, 0, 0, 213662306a36Sopenharmony_ci IPV6_TLV_PADN, 0 }; 213762306a36Sopenharmony_ci struct flowi6 fl6; 213862306a36Sopenharmony_ci struct dst_entry *dst; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci if (type == ICMPV6_MGM_REDUCTION) 214162306a36Sopenharmony_ci snd_addr = &in6addr_linklocal_allrouters; 214262306a36Sopenharmony_ci else 214362306a36Sopenharmony_ci snd_addr = addr; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); 214662306a36Sopenharmony_ci payload_len = len + sizeof(ra); 214762306a36Sopenharmony_ci full_len = sizeof(struct ipv6hdr) + payload_len; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci rcu_read_lock(); 215062306a36Sopenharmony_ci IP6_UPD_PO_STATS(net, __in6_dev_get(dev), 215162306a36Sopenharmony_ci IPSTATS_MIB_OUT, full_len); 215262306a36Sopenharmony_ci rcu_read_unlock(); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci skb = sock_alloc_send_skb(sk, hlen + tlen + full_len, 1, &err); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci if (!skb) { 215762306a36Sopenharmony_ci rcu_read_lock(); 215862306a36Sopenharmony_ci IP6_INC_STATS(net, __in6_dev_get(dev), 215962306a36Sopenharmony_ci IPSTATS_MIB_OUTDISCARDS); 216062306a36Sopenharmony_ci rcu_read_unlock(); 216162306a36Sopenharmony_ci return; 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci skb->priority = TC_PRIO_CONTROL; 216462306a36Sopenharmony_ci skb_reserve(skb, hlen); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) { 216762306a36Sopenharmony_ci /* <draft-ietf-magma-mld-source-05.txt>: 216862306a36Sopenharmony_ci * use unspecified address as the source address 216962306a36Sopenharmony_ci * when a valid link-local address is not available. 217062306a36Sopenharmony_ci */ 217162306a36Sopenharmony_ci saddr = &in6addr_any; 217262306a36Sopenharmony_ci } else 217362306a36Sopenharmony_ci saddr = &addr_buf; 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci ip6_mc_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci skb_put_data(skb, ra, sizeof(ra)); 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci hdr = skb_put_zero(skb, sizeof(struct mld_msg)); 218062306a36Sopenharmony_ci hdr->mld_type = type; 218162306a36Sopenharmony_ci hdr->mld_mca = *addr; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci hdr->mld_cksum = csum_ipv6_magic(saddr, snd_addr, len, 218462306a36Sopenharmony_ci IPPROTO_ICMPV6, 218562306a36Sopenharmony_ci csum_partial(hdr, len, 0)); 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci rcu_read_lock(); 218862306a36Sopenharmony_ci idev = __in6_dev_get(skb->dev); 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci icmpv6_flow_init(sk, &fl6, type, 219162306a36Sopenharmony_ci &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 219262306a36Sopenharmony_ci skb->dev->ifindex); 219362306a36Sopenharmony_ci dst = icmp6_dst_alloc(skb->dev, &fl6); 219462306a36Sopenharmony_ci if (IS_ERR(dst)) { 219562306a36Sopenharmony_ci err = PTR_ERR(dst); 219662306a36Sopenharmony_ci goto err_out; 219762306a36Sopenharmony_ci } 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci skb_dst_set(skb, dst); 220062306a36Sopenharmony_ci err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, 220162306a36Sopenharmony_ci net, sk, skb, NULL, skb->dev, 220262306a36Sopenharmony_ci dst_output); 220362306a36Sopenharmony_ciout: 220462306a36Sopenharmony_ci if (!err) { 220562306a36Sopenharmony_ci ICMP6MSGOUT_INC_STATS(net, idev, type); 220662306a36Sopenharmony_ci ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); 220762306a36Sopenharmony_ci } else 220862306a36Sopenharmony_ci IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci rcu_read_unlock(); 221162306a36Sopenharmony_ci return; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_cierr_out: 221462306a36Sopenharmony_ci kfree_skb(skb); 221562306a36Sopenharmony_ci goto out; 221662306a36Sopenharmony_ci} 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci/* called with mc_lock */ 221962306a36Sopenharmony_cistatic void mld_send_initial_cr(struct inet6_dev *idev) 222062306a36Sopenharmony_ci{ 222162306a36Sopenharmony_ci struct sk_buff *skb; 222262306a36Sopenharmony_ci struct ifmcaddr6 *pmc; 222362306a36Sopenharmony_ci int type; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci if (mld_in_v1_mode(idev)) 222662306a36Sopenharmony_ci return; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci skb = NULL; 222962306a36Sopenharmony_ci for_each_mc_mclock(idev, pmc) { 223062306a36Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) 223162306a36Sopenharmony_ci type = MLD2_CHANGE_TO_EXCLUDE; 223262306a36Sopenharmony_ci else 223362306a36Sopenharmony_ci type = MLD2_ALLOW_NEW_SOURCES; 223462306a36Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0, 1); 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci if (skb) 223762306a36Sopenharmony_ci mld_sendpack(skb); 223862306a36Sopenharmony_ci} 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_civoid ipv6_mc_dad_complete(struct inet6_dev *idev) 224162306a36Sopenharmony_ci{ 224262306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 224362306a36Sopenharmony_ci idev->mc_dad_count = idev->mc_qrv; 224462306a36Sopenharmony_ci if (idev->mc_dad_count) { 224562306a36Sopenharmony_ci mld_send_initial_cr(idev); 224662306a36Sopenharmony_ci idev->mc_dad_count--; 224762306a36Sopenharmony_ci if (idev->mc_dad_count) 224862306a36Sopenharmony_ci mld_dad_start_work(idev, 224962306a36Sopenharmony_ci unsolicited_report_interval(idev)); 225062306a36Sopenharmony_ci } 225162306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 225262306a36Sopenharmony_ci} 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_cistatic void mld_dad_work(struct work_struct *work) 225562306a36Sopenharmony_ci{ 225662306a36Sopenharmony_ci struct inet6_dev *idev = container_of(to_delayed_work(work), 225762306a36Sopenharmony_ci struct inet6_dev, 225862306a36Sopenharmony_ci mc_dad_work); 225962306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 226062306a36Sopenharmony_ci mld_send_initial_cr(idev); 226162306a36Sopenharmony_ci if (idev->mc_dad_count) { 226262306a36Sopenharmony_ci idev->mc_dad_count--; 226362306a36Sopenharmony_ci if (idev->mc_dad_count) 226462306a36Sopenharmony_ci mld_dad_start_work(idev, 226562306a36Sopenharmony_ci unsolicited_report_interval(idev)); 226662306a36Sopenharmony_ci } 226762306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 226862306a36Sopenharmony_ci in6_dev_put(idev); 226962306a36Sopenharmony_ci} 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci/* called with mc_lock */ 227262306a36Sopenharmony_cistatic int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, 227362306a36Sopenharmony_ci const struct in6_addr *psfsrc) 227462306a36Sopenharmony_ci{ 227562306a36Sopenharmony_ci struct ip6_sf_list *psf, *psf_prev; 227662306a36Sopenharmony_ci int rv = 0; 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci psf_prev = NULL; 227962306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) { 228062306a36Sopenharmony_ci if (ipv6_addr_equal(&psf->sf_addr, psfsrc)) 228162306a36Sopenharmony_ci break; 228262306a36Sopenharmony_ci psf_prev = psf; 228362306a36Sopenharmony_ci } 228462306a36Sopenharmony_ci if (!psf || psf->sf_count[sfmode] == 0) { 228562306a36Sopenharmony_ci /* source filter not found, or count wrong => bug */ 228662306a36Sopenharmony_ci return -ESRCH; 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci psf->sf_count[sfmode]--; 228962306a36Sopenharmony_ci if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) { 229062306a36Sopenharmony_ci struct inet6_dev *idev = pmc->idev; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci /* no more filters for this source */ 229362306a36Sopenharmony_ci if (psf_prev) 229462306a36Sopenharmony_ci rcu_assign_pointer(psf_prev->sf_next, 229562306a36Sopenharmony_ci mc_dereference(psf->sf_next, idev)); 229662306a36Sopenharmony_ci else 229762306a36Sopenharmony_ci rcu_assign_pointer(pmc->mca_sources, 229862306a36Sopenharmony_ci mc_dereference(psf->sf_next, idev)); 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci if (psf->sf_oldin && !(pmc->mca_flags & MAF_NOREPORT) && 230162306a36Sopenharmony_ci !mld_in_v1_mode(idev)) { 230262306a36Sopenharmony_ci psf->sf_crcount = idev->mc_qrv; 230362306a36Sopenharmony_ci rcu_assign_pointer(psf->sf_next, 230462306a36Sopenharmony_ci mc_dereference(pmc->mca_tomb, idev)); 230562306a36Sopenharmony_ci rcu_assign_pointer(pmc->mca_tomb, psf); 230662306a36Sopenharmony_ci rv = 1; 230762306a36Sopenharmony_ci } else { 230862306a36Sopenharmony_ci kfree_rcu(psf, rcu); 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci } 231162306a36Sopenharmony_ci return rv; 231262306a36Sopenharmony_ci} 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci/* called with mc_lock */ 231562306a36Sopenharmony_cistatic int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca, 231662306a36Sopenharmony_ci int sfmode, int sfcount, const struct in6_addr *psfsrc, 231762306a36Sopenharmony_ci int delta) 231862306a36Sopenharmony_ci{ 231962306a36Sopenharmony_ci struct ifmcaddr6 *pmc; 232062306a36Sopenharmony_ci int changerec = 0; 232162306a36Sopenharmony_ci int i, err; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci if (!idev) 232462306a36Sopenharmony_ci return -ENODEV; 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci for_each_mc_mclock(idev, pmc) { 232762306a36Sopenharmony_ci if (ipv6_addr_equal(pmca, &pmc->mca_addr)) 232862306a36Sopenharmony_ci break; 232962306a36Sopenharmony_ci } 233062306a36Sopenharmony_ci if (!pmc) 233162306a36Sopenharmony_ci return -ESRCH; 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci sf_markstate(pmc); 233462306a36Sopenharmony_ci if (!delta) { 233562306a36Sopenharmony_ci if (!pmc->mca_sfcount[sfmode]) 233662306a36Sopenharmony_ci return -EINVAL; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci pmc->mca_sfcount[sfmode]--; 233962306a36Sopenharmony_ci } 234062306a36Sopenharmony_ci err = 0; 234162306a36Sopenharmony_ci for (i = 0; i < sfcount; i++) { 234262306a36Sopenharmony_ci int rv = ip6_mc_del1_src(pmc, sfmode, &psfsrc[i]); 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci changerec |= rv > 0; 234562306a36Sopenharmony_ci if (!err && rv < 0) 234662306a36Sopenharmony_ci err = rv; 234762306a36Sopenharmony_ci } 234862306a36Sopenharmony_ci if (pmc->mca_sfmode == MCAST_EXCLUDE && 234962306a36Sopenharmony_ci pmc->mca_sfcount[MCAST_EXCLUDE] == 0 && 235062306a36Sopenharmony_ci pmc->mca_sfcount[MCAST_INCLUDE]) { 235162306a36Sopenharmony_ci struct ip6_sf_list *psf; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci /* filter mode change */ 235462306a36Sopenharmony_ci pmc->mca_sfmode = MCAST_INCLUDE; 235562306a36Sopenharmony_ci pmc->mca_crcount = idev->mc_qrv; 235662306a36Sopenharmony_ci idev->mc_ifc_count = pmc->mca_crcount; 235762306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) 235862306a36Sopenharmony_ci psf->sf_crcount = 0; 235962306a36Sopenharmony_ci mld_ifc_event(pmc->idev); 236062306a36Sopenharmony_ci } else if (sf_setstate(pmc) || changerec) { 236162306a36Sopenharmony_ci mld_ifc_event(pmc->idev); 236262306a36Sopenharmony_ci } 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci return err; 236562306a36Sopenharmony_ci} 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci/* 236862306a36Sopenharmony_ci * Add multicast single-source filter to the interface list 236962306a36Sopenharmony_ci * called with mc_lock 237062306a36Sopenharmony_ci */ 237162306a36Sopenharmony_cistatic int ip6_mc_add1_src(struct ifmcaddr6 *pmc, int sfmode, 237262306a36Sopenharmony_ci const struct in6_addr *psfsrc) 237362306a36Sopenharmony_ci{ 237462306a36Sopenharmony_ci struct ip6_sf_list *psf, *psf_prev; 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci psf_prev = NULL; 237762306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) { 237862306a36Sopenharmony_ci if (ipv6_addr_equal(&psf->sf_addr, psfsrc)) 237962306a36Sopenharmony_ci break; 238062306a36Sopenharmony_ci psf_prev = psf; 238162306a36Sopenharmony_ci } 238262306a36Sopenharmony_ci if (!psf) { 238362306a36Sopenharmony_ci psf = kzalloc(sizeof(*psf), GFP_KERNEL); 238462306a36Sopenharmony_ci if (!psf) 238562306a36Sopenharmony_ci return -ENOBUFS; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci psf->sf_addr = *psfsrc; 238862306a36Sopenharmony_ci if (psf_prev) { 238962306a36Sopenharmony_ci rcu_assign_pointer(psf_prev->sf_next, psf); 239062306a36Sopenharmony_ci } else { 239162306a36Sopenharmony_ci rcu_assign_pointer(pmc->mca_sources, psf); 239262306a36Sopenharmony_ci } 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci psf->sf_count[sfmode]++; 239562306a36Sopenharmony_ci return 0; 239662306a36Sopenharmony_ci} 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci/* called with mc_lock */ 239962306a36Sopenharmony_cistatic void sf_markstate(struct ifmcaddr6 *pmc) 240062306a36Sopenharmony_ci{ 240162306a36Sopenharmony_ci struct ip6_sf_list *psf; 240262306a36Sopenharmony_ci int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE]; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) { 240562306a36Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) { 240662306a36Sopenharmony_ci psf->sf_oldin = mca_xcount == 240762306a36Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE] && 240862306a36Sopenharmony_ci !psf->sf_count[MCAST_INCLUDE]; 240962306a36Sopenharmony_ci } else { 241062306a36Sopenharmony_ci psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0; 241162306a36Sopenharmony_ci } 241262306a36Sopenharmony_ci } 241362306a36Sopenharmony_ci} 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci/* called with mc_lock */ 241662306a36Sopenharmony_cistatic int sf_setstate(struct ifmcaddr6 *pmc) 241762306a36Sopenharmony_ci{ 241862306a36Sopenharmony_ci struct ip6_sf_list *psf, *dpsf; 241962306a36Sopenharmony_ci int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE]; 242062306a36Sopenharmony_ci int qrv = pmc->idev->mc_qrv; 242162306a36Sopenharmony_ci int new_in, rv; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci rv = 0; 242462306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) { 242562306a36Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) { 242662306a36Sopenharmony_ci new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && 242762306a36Sopenharmony_ci !psf->sf_count[MCAST_INCLUDE]; 242862306a36Sopenharmony_ci } else 242962306a36Sopenharmony_ci new_in = psf->sf_count[MCAST_INCLUDE] != 0; 243062306a36Sopenharmony_ci if (new_in) { 243162306a36Sopenharmony_ci if (!psf->sf_oldin) { 243262306a36Sopenharmony_ci struct ip6_sf_list *prev = NULL; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci for_each_psf_tomb(pmc, dpsf) { 243562306a36Sopenharmony_ci if (ipv6_addr_equal(&dpsf->sf_addr, 243662306a36Sopenharmony_ci &psf->sf_addr)) 243762306a36Sopenharmony_ci break; 243862306a36Sopenharmony_ci prev = dpsf; 243962306a36Sopenharmony_ci } 244062306a36Sopenharmony_ci if (dpsf) { 244162306a36Sopenharmony_ci if (prev) 244262306a36Sopenharmony_ci rcu_assign_pointer(prev->sf_next, 244362306a36Sopenharmony_ci mc_dereference(dpsf->sf_next, 244462306a36Sopenharmony_ci pmc->idev)); 244562306a36Sopenharmony_ci else 244662306a36Sopenharmony_ci rcu_assign_pointer(pmc->mca_tomb, 244762306a36Sopenharmony_ci mc_dereference(dpsf->sf_next, 244862306a36Sopenharmony_ci pmc->idev)); 244962306a36Sopenharmony_ci kfree_rcu(dpsf, rcu); 245062306a36Sopenharmony_ci } 245162306a36Sopenharmony_ci psf->sf_crcount = qrv; 245262306a36Sopenharmony_ci rv++; 245362306a36Sopenharmony_ci } 245462306a36Sopenharmony_ci } else if (psf->sf_oldin) { 245562306a36Sopenharmony_ci psf->sf_crcount = 0; 245662306a36Sopenharmony_ci /* 245762306a36Sopenharmony_ci * add or update "delete" records if an active filter 245862306a36Sopenharmony_ci * is now inactive 245962306a36Sopenharmony_ci */ 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci for_each_psf_tomb(pmc, dpsf) 246262306a36Sopenharmony_ci if (ipv6_addr_equal(&dpsf->sf_addr, 246362306a36Sopenharmony_ci &psf->sf_addr)) 246462306a36Sopenharmony_ci break; 246562306a36Sopenharmony_ci if (!dpsf) { 246662306a36Sopenharmony_ci dpsf = kmalloc(sizeof(*dpsf), GFP_KERNEL); 246762306a36Sopenharmony_ci if (!dpsf) 246862306a36Sopenharmony_ci continue; 246962306a36Sopenharmony_ci *dpsf = *psf; 247062306a36Sopenharmony_ci rcu_assign_pointer(dpsf->sf_next, 247162306a36Sopenharmony_ci mc_dereference(pmc->mca_tomb, pmc->idev)); 247262306a36Sopenharmony_ci rcu_assign_pointer(pmc->mca_tomb, dpsf); 247362306a36Sopenharmony_ci } 247462306a36Sopenharmony_ci dpsf->sf_crcount = qrv; 247562306a36Sopenharmony_ci rv++; 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci } 247862306a36Sopenharmony_ci return rv; 247962306a36Sopenharmony_ci} 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci/* 248262306a36Sopenharmony_ci * Add multicast source filter list to the interface list 248362306a36Sopenharmony_ci * called with mc_lock 248462306a36Sopenharmony_ci */ 248562306a36Sopenharmony_cistatic int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca, 248662306a36Sopenharmony_ci int sfmode, int sfcount, const struct in6_addr *psfsrc, 248762306a36Sopenharmony_ci int delta) 248862306a36Sopenharmony_ci{ 248962306a36Sopenharmony_ci struct ifmcaddr6 *pmc; 249062306a36Sopenharmony_ci int isexclude; 249162306a36Sopenharmony_ci int i, err; 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci if (!idev) 249462306a36Sopenharmony_ci return -ENODEV; 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci for_each_mc_mclock(idev, pmc) { 249762306a36Sopenharmony_ci if (ipv6_addr_equal(pmca, &pmc->mca_addr)) 249862306a36Sopenharmony_ci break; 249962306a36Sopenharmony_ci } 250062306a36Sopenharmony_ci if (!pmc) 250162306a36Sopenharmony_ci return -ESRCH; 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci sf_markstate(pmc); 250462306a36Sopenharmony_ci isexclude = pmc->mca_sfmode == MCAST_EXCLUDE; 250562306a36Sopenharmony_ci if (!delta) 250662306a36Sopenharmony_ci pmc->mca_sfcount[sfmode]++; 250762306a36Sopenharmony_ci err = 0; 250862306a36Sopenharmony_ci for (i = 0; i < sfcount; i++) { 250962306a36Sopenharmony_ci err = ip6_mc_add1_src(pmc, sfmode, &psfsrc[i]); 251062306a36Sopenharmony_ci if (err) 251162306a36Sopenharmony_ci break; 251262306a36Sopenharmony_ci } 251362306a36Sopenharmony_ci if (err) { 251462306a36Sopenharmony_ci int j; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci if (!delta) 251762306a36Sopenharmony_ci pmc->mca_sfcount[sfmode]--; 251862306a36Sopenharmony_ci for (j = 0; j < i; j++) 251962306a36Sopenharmony_ci ip6_mc_del1_src(pmc, sfmode, &psfsrc[j]); 252062306a36Sopenharmony_ci } else if (isexclude != (pmc->mca_sfcount[MCAST_EXCLUDE] != 0)) { 252162306a36Sopenharmony_ci struct ip6_sf_list *psf; 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci /* filter mode change */ 252462306a36Sopenharmony_ci if (pmc->mca_sfcount[MCAST_EXCLUDE]) 252562306a36Sopenharmony_ci pmc->mca_sfmode = MCAST_EXCLUDE; 252662306a36Sopenharmony_ci else if (pmc->mca_sfcount[MCAST_INCLUDE]) 252762306a36Sopenharmony_ci pmc->mca_sfmode = MCAST_INCLUDE; 252862306a36Sopenharmony_ci /* else no filters; keep old mode for reports */ 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci pmc->mca_crcount = idev->mc_qrv; 253162306a36Sopenharmony_ci idev->mc_ifc_count = pmc->mca_crcount; 253262306a36Sopenharmony_ci for_each_psf_mclock(pmc, psf) 253362306a36Sopenharmony_ci psf->sf_crcount = 0; 253462306a36Sopenharmony_ci mld_ifc_event(idev); 253562306a36Sopenharmony_ci } else if (sf_setstate(pmc)) { 253662306a36Sopenharmony_ci mld_ifc_event(idev); 253762306a36Sopenharmony_ci } 253862306a36Sopenharmony_ci return err; 253962306a36Sopenharmony_ci} 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci/* called with mc_lock */ 254262306a36Sopenharmony_cistatic void ip6_mc_clear_src(struct ifmcaddr6 *pmc) 254362306a36Sopenharmony_ci{ 254462306a36Sopenharmony_ci struct ip6_sf_list *psf, *nextpsf; 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci for (psf = mc_dereference(pmc->mca_tomb, pmc->idev); 254762306a36Sopenharmony_ci psf; 254862306a36Sopenharmony_ci psf = nextpsf) { 254962306a36Sopenharmony_ci nextpsf = mc_dereference(psf->sf_next, pmc->idev); 255062306a36Sopenharmony_ci kfree_rcu(psf, rcu); 255162306a36Sopenharmony_ci } 255262306a36Sopenharmony_ci RCU_INIT_POINTER(pmc->mca_tomb, NULL); 255362306a36Sopenharmony_ci for (psf = mc_dereference(pmc->mca_sources, pmc->idev); 255462306a36Sopenharmony_ci psf; 255562306a36Sopenharmony_ci psf = nextpsf) { 255662306a36Sopenharmony_ci nextpsf = mc_dereference(psf->sf_next, pmc->idev); 255762306a36Sopenharmony_ci kfree_rcu(psf, rcu); 255862306a36Sopenharmony_ci } 255962306a36Sopenharmony_ci RCU_INIT_POINTER(pmc->mca_sources, NULL); 256062306a36Sopenharmony_ci pmc->mca_sfmode = MCAST_EXCLUDE; 256162306a36Sopenharmony_ci pmc->mca_sfcount[MCAST_INCLUDE] = 0; 256262306a36Sopenharmony_ci pmc->mca_sfcount[MCAST_EXCLUDE] = 1; 256362306a36Sopenharmony_ci} 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci/* called with mc_lock */ 256662306a36Sopenharmony_cistatic void igmp6_join_group(struct ifmcaddr6 *ma) 256762306a36Sopenharmony_ci{ 256862306a36Sopenharmony_ci unsigned long delay; 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci if (ma->mca_flags & MAF_NOREPORT) 257162306a36Sopenharmony_ci return; 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci delay = get_random_u32_below(unsolicited_report_interval(ma->idev)); 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci if (cancel_delayed_work(&ma->mca_work)) { 257862306a36Sopenharmony_ci refcount_dec(&ma->mca_refcnt); 257962306a36Sopenharmony_ci delay = ma->mca_work.timer.expires - jiffies; 258062306a36Sopenharmony_ci } 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci if (!mod_delayed_work(mld_wq, &ma->mca_work, delay)) 258362306a36Sopenharmony_ci refcount_inc(&ma->mca_refcnt); 258462306a36Sopenharmony_ci ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER; 258562306a36Sopenharmony_ci} 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_cistatic int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, 258862306a36Sopenharmony_ci struct inet6_dev *idev) 258962306a36Sopenharmony_ci{ 259062306a36Sopenharmony_ci struct ip6_sf_socklist *psl; 259162306a36Sopenharmony_ci int err; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci psl = sock_dereference(iml->sflist, sk); 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci if (idev) 259662306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci if (!psl) { 259962306a36Sopenharmony_ci /* any-source empty exclude case */ 260062306a36Sopenharmony_ci err = ip6_mc_del_src(idev, &iml->addr, iml->sfmode, 0, NULL, 0); 260162306a36Sopenharmony_ci } else { 260262306a36Sopenharmony_ci err = ip6_mc_del_src(idev, &iml->addr, iml->sfmode, 260362306a36Sopenharmony_ci psl->sl_count, psl->sl_addr, 0); 260462306a36Sopenharmony_ci RCU_INIT_POINTER(iml->sflist, NULL); 260562306a36Sopenharmony_ci atomic_sub(struct_size(psl, sl_addr, psl->sl_max), 260662306a36Sopenharmony_ci &sk->sk_omem_alloc); 260762306a36Sopenharmony_ci kfree_rcu(psl, rcu); 260862306a36Sopenharmony_ci } 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci if (idev) 261162306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci return err; 261462306a36Sopenharmony_ci} 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci/* called with mc_lock */ 261762306a36Sopenharmony_cistatic void igmp6_leave_group(struct ifmcaddr6 *ma) 261862306a36Sopenharmony_ci{ 261962306a36Sopenharmony_ci if (mld_in_v1_mode(ma->idev)) { 262062306a36Sopenharmony_ci if (ma->mca_flags & MAF_LAST_REPORTER) { 262162306a36Sopenharmony_ci igmp6_send(&ma->mca_addr, ma->idev->dev, 262262306a36Sopenharmony_ci ICMPV6_MGM_REDUCTION); 262362306a36Sopenharmony_ci } 262462306a36Sopenharmony_ci } else { 262562306a36Sopenharmony_ci mld_add_delrec(ma->idev, ma); 262662306a36Sopenharmony_ci mld_ifc_event(ma->idev); 262762306a36Sopenharmony_ci } 262862306a36Sopenharmony_ci} 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_cistatic void mld_gq_work(struct work_struct *work) 263162306a36Sopenharmony_ci{ 263262306a36Sopenharmony_ci struct inet6_dev *idev = container_of(to_delayed_work(work), 263362306a36Sopenharmony_ci struct inet6_dev, 263462306a36Sopenharmony_ci mc_gq_work); 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 263762306a36Sopenharmony_ci mld_send_report(idev, NULL); 263862306a36Sopenharmony_ci idev->mc_gq_running = 0; 263962306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci in6_dev_put(idev); 264262306a36Sopenharmony_ci} 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_cistatic void mld_ifc_work(struct work_struct *work) 264562306a36Sopenharmony_ci{ 264662306a36Sopenharmony_ci struct inet6_dev *idev = container_of(to_delayed_work(work), 264762306a36Sopenharmony_ci struct inet6_dev, 264862306a36Sopenharmony_ci mc_ifc_work); 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 265162306a36Sopenharmony_ci mld_send_cr(idev); 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci if (idev->mc_ifc_count) { 265462306a36Sopenharmony_ci idev->mc_ifc_count--; 265562306a36Sopenharmony_ci if (idev->mc_ifc_count) 265662306a36Sopenharmony_ci mld_ifc_start_work(idev, 265762306a36Sopenharmony_ci unsolicited_report_interval(idev)); 265862306a36Sopenharmony_ci } 265962306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 266062306a36Sopenharmony_ci in6_dev_put(idev); 266162306a36Sopenharmony_ci} 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci/* called with mc_lock */ 266462306a36Sopenharmony_cistatic void mld_ifc_event(struct inet6_dev *idev) 266562306a36Sopenharmony_ci{ 266662306a36Sopenharmony_ci if (mld_in_v1_mode(idev)) 266762306a36Sopenharmony_ci return; 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci idev->mc_ifc_count = idev->mc_qrv; 267062306a36Sopenharmony_ci mld_ifc_start_work(idev, 1); 267162306a36Sopenharmony_ci} 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_cistatic void mld_mca_work(struct work_struct *work) 267462306a36Sopenharmony_ci{ 267562306a36Sopenharmony_ci struct ifmcaddr6 *ma = container_of(to_delayed_work(work), 267662306a36Sopenharmony_ci struct ifmcaddr6, mca_work); 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci mutex_lock(&ma->idev->mc_lock); 267962306a36Sopenharmony_ci if (mld_in_v1_mode(ma->idev)) 268062306a36Sopenharmony_ci igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); 268162306a36Sopenharmony_ci else 268262306a36Sopenharmony_ci mld_send_report(ma->idev, ma); 268362306a36Sopenharmony_ci ma->mca_flags |= MAF_LAST_REPORTER; 268462306a36Sopenharmony_ci ma->mca_flags &= ~MAF_TIMER_RUNNING; 268562306a36Sopenharmony_ci mutex_unlock(&ma->idev->mc_lock); 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci ma_put(ma); 268862306a36Sopenharmony_ci} 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci/* Device changing type */ 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_civoid ipv6_mc_unmap(struct inet6_dev *idev) 269362306a36Sopenharmony_ci{ 269462306a36Sopenharmony_ci struct ifmcaddr6 *i; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci /* Install multicast list, except for all-nodes (already installed) */ 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 269962306a36Sopenharmony_ci for_each_mc_mclock(idev, i) 270062306a36Sopenharmony_ci igmp6_group_dropped(i); 270162306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 270262306a36Sopenharmony_ci} 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_civoid ipv6_mc_remap(struct inet6_dev *idev) 270562306a36Sopenharmony_ci{ 270662306a36Sopenharmony_ci ipv6_mc_up(idev); 270762306a36Sopenharmony_ci} 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci/* Device going down */ 271062306a36Sopenharmony_civoid ipv6_mc_down(struct inet6_dev *idev) 271162306a36Sopenharmony_ci{ 271262306a36Sopenharmony_ci struct ifmcaddr6 *i; 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 271562306a36Sopenharmony_ci /* Withdraw multicast list */ 271662306a36Sopenharmony_ci for_each_mc_mclock(idev, i) 271762306a36Sopenharmony_ci igmp6_group_dropped(i); 271862306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci /* Should stop work after group drop. or we will 272162306a36Sopenharmony_ci * start work again in mld_ifc_event() 272262306a36Sopenharmony_ci */ 272362306a36Sopenharmony_ci mld_query_stop_work(idev); 272462306a36Sopenharmony_ci mld_report_stop_work(idev); 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 272762306a36Sopenharmony_ci mld_ifc_stop_work(idev); 272862306a36Sopenharmony_ci mld_gq_stop_work(idev); 272962306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci mld_dad_stop_work(idev); 273262306a36Sopenharmony_ci} 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_cistatic void ipv6_mc_reset(struct inet6_dev *idev) 273562306a36Sopenharmony_ci{ 273662306a36Sopenharmony_ci idev->mc_qrv = sysctl_mld_qrv; 273762306a36Sopenharmony_ci idev->mc_qi = MLD_QI_DEFAULT; 273862306a36Sopenharmony_ci idev->mc_qri = MLD_QRI_DEFAULT; 273962306a36Sopenharmony_ci idev->mc_v1_seen = 0; 274062306a36Sopenharmony_ci idev->mc_maxdelay = unsolicited_report_interval(idev); 274162306a36Sopenharmony_ci} 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci/* Device going up */ 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_civoid ipv6_mc_up(struct inet6_dev *idev) 274662306a36Sopenharmony_ci{ 274762306a36Sopenharmony_ci struct ifmcaddr6 *i; 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci /* Install multicast list, except for all-nodes (already installed) */ 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci ipv6_mc_reset(idev); 275262306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 275362306a36Sopenharmony_ci for_each_mc_mclock(idev, i) { 275462306a36Sopenharmony_ci mld_del_delrec(idev, i); 275562306a36Sopenharmony_ci igmp6_group_added(i); 275662306a36Sopenharmony_ci } 275762306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 275862306a36Sopenharmony_ci} 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci/* IPv6 device initialization. */ 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_civoid ipv6_mc_init_dev(struct inet6_dev *idev) 276362306a36Sopenharmony_ci{ 276462306a36Sopenharmony_ci idev->mc_gq_running = 0; 276562306a36Sopenharmony_ci INIT_DELAYED_WORK(&idev->mc_gq_work, mld_gq_work); 276662306a36Sopenharmony_ci RCU_INIT_POINTER(idev->mc_tomb, NULL); 276762306a36Sopenharmony_ci idev->mc_ifc_count = 0; 276862306a36Sopenharmony_ci INIT_DELAYED_WORK(&idev->mc_ifc_work, mld_ifc_work); 276962306a36Sopenharmony_ci INIT_DELAYED_WORK(&idev->mc_dad_work, mld_dad_work); 277062306a36Sopenharmony_ci INIT_DELAYED_WORK(&idev->mc_query_work, mld_query_work); 277162306a36Sopenharmony_ci INIT_DELAYED_WORK(&idev->mc_report_work, mld_report_work); 277262306a36Sopenharmony_ci skb_queue_head_init(&idev->mc_query_queue); 277362306a36Sopenharmony_ci skb_queue_head_init(&idev->mc_report_queue); 277462306a36Sopenharmony_ci spin_lock_init(&idev->mc_query_lock); 277562306a36Sopenharmony_ci spin_lock_init(&idev->mc_report_lock); 277662306a36Sopenharmony_ci mutex_init(&idev->mc_lock); 277762306a36Sopenharmony_ci ipv6_mc_reset(idev); 277862306a36Sopenharmony_ci} 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci/* 278162306a36Sopenharmony_ci * Device is about to be destroyed: clean up. 278262306a36Sopenharmony_ci */ 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_civoid ipv6_mc_destroy_dev(struct inet6_dev *idev) 278562306a36Sopenharmony_ci{ 278662306a36Sopenharmony_ci struct ifmcaddr6 *i; 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci /* Deactivate works */ 278962306a36Sopenharmony_ci ipv6_mc_down(idev); 279062306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 279162306a36Sopenharmony_ci mld_clear_delrec(idev); 279262306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 279362306a36Sopenharmony_ci mld_clear_query(idev); 279462306a36Sopenharmony_ci mld_clear_report(idev); 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci /* Delete all-nodes address. */ 279762306a36Sopenharmony_ci /* We cannot call ipv6_dev_mc_dec() directly, our caller in 279862306a36Sopenharmony_ci * addrconf.c has NULL'd out dev->ip6_ptr so in6_dev_get() will 279962306a36Sopenharmony_ci * fail. 280062306a36Sopenharmony_ci */ 280162306a36Sopenharmony_ci __ipv6_dev_mc_dec(idev, &in6addr_linklocal_allnodes); 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci if (idev->cnf.forwarding) 280462306a36Sopenharmony_ci __ipv6_dev_mc_dec(idev, &in6addr_linklocal_allrouters); 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 280762306a36Sopenharmony_ci while ((i = mc_dereference(idev->mc_list, idev))) { 280862306a36Sopenharmony_ci rcu_assign_pointer(idev->mc_list, mc_dereference(i->next, idev)); 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci ip6_mc_clear_src(i); 281162306a36Sopenharmony_ci ma_put(i); 281262306a36Sopenharmony_ci } 281362306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 281462306a36Sopenharmony_ci} 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_cistatic void ipv6_mc_rejoin_groups(struct inet6_dev *idev) 281762306a36Sopenharmony_ci{ 281862306a36Sopenharmony_ci struct ifmcaddr6 *pmc; 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci ASSERT_RTNL(); 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci mutex_lock(&idev->mc_lock); 282362306a36Sopenharmony_ci if (mld_in_v1_mode(idev)) { 282462306a36Sopenharmony_ci for_each_mc_mclock(idev, pmc) 282562306a36Sopenharmony_ci igmp6_join_group(pmc); 282662306a36Sopenharmony_ci } else { 282762306a36Sopenharmony_ci mld_send_report(idev, NULL); 282862306a36Sopenharmony_ci } 282962306a36Sopenharmony_ci mutex_unlock(&idev->mc_lock); 283062306a36Sopenharmony_ci} 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_cistatic int ipv6_mc_netdev_event(struct notifier_block *this, 283362306a36Sopenharmony_ci unsigned long event, 283462306a36Sopenharmony_ci void *ptr) 283562306a36Sopenharmony_ci{ 283662306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 283762306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_ci switch (event) { 284062306a36Sopenharmony_ci case NETDEV_RESEND_IGMP: 284162306a36Sopenharmony_ci if (idev) 284262306a36Sopenharmony_ci ipv6_mc_rejoin_groups(idev); 284362306a36Sopenharmony_ci break; 284462306a36Sopenharmony_ci default: 284562306a36Sopenharmony_ci break; 284662306a36Sopenharmony_ci } 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci return NOTIFY_DONE; 284962306a36Sopenharmony_ci} 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_cistatic struct notifier_block igmp6_netdev_notifier = { 285262306a36Sopenharmony_ci .notifier_call = ipv6_mc_netdev_event, 285362306a36Sopenharmony_ci}; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 285662306a36Sopenharmony_cistruct igmp6_mc_iter_state { 285762306a36Sopenharmony_ci struct seq_net_private p; 285862306a36Sopenharmony_ci struct net_device *dev; 285962306a36Sopenharmony_ci struct inet6_dev *idev; 286062306a36Sopenharmony_ci}; 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci#define igmp6_mc_seq_private(seq) ((struct igmp6_mc_iter_state *)(seq)->private) 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_cistatic inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq) 286562306a36Sopenharmony_ci{ 286662306a36Sopenharmony_ci struct ifmcaddr6 *im = NULL; 286762306a36Sopenharmony_ci struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); 286862306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci state->idev = NULL; 287162306a36Sopenharmony_ci for_each_netdev_rcu(net, state->dev) { 287262306a36Sopenharmony_ci struct inet6_dev *idev; 287362306a36Sopenharmony_ci idev = __in6_dev_get(state->dev); 287462306a36Sopenharmony_ci if (!idev) 287562306a36Sopenharmony_ci continue; 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_ci im = rcu_dereference(idev->mc_list); 287862306a36Sopenharmony_ci if (im) { 287962306a36Sopenharmony_ci state->idev = idev; 288062306a36Sopenharmony_ci break; 288162306a36Sopenharmony_ci } 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci return im; 288462306a36Sopenharmony_ci} 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_cistatic struct ifmcaddr6 *igmp6_mc_get_next(struct seq_file *seq, struct ifmcaddr6 *im) 288762306a36Sopenharmony_ci{ 288862306a36Sopenharmony_ci struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci im = rcu_dereference(im->next); 289162306a36Sopenharmony_ci while (!im) { 289262306a36Sopenharmony_ci state->dev = next_net_device_rcu(state->dev); 289362306a36Sopenharmony_ci if (!state->dev) { 289462306a36Sopenharmony_ci state->idev = NULL; 289562306a36Sopenharmony_ci break; 289662306a36Sopenharmony_ci } 289762306a36Sopenharmony_ci state->idev = __in6_dev_get(state->dev); 289862306a36Sopenharmony_ci if (!state->idev) 289962306a36Sopenharmony_ci continue; 290062306a36Sopenharmony_ci im = rcu_dereference(state->idev->mc_list); 290162306a36Sopenharmony_ci } 290262306a36Sopenharmony_ci return im; 290362306a36Sopenharmony_ci} 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_cistatic struct ifmcaddr6 *igmp6_mc_get_idx(struct seq_file *seq, loff_t pos) 290662306a36Sopenharmony_ci{ 290762306a36Sopenharmony_ci struct ifmcaddr6 *im = igmp6_mc_get_first(seq); 290862306a36Sopenharmony_ci if (im) 290962306a36Sopenharmony_ci while (pos && (im = igmp6_mc_get_next(seq, im)) != NULL) 291062306a36Sopenharmony_ci --pos; 291162306a36Sopenharmony_ci return pos ? NULL : im; 291262306a36Sopenharmony_ci} 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_cistatic void *igmp6_mc_seq_start(struct seq_file *seq, loff_t *pos) 291562306a36Sopenharmony_ci __acquires(RCU) 291662306a36Sopenharmony_ci{ 291762306a36Sopenharmony_ci rcu_read_lock(); 291862306a36Sopenharmony_ci return igmp6_mc_get_idx(seq, *pos); 291962306a36Sopenharmony_ci} 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_cistatic void *igmp6_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 292262306a36Sopenharmony_ci{ 292362306a36Sopenharmony_ci struct ifmcaddr6 *im = igmp6_mc_get_next(seq, v); 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_ci ++*pos; 292662306a36Sopenharmony_ci return im; 292762306a36Sopenharmony_ci} 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_cistatic void igmp6_mc_seq_stop(struct seq_file *seq, void *v) 293062306a36Sopenharmony_ci __releases(RCU) 293162306a36Sopenharmony_ci{ 293262306a36Sopenharmony_ci struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_ci if (likely(state->idev)) 293562306a36Sopenharmony_ci state->idev = NULL; 293662306a36Sopenharmony_ci state->dev = NULL; 293762306a36Sopenharmony_ci rcu_read_unlock(); 293862306a36Sopenharmony_ci} 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_cistatic int igmp6_mc_seq_show(struct seq_file *seq, void *v) 294162306a36Sopenharmony_ci{ 294262306a36Sopenharmony_ci struct ifmcaddr6 *im = (struct ifmcaddr6 *)v; 294362306a36Sopenharmony_ci struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci seq_printf(seq, 294662306a36Sopenharmony_ci "%-4d %-15s %pi6 %5d %08X %ld\n", 294762306a36Sopenharmony_ci state->dev->ifindex, state->dev->name, 294862306a36Sopenharmony_ci &im->mca_addr, 294962306a36Sopenharmony_ci im->mca_users, im->mca_flags, 295062306a36Sopenharmony_ci (im->mca_flags & MAF_TIMER_RUNNING) ? 295162306a36Sopenharmony_ci jiffies_to_clock_t(im->mca_work.timer.expires - jiffies) : 0); 295262306a36Sopenharmony_ci return 0; 295362306a36Sopenharmony_ci} 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_cistatic const struct seq_operations igmp6_mc_seq_ops = { 295662306a36Sopenharmony_ci .start = igmp6_mc_seq_start, 295762306a36Sopenharmony_ci .next = igmp6_mc_seq_next, 295862306a36Sopenharmony_ci .stop = igmp6_mc_seq_stop, 295962306a36Sopenharmony_ci .show = igmp6_mc_seq_show, 296062306a36Sopenharmony_ci}; 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_cistruct igmp6_mcf_iter_state { 296362306a36Sopenharmony_ci struct seq_net_private p; 296462306a36Sopenharmony_ci struct net_device *dev; 296562306a36Sopenharmony_ci struct inet6_dev *idev; 296662306a36Sopenharmony_ci struct ifmcaddr6 *im; 296762306a36Sopenharmony_ci}; 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_ci#define igmp6_mcf_seq_private(seq) ((struct igmp6_mcf_iter_state *)(seq)->private) 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_cistatic inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq) 297262306a36Sopenharmony_ci{ 297362306a36Sopenharmony_ci struct ip6_sf_list *psf = NULL; 297462306a36Sopenharmony_ci struct ifmcaddr6 *im = NULL; 297562306a36Sopenharmony_ci struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); 297662306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci state->idev = NULL; 297962306a36Sopenharmony_ci state->im = NULL; 298062306a36Sopenharmony_ci for_each_netdev_rcu(net, state->dev) { 298162306a36Sopenharmony_ci struct inet6_dev *idev; 298262306a36Sopenharmony_ci idev = __in6_dev_get(state->dev); 298362306a36Sopenharmony_ci if (unlikely(idev == NULL)) 298462306a36Sopenharmony_ci continue; 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci im = rcu_dereference(idev->mc_list); 298762306a36Sopenharmony_ci if (likely(im)) { 298862306a36Sopenharmony_ci psf = rcu_dereference(im->mca_sources); 298962306a36Sopenharmony_ci if (likely(psf)) { 299062306a36Sopenharmony_ci state->im = im; 299162306a36Sopenharmony_ci state->idev = idev; 299262306a36Sopenharmony_ci break; 299362306a36Sopenharmony_ci } 299462306a36Sopenharmony_ci } 299562306a36Sopenharmony_ci } 299662306a36Sopenharmony_ci return psf; 299762306a36Sopenharmony_ci} 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_cistatic struct ip6_sf_list *igmp6_mcf_get_next(struct seq_file *seq, struct ip6_sf_list *psf) 300062306a36Sopenharmony_ci{ 300162306a36Sopenharmony_ci struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); 300262306a36Sopenharmony_ci 300362306a36Sopenharmony_ci psf = rcu_dereference(psf->sf_next); 300462306a36Sopenharmony_ci while (!psf) { 300562306a36Sopenharmony_ci state->im = rcu_dereference(state->im->next); 300662306a36Sopenharmony_ci while (!state->im) { 300762306a36Sopenharmony_ci state->dev = next_net_device_rcu(state->dev); 300862306a36Sopenharmony_ci if (!state->dev) { 300962306a36Sopenharmony_ci state->idev = NULL; 301062306a36Sopenharmony_ci goto out; 301162306a36Sopenharmony_ci } 301262306a36Sopenharmony_ci state->idev = __in6_dev_get(state->dev); 301362306a36Sopenharmony_ci if (!state->idev) 301462306a36Sopenharmony_ci continue; 301562306a36Sopenharmony_ci state->im = rcu_dereference(state->idev->mc_list); 301662306a36Sopenharmony_ci } 301762306a36Sopenharmony_ci if (!state->im) 301862306a36Sopenharmony_ci break; 301962306a36Sopenharmony_ci psf = rcu_dereference(state->im->mca_sources); 302062306a36Sopenharmony_ci } 302162306a36Sopenharmony_ciout: 302262306a36Sopenharmony_ci return psf; 302362306a36Sopenharmony_ci} 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_cistatic struct ip6_sf_list *igmp6_mcf_get_idx(struct seq_file *seq, loff_t pos) 302662306a36Sopenharmony_ci{ 302762306a36Sopenharmony_ci struct ip6_sf_list *psf = igmp6_mcf_get_first(seq); 302862306a36Sopenharmony_ci if (psf) 302962306a36Sopenharmony_ci while (pos && (psf = igmp6_mcf_get_next(seq, psf)) != NULL) 303062306a36Sopenharmony_ci --pos; 303162306a36Sopenharmony_ci return pos ? NULL : psf; 303262306a36Sopenharmony_ci} 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_cistatic void *igmp6_mcf_seq_start(struct seq_file *seq, loff_t *pos) 303562306a36Sopenharmony_ci __acquires(RCU) 303662306a36Sopenharmony_ci{ 303762306a36Sopenharmony_ci rcu_read_lock(); 303862306a36Sopenharmony_ci return *pos ? igmp6_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 303962306a36Sopenharmony_ci} 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_cistatic void *igmp6_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos) 304262306a36Sopenharmony_ci{ 304362306a36Sopenharmony_ci struct ip6_sf_list *psf; 304462306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 304562306a36Sopenharmony_ci psf = igmp6_mcf_get_first(seq); 304662306a36Sopenharmony_ci else 304762306a36Sopenharmony_ci psf = igmp6_mcf_get_next(seq, v); 304862306a36Sopenharmony_ci ++*pos; 304962306a36Sopenharmony_ci return psf; 305062306a36Sopenharmony_ci} 305162306a36Sopenharmony_ci 305262306a36Sopenharmony_cistatic void igmp6_mcf_seq_stop(struct seq_file *seq, void *v) 305362306a36Sopenharmony_ci __releases(RCU) 305462306a36Sopenharmony_ci{ 305562306a36Sopenharmony_ci struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); 305662306a36Sopenharmony_ci 305762306a36Sopenharmony_ci if (likely(state->im)) 305862306a36Sopenharmony_ci state->im = NULL; 305962306a36Sopenharmony_ci if (likely(state->idev)) 306062306a36Sopenharmony_ci state->idev = NULL; 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci state->dev = NULL; 306362306a36Sopenharmony_ci rcu_read_unlock(); 306462306a36Sopenharmony_ci} 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_cistatic int igmp6_mcf_seq_show(struct seq_file *seq, void *v) 306762306a36Sopenharmony_ci{ 306862306a36Sopenharmony_ci struct ip6_sf_list *psf = (struct ip6_sf_list *)v; 306962306a36Sopenharmony_ci struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) { 307262306a36Sopenharmony_ci seq_puts(seq, "Idx Device Multicast Address Source Address INC EXC\n"); 307362306a36Sopenharmony_ci } else { 307462306a36Sopenharmony_ci seq_printf(seq, 307562306a36Sopenharmony_ci "%3d %6.6s %pi6 %pi6 %6lu %6lu\n", 307662306a36Sopenharmony_ci state->dev->ifindex, state->dev->name, 307762306a36Sopenharmony_ci &state->im->mca_addr, 307862306a36Sopenharmony_ci &psf->sf_addr, 307962306a36Sopenharmony_ci psf->sf_count[MCAST_INCLUDE], 308062306a36Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]); 308162306a36Sopenharmony_ci } 308262306a36Sopenharmony_ci return 0; 308362306a36Sopenharmony_ci} 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_cistatic const struct seq_operations igmp6_mcf_seq_ops = { 308662306a36Sopenharmony_ci .start = igmp6_mcf_seq_start, 308762306a36Sopenharmony_ci .next = igmp6_mcf_seq_next, 308862306a36Sopenharmony_ci .stop = igmp6_mcf_seq_stop, 308962306a36Sopenharmony_ci .show = igmp6_mcf_seq_show, 309062306a36Sopenharmony_ci}; 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_cistatic int __net_init igmp6_proc_init(struct net *net) 309362306a36Sopenharmony_ci{ 309462306a36Sopenharmony_ci int err; 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci err = -ENOMEM; 309762306a36Sopenharmony_ci if (!proc_create_net("igmp6", 0444, net->proc_net, &igmp6_mc_seq_ops, 309862306a36Sopenharmony_ci sizeof(struct igmp6_mc_iter_state))) 309962306a36Sopenharmony_ci goto out; 310062306a36Sopenharmony_ci if (!proc_create_net("mcfilter6", 0444, net->proc_net, 310162306a36Sopenharmony_ci &igmp6_mcf_seq_ops, 310262306a36Sopenharmony_ci sizeof(struct igmp6_mcf_iter_state))) 310362306a36Sopenharmony_ci goto out_proc_net_igmp6; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci err = 0; 310662306a36Sopenharmony_ciout: 310762306a36Sopenharmony_ci return err; 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_ciout_proc_net_igmp6: 311062306a36Sopenharmony_ci remove_proc_entry("igmp6", net->proc_net); 311162306a36Sopenharmony_ci goto out; 311262306a36Sopenharmony_ci} 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_cistatic void __net_exit igmp6_proc_exit(struct net *net) 311562306a36Sopenharmony_ci{ 311662306a36Sopenharmony_ci remove_proc_entry("mcfilter6", net->proc_net); 311762306a36Sopenharmony_ci remove_proc_entry("igmp6", net->proc_net); 311862306a36Sopenharmony_ci} 311962306a36Sopenharmony_ci#else 312062306a36Sopenharmony_cistatic inline int igmp6_proc_init(struct net *net) 312162306a36Sopenharmony_ci{ 312262306a36Sopenharmony_ci return 0; 312362306a36Sopenharmony_ci} 312462306a36Sopenharmony_cistatic inline void igmp6_proc_exit(struct net *net) 312562306a36Sopenharmony_ci{ 312662306a36Sopenharmony_ci} 312762306a36Sopenharmony_ci#endif 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_cistatic int __net_init igmp6_net_init(struct net *net) 313062306a36Sopenharmony_ci{ 313162306a36Sopenharmony_ci int err; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci err = inet_ctl_sock_create(&net->ipv6.igmp_sk, PF_INET6, 313462306a36Sopenharmony_ci SOCK_RAW, IPPROTO_ICMPV6, net); 313562306a36Sopenharmony_ci if (err < 0) { 313662306a36Sopenharmony_ci pr_err("Failed to initialize the IGMP6 control socket (err %d)\n", 313762306a36Sopenharmony_ci err); 313862306a36Sopenharmony_ci goto out; 313962306a36Sopenharmony_ci } 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_ci inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1; 314262306a36Sopenharmony_ci net->ipv6.igmp_sk->sk_allocation = GFP_KERNEL; 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci err = inet_ctl_sock_create(&net->ipv6.mc_autojoin_sk, PF_INET6, 314562306a36Sopenharmony_ci SOCK_RAW, IPPROTO_ICMPV6, net); 314662306a36Sopenharmony_ci if (err < 0) { 314762306a36Sopenharmony_ci pr_err("Failed to initialize the IGMP6 autojoin socket (err %d)\n", 314862306a36Sopenharmony_ci err); 314962306a36Sopenharmony_ci goto out_sock_create; 315062306a36Sopenharmony_ci } 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci err = igmp6_proc_init(net); 315362306a36Sopenharmony_ci if (err) 315462306a36Sopenharmony_ci goto out_sock_create_autojoin; 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci return 0; 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_ciout_sock_create_autojoin: 315962306a36Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk); 316062306a36Sopenharmony_ciout_sock_create: 316162306a36Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.igmp_sk); 316262306a36Sopenharmony_ciout: 316362306a36Sopenharmony_ci return err; 316462306a36Sopenharmony_ci} 316562306a36Sopenharmony_ci 316662306a36Sopenharmony_cistatic void __net_exit igmp6_net_exit(struct net *net) 316762306a36Sopenharmony_ci{ 316862306a36Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.igmp_sk); 316962306a36Sopenharmony_ci inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk); 317062306a36Sopenharmony_ci igmp6_proc_exit(net); 317162306a36Sopenharmony_ci} 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_cistatic struct pernet_operations igmp6_net_ops = { 317462306a36Sopenharmony_ci .init = igmp6_net_init, 317562306a36Sopenharmony_ci .exit = igmp6_net_exit, 317662306a36Sopenharmony_ci}; 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ciint __init igmp6_init(void) 317962306a36Sopenharmony_ci{ 318062306a36Sopenharmony_ci int err; 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci err = register_pernet_subsys(&igmp6_net_ops); 318362306a36Sopenharmony_ci if (err) 318462306a36Sopenharmony_ci return err; 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_ci mld_wq = create_workqueue("mld"); 318762306a36Sopenharmony_ci if (!mld_wq) { 318862306a36Sopenharmony_ci unregister_pernet_subsys(&igmp6_net_ops); 318962306a36Sopenharmony_ci return -ENOMEM; 319062306a36Sopenharmony_ci } 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_ci return err; 319362306a36Sopenharmony_ci} 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ciint __init igmp6_late_init(void) 319662306a36Sopenharmony_ci{ 319762306a36Sopenharmony_ci return register_netdevice_notifier(&igmp6_netdev_notifier); 319862306a36Sopenharmony_ci} 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_civoid igmp6_cleanup(void) 320162306a36Sopenharmony_ci{ 320262306a36Sopenharmony_ci unregister_pernet_subsys(&igmp6_net_ops); 320362306a36Sopenharmony_ci destroy_workqueue(mld_wq); 320462306a36Sopenharmony_ci} 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_civoid igmp6_late_cleanup(void) 320762306a36Sopenharmony_ci{ 320862306a36Sopenharmony_ci unregister_netdevice_notifier(&igmp6_netdev_notifier); 320962306a36Sopenharmony_ci} 3210